initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.env
|
23
anime_episode_cache.go
Normal file
23
anime_episode_cache.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/anilist"
|
||||
)
|
||||
|
||||
var AnimeEpisodeCache = map[anilist.MediaID]map[int]model.AnimeEpisode{}
|
||||
|
||||
func GetAnimeEpisode(anime *anilist.Media, episode int) model.AnimeEpisode {
|
||||
if _, ok := AnimeEpisodeCache[anime.ID]; !ok {
|
||||
AnimeEpisodeCache[anime.ID] = map[int]model.AnimeEpisode{}
|
||||
}
|
||||
|
||||
if _, ok := AnimeEpisodeCache[anime.ID][episode]; !ok {
|
||||
AnimeEpisodeCache[anime.ID][episode] = model.AnimeEpisode{
|
||||
Anime: anime,
|
||||
Episode: episode,
|
||||
}
|
||||
}
|
||||
|
||||
return AnimeEpisodeCache[anime.ID][episode]
|
||||
}
|
93
check_torrents.go
Normal file
93
check_torrents.go
Normal file
@ -0,0 +1,93 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/adverr/v2"
|
||||
"git.tordarus.net/tordarus/slices"
|
||||
)
|
||||
|
||||
func CheckTorrents() {
|
||||
log.Println("checking torrents")
|
||||
start := time.Now()
|
||||
|
||||
torrents, err := GetTorrents()
|
||||
if err != nil {
|
||||
fmt.Println(adverr.Wrap("retrieving torrents failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
animeList, err := logic.GetAnimeListByAnimeID(logic.AnimeStatuses)
|
||||
if err != nil {
|
||||
fmt.Println(adverr.Wrap("retrieving anime list failed", err))
|
||||
return
|
||||
}
|
||||
|
||||
// parse torrents
|
||||
parsedTorrentsByAnimeEp := ParseTorrentsByAnimeEp(torrents)
|
||||
|
||||
// filter not on anime list
|
||||
animeListTorrents := FilterTorrentsByAnimeList(parsedTorrentsByAnimeEp, animeList)
|
||||
|
||||
// filter essential properties
|
||||
essentialTorrents := FilterEssentialTorrents(animeListTorrents)
|
||||
|
||||
// filter preferred properties
|
||||
preferredTorrents := GetTorrentsWithMaxPrioByAnimeEp(essentialTorrents)
|
||||
|
||||
// information gathered for logging purposes
|
||||
downloadingEpisodes := map[model.AnimeEpisode]struct{}{}
|
||||
inCollectionEpisodes := map[model.AnimeEpisode]bool{}
|
||||
collectionPreferredTorrents := map[*model.ParsedTorrent]bool{}
|
||||
downloadedTorrents := map[model.AnimeEpisode]*TorrentPriority{}
|
||||
|
||||
for animeEp, torrentPrio := range preferredTorrents {
|
||||
if IsCurrentlyDownloading(animeEp) {
|
||||
downloadingEpisodes[animeEp] = struct{}{} // debug output
|
||||
continue
|
||||
}
|
||||
|
||||
props, inCollection := logic.GetAnimeEpProps(animeEp)
|
||||
|
||||
inCollectionEpisodes[animeEp] = inCollection // debug output
|
||||
if inCollection {
|
||||
collectionPreferred := props.Priority >= torrentPrio.Priority
|
||||
collectionPreferredTorrents[torrentPrio.ParsedTorrent] = collectionPreferred // debug output
|
||||
if collectionPreferred {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := DownloadTorrent(animeEp, torrentPrio.ParsedTorrent); err != nil {
|
||||
fmt.Println(fmt.Sprintf("could not download torrent %s", torrentPrio.ParsedTorrent.Torrent.ID), err)
|
||||
}
|
||||
|
||||
downloadedTorrents[animeEp] = torrentPrio // debug output
|
||||
}
|
||||
|
||||
duration := time.Since(start)
|
||||
|
||||
ShowDebugInfo(
|
||||
parsedTorrentsByAnimeEp,
|
||||
animeListTorrents,
|
||||
essentialTorrents,
|
||||
preferredTorrents,
|
||||
downloadingEpisodes,
|
||||
inCollectionEpisodes,
|
||||
collectionPreferredTorrents,
|
||||
downloadedTorrents,
|
||||
)
|
||||
|
||||
downloadedAnimeEpisodes := slices.OfMap(downloadedTorrents, func(animeEp model.AnimeEpisode, torrentPrio *TorrentPriority) model.AnimeEpisode {
|
||||
return animeEp
|
||||
})
|
||||
sort.Slice(downloadedAnimeEpisodes, GetAnimeEpisodesSortFunc(downloadedAnimeEpisodes))
|
||||
SendTelegramAnimeEpMessage(TelegramDownloadMessagePattern, downloadedAnimeEpisodes)
|
||||
|
||||
log.Printf("check took %s. sleeping for %s\n", duration.Truncate(time.Millisecond), (PollRate - duration).Truncate(time.Millisecond))
|
||||
}
|
40
download_torrent_file.go
Normal file
40
download_torrent_file.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
)
|
||||
|
||||
func DownloadTorrent(animeEp model.AnimeEpisode, torrent *model.ParsedTorrent) error {
|
||||
if err := SetCurrentlyDownloading(animeEp); err != nil {
|
||||
return ErrLockFileCreationFailed.Wrap(err, animeEp.Anime.Title.Romaji, animeEp.Episode)
|
||||
}
|
||||
|
||||
torrentFilePath := filepath.Join(TorrentPath, path.Base(torrent.Torrent.Link))
|
||||
|
||||
//fmt.Printf("download: %s -> %s\n", torrent.Torrent.Link, torrentFilePath)
|
||||
|
||||
resp, err := http.Get(torrent.Torrent.Link)
|
||||
if err != nil {
|
||||
return ErrDownloadTorrentFileFailed.Wrap(err, torrent.Torrent.Link)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
file, err := os.Create(torrentFilePath)
|
||||
if err != nil {
|
||||
return ErrSaveTorrentFileFailed.Wrap(err, torrent.Torrent.Link)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, resp.Body)
|
||||
if err != nil {
|
||||
return ErrSaveTorrentFileFailed.Wrap(err, torrent.Torrent.Link)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
25
envvars.go
Normal file
25
envvars.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/tordarus/envvars"
|
||||
)
|
||||
|
||||
var (
|
||||
PollRate = envvars.Object("POLL_RATE", 30*time.Minute, time.ParseDuration)
|
||||
|
||||
TorrentPath = envvars.String("TORRENT_PATH", "")
|
||||
|
||||
DebugAnimeEpisodePatternStr = envvars.String("DEBUG_ANIME_EPISODE_PATTERN", `{{.Anime.Title.UserPreferred}} episode {{.Episode}}`)
|
||||
DebugAnimeEpisodePattern = template.Must(template.New("DEBUG_ANIME_EPISODE_PATTERN").Parse(DebugAnimeEpisodePatternStr))
|
||||
|
||||
TelegramBotToken = envvars.String("TELEGRAM_API_TOKEN", "")
|
||||
TelegramChatID = envvars.Int64("TELEGRAM_CHAT_ID", 0)
|
||||
TelegramDownloadMessagePatternStr = logic.EscSeqReplacer.Replace(envvars.String("TELEGRAM_DOWNLOAD_MESSAGE_PATTERN", `Download started{{range .}}\n{{.Anime.Title.UserPreferred}} episode {{.Episode}}{{end}}`))
|
||||
TelegramDownloadMessagePattern = template.Must(template.New("TELEGRAM_DOWNLOAD_MESSAGE_PATTERN").Parse(TelegramDownloadMessagePatternStr))
|
||||
|
||||
DownloadAll = envvars.Bool("DOWNLOAD_ALL_ANIMES", false)
|
||||
)
|
12
errors.go
Normal file
12
errors.go
Normal file
@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
import "git.tordarus.net/tordarus/adverr/v2"
|
||||
|
||||
var (
|
||||
ErrNoSuitableParser = adverr.NewErrTmpl("ErrNoSuitableParser", "could not parse torrent with ID %s because no suitable parser found")
|
||||
ErrTorrentParseFailed = adverr.NewErrTmpl("ErrTorrentParseFailed", "could not parse torrent with ID %s (parsed with '%s')")
|
||||
ErrTorrentNotObtainable = adverr.NewErrTmpl("ErrTorrentNotObtainable", "torrents from nyaa.si not obtainable (reason: %s)")
|
||||
ErrDownloadTorrentFileFailed = adverr.NewErrTmpl("ErrDownloadTorrentFileFailed", "torrent file download failed: %s")
|
||||
ErrLockFileCreationFailed = adverr.NewErrTmpl("ErrLockFileCreationFailed", "creation of lock file for anime '%s' and episode %d failed")
|
||||
ErrSaveTorrentFileFailed = adverr.NewErrTmpl("ErrSaveTorrentFileFailed", "torrent file could not be saved: %s")
|
||||
)
|
78
get_torrents.go
Normal file
78
get_torrents.go
Normal file
@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
var torrentLinkRegex = regexp.MustCompile(`https:\/\/nyaa\.si\/download\/(\d+?)\.torrent`)
|
||||
|
||||
func GetTorrents() ([]model.Torrent, error) {
|
||||
resp, err := http.Get("https://nyaa.si/?page=rss&f=0&c=1_0")
|
||||
if err != nil {
|
||||
return nil, ErrTorrentNotObtainable.Wrap(err, "torrent data acqusition failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, ErrTorrentNotObtainable.New("invalid status code from nyaa.si: " + strconv.Itoa(resp.StatusCode))
|
||||
}
|
||||
|
||||
nyaa, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, ErrTorrentNotObtainable.Wrap(err, "nyaa.si response parsing failed")
|
||||
}
|
||||
|
||||
torrents := make([]model.Torrent, 0, 75)
|
||||
|
||||
nyaa.Find("item").Each(func(i int, s *goquery.Selection) {
|
||||
time, err := time.Parse(time.RFC1123Z, s.Find("pubDate").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse time:", s.Find("pubDate"))
|
||||
return
|
||||
}
|
||||
|
||||
seeders, err := strconv.Atoi(s.Find("nyaa\\:seeders").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse seeders:", s.Find("nyaa\\:seeders").Text())
|
||||
return
|
||||
}
|
||||
|
||||
leechers, err := strconv.Atoi(s.Find("nyaa\\:leechers").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse leechers:", s.Find("nyaa\\:leechers").Text())
|
||||
return
|
||||
}
|
||||
|
||||
downloads, err := strconv.Atoi(s.Find("nyaa\\:downloads").Text())
|
||||
if err != nil {
|
||||
fmt.Println("could not parse downloads:", s.Find("nyaa\\:downloads").Text())
|
||||
return
|
||||
}
|
||||
|
||||
// goquery can't parse the link tag for some reason
|
||||
// therefore I have to get around this bug by exploiting regex
|
||||
matches := torrentLinkRegex.FindStringSubmatch(s.Text())
|
||||
link := matches[0]
|
||||
id := matches[1]
|
||||
|
||||
torrents = append(torrents, model.Torrent{
|
||||
ID: model.TorrentID(id),
|
||||
Title: s.Find("title").Text(),
|
||||
Link: link,
|
||||
Time: time,
|
||||
Seeders: seeders,
|
||||
Leechers: leechers,
|
||||
Downloads: downloads,
|
||||
Trusted: strings.Contains(strings.ToLower(s.Find("nyaa\\:trusted").Text()), "yes"),
|
||||
})
|
||||
})
|
||||
|
||||
return torrents, nil
|
||||
}
|
33
go.mod
Normal file
33
go.mod
Normal file
@ -0,0 +1,33 @@
|
||||
module git.tordarus.net/nyaanime/downloader
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.3
|
||||
|
||||
require (
|
||||
git.tordarus.net/nyaanime/logic v0.0.1
|
||||
git.tordarus.net/nyaanime/model v0.0.1
|
||||
git.tordarus.net/nyaanime/parsers v0.0.2
|
||||
git.tordarus.net/tordarus/adverr/v2 v2.0.2
|
||||
git.tordarus.net/tordarus/anilist v1.5.2
|
||||
git.tordarus.net/tordarus/envvars v0.0.0-20250114175450-d73e12b838a5
|
||||
git.tordarus.net/tordarus/slices v0.0.14
|
||||
git.tordarus.net/tordarus/tprint v0.0.1
|
||||
github.com/PuerkitoBio/goquery v1.10.3
|
||||
github.com/fatih/color v1.18.0
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
)
|
||||
|
||||
require (
|
||||
git.tordarus.net/tordarus/channel v0.1.19 // indirect
|
||||
git.tordarus.net/tordarus/gmath v0.0.7 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.2.1 // indirect
|
||||
)
|
112
go.sum
Normal file
112
go.sum
Normal file
@ -0,0 +1,112 @@
|
||||
git.tordarus.net/nyaanime/logic v0.0.1 h1:fUa/O9/WgzJhmGFGSuy6gx+Rr2Y+/RLee3qaNO0l1lI=
|
||||
git.tordarus.net/nyaanime/logic v0.0.1/go.mod h1:+4sKxSzwgCoeZv3lyWB3yFa5CVyi/frmwsTqDf8C3mE=
|
||||
git.tordarus.net/nyaanime/model v0.0.1 h1:/I+87Z6eEw/o2adltKnCk4FZai2mPekjYlzEjY1ppyQ=
|
||||
git.tordarus.net/nyaanime/model v0.0.1/go.mod h1:oHV82UMNy4XgPHkI6tZiwabdi6myqHXgjMi9sNZ+rG4=
|
||||
git.tordarus.net/nyaanime/parsers v0.0.2 h1:UxfDGxgS2guldLhRtlLNjst/UyeA8OC44oj7nUeRUB8=
|
||||
git.tordarus.net/nyaanime/parsers v0.0.2/go.mod h1:sx8HyJCpG7zzwRAEE1ZlmVPirPc3fdArlCM5L1sxEaQ=
|
||||
git.tordarus.net/tordarus/adverr/v2 v2.0.2 h1:7nvNjMMjtGPq0EY6duMiv+seJ7MacNvKSBmckHl6Erg=
|
||||
git.tordarus.net/tordarus/adverr/v2 v2.0.2/go.mod h1:gCC46KsWosZJh7MVNDEU99hKQoxEWZgHITDHtmFwwiQ=
|
||||
git.tordarus.net/tordarus/anilist v1.5.2 h1:SxlovS+e3lgL2SowQQwj8dQrIZzRFPomcGCw3V+My0Q=
|
||||
git.tordarus.net/tordarus/anilist v1.5.2/go.mod h1:Mrhx/9+8HJVj5ebQ5fJuXqL220tEJhgQIqFK2WKPXgA=
|
||||
git.tordarus.net/tordarus/channel v0.1.19 h1:d9xnSwFyvBh4B1/82mt0A7Gpm2nIZJTc+9ceJMIOu5Q=
|
||||
git.tordarus.net/tordarus/channel v0.1.19/go.mod h1:8/dWFTdGO7g4AeSZ7cF6GerkGbe9c4dBVMVDBxOd9m4=
|
||||
git.tordarus.net/tordarus/envvars v0.0.0-20250114175450-d73e12b838a5 h1:rKNDX/YGunqg8TEU6q1rgS2BcDKVmUW2cg61JOE/wws=
|
||||
git.tordarus.net/tordarus/envvars v0.0.0-20250114175450-d73e12b838a5/go.mod h1:/qVGwrEmqtIrZyuuoIQl4vquSkPWUNJmlGNedDrdYfg=
|
||||
git.tordarus.net/tordarus/gmath v0.0.7 h1:tR48idt9AUL0r556ww3ZxByTKJEr6NWCTlhl2ihzYxQ=
|
||||
git.tordarus.net/tordarus/gmath v0.0.7/go.mod h1:mO7aPlvNrGVE9UFXEuuACjZgMDsM63l3OcQy6xSQnoE=
|
||||
git.tordarus.net/tordarus/slices v0.0.14 h1:Jy1VRMs777WewJ7mxTgjyQIMm/Zr+co18/XoQ01YZ3A=
|
||||
git.tordarus.net/tordarus/slices v0.0.14/go.mod h1:RgE7A1aSAezIvPUgcbUuMHu0q4xGKoRevT+DC0eJmwI=
|
||||
git.tordarus.net/tordarus/tprint v0.0.1 h1:aM5c0nLwicUIoic/xguwE5fQdQ2bB3z0+FQEN/Yt0H4=
|
||||
git.tordarus.net/tordarus/tprint v0.0.1/go.mod h1:2UdHVY/ue8vXeJU/IJY1xBikDaH35kaMzxjk9ryKB8Q=
|
||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
|
||||
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.2.1 h1:sFV08OT1eZ1yroLCZVClIVd9YySgCh9eGjBWO0oRayI=
|
||||
gopkg.in/vansante/go-ffprobe.v2 v2.2.1/go.mod h1:qF0AlAjk7Nqzqf3y333Ly+KxN3cKF2JqA3JT5ZheUGE=
|
31
local_file_check.go
Normal file
31
local_file_check.go
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
)
|
||||
|
||||
func IsCurrentlyDownloading(animeEp model.AnimeEpisode) bool {
|
||||
animeEpPath := logic.GetAnimeEpFilepath(animeEp, "lock")
|
||||
_, err := os.Stat(animeEpPath)
|
||||
return !errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
func SetCurrentlyDownloading(animeEp model.AnimeEpisode) error {
|
||||
animeEpPath := logic.GetAnimeEpFilepath(animeEp, "lock")
|
||||
|
||||
dir := filepath.Dir(animeEpPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(animeEpPath)
|
||||
if err != nil {
|
||||
defer file.Close()
|
||||
}
|
||||
return err
|
||||
}
|
39
main.go
Normal file
39
main.go
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// check for ffmpeg in PATH
|
||||
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
||||
panic(err) // TODO error handling
|
||||
}
|
||||
|
||||
// check for ffprobe in PATH
|
||||
if _, err := exec.LookPath("ffprobe"); err != nil {
|
||||
panic(err) // TODO error handling
|
||||
}
|
||||
|
||||
// get access token once at startup to be sure that an access token is obtainable at all
|
||||
if _, err := logic.GetAnilistAccessToken(); err != nil {
|
||||
panic(err) // TODO error handling
|
||||
}
|
||||
|
||||
if err := InitTelegramBot(); err != nil {
|
||||
panic(err) // TODO error handling
|
||||
}
|
||||
|
||||
logic.PrintPriorityTables()
|
||||
|
||||
ticker := time.NewTicker(PollRate)
|
||||
defer ticker.Stop()
|
||||
|
||||
CheckTorrents()
|
||||
for range ticker.C {
|
||||
CheckTorrents()
|
||||
}
|
||||
}
|
86
show_debug_info.go
Normal file
86
show_debug_info.go
Normal file
@ -0,0 +1,86 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/slices"
|
||||
"git.tordarus.net/tordarus/tprint"
|
||||
)
|
||||
|
||||
var (
|
||||
BoldText = color.New(color.Bold)
|
||||
)
|
||||
|
||||
func ShowDebugInfo(
|
||||
parsedTorrentsByAnimeEp, animeListTorrents, essentialTorrents map[model.AnimeEpisode][]*model.ParsedTorrent,
|
||||
preferredTorrents map[model.AnimeEpisode]*TorrentPriority,
|
||||
downloadingEpisodes map[model.AnimeEpisode]struct{},
|
||||
inCollectionEpisodes map[model.AnimeEpisode]bool,
|
||||
collectionPreferredTorrents map[*model.ParsedTorrent]bool,
|
||||
downloadedTorrents map[model.AnimeEpisode]*TorrentPriority) {
|
||||
|
||||
for animeEp, parsedTorrents := range parsedTorrentsByAnimeEp {
|
||||
table := tprint.NewTable("id", "resolution", "languages", "subtitles", "seeders", "leechers", "downloads", "trusted", "group", "evaluation")
|
||||
|
||||
for _, torrent := range parsedTorrents {
|
||||
eval := getEvaluation(
|
||||
slices.Contains(essentialTorrents[animeEp], torrent),
|
||||
preferredTorrents[animeEp] != nil && preferredTorrents[animeEp].ParsedTorrent == torrent,
|
||||
collectionPreferredTorrents[torrent],
|
||||
downloadedTorrents[animeEp] != nil && downloadedTorrents[animeEp].ParsedTorrent == torrent,
|
||||
)
|
||||
|
||||
table.AddRow(
|
||||
torrent.Torrent.ID,
|
||||
torrent.Resolution,
|
||||
strings.Join(torrent.Languages, ", "),
|
||||
strings.Join(torrent.Subtitles, ", "),
|
||||
torrent.Torrent.Seeders,
|
||||
torrent.Torrent.Leechers,
|
||||
torrent.Torrent.Downloads,
|
||||
torrent.Torrent.Trusted,
|
||||
torrent.Parser.Identity,
|
||||
eval,
|
||||
)
|
||||
}
|
||||
|
||||
var epState string
|
||||
|
||||
if _, onList := animeListTorrents[animeEp]; onList {
|
||||
if _, downloading := downloadingEpisodes[animeEp]; downloading {
|
||||
epState = color.BlueString("CURRENTLY DOWNLOADING")
|
||||
} else if inCollectionEpisodes[animeEp] {
|
||||
epState = color.GreenString("IN COLLECTION")
|
||||
} else {
|
||||
epState = color.YellowString("ON LIST")
|
||||
}
|
||||
} else {
|
||||
epState = color.RedString("NOT ON LIST")
|
||||
}
|
||||
|
||||
b := new(strings.Builder)
|
||||
if err := DebugAnimeEpisodePattern.Execute(b, animeEp); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
header := BoldText.Sprintf("%s (%s)", color.MagentaString(b.String()), epState)
|
||||
fmt.Println(tprint.FormatHeaderTable(header, table))
|
||||
}
|
||||
}
|
||||
|
||||
func getEvaluation(essential, preferred, collectionPreferred, downloaded bool) string {
|
||||
if downloaded {
|
||||
return color.GreenString("DOWNLOAD STARTED")
|
||||
} else if collectionPreferred {
|
||||
return color.GreenString("collection preferred")
|
||||
} else if preferred {
|
||||
return color.BlueString("torrent preferred")
|
||||
} else if essential {
|
||||
return color.YellowString("torrent considered")
|
||||
} else {
|
||||
return color.RedString("torrent ignored")
|
||||
}
|
||||
}
|
48
telegram.go
Normal file
48
telegram.go
Normal file
@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/adverr/v2"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
)
|
||||
|
||||
var TelegramBot *tgbotapi.BotAPI
|
||||
|
||||
func InitTelegramBot() error {
|
||||
if TelegramBotToken != "" && TelegramChatID != 0 {
|
||||
bot, err := tgbotapi.NewBotAPI(TelegramBotToken)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
TelegramBot = bot
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendTelegramMessage(text string) {
|
||||
if TelegramBot == nil || strings.TrimSpace(text) == "" {
|
||||
return
|
||||
}
|
||||
|
||||
msg := tgbotapi.NewMessage(TelegramChatID, text)
|
||||
_, err := TelegramBot.Send(msg)
|
||||
if err != nil {
|
||||
adverr.Println(adverr.Wrap("could not send telegram message", err))
|
||||
}
|
||||
}
|
||||
|
||||
func SendTelegramAnimeEpMessage(messagePattern *template.Template, animeEps []model.AnimeEpisode) {
|
||||
if len(animeEps) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
b := new(strings.Builder)
|
||||
if err := messagePattern.Execute(b, animeEps); err != nil {
|
||||
adverr.Println(adverr.Wrap("could not send telegram message", err))
|
||||
}
|
||||
|
||||
SendTelegramMessage(b.String())
|
||||
}
|
70
torrent_filter.go
Normal file
70
torrent_filter.go
Normal file
@ -0,0 +1,70 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/anilist"
|
||||
"git.tordarus.net/tordarus/slices"
|
||||
)
|
||||
|
||||
func FilterTorrentsByAnimeList(allTorrents map[model.AnimeEpisode][]*model.ParsedTorrent, animeList map[anilist.MediaID]*anilist.MediaList) map[model.AnimeEpisode][]*model.ParsedTorrent {
|
||||
if DownloadAll {
|
||||
return allTorrents
|
||||
}
|
||||
|
||||
filtered := map[model.AnimeEpisode][]*model.ParsedTorrent{}
|
||||
for animeEp, torrents := range allTorrents {
|
||||
if _, ok := animeList[animeEp.Anime.ID]; ok {
|
||||
filtered[animeEp] = torrents
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func FilterEssentialTorrents(allTorrents map[model.AnimeEpisode][]*model.ParsedTorrent) map[model.AnimeEpisode][]*model.ParsedTorrent {
|
||||
filtered := map[model.AnimeEpisode][]*model.ParsedTorrent{}
|
||||
for animeEpisode, parsedTorrents := range allTorrents {
|
||||
for _, parsedTorrent := range parsedTorrents {
|
||||
if HasEssentialProperties(parsedTorrent) {
|
||||
filtered[animeEpisode] = append(filtered[animeEpisode], parsedTorrent)
|
||||
}
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func HasEssentialProperties(torrent *model.ParsedTorrent) bool {
|
||||
if torrent.Resolution < logic.MinResolution || torrent.Resolution > logic.MaxResolution {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Seeders < logic.MinSeeders || torrent.Torrent.Seeders > logic.MaxSeeders {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Leechers < logic.MinLeechers || torrent.Torrent.Leechers > logic.MaxLeechers {
|
||||
return false
|
||||
}
|
||||
|
||||
if torrent.Torrent.Downloads < logic.MinDownloads || torrent.Torrent.Downloads > logic.MaxDownloads {
|
||||
return false
|
||||
}
|
||||
|
||||
if logic.TrustedOnly && !torrent.Torrent.Trusted {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, essentialLanguage := range logic.EssentialLanguages {
|
||||
if !slices.Contains(torrent.Languages, essentialLanguage) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, essentialSubtitle := range logic.EssentialSubtitles {
|
||||
if !slices.Contains(torrent.Subtitles, essentialSubtitle) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
52
torrent_parse.go
Normal file
52
torrent_parse.go
Normal file
@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/nyaanime/parsers"
|
||||
"git.tordarus.net/tordarus/adverr/v2"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
func ParseTorrent(torrent *model.Torrent) (*model.ParsedTorrent, error) {
|
||||
for _, parser := range parsers.Parsers {
|
||||
parsedTorrent, ok := parser.TorrentParser(&parser, torrent)
|
||||
if ok {
|
||||
anime, err := logic.SearchAnimeByTitle(parsedTorrent.OriginalAnimeTitle)
|
||||
if err != nil {
|
||||
return parsedTorrent, ErrTorrentParseFailed.Wrap(err, torrent.ID, parser.String())
|
||||
}
|
||||
|
||||
parsedTorrent.Anime = anime
|
||||
return parsedTorrent, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrNoSuitableParser.New(torrent.ID)
|
||||
}
|
||||
|
||||
func ParseTorrentsByAnimeEp(torrents []model.Torrent) map[model.AnimeEpisode][]*model.ParsedTorrent {
|
||||
torrentsByAnimeEp := map[model.AnimeEpisode][]*model.ParsedTorrent{}
|
||||
|
||||
for _, torrent := range torrents {
|
||||
torrent := torrent
|
||||
parsedTorrent, err := ParseTorrent(&torrent)
|
||||
if err != nil {
|
||||
if errors.Is(err, logic.ErrAnimeNotFound) {
|
||||
fmt.Fprintln(os.Stderr, color.RedString("torrent with ID %s could not be parsed because anime was not found: \"%s\"", torrent.ID, parsedTorrent.OriginalAnimeTitle))
|
||||
} else if !errors.Is(err, ErrNoSuitableParser) {
|
||||
adverr.Println(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
animeEp := GetAnimeEpisode(parsedTorrent.Anime, parsedTorrent.Episode)
|
||||
torrentsByAnimeEp[animeEp] = append(torrentsByAnimeEp[animeEp], parsedTorrent)
|
||||
}
|
||||
|
||||
return torrentsByAnimeEp
|
||||
}
|
28
torrent_priority.go
Normal file
28
torrent_priority.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.tordarus.net/nyaanime/logic"
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
)
|
||||
|
||||
type TorrentPriority struct {
|
||||
ParsedTorrent *model.ParsedTorrent
|
||||
Priority int
|
||||
PreferredProperties map[string]int
|
||||
}
|
||||
|
||||
func NewTorrentPriority(torrent *model.ParsedTorrent) *TorrentPriority {
|
||||
priority, preferredProperties := logic.DeterminePriority(torrent)
|
||||
|
||||
return &TorrentPriority{
|
||||
ParsedTorrent: torrent,
|
||||
Priority: priority,
|
||||
PreferredProperties: preferredProperties,
|
||||
}
|
||||
}
|
||||
|
||||
func (tp TorrentPriority) String() string {
|
||||
return fmt.Sprintf("%s | priority: %d", tp.ParsedTorrent.String(), tp.Priority)
|
||||
}
|
28
torrent_sort.go
Normal file
28
torrent_sort.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/slices"
|
||||
)
|
||||
|
||||
func GetTorrentsWithMaxPrioByAnimeEp(torrents map[model.AnimeEpisode][]*model.ParsedTorrent) map[model.AnimeEpisode]*TorrentPriority {
|
||||
torrentsWithPrio := map[model.AnimeEpisode]*TorrentPriority{}
|
||||
|
||||
for animeEp, torrentList := range torrents {
|
||||
torrentPrioList := slices.Map(torrentList, NewTorrentPriority)
|
||||
|
||||
var maxPrio *TorrentPriority
|
||||
|
||||
for _, torrentPrio := range torrentPrioList {
|
||||
if maxPrio == nil || torrentPrio.Priority > maxPrio.Priority {
|
||||
maxPrio = torrentPrio
|
||||
}
|
||||
}
|
||||
|
||||
if maxPrio != nil {
|
||||
torrentsWithPrio[animeEp] = maxPrio
|
||||
}
|
||||
}
|
||||
|
||||
return torrentsWithPrio
|
||||
}
|
36
utils.go
Normal file
36
utils.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"git.tordarus.net/nyaanime/model"
|
||||
"git.tordarus.net/tordarus/tprint"
|
||||
)
|
||||
|
||||
func Map2Table[K comparable](title string, m map[K]int) string {
|
||||
table := tprint.NewTable(title, "priority")
|
||||
|
||||
entries := make([]model.Pair[K, int], 0, len(m))
|
||||
for name, priority := range m {
|
||||
entries = append(entries, model.Pair[K, int]{First: name, Second: priority})
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool { return entries[i].Second > entries[j].Second })
|
||||
|
||||
for _, entry := range entries {
|
||||
table.AddRow(entry.First, entry.Second)
|
||||
}
|
||||
|
||||
return table.String()
|
||||
}
|
||||
|
||||
func GetAnimeEpisodesSortFunc(s []model.AnimeEpisode) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
if s[i].Anime.ID < s[j].Anime.ID {
|
||||
return true
|
||||
} else if s[i].Anime.ID > s[j].Anime.ID {
|
||||
return false
|
||||
} else {
|
||||
return s[i].Episode <= s[j].Episode
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user