package model import ( "fmt" "strconv" "strings" ) type Feature[T any] struct { name string value T priority int essential bool } var ( Feature1080p = &Feature[bool]{ name: "1080p", value: true, } Feature720p = &Feature[bool]{ name: "720p", value: true, } FeatureTrusted = &Feature[bool]{ name: "trusted", value: true, } ) var ( featureLangOther = Feature[string]{ name: "lang", value: "_", } featureSubOther = Feature[string]{ name: "sub", value: "_", } ) func PrintAllFeatures() { fmt.Printf("string features: %#v\n", stringFeatures) fmt.Printf("bool features: %#v\n", boolFeatures) } var ( // string features are user-defined. 'primary key' is name and value stringFeatures = []*Feature[string]{} // bool features are known at compile time. 'primary key' is value only boolFeatures = []*Feature[bool]{Feature1080p, Feature720p, FeatureTrusted} ) func makeStringFeature(name, value string, priority int, essential bool) *Feature[string] { for _, feature := range stringFeatures { if feature.name == name && feature.value == value { panic(ErrFeatureAlreadyCreated.New(fmt.Sprintf("%s:%s", name, value))) } } feature := &Feature[string]{ name: name, value: value, priority: priority, essential: essential, } stringFeatures = append(stringFeatures, feature) return feature } func GetStringFeature(name, value string) *Feature[string] { for _, feature := range stringFeatures { if feature.name == name && feature.value == value { return feature } } // lang and sub features have their special '_' value if name == "lang" { return makeStringFeature(name, value, featureLangOther.priority, featureLangOther.essential) } else if name == "sub" { return makeStringFeature(name, value, featureSubOther.priority, featureSubOther.essential) } panic("TODO") } func GetBoolFeature(name string, value bool) *Feature[bool] { for _, feature := range boolFeatures { if feature.name == name { return feature } } panic("TODO") } func ParseEssentialFeatures(essentialFeatureString string) { featureSpecs := strings.Split(essentialFeatureString, ",") for _, featureSpec := range featureSpecs { handleFeatureSpec(featureSpec, -1, true) } } func handleFeatureSpec(featureSpec string, priority int, essential bool) { data := strings.Split(featureSpec, ":") name := data[0] if name == "lang" || name == "sub" { if len(data) < 2 { panic(ErrFeatureWithoutValue.New(featureSpec)) } value := data[1] makeStringFeature(name, value, priority, essential) return } value := "" if len(data) >= 2 { value = data[1] } var err error switch name { case "1080p": { Feature1080p.value, err = strconv.ParseBool(value) if err != nil { panic(ErrInvalidFeatureValue.New("bool")) } Feature1080p.priority = priority Feature1080p.essential = essential } case "720p": { Feature720p.value, err = strconv.ParseBool(value) if err != nil { panic(ErrInvalidFeatureValue.New("bool")) } Feature720p.priority = priority Feature720p.essential = essential } case "trusted": { FeatureTrusted.value, err = strconv.ParseBool(value) if err != nil { panic(ErrInvalidFeatureValue.New("bool")) } FeatureTrusted.priority = priority FeatureTrusted.essential = essential } } } /* examples for feature priorization: # if multiple torrents are available, the torrent with most features of this list will be prioritized. # when a new torrent with more features is uploaded, it will replace the files of the old torrent. # that ensures availability of latest episodes but highest preferred quality on the long run # # This list is ordered by the separator '|'. Features within the same '|' block (separated by comma) get same priority. # the first '|' block is prioritized highest. # # '_' is a placeholder for all other features not directly mentioned in the list. # It can be used for a default priority. If not default priority is given, it will be set to 0. # # This example basically means: # torrents with japanese language have priority 3 # torrents with english or german languages or subtitles in english or german have priority 2 # torrents with any other languages or subtitles will have priority 1 TORRENT_PRIORITIZED_FEATURES="sub:ja|lang:en,sub:en,lang:de,sub:de|lang:_,sub:_" # torrents without these features will never be downloaded ('_' features not supported)- # # essential features will be parsed before prioritized features. # a feature cannot be both essential and prioritized. # # This example basically means: Only download torrents with japanese language and subtitles TORRENT_ESSENTIAL_FEATURES="lang:ja,1080p" # complete feature list: # "lang:": language (audio) # "sub:": subtitles # "1080p[:]": full hd resolution with optional bool flag # "720p[:]": hd resolution # "trusted[:]": trusted torrent uploader (highlighted in green on nyaa.si) TODO: add feature min-seeders: TODO: add feature max-seeders: TODO: add feature min-leechers: TODO: add feature max-leechers: TODO: add feature min-downloads: TODO: add feature max-downloads: TODO: add feature min-upload-time: TODO: add feature max-upload-time: */