preferred properties implemented

This commit is contained in:
Timon Ringwald 2022-08-21 21:14:44 +02:00
parent a45849821a
commit f5066d37f1
9 changed files with 195 additions and 30 deletions

View File

@ -13,7 +13,7 @@ type Pair[A, B any] struct {
Second B Second B
} }
func GetCurrentlyWatchingAnimesByAnimeID() (map[anilist.MediaID]*anilist.MediaList, error) { func GetAnimesToDownloadByAnimeID() (map[anilist.MediaID]*anilist.MediaList, error) {
watchingAnimesChannel, err := GetCurrentlyWatchingAnimes() watchingAnimesChannel, err := GetCurrentlyWatchingAnimes()
if err != nil { if err != nil {
return nil, err return nil, err

8
go.mod
View File

@ -3,15 +3,15 @@ module git.milar.in/nyaanime/downloader
go 1.18 go 1.18
require ( require (
git.milar.in/milarin/adverr v0.2.1 git.milar.in/milarin/adverr v1.0.0
git.milar.in/milarin/anilist v1.5.0 git.milar.in/milarin/anilist v1.5.0
git.milar.in/milarin/channel v0.0.7 git.milar.in/milarin/channel v0.0.7
git.milar.in/milarin/envvars v1.0.3 git.milar.in/milarin/envvars v1.0.3
git.milar.in/milarin/slices v0.0.0-20220818114116-f7ab541d0a5b git.milar.in/milarin/gmath v0.0.2
git.milar.in/nyaanime/model v0.0.0-20220815143950-c3d8a5af20c3 git.milar.in/milarin/slices v0.0.2
git.milar.in/nyaanime/model v0.0.0-20220821124037-0a28c6b41556
git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27 git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27
github.com/PuerkitoBio/goquery v1.8.0 github.com/PuerkitoBio/goquery v1.8.0
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e
) )
require ( require (

16
go.sum
View File

@ -1,23 +1,23 @@
git.milar.in/milarin/adverr v0.2.1 h1:eyXFGC+Ui/kcNt2+NqP3HiAplwxzqeNr9DfitsUb3c4= git.milar.in/milarin/adverr v1.0.0 h1:9povP074PjpoZlNuiKPa9w6fDA8a4RSI5hEDo5T9Tqo=
git.milar.in/milarin/adverr v0.2.1/go.mod h1:wwfglcey4R3vqjNL/d8mbnvFJGzETRXzAEolIHZY32w= git.milar.in/milarin/adverr v1.0.0/go.mod h1:wwfglcey4R3vqjNL/d8mbnvFJGzETRXzAEolIHZY32w=
git.milar.in/milarin/anilist v1.5.0 h1:fSiAXY/topNk4ISEp2QtcG9HHKLJfMc8w05iqc+Paf0= git.milar.in/milarin/anilist v1.5.0 h1:fSiAXY/topNk4ISEp2QtcG9HHKLJfMc8w05iqc+Paf0=
git.milar.in/milarin/anilist v1.5.0/go.mod h1:8PTHXFMA45JpfRFIpcdrKwDHue8fbT/wwV1BuHFn6c0= git.milar.in/milarin/anilist v1.5.0/go.mod h1:8PTHXFMA45JpfRFIpcdrKwDHue8fbT/wwV1BuHFn6c0=
git.milar.in/milarin/channel v0.0.7 h1:cVKtwgH/EE7U+XTHcoFCClJ4LR349KanzjX9xKwRcNg= git.milar.in/milarin/channel v0.0.7 h1:cVKtwgH/EE7U+XTHcoFCClJ4LR349KanzjX9xKwRcNg=
git.milar.in/milarin/channel v0.0.7/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM= git.milar.in/milarin/channel v0.0.7/go.mod h1:We83LTI8S7u7II3pD+A2ChCDWJfCkcBUCUqii9HjTtM=
git.milar.in/milarin/envvars v1.0.3 h1:go6pYExUzPx+aLvJ5BKNkaANkBNmcrlRJ8TtWsk4uWY= git.milar.in/milarin/envvars v1.0.3 h1:go6pYExUzPx+aLvJ5BKNkaANkBNmcrlRJ8TtWsk4uWY=
git.milar.in/milarin/envvars v1.0.3/go.mod h1:rLh/HN6S254h6m2lklnImcpsy4kHFxaOjM6+Nv9GHKI= git.milar.in/milarin/envvars v1.0.3/go.mod h1:rLh/HN6S254h6m2lklnImcpsy4kHFxaOjM6+Nv9GHKI=
git.milar.in/milarin/slices v0.0.0-20220818114116-f7ab541d0a5b h1:886jJlrbGpjKijCvtK9Nzwi4JZhWFFCTRWBc4yiM+TY= git.milar.in/milarin/gmath v0.0.2 h1:avz+75f8XqAYA1wEB6kis0R5xvRuepBKTqBuJBjh6Yw=
git.milar.in/milarin/slices v0.0.0-20220818114116-f7ab541d0a5b/go.mod h1:XRNfE99aNKeaPOY1phjOlpIQqeGCW1LOqqh8UHS+vAk= git.milar.in/milarin/gmath v0.0.2/go.mod h1:HDLftG5RLpiNGKiIWh+O2G1PYkNzyLDADO8Cd/1abiE=
git.milar.in/nyaanime/model v0.0.0-20220815143950-c3d8a5af20c3 h1:GyAhYs3Nl8CuzfOeLKmWwRe/Z+pEEtnWty39HfGz6iE= git.milar.in/milarin/slices v0.0.2 h1:j92MuP0HWKSaHJMq/FRxDtSDIGiOTvw+KogUTwuulr0=
git.milar.in/nyaanime/model v0.0.0-20220815143950-c3d8a5af20c3/go.mod h1:OzhQgj0b/Hf9fg8VXYxYt8ONQOvHm8xC44TmS9kQ150= git.milar.in/milarin/slices v0.0.2/go.mod h1:XRNfE99aNKeaPOY1phjOlpIQqeGCW1LOqqh8UHS+vAk=
git.milar.in/nyaanime/model v0.0.0-20220821124037-0a28c6b41556 h1:RplYz4+CMK9ByI3ELusBltWFRlDs6VMOGk5EyENnLi0=
git.milar.in/nyaanime/model v0.0.0-20220821124037-0a28c6b41556/go.mod h1:OzhQgj0b/Hf9fg8VXYxYt8ONQOvHm8xC44TmS9kQ150=
git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27 h1:0+5j9MMJQS8+Luss19hD6hvNFxcBDRal2XwSUTyq7WU= git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27 h1:0+5j9MMJQS8+Luss19hD6hvNFxcBDRal2XwSUTyq7WU=
git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27/go.mod h1:qm6fIFBFs90uz7IJ8RKgDir0K8Fa8isixGLgrtC6kgU= git.milar.in/nyaanime/parsers v0.0.0-20220815144327-52de61265e27/go.mod h1:qm6fIFBFs90uz7IJ8RKgDir0K8Fa8isixGLgrtC6kgU=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI= github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=

43
main.go
View File

@ -28,8 +28,8 @@ var (
MaxResolution = envvars.Object("MAX_RESOLUTION", model.Resolution4K, model.ParseResolution) MaxResolution = envvars.Object("MAX_RESOLUTION", model.Resolution4K, model.ParseResolution)
MinResolution = envvars.Object("MIN_RESOLUTION", model.ResolutionHD, model.ParseResolution) MinResolution = envvars.Object("MIN_RESOLUTION", model.ResolutionHD, model.ParseResolution)
EssentialLanguages = envvars.StringSlice("ESSENTIAL_LANGUAGES", ",") EssentialLanguages = envvars.StringSlice("ESSENTIAL_LANGUAGES", "|")
EssentialSubtitles = envvars.StringSlice("ESSENTIAL_SUBTITLES", ",") EssentialSubtitles = envvars.StringSlice("ESSENTIAL_SUBTITLES", "|")
MaxSeeders = envvars.Int("MAX_SEEDERS", math.MaxInt) MaxSeeders = envvars.Int("MAX_SEEDERS", math.MaxInt)
MinSeeders = envvars.Int("MIN_SEEDERS", 0) MinSeeders = envvars.Int("MIN_SEEDERS", 0)
@ -44,8 +44,9 @@ var (
// preferred torrent properties // preferred torrent properties
PreferredLanguages = envvars.StringSlice("PREFERRED_LANGUAGES", ",") PreferredLanguages = ParsePreferredStringProps(envvars.StringSlice("PREFERRED_LANGUAGES", "|"))
PreferredSubtitles = envvars.StringSlice("PREFERRED_SUBTITLES", ",") PreferredSubtitles = ParsePreferredStringProps(envvars.StringSlice("PREFERRED_SUBTITLES", "|"))
PreferredResolutions = ParsePreferredProps(envvars.StringSlice("PREFERRED_RESOLUTIONS", "|"), model.ParseResolution)
/* /*
TODO TODO
@ -56,6 +57,10 @@ var (
) )
func main() { func main() {
fmt.Println("language priorites:", PreferredLanguages)
fmt.Println("subtitle priorites:", PreferredSubtitles)
fmt.Println("resolution priorites:", PreferredResolutions)
if len(AnimeStatus) == 0 { if len(AnimeStatus) == 0 {
AnimeStatus = []anilist.MediaListStatus{ AnimeStatus = []anilist.MediaListStatus{
anilist.MediaListStatusCurrent, anilist.MediaListStatusCurrent,
@ -87,20 +92,36 @@ func checkTorrents() {
return return
} }
_, err = GetCurrentlyWatchingAnimesByAnimeID() /*animes, err = GetAnimesToDownloadByAnimeID()
if err != nil { if err != nil {
fmt.Println(adverr.Wrap("retrieving anime list failed", err)) fmt.Println(adverr.Wrap("retrieving anime list failed", err))
return return
} }*/
parsedTorrentsByAnimeEp := FilterEssentialTorrents(ParseTorrentsByAnimeEp(torrents)) parsedTorrents := ParseTorrentsByAnimeEpSortedByProperties(torrents)
for _, parsedTorrents := range parsedTorrentsByAnimeEp { for animeEp, torrentPriorities := range parsedTorrents {
for _, parsedTorrent := range parsedTorrents { fmt.Printf("\nanime: %s | episode: %d | torrents found: %d\n", animeEp.Anime, animeEp.Episode, len(torrentPriorities))
fmt.Println(parsedTorrent, "|", parsedTorrent.Torrent.StringWithoutTitle()) for _, torrentPriority := range torrentPriorities {
PrintTorrentPriority(torrentPriority)
} }
} }
// TODO store preferred properties of downloaded torrents in db
duration := time.Since(start) duration := time.Since(start)
fmt.Printf("check took %s. sleeping for %s\n", duration, PollRate-duration) fmt.Printf("\ncheck took %s. sleeping for %s\n", duration.Truncate(time.Millisecond), (PollRate - duration).Truncate(time.Millisecond))
}
func ParseTorrentsByAnimeEpSortedByProperties(torrents []model.Torrent) map[model.AnimeEpisode][]*TorrentPriority {
// parse torrents
parsedTorrentsByAnimeEp := ParseTorrentsByAnimeEp(torrents)
// filter out torrents without essential properties
filteredTorrentsByAnimeEp := FilterEssentialTorrents(parsedTorrentsByAnimeEp)
// sort torrents by preferred properties
sortedTorrentsByAnimeEp := SortTorrentsByPreferredProperties(filteredTorrentsByAnimeEp)
return sortedTorrentsByAnimeEp
} }

53
parse_preferred_props.go Normal file
View File

@ -0,0 +1,53 @@
package main
import (
"strings"
"git.milar.in/milarin/gmath"
"git.milar.in/milarin/slices"
)
// ParsePreferredProps parses properties and its corresponding priority.
// priorities are distributed exponentially in reverse order.
//
// That means the last entry will have priority 1, the second last 2, then 4, 8 and so on.
//
// Properties with name "_" will be ignored and function as a placeholder to increase the priority
// of the properties which comes before them.
//
// Properties separated by comma will have the same priorities.
//
// str usually is the return value of a call to strings.Split(str, "|")
//
// Examples:
// str = "a|b|c" -> c:1 b:2 a:4
// str = "a|b|_|c" -> c:1 b:4 a:8
// str = "a,b|c" -> c:1 b:4 a:4
// str = "d|_|a,b|c" -> c:1 b:4 a:4 d:16
//
// Additionally, properties can be converted to a generic type with the converter function
func ParsePreferredProps[T comparable](str []string, converter func(string) (T, error)) map[T]int {
props := map[T]int{}
for i, subProps := range slices.Reverse(str) {
if subProps == "_" {
continue
}
propPriority := gmath.Pow(2, i)
for _, subProp := range strings.Split(subProps, ",") {
subPropT, err := converter(subProp)
if err != nil {
continue
}
props[subPropT] = propPriority
}
}
return props
}
func ParsePreferredStringProps(str []string) map[string]int {
return ParsePreferredProps(str, func(s string) (string, error) { return s, nil })
}

View File

@ -1,8 +1,8 @@
package main package main
import ( import (
"git.milar.in/milarin/slices"
"git.milar.in/nyaanime/model" "git.milar.in/nyaanime/model"
"golang.org/x/exp/slices"
) )
func FilterEssentialTorrents(parsedTorrentsByAnimeEp map[model.AnimeEpisode][]*model.ParsedTorrent) map[model.AnimeEpisode][]*model.ParsedTorrent { func FilterEssentialTorrents(parsedTorrentsByAnimeEp map[model.AnimeEpisode][]*model.ParsedTorrent) map[model.AnimeEpisode][]*model.ParsedTorrent {

27
torrent_priority.go Normal file
View File

@ -0,0 +1,27 @@
package main
import (
"fmt"
"git.milar.in/nyaanime/model"
)
type TorrentPriority struct {
ParsedTorrent *model.ParsedTorrent
Priority int
PreferredProperties map[string]int
}
func NewTorrentPriority(torrent *model.ParsedTorrent) *TorrentPriority {
priority, preferredProperties := 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)
}

View File

@ -1,9 +1,45 @@
package main package main
import "git.milar.in/nyaanime/model" import (
"sort"
func SortTorrentsByPreferredProperties(torrents map[model.AnimeEpisode][]*model.ParsedTorrent) { "git.milar.in/milarin/slices"
// TODO "git.milar.in/nyaanime/model"
// sort torrent slices by preferred properties )
// store prefered properties in db somewhere
func SortTorrentsByPreferredProperties(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)
sort.Slice(torrentPrioList, func(i, j int) bool { return torrentPrioList[i].Priority > torrentPrioList[j].Priority })
torrentsWithPrio[animeEp] = torrentPrioList
}
return torrentsWithPrio
}
func DeterminePriority(torrent *model.ParsedTorrent) (priority int, preferredProperties map[string]int) {
preferredProperties = map[string]int{}
for _, lang := range torrent.Languages {
if langPriority, ok := PreferredLanguages[lang]; ok {
priority += langPriority
preferredProperties["lang/"+lang] = langPriority
}
}
for _, sub := range torrent.Subtitles {
if subPriority, ok := PreferredSubtitles[sub]; ok {
priority += subPriority
preferredProperties["sub/"+sub] = subPriority
}
}
if prefRes, ok := PreferredResolutions[torrent.Resolution]; ok {
priority += prefRes
preferredProperties["res/"+torrent.Resolution.String()] = prefRes
}
return
} }

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"strings" "strings"
"git.milar.in/milarin/anilist" "git.milar.in/milarin/anilist"
@ -29,3 +30,30 @@ func ParseMediaListStatus(str string) (anilist.MediaListStatus, error) {
return s, nil return s, nil
} }
func Map2Str[K comparable, T any](m map[K]T) string {
b := new(strings.Builder)
for k, v := range m {
b.WriteString(fmt.Sprintf("%v:%v ", k, v))
}
str := b.String()
return str[:len(str)-1]
}
func PrintTorrentPriority(torrentPriority *TorrentPriority) {
fmt.Printf("id: %s | resolution: %d | languages: %s | subtitles: %s | seeders: %d | leechers: %d, | downloads: %d | trusted: %t | preferred properties: %s | priority: %d\n",
torrentPriority.ParsedTorrent.Torrent.ID,
torrentPriority.ParsedTorrent.Resolution,
strings.Join(torrentPriority.ParsedTorrent.Languages, ","),
strings.Join(torrentPriority.ParsedTorrent.Subtitles, ","),
torrentPriority.ParsedTorrent.Torrent.Seeders,
torrentPriority.ParsedTorrent.Torrent.Leechers,
torrentPriority.ParsedTorrent.Torrent.Downloads,
torrentPriority.ParsedTorrent.Torrent.Trusted,
Map2Str(torrentPriority.PreferredProperties),
torrentPriority.Priority,
)
}