TorrentFilter/main.go
lordwelch 3acdb54b62 Better error handling for transmission
Remove unnessesary removal of links
Reduce stdout spam
2018-01-11 15:54:03 -08:00

331 lines
7.8 KiB
Go

package main
import (
"bufio"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
"timmy.narnian.us/git/timmy/scene"
"github.com/alexflint/go-arg"
"github.com/lordwelch/transmission"
)
var (
CurrentTorrents map[string]SeriesTorrent
unselectedDir string
Transmission *transmission.Client
CurrentHashes []string
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"`
HOST string `arg:"-H,help:Host for transmission"`
}
)
func main() {
var (
stdC = make(chan *SceneVideoTorrent)
)
initialize()
go stdinLoop(stdC)
for i := 0; true; {
i++
fmt.Println(i)
select {
case <-time.After(time.Minute * 15):
download()
case current := <-stdC:
addtorrent(CurrentTorrents[current.Title], current)
}
}
}
func stdinLoop(C chan *SceneVideoTorrent) {
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
url := strings.TrimSpace(scanner.Text())
fmt.Println("url:", url)
torrentName := filepath.Base(url)
torrentPath := filepath.Join(unselectedDir, torrentName)
_, err := os.Stat(torrentPath)
if !os.IsNotExist(err) {
fmt.Println(err)
continue
}
cmd := exec.Command("wget", url, "-q", "-O", torrentPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
fmt.Println("url failed: ", url)
fmt.Println(err)
continue
}
if filepath.Ext(torrentPath) == ".torrent" {
current := process(torrentPath)
for _, title := range args.Series {
if current.Title == title {
C <- current
break
}
}
}
fmt.Println("Torrent:", torrentName)
}
}
// Get hashes of torrents that were previously selected then remove the link to them
func getLinks() (hash []string) {
// get files in selected dir
selectedDir := filepath.Join(unselectedDir, "../")
selectedFolder, _ := os.Open(selectedDir)
defer selectedFolder.Close()
selectedNames, _ := selectedFolder.Readdirnames(0)
// Add hashes of currently selected torrents
for _, lnk := range selectedNames {
target, err := os.Readlink(filepath.Join(selectedDir, lnk))
if err == nil {
if filepath.Base(filepath.Dir(target)) == "unselected" {
hash = append(hash, process(target).Meta.Hash)
}
}
}
selectedNames, _ = selectedFolder.Readdirnames(0)
fmt.Println(selectedNames)
return
}
func download() {
var (
run bool = true
err error
)
hash := getLinks()
if Transmission != nil {
stopDownloads(hash)
}
for _, s := range CurrentTorrents {
for _, se := range s {
for _, ep := range se {
var CurrentHash bool
for _, HASH := range hash {
CurrentHash = HASH == ep.Ep[0].Meta.Hash
}
if !CurrentHash {
fmt.Printf("Better file found for: %s S%sE%s\n", ep.Ep[0].Title, ep.Ep[0].Season, ep.Ep[0].Episode)
os.Remove(filepath.Join(filepath.Join(unselectedDir, "../"), filepath.Base(ep.Ep[0].Meta.FilePath)))
os.Symlink(ep.Ep[0].Meta.FilePath, filepath.Join(filepath.Join(unselectedDir, "../"), filepath.Base(ep.Ep[0].Meta.FilePath)))
}
}
}
}
if Transmission != nil {
time.Sleep(time.Second * 30)
tmap, _ := Transmission.GetTorrentMap()
if err != nil {
run = false
if timeoutErr, ok := err.(net.Error); ok && timeoutErr.Timeout() {
tmap, err = Transmission.GetTorrentMap()
if err != nil {
run = true
}
} else {
Transmission = nil
}
}
if run {
for _, s := range CurrentTorrents {
for _, se := range s {
for _, ep := range se {
v, ok := tmap[ep.Ep[0].Meta.Hash]
if ok {
v.Set(transmission.SetTorrentArg{
SeedRatioMode: 1,
SeedRatioLimit: 1.0,
})
}
}
}
}
}
}
}
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)
}
func stopDownloads(hash []string) {
var (
run bool = true
tmap transmission.TorrentMap
err error
)
if Transmission != nil {
tmap, err = Transmission.GetTorrentMap()
if err != nil {
run = false
if timeoutErr, ok := err.(net.Error); ok && timeoutErr.Timeout() {
tmap, err = Transmission.GetTorrentMap()
if err != nil {
run = true
}
} else {
Transmission = nil
}
}
if run {
thash := make([]*transmission.Torrent, 0, len(hash))
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)
}
}
}
// Removes torrents from transmission that are not selected this time
for _, torrent := range thash {
torrent.Stop()
}
}
}
}
func initialize() {
var (
err error
)
args.PATH, _ = os.Getwd()
args.RES = "-1"
args.HOST = "localhost"
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/"))
// Load all downloaded torrents
if !args.NEW {
unselectedFolder, _ := os.Open(unselectedDir)
defer unselectedFolder.Close()
unselectedNames, _ := unselectedFolder.Readdirnames(0)
sort.Strings(unselectedNames)
for _, name := range unselectedNames {
if filepath.Ext(name) == ".torrent" {
current := process(filepath.Join(unselectedDir, name))
for _, title := range args.Series {
if current.Title == title {
addtorrent(CurrentTorrents[title], current)
break
}
}
}
}
}
username := "lordwelch"
passwd := "hello"
cl := &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 10 * time.Second,
}).Dial,
},
Timeout: time.Second * 30,
}
req, err := http.NewRequest("GET", "http://"+args.HOST+":9091/transmission/rpc", nil)
req.SetBasicAuth(username, passwd)
resp, err := cl.Do(req)
if err != nil {
fmt.Println(err)
} else {
resp.Body.Close()
Transmission, _ = transmission.New(transmission.Config{
User: username,
Password: passwd,
Address: "http://" + args.HOST + ":9091/transmission/rpc",
HTTPClient: cl,
})
}
download()
}
func process(torrentFile string) *SceneVideoTorrent {
var (
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, filepath.Ext(vt.Name)))
//fmt.Println(vt.Original)
//fmt.Println(vt.Title)
return vt
}