diff --git a/.gitignore b/.gitignore index 1d13c17..a0f199d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ TorrentFilter +vendor diff --git a/main.go b/main.go index 00e9b6a..2a9fe8e 100644 --- a/main.go +++ b/main.go @@ -2,69 +2,253 @@ package main import ( "bufio" + "crypto/tls" "fmt" + "net/http" "os" "os/exec" - "path" "path/filepath" + "sort" + "strconv" "strings" + "sync" + "time" + + "timmy.narnian.us/git/timmy/scene" "github.com/alexflint/go-arg" + "github.com/lordwelch/transmission" ) var ( - current_torrents SeriesTorrent - unselectedDir string + CurrentTorrents map[string]SeriesTorrent + unselectedDir string + Transmission *transmission.Client + mutex = new(sync.Mutex) + + args struct { + RES string `arg:"help:Resolution preference [480/720/1080]"` + RELEASE []string `arg:"-r,help:Release group preference order."` + NRELEASE []string `arg:"-R,help:Release groups to use only as a lost resort."` + TAGS []string `arg:"-t,help:Tags to prefer -t internal would choose an internal over another. Whichever file with the most tags is chosen. Release Group takes priority"` + Series []string `arg:"required,positional,help:TV series to download torrent file for"` + NEW bool `arg:"-n,help:Only modify new torrents"` + PATH string `arg:"-P,required,help:Path to torrent files"` + } ) func main() { var ( - torrentName string - torrentPath string - args struct { - RES string `arg:"help:Resolution preference [480/720/1080]"` - RELEASE []string `arg:"-r,help:Release group preference order."` - TAGS []string `arg:"-t, help:Tags to prefer -t internal would choose an internal over another"` - Series []string `arg:"required,positional,help:TV series to download"` - NEW bool `arg:"-n,help:Only modify new torrents"` - PATH string `arg:"-P,help:Path to torrent files"` - } + stdC = make(chan *SceneVideoTorrent) ) + initialize() + go stdinLoop(stdC) - arg.MustParse(&args) - if len(args.PATH) < 1 { - args.PATH, _ = os.Getwd() + for { + select { + case <-time.After(time.Minute * 15): + download() + + case current := <-stdC: + mutex.Lock() + addtorrent(CurrentTorrents[current.Title], current) + mutex.Unlock() + } } - unselectedDir = filepath.Clean(args.PATH + "/unselected/") +} +func stdinLoop(C chan *SceneVideoTorrent) { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { url := strings.TrimSpace(scanner.Text()) - torrentName = filepath.Base(url) - torrentPath = filepath.Join(unselectedDir, torrentName) + torrentName := filepath.Base(url) + torrentPath := filepath.Join(unselectedDir, torrentName) + _, err := os.Stat(torrentPath) + if !os.IsNotExist(err) { + continue + } cmd := exec.Command("wget", url, "-q", "-O", torrentPath) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := cmd.Run() - fmt.Println() + + err = cmd.Run() + if err != nil { fmt.Println("url failed: ", url) fmt.Println(err) continue } - process(torrentPath) + current := process(torrentPath) + for _, title := range args.Series { + if current.Title == title { + C <- current + break + } + } } } +func download() { + mutex.Lock() + defer mutex.Unlock() + + hash := removeLinks() + removeDownloads(hash) + + for _, s := range CurrentTorrents { + for _, se := range s { + for _, ep := range se { + fmt.Println("symlink", ep.Ep[0].Title, ep.Ep[0].Season, ep.Ep[0].Episode) + os.Symlink(ep.Ep[0].Meta.FilePath, filepath.Join(filepath.Join(ep.Ep[0].Meta.FilePath, "../../"), filepath.Base(ep.Ep[0].Meta.FilePath))) + } + } + } + +} + +func addtorrent(St SeriesTorrent, torrent *SceneVideoTorrent) { + _, ok := St[torrent.Season] + if !ok { + St[torrent.Season] = make(SeasonTorrent, 20) + } + Ep := St[torrent.Season][torrent.Episode] + if Ep == nil { + RES, _ := strconv.Atoi(args.RES) + St[torrent.Season][torrent.Episode] = &EpisodeTorrent{ + Tags: make(map[string]int), + Release: make(map[string]int), + Res: scene.Res(RES), + } + + for i, v := range args.RELEASE { + if i+1 == 0 { + panic("You do not exist in a world that I know of") + } + St[torrent.Season][torrent.Episode].Release[v] = i + 1 + } + for i, v := range args.NRELEASE { + if i+1 == 0 { + panic("You do not exist in a world that I know of") + } + St[torrent.Season][torrent.Episode].Release[v] = i + 1000000 + } + for i, v := range args.TAGS { + if i+1 == 0 { + panic("You do not exist in a world that I know of") + } + St[torrent.Season][torrent.Episode].Tags[v] = i + 1 + } + } + St[torrent.Season][torrent.Episode].Add(torrent) +} + +// Get hashes of torrents that were previously selected then remove the link to them +func removeLinks() (hash []string) { + selectedDir := filepath.Join(unselectedDir, "../") + selectedFolder, _ := os.Open(selectedDir) + //fmt.Println("selected dir", selectedDir) + defer selectedFolder.Close() + + selectedNames, _ := selectedFolder.Readdirnames(0) + for _, lnk := range selectedNames { + target, err := os.Readlink(filepath.Join(selectedDir, lnk)) + //fmt.Println(target) + //fmt.Println(err) + + if err == nil { + if filepath.Base(filepath.Dir(target)) == "unselected" { + hash = append(hash, process(target).Meta.Hash) + + os.Remove(filepath.Join(selectedDir, lnk)) + } + } + } + selectedNames, _ = selectedFolder.Readdirnames(0) + fmt.Println(selectedNames) + return +} + +func removeDownloads(hash []string) { + tmap, err := Transmission.GetTorrentMap() + if err != nil { + panic(err) + } + thash := make([]string, len(hash)) + // Removes torrents from transmission that are not selected this time + for _, CHash := range hash { + v, ok := tmap[CHash] + if ok { + current := scene.Parse(v.Name) + if CurrentTorrents[current.Title][current.Season][current.Episode].Ep[0].Meta.Hash != CHash { + thash = append(thash, v) + } + } + } + Transmission.RemoveTorrents(false, thash...) +} + +func initialize() { + var ( + err error + ) + + args.PATH, _ = os.Getwd() + args.RES = "-1" + arg.MustParse(&args) + + CurrentTorrents = make(map[string]SeriesTorrent, len(args.Series)) + for _, title := range args.Series { + fmt.Println(title) + CurrentTorrents[title] = make(SeriesTorrent, 10) + } + + unselectedDir, _ = filepath.Abs(filepath.Join(args.PATH, "unselected/")) + + //fmt.Println("unselected dir:", unselectedDir) + // Load all downloaded torrents + unselectedFolder, _ := os.Open(unselectedDir) + defer unselectedFolder.Close() + unselectedNames, _ := unselectedFolder.Readdirnames(0) + sort.Strings(unselectedNames) + for _, name := range unselectedNames { + current := process(filepath.Join(unselectedDir, name)) + for _, title := range args.Series { + if current.Title == title { + addtorrent(CurrentTorrents[title], current) + break + } + } + } + + Transmission, err = transmission.New(transmission.Config{ + Address: "http://timmy:9091/transmission/rpc", + HTTPClient: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + }, + }, + }, + }) + if err != nil { + panic(err) + } + + download() +} + func process(torrentFile string) *SceneVideoTorrent { var ( - mt *MetaTorrent = new(MetaTorrent) - vt *SceneVideoTorrent = new(SceneVideoTorrent) + mt = new(MetaTorrent) + vt = new(SceneVideoTorrent) ) + f, _ := os.OpenFile(torrentFile, os.O_RDONLY, 755) + defer f.Close() mt.ReadFile(f) vt.Torrent = NewTorrent(*mt) - vt.Parse(strings.TrimSuffix(vt.Name, path.Ext(vt.Name))) - fmt.Printf("%v\n", *vt) + vt.Parse(strings.TrimSuffix(vt.Name, filepath.Ext(vt.Name))) + //fmt.Println(vt.Original) + fmt.Println(vt.Title) return vt } diff --git a/torrentFiles/The.Librarians.US.S02E09.720p.HDTV.x264-0SEC[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S02E09.720p.HDTV.x264-0SEC[eztv].mkv.torrent deleted file mode 100644 index 4dc0dc0..0000000 Binary files a/torrentFiles/The.Librarians.US.S02E09.720p.HDTV.x264-0SEC[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S02E09.HDTV.x264-KILLERS[eztv].mp4.torrent b/torrentFiles/The.Librarians.US.S02E09.HDTV.x264-KILLERS[eztv].mp4.torrent deleted file mode 100644 index 7873c7e..0000000 Binary files a/torrentFiles/The.Librarians.US.S02E09.HDTV.x264-KILLERS[eztv].mp4.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S02E10.720p.HDTV.x264-KILLERS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S02E10.720p.HDTV.x264-KILLERS[eztv].mkv.torrent deleted file mode 100644 index 4b40dd6..0000000 Binary files a/torrentFiles/The.Librarians.US.S02E10.720p.HDTV.x264-KILLERS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S02E10.HDTV.x264-KILLERS[eztv].mp4.torrent b/torrentFiles/The.Librarians.US.S02E10.HDTV.x264-KILLERS[eztv].mp4.torrent deleted file mode 100644 index 338d61d..0000000 Binary files a/torrentFiles/The.Librarians.US.S02E10.HDTV.x264-KILLERS[eztv].mp4.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E01.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E01.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index efc5187..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E01.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E01.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E01.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index 01b813a..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E01.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E02.720p.HDTV.x264-BRISK[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E02.720p.HDTV.x264-BRISK[eztv].mkv.torrent deleted file mode 100644 index 5b58f88..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E02.720p.HDTV.x264-BRISK[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E02.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E02.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index ee72207..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E02.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E04.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E04.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 6b06281..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E04.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E04.HDTV.x264-BRISK[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E04.HDTV.x264-BRISK[eztv].mkv.torrent deleted file mode 100644 index 8b0e8da..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E04.HDTV.x264-BRISK[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E05.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E05.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 4e75503..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E05.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E05.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E05.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index 02c9669..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E05.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E06.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E06.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 1565ffc..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E06.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E06.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E06.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index 667f4d7..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E06.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E07.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E07.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 3657afe..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E07.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E07.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E07.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index aecde79..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E07.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E08.720p.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E08.720p.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index ed9da3b..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E08.720p.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E08.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E08.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index 0e83f67..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E08.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E09.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E09.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 2214e9a..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E09.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E09.HDTV.x264-KILLERS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E09.HDTV.x264-KILLERS[eztv].mkv.torrent deleted file mode 100644 index d003a86..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E09.HDTV.x264-KILLERS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E10.720p.HDTV.x264-AVS[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E10.720p.HDTV.x264-AVS[eztv].mkv.torrent deleted file mode 100644 index 3355326..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E10.720p.HDTV.x264-AVS[eztv].mkv.torrent and /dev/null differ diff --git a/torrentFiles/The.Librarians.US.S03E10.HDTV.x264-FLEET[eztv].mkv.torrent b/torrentFiles/The.Librarians.US.S03E10.HDTV.x264-FLEET[eztv].mkv.torrent deleted file mode 100644 index 6d2e1a2..0000000 Binary files a/torrentFiles/The.Librarians.US.S03E10.HDTV.x264-FLEET[eztv].mkv.torrent and /dev/null differ diff --git a/type.go b/type.go index dfe68dd..44f7e07 100644 --- a/type.go +++ b/type.go @@ -1,27 +1,34 @@ package main import ( + "crypto/sha1" "fmt" - "io" + "os" + "path/filepath" + "sort" + + "timmy.narnian.us/git/timmy/scene" - "github.com/lordwelch/SceneParse" "github.com/zeebo/bencode" ) type MetaTorrent struct { - Path string + Hash string `bencode:"-"` + FilePath string `bencode:"-"` + Path string `bencode:"path"` Announce string `bencode:"announce"` - Announcelist [][]string `bencode:"announce-list"` - Comment string `bencode:"comment"` - CreatedBy string `bencode:"created by"` + Announcelist [][]string `bencode:"announce-list,omitempty"` + Comment string `bencode:"comment,omitempty"` + CreatedBy string `bencode:"created by,omitempty"` Info struct { - Name string `bencode:"name"` - Piece_length int64 `bencode:"piece length"` - Length int64 `bencode:"length"` - Files []struct { + Name string `bencode:"name"` + PieceLength int64 `bencode:"piece length"` + Length int64 `bencode:"length,omitempty"` + Pieces string `bencode:"pieces"` + Files []struct { Length int64 `bencode:"length"` Path []string `bencode:"path"` - } `bencode:"files"` + } `bencode:"files,omitempty"` } `bencode:"info"` } @@ -35,22 +42,44 @@ type Torrent struct { type SceneVideoTorrent struct { Torrent - Scene.Scene + scene.Scene } type EpisodeTorrent struct { - Episode []SceneVideoTorrent - Release string + Ep []*SceneVideoTorrent + Tags map[string]int + Res scene.Res + Release map[string]int } +type SeasonTorrent map[string]*EpisodeTorrent +type SeriesTorrent map[string]SeasonTorrent -type SeriesTorrent []EpisodeTorrent +func OrderedBy(fns ...func(int, int) bool) func(int, int) bool { + return func(i, j int) bool { + // Try all but the last comparison. + for _, less := range fns { + switch { + case less(i, j): + // i < j, so we have a decision. + return true + case less(j, i): + // i > j, so we have a decision. + return false + } + // i == j; try the next comparison. + } + // All comparisons to here said "equal", so just return whatever + // the final comparison reports. + return fns[len(fns)-1](i, j) + } +} func NewTorrent(mt MetaTorrent) (T Torrent) { if mt.Info.Length == 0 { - for i, path := range mt.Info.Files { + for _, path := range mt.Info.Files { for _, file := range path.Path { if file[len(file)-3:] == "mkv" || file[len(file)-3:] == "mp4" { - T.Size = mt.Info.Files[i].Length + T.Size = path.Length T.Name = file } } @@ -65,14 +94,113 @@ func NewTorrent(mt MetaTorrent) (T Torrent) { return T } -func (Mt *MetaTorrent) ReadFile(r io.Reader) error { - return bencode.NewDecoder(r).Decode(Mt) +func (Mt *MetaTorrent) ReadFile(r *os.File) error { + err := bencode.NewDecoder(r).Decode(Mt) + if err != nil { + return err + } + str, _ := bencode.EncodeString(Mt.Info) + Mt.Hash = fmt.Sprintf("%x", sha1.Sum([]byte(str))) + Mt.FilePath, err = filepath.Abs(r.Name()) + if err != nil { + return err + } + + return nil } func (Vt SceneVideoTorrent) String() string { - return fmt.Sprint(Vt.Scene) + return Vt.Torrent.Meta.FilePath } -func (s SeriesTorrent) Title() string { - return s[0].Episode[0].Title +func (Et *EpisodeTorrent) ByRelease(i, j int) bool { + var ( + ii int + ij int + ret bool + ) + ii = Et.Release[Et.Ep[i].Release] + ij = Et.Release[Et.Ep[j].Release] + if ii == 0 { + ii = 999999 + } + if ij == 0 { + ij = 999999 + } + if ii == ij { + ret = Et.Ep[i].Release > Et.Ep[j].Release + //fmt.Println(Et.Ep[i].Release, ">", Et.Ep[j].Release, "=", ret, Et) + } else { + ret = ii < ij + } + return ret +} + +func (Et *EpisodeTorrent) ByTag(i, j int) bool { + var ( + ii int + ij int + ret bool + ) + for k := range Et.Ep[i].Tags { + if Et.Tags[k] > 0 { + ii++ + } + } + for k := range Et.Ep[j].Tags { + if Et.Tags[k] > 0 { + ij++ + } + } + + if ii == ij { + ret = len(Et.Ep[i].Tags) < len(Et.Ep[j].Tags) + //fmt.Println(len(Et.Ep[i].Tags), "<", len(Et.Ep[j].Tags), "=", ret) + } else { + ret = ii > ij + } + + return ret +} + +func (Et *EpisodeTorrent) ByRes(i, j int) bool { + var ret bool + ret = Et.Ep[i].Resolution > Et.Ep[j].Resolution + //fmt.Println(Et.Ep[i].Resolution, ">", Et.Ep[j].Resolution, "=", ret) + + if Et.Res == Et.Ep[i].Resolution && Et.Ep[i].Resolution != Et.Ep[j].Resolution { + ret = true + } + return ret +} + +func (Et *EpisodeTorrent) Len() int { + return len(Et.Ep) +} + +func (Et *EpisodeTorrent) Swap(i, j int) { + Et.Ep[i], Et.Ep[j] = Et.Ep[j], Et.Ep[i] + //fmt.Println(Et.Ep) +} + +func (Et *EpisodeTorrent) Less(i, j int) bool { + return OrderedBy(Et.ByRelease, Et.ByRes, Et.ByTag)(i, j) +} + +func (Et *EpisodeTorrent) Add(Vt *SceneVideoTorrent) { + Et.Ep = append(Et.Ep, Vt) + sort.Stable(Et) +} + +func (St SeriesTorrent) SearchHash(hash string) *SceneVideoTorrent { + for _, v := range St { + for _, v2 := range v { + for _, v3 := range v2.Ep { + if v3.Meta.Hash == hash { + return v3 + } + } + } + } + return &SceneVideoTorrent{} }