autobrr/internal/domain/indexer.go
E7 48d6468503
feat(indexers): TorrentName templating (#381)
* feat(indexers): animebytes improve parsing

* Update animebytes.yaml

fix music parsing

* parse releaseEpisode

* add torrentname template

* improve releaseTags parsing

* add torrentName templating
conditional rendering of group and episode number
add freeleech parsing

* fix(indexers): improve ab releasename parsing

* feat(macros): expose TorrentID, GroupID, and Size

* Revert "feat(macros): expose TorrentID, GroupID, and Size"

This reverts commit dae40116a1cce40f3c18d057d0af697af4407274.

* change year to use parentheses

Co-authored-by: ze0s <ze0s@riseup.net>
Co-authored-by: varoOP <varoOP@protonmail.com>
2022-08-25 14:47:41 +02:00

215 lines
5.7 KiB
Go

package domain
import (
"bytes"
"context"
"errors"
"net/url"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/dustin/go-humanize"
)
type IndexerRepo interface {
Store(ctx context.Context, indexer Indexer) (*Indexer, error)
Update(ctx context.Context, indexer Indexer) (*Indexer, error)
List(ctx context.Context) ([]Indexer, error)
Delete(ctx context.Context, id int) error
FindByFilterID(ctx context.Context, id int) ([]Indexer, error)
FindByID(ctx context.Context, id int) (*Indexer, error)
}
type Indexer struct {
ID int64 `json:"id"`
Name string `json:"name"`
Identifier string `json:"identifier"`
Enabled bool `json:"enabled"`
Implementation string `json:"implementation"`
Settings map[string]string `json:"settings,omitempty"`
}
type IndexerDefinition struct {
ID int `json:"id,omitempty"`
Name string `json:"name"`
Identifier string `json:"identifier"`
Implementation string `json:"implementation"`
Enabled bool `json:"enabled,omitempty"`
Description string `json:"description"`
Language string `json:"language"`
Privacy string `json:"privacy"`
Protocol string `json:"protocol"`
URLS []string `json:"urls"`
Supports []string `json:"supports"`
Settings []IndexerSetting `json:"settings,omitempty"`
SettingsMap map[string]string `json:"-"`
IRC *IndexerIRC `json:"irc,omitempty"`
Torznab *Torznab `json:"torznab,omitempty"`
RSS *FeedSettings `json:"rss,omitempty"`
Parse *IndexerParse `json:"parse,omitempty"`
}
func (i IndexerDefinition) HasApi() bool {
for _, a := range i.Supports {
if a == "api" {
return true
}
}
return false
}
type IndexerSetting struct {
Name string `json:"name"`
Required bool `json:"required,omitempty"`
Type string `json:"type"`
Value string `json:"value,omitempty"`
Label string `json:"label"`
Default string `json:"default,omitempty"`
Description string `json:"description,omitempty"`
Help string `json:"help,omitempty"`
Regex string `json:"regex,omitempty"`
}
type Torznab struct {
MinInterval int `json:"minInterval"`
Settings []IndexerSetting `json:"settings"`
}
type FeedSettings struct {
MinInterval int `json:"minInterval"`
Settings []IndexerSetting `json:"settings"`
}
type IndexerIRC struct {
Network string `json:"network"`
Server string `json:"server"`
Port int `json:"port"`
TLS bool `json:"tls"`
Channels []string `json:"channels"`
Announcers []string `json:"announcers"`
SettingsMap map[string]string `json:"-"`
Settings []IndexerSetting `json:"settings"`
}
func (i IndexerIRC) ValidAnnouncer(announcer string) bool {
for _, a := range i.Announcers {
if a == announcer {
return true
}
}
return false
}
func (i IndexerIRC) ValidChannel(channel string) bool {
for _, a := range i.Channels {
if a == channel {
return true
}
}
return false
}
type IndexerParse struct {
Type string `json:"type"`
ForceSizeUnit string `json:"forcesizeunit"`
Lines []IndexerParseExtract `json:"lines"`
Match IndexerParseMatch `json:"match"`
}
type IndexerParseExtract struct {
Test []string `json:"test"`
Pattern string `json:"pattern"`
Vars []string `json:"vars"`
}
type IndexerParseMatch struct {
TorrentURL string `json:"torrenturl"`
TorrentName string `json:"torrentname"`
Encode []string `json:"encode"`
}
func (p *IndexerParse) ParseMatch(vars map[string]string, extraVars map[string]string, release *Release) error {
tmpVars := map[string]string{}
// copy vars to new tmp map
for k, v := range vars {
tmpVars[k] = v
}
// merge extra vars with vars
if extraVars != nil {
for k, v := range extraVars {
tmpVars[k] = v
}
}
// handle url encode of values
if p.Match.Encode != nil {
for _, e := range p.Match.Encode {
if v, ok := tmpVars[e]; ok {
// url encode value
t := url.QueryEscape(v)
tmpVars[e] = t
}
}
}
if p.Match.TorrentURL != "" {
// setup text template to inject variables into
tmpl, err := template.New("torrenturl").Funcs(sprig.TxtFuncMap()).Parse(p.Match.TorrentURL)
if err != nil {
return errors.New("could not create torrent url template")
}
var urlBytes bytes.Buffer
err = tmpl.Execute(&urlBytes, &tmpVars)
if err != nil {
return errors.New("could not write torrent url template output")
}
release.TorrentURL = urlBytes.String()
}
if p.Match.TorrentName != "" {
// setup text template to inject variables into
tmplName, err := template.New("torrentname").Funcs(sprig.TxtFuncMap()).Parse(p.Match.TorrentName)
if err != nil {
return err
}
var nameBytes bytes.Buffer
err = tmplName.Execute(&nameBytes, &tmpVars)
if err != nil {
return errors.New("could not write torrent name template output")
}
release.TorrentName = nameBytes.String()
}
// handle cookies
if v, ok := extraVars["cookie"]; ok {
release.RawCookie = v
}
return nil
}
type TorrentBasic struct {
Id string `json:"Id"`
TorrentId string `json:"TorrentId,omitempty"`
InfoHash string `json:"InfoHash"`
Size string `json:"Size"`
}
func (t TorrentBasic) ReleaseSizeBytes() uint64 {
if t.Size == "" {
return 0
}
releaseSizeBytes, err := humanize.ParseBytes(t.Size)
if err != nil {
// log could not parse into bytes
return 0
}
return releaseSizeBytes
}