mirror of
https://github.com/idanoo/autobrr
synced 2025-07-23 16:59:12 +00:00
feat(filters): add music filters (#91)
* feat(filters): add music filters * feat: improve parsing and filtering * feat: add red api support
This commit is contained in:
parent
30c11d4ef1
commit
00bc8298ac
20 changed files with 1053 additions and 52 deletions
|
@ -58,7 +58,7 @@ func (r *FilterRepo) FindByID(filterID int) (*domain.Filter, error) {
|
|||
//r.db.lock.RLock()
|
||||
//defer r.db.lock.RUnlock()
|
||||
|
||||
row := r.db.handler.QueryRow("SELECT id, enabled, name, min_size, max_size, delay, match_releases, except_releases, use_regex, match_release_groups, except_release_groups, scene, freeleech, freeleech_percent, shows, seasons, episodes, resolutions, codecs, sources, containers, match_hdr, except_hdr, years, match_categories, except_categories, match_uploaders, except_uploaders, tags, except_tags, created_at, updated_at FROM filter WHERE id = ?", filterID)
|
||||
row := r.db.handler.QueryRow("SELECT id, enabled, name, min_size, max_size, delay, match_releases, except_releases, use_regex, match_release_groups, except_release_groups, scene, freeleech, freeleech_percent, shows, seasons, episodes, resolutions, codecs, sources, containers, match_hdr, except_hdr, years, artists, albums, release_types_match, formats, quality, log_score, has_log, has_cue, perfect_flac, match_categories, except_categories, match_uploaders, except_uploaders, tags, except_tags, created_at, updated_at FROM filter WHERE id = ?", filterID)
|
||||
|
||||
var f domain.Filter
|
||||
|
||||
|
@ -66,11 +66,11 @@ func (r *FilterRepo) FindByID(filterID int) (*domain.Filter, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString
|
||||
var useRegex, scene, freeleech sql.NullBool
|
||||
var delay sql.NullInt32
|
||||
var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString
|
||||
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
|
||||
var delay, logScore sql.NullInt32
|
||||
|
||||
if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if err := row.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
log.Error().Stack().Err(err).Msgf("filter: %v : error scanning data to struct", filterID)
|
||||
return nil, err
|
||||
}
|
||||
|
@ -87,6 +87,12 @@ func (r *FilterRepo) FindByID(filterID int) (*domain.Filter, error) {
|
|||
f.Seasons = seasons.String
|
||||
f.Episodes = episodes.String
|
||||
f.Years = years.String
|
||||
f.Artists = artists.String
|
||||
f.Albums = albums.String
|
||||
f.LogScore = int(logScore.Int32)
|
||||
f.Log = hasLog.Bool
|
||||
f.Cue = hasCue.Bool
|
||||
f.PerfectFlac = perfectFlac.Bool
|
||||
f.MatchCategories = matchCategories.String
|
||||
f.ExceptCategories = exceptCategories.String
|
||||
f.MatchUploaders = matchUploaders.String
|
||||
|
@ -131,6 +137,15 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e
|
|||
f.match_hdr,
|
||||
f.except_hdr,
|
||||
f.years,
|
||||
f.artists,
|
||||
f.albums,
|
||||
f.release_types_match,
|
||||
f.formats,
|
||||
f.quality,
|
||||
f.log_score,
|
||||
f.has_log,
|
||||
f.has_cue,
|
||||
f.perfect_flac,
|
||||
f.match_categories,
|
||||
f.except_categories,
|
||||
f.match_uploaders,
|
||||
|
@ -155,11 +170,11 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e
|
|||
for rows.Next() {
|
||||
var f domain.Filter
|
||||
|
||||
var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString
|
||||
var useRegex, scene, freeleech sql.NullBool
|
||||
var delay sql.NullInt32
|
||||
var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString
|
||||
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
|
||||
var delay, logScore sql.NullInt32
|
||||
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &minSize, &maxSize, &delay, &matchReleases, &exceptReleases, &useRegex, &matchReleaseGroups, &exceptReleaseGroups, &scene, &freeleech, &freeleechPercent, &shows, &seasons, &episodes, pq.Array(&f.Resolutions), pq.Array(&f.Codecs), pq.Array(&f.Sources), pq.Array(&f.Containers), pq.Array(&f.MatchHDR), pq.Array(&f.ExceptHDR), &years, &artists, &albums, pq.Array(&f.MatchReleaseTypes), pq.Array(&f.Formats), pq.Array(&f.Quality), &logScore, &hasLog, &hasCue, &perfectFlac, &matchCategories, &exceptCategories, &matchUploaders, &exceptUploaders, &tags, &exceptTags, &f.CreatedAt, &f.UpdatedAt); err != nil {
|
||||
log.Error().Stack().Err(err).Msg("error scanning data to struct")
|
||||
return nil, err
|
||||
}
|
||||
|
@ -176,6 +191,12 @@ func (r *FilterRepo) FindByIndexerIdentifier(indexer string) ([]domain.Filter, e
|
|||
f.Seasons = seasons.String
|
||||
f.Episodes = episodes.String
|
||||
f.Years = years.String
|
||||
f.Artists = artists.String
|
||||
f.Albums = albums.String
|
||||
f.LogScore = int(logScore.Int32)
|
||||
f.Log = hasLog.Bool
|
||||
f.Cue = hasCue.Bool
|
||||
f.PerfectFlac = perfectFlac.Bool
|
||||
f.MatchCategories = matchCategories.String
|
||||
f.ExceptCategories = exceptCategories.String
|
||||
f.MatchUploaders = matchUploaders.String
|
||||
|
@ -234,9 +255,18 @@ func (r *FilterRepo) Store(filter domain.Filter) (*domain.Filter, error) {
|
|||
match_uploaders,
|
||||
except_uploaders,
|
||||
tags,
|
||||
except_tags
|
||||
except_tags,
|
||||
artists,
|
||||
albums,
|
||||
release_types_match,
|
||||
formats,
|
||||
quality,
|
||||
log_score,
|
||||
has_log,
|
||||
has_cue,
|
||||
perfect_flac
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29) ON CONFLICT DO NOTHING`,
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38) ON CONFLICT DO NOTHING`,
|
||||
filter.Name,
|
||||
filter.Enabled,
|
||||
filter.MinSize,
|
||||
|
@ -266,6 +296,15 @@ func (r *FilterRepo) Store(filter domain.Filter) (*domain.Filter, error) {
|
|||
filter.ExceptUploaders,
|
||||
filter.Tags,
|
||||
filter.ExceptTags,
|
||||
filter.Artists,
|
||||
filter.Albums,
|
||||
pq.Array(filter.MatchReleaseTypes),
|
||||
pq.Array(filter.Formats),
|
||||
pq.Array(filter.Quality),
|
||||
filter.LogScore,
|
||||
filter.Log,
|
||||
filter.Cue,
|
||||
filter.PerfectFlac,
|
||||
)
|
||||
if err != nil {
|
||||
log.Error().Stack().Err(err).Msg("error executing query")
|
||||
|
@ -317,6 +356,15 @@ func (r *FilterRepo) Update(ctx context.Context, filter domain.Filter) (*domain.
|
|||
except_uploaders = ?,
|
||||
tags = ?,
|
||||
except_tags = ?,
|
||||
artists = ?,
|
||||
albums = ?,
|
||||
release_types_match = ?,
|
||||
formats = ?,
|
||||
quality = ?,
|
||||
log_score = ?,
|
||||
has_log = ?,
|
||||
has_cue = ?,
|
||||
perfect_flac = ?,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = ?`,
|
||||
filter.Name,
|
||||
|
@ -348,6 +396,15 @@ func (r *FilterRepo) Update(ctx context.Context, filter domain.Filter) (*domain.
|
|||
filter.ExceptUploaders,
|
||||
filter.Tags,
|
||||
filter.ExceptTags,
|
||||
filter.Artists,
|
||||
filter.Albums,
|
||||
pq.Array(filter.MatchReleaseTypes),
|
||||
pq.Array(filter.Formats),
|
||||
pq.Array(filter.Quality),
|
||||
filter.LogScore,
|
||||
filter.Log,
|
||||
filter.Cue,
|
||||
filter.PerfectFlac,
|
||||
filter.ID,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -84,6 +84,16 @@ CREATE TABLE filter
|
|||
match_hdr TEXT [] DEFAULT '{}',
|
||||
except_hdr TEXT [] DEFAULT '{}',
|
||||
years TEXT,
|
||||
artists TEXT,
|
||||
albums TEXT,
|
||||
release_types_match TEXT [] DEFAULT '{}',
|
||||
release_types_ignore TEXT [] DEFAULT '{}',
|
||||
formats TEXT [] DEFAULT '{}',
|
||||
quality TEXT [] DEFAULT '{}',
|
||||
log_score INTEGER,
|
||||
has_log BOOLEAN,
|
||||
has_cue BOOLEAN,
|
||||
perfect_flac BOOLEAN,
|
||||
match_categories TEXT,
|
||||
except_categories TEXT,
|
||||
match_uploaders TEXT,
|
||||
|
@ -178,7 +188,7 @@ CREATE TABLE "release"
|
|||
artists TEXT [] DEFAULT '{}' NOT NULL,
|
||||
type TEXT,
|
||||
format TEXT,
|
||||
bitrate TEXT,
|
||||
quality TEXT,
|
||||
log_score INTEGER,
|
||||
has_log BOOLEAN,
|
||||
has_cue BOOLEAN,
|
||||
|
@ -289,6 +299,40 @@ var migrations = []string{
|
|||
ALTER TABLE "filter"
|
||||
ADD COLUMN except_hdr TEXT [] DEFAULT '{}';
|
||||
`,
|
||||
`
|
||||
ALTER TABLE "release"
|
||||
RENAME COLUMN bitrate TO quality;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN artists TEXT;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN albums TEXT;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN release_types_match TEXT [] DEFAULT '{}';
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN release_types_ignore TEXT [] DEFAULT '{}';
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN formats TEXT [] DEFAULT '{}';
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN quality TEXT [] DEFAULT '{}';
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN log_score INTEGER;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN has_log BOOLEAN;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN has_cue BOOLEAN;
|
||||
|
||||
ALTER TABLE "filter"
|
||||
ADD COLUMN perfect_flac BOOLEAN;
|
||||
`,
|
||||
}
|
||||
|
||||
func (db *SqliteDB) migrate() error {
|
||||
|
|
|
@ -24,8 +24,8 @@ func (repo *ReleaseRepo) Store(ctx context.Context, r *domain.Release) (*domain.
|
|||
|
||||
query, args, err := sq.
|
||||
Insert("release").
|
||||
Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "raw", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "audio", "release_group", "region", "language", "edition", "unrated", "hybrid", "proper", "repack", "website", "artists", "type", "format", "bitrate", "log_score", "has_log", "has_cue", "is_scene", "origin", "tags", "freeleech", "freeleech_percent", "uploader", "pre_time").
|
||||
Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Raw, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, r.Codec, r.Container, r.HDR, r.Audio, r.Group, r.Region, r.Language, r.Edition, r.Unrated, r.Hybrid, r.Proper, r.Repack, r.Website, pq.Array(r.Artists), r.Type, r.Format, r.Bitrate, r.LogScore, r.HasLog, r.HasCue, r.IsScene, r.Origin, pq.Array(r.Tags), r.Freeleech, r.FreeleechPercent, r.Uploader, r.PreTime).
|
||||
Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "torrent_name", "size", "raw", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "audio", "release_group", "region", "language", "edition", "unrated", "hybrid", "proper", "repack", "website", "artists", "type", "format", "quality", "log_score", "has_log", "has_cue", "is_scene", "origin", "tags", "freeleech", "freeleech_percent", "uploader", "pre_time").
|
||||
Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer, r.FilterName, r.Protocol, r.Implementation, r.Timestamp, r.GroupID, r.TorrentID, r.TorrentName, r.Size, r.Raw, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, r.Codec, r.Container, r.HDR, r.Audio, r.Group, r.Region, r.Language, r.Edition, r.Unrated, r.Hybrid, r.Proper, r.Repack, r.Website, pq.Array(r.Artists), r.Type, r.Format, r.Quality, r.LogScore, r.HasLog, r.HasCue, r.IsScene, r.Origin, pq.Array(r.Tags), r.Freeleech, r.FreeleechPercent, r.Uploader, r.PreTime).
|
||||
ToSql()
|
||||
|
||||
res, err := repo.db.handler.ExecContext(ctx, query, args...)
|
||||
|
|
|
@ -44,7 +44,7 @@ func (db *SqliteDB) Open() error {
|
|||
|
||||
// Set busy timeout
|
||||
if _, err = db.handler.Exec(`PRAGMA busy_timeout = 5000;`); err != nil {
|
||||
return fmt.Errorf("busy timeout pragma")
|
||||
return fmt.Errorf("busy timeout pragma: %w", err)
|
||||
}
|
||||
|
||||
// Enable WAL. SQLite performs better with the WAL because it allows
|
||||
|
|
|
@ -53,22 +53,23 @@ type Filter struct {
|
|||
Years string `json:"years"`
|
||||
Artists string `json:"artists"`
|
||||
Albums string `json:"albums"`
|
||||
MatchReleaseTypes string `json:"match_release_types"` // Album,Single,EP
|
||||
MatchReleaseTypes []string `json:"match_release_types"` // Album,Single,EP
|
||||
ExceptReleaseTypes string `json:"except_release_types"`
|
||||
Formats []string `json:"formats"` // MP3, FLAC, Ogg, AAC, AC3, DTS
|
||||
Bitrates []string `json:"bitrates"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
|
||||
Media []string `json:"media"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
|
||||
Cue bool `json:"cue"`
|
||||
Log bool `json:"log"`
|
||||
LogScores string `json:"log_scores"`
|
||||
MatchCategories string `json:"match_categories"`
|
||||
ExceptCategories string `json:"except_categories"`
|
||||
MatchUploaders string `json:"match_uploaders"`
|
||||
ExceptUploaders string `json:"except_uploaders"`
|
||||
Tags string `json:"tags"`
|
||||
ExceptTags string `json:"except_tags"`
|
||||
TagsAny string `json:"tags_any"`
|
||||
ExceptTagsAny string `json:"except_tags_any"`
|
||||
Actions []Action `json:"actions"`
|
||||
Indexers []Indexer `json:"indexers"`
|
||||
Formats []string `json:"formats"` // MP3, FLAC, Ogg, AAC, AC3, DTS
|
||||
Quality []string `json:"quality"` // 192, 320, APS (VBR), V2 (VBR), V1 (VBR), APX (VBR), V0 (VBR), q8.x (VBR), Lossless, 24bit Lossless, Other
|
||||
//Media []string `json:"media"` // CD, DVD, Vinyl, Soundboard, SACD, DAT, Cassette, WEB, Other
|
||||
PerfectFlac bool `json:"perfect_flac"`
|
||||
Cue bool `json:"cue"`
|
||||
Log bool `json:"log"`
|
||||
LogScore int `json:"log_score"`
|
||||
MatchCategories string `json:"match_categories"`
|
||||
ExceptCategories string `json:"except_categories"`
|
||||
MatchUploaders string `json:"match_uploaders"`
|
||||
ExceptUploaders string `json:"except_uploaders"`
|
||||
Tags string `json:"tags"`
|
||||
ExceptTags string `json:"except_tags"`
|
||||
TagsAny string `json:"tags_any"`
|
||||
ExceptTagsAny string `json:"except_tags_any"`
|
||||
Actions []Action `json:"actions"`
|
||||
Indexers []Indexer `json:"indexers"`
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ type Release struct {
|
|||
Artists []string `json:"artists"`
|
||||
Type string `json:"type"` // Album,Single,EP
|
||||
Format string `json:"format"` // music only
|
||||
Bitrate string `json:"bitrate"` // bitrate
|
||||
Quality string `json:"quality"` // quality
|
||||
LogScore int `json:"log_score"`
|
||||
HasLog bool `json:"has_log"`
|
||||
HasCue bool `json:"has_cue"`
|
||||
|
@ -151,6 +151,10 @@ func (r *Release) Parse() error {
|
|||
}
|
||||
|
||||
func (r *Release) extractYear() error {
|
||||
if r.Year > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
y, err := findLastInt(r.TorrentName, `\b(((?:19[0-9]|20[0-9])[0-9]))\b`)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -285,7 +289,7 @@ func (r *Release) extractHDR() error {
|
|||
}
|
||||
|
||||
func (r *Release) extractAudio() error {
|
||||
v, err := findLast(r.TorrentName, `(?i)(MP3|FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`)
|
||||
v, err := findLast(r.TorrentName, `(?i)(FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -299,7 +303,7 @@ func (r *Release) extractAudioFromTags(tag string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
v, err := findLast(tag, `(?i)(MP3|Ogg Vorbis|FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`)
|
||||
v, err := findLast(tag, `(?i)(FLAC[\. ][1-7][\. ][0-2]|FLAC|Opus|DD-EX|DDP[\. ]?[124567][\. ][012] Atmos|DDP[\. ]?[124567][\. ][012]|DDP|DD[1-7][\. ][0-2]|Dual[\- ]Audio|LiNE|PCM|Dolby TrueHD [0-9][\. ][0-4]|TrueHD [0-9][\. ][0-4] Atmos|TrueHD [0-9][\. ][0-4]|DTS X|DTS-HD MA [0-9][\. ][0-4]|DTS-HD MA|DTS-ES|DTS [1-7][\. ][0-2]|DTS|DD|DD[12][\. ]0|Dolby Atmos|TrueHD ATMOS|TrueHD|Atmos|Dolby Digital Plus|Dolby Digital Audio|Dolby Digital|AAC[.-]LC|AAC (?:\.?[1-7]\.[0-2])?|AAC|eac3|AC3(?:\.5\.1)?)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -308,6 +312,20 @@ func (r *Release) extractAudioFromTags(tag string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Release) extractFormatsFromTags(tag string) error {
|
||||
if r.Format != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, err := findLast(tag, `(?:MP3|FLAC|Ogg Vorbis|AAC|AC3|DTS)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Format = v
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//func (r *Release) extractCueFromTags(tag string) error {
|
||||
// v, err := findLast(tag, `Cue`)
|
||||
// if err != nil {
|
||||
|
@ -477,14 +495,14 @@ func (r *Release) extractLogScoreFromTags(tag string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (r *Release) extractBitrateFromTags(tag string) error {
|
||||
if r.Bitrate != "" {
|
||||
func (r *Release) extractQualityFromTags(tag string) error {
|
||||
if r.Quality != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start with the basic most common ones
|
||||
|
||||
rxp, err := regexp.Compile(`^(?:vbr|aps|apx|v\d|\d{2,4}|\d+\.\d+|q\d+\.[\dx]+|Other)?(?:\s*kbps|\s*kbits?|\s*k)?(?:\s*\(?(?:vbr|cbr)\)?)?$`)
|
||||
rxp, err := regexp.Compile(`(Lossless|24bit Lossless|V0 \(VBR\)|V1 \(VBR\)|V2 \(VBR\)|APS \(VBR\)|APX \(VBR\)|320|256|192)`)
|
||||
if err != nil {
|
||||
return err
|
||||
//return errors.Wrapf(err, "invalid regex: %s", value)
|
||||
|
@ -496,7 +514,7 @@ func (r *Release) extractBitrateFromTags(tag string) error {
|
|||
if len(matches) >= 1 {
|
||||
last := matches[len(matches)-1]
|
||||
|
||||
r.Bitrate = last
|
||||
r.Quality = last
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
@ -516,13 +534,14 @@ func (r *Release) extractReleaseTags() error {
|
|||
|
||||
var err error
|
||||
err = r.extractAudioFromTags(t)
|
||||
err = r.extractFormatsFromTags(t)
|
||||
err = r.extractResolutionFromTags(t)
|
||||
err = r.extractCodecFromTags(t)
|
||||
err = r.extractContainerFromTags(t)
|
||||
err = r.extractSourceFromTags(t)
|
||||
err = r.extractFreeleechFromTags(t)
|
||||
err = r.extractLogScoreFromTags(t)
|
||||
err = r.extractBitrateFromTags(t)
|
||||
err = r.extractQualityFromTags(t)
|
||||
err = r.extractAnimeGroupFromTags(t)
|
||||
|
||||
if err != nil {
|
||||
|
@ -781,6 +800,11 @@ func (r *Release) CheckFilter(filter Filter) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(filter.MatchReleaseTypes) > 0 && !checkFilterSlice(r.Category, filter.MatchReleaseTypes) {
|
||||
r.addRejection("release type not matching")
|
||||
return false
|
||||
}
|
||||
|
||||
if (filter.MinSize != "" || filter.MaxSize != "") && !r.CheckSizeFilter(filter.MinSize, filter.MaxSize) {
|
||||
return false
|
||||
}
|
||||
|
@ -795,6 +819,49 @@ func (r *Release) CheckFilter(filter Filter) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if len(filter.Artists) > 0 && !checkFilterStrings(r.TorrentName, filter.Artists) {
|
||||
r.addRejection("artists not matching")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(filter.Albums) > 0 && !checkFilterStrings(r.TorrentName, filter.Albums) {
|
||||
r.addRejection("albums not matching")
|
||||
return false
|
||||
}
|
||||
|
||||
// Perfect flac requires Cue, Log, Log Score 100, FLAC and 24bit Lossless
|
||||
if filter.PerfectFlac {
|
||||
if !r.HasLog || !r.HasCue || r.LogScore != 100 || r.Format != "FLAC" && !checkFilterSlice(r.Quality, []string{"Lossless", "24bit Lossless"}) {
|
||||
r.addRejection("wanted: log")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if len(filter.Formats) > 0 && !checkFilterSlice(r.Format, filter.Formats) {
|
||||
r.addRejection("formats not matching")
|
||||
return false
|
||||
}
|
||||
|
||||
if len(filter.Quality) > 0 && !checkFilterSlice(r.Quality, filter.Quality) {
|
||||
r.addRejection("formats not matching")
|
||||
return false
|
||||
}
|
||||
|
||||
if filter.Log && r.HasLog != filter.Log {
|
||||
r.addRejection("wanted: log")
|
||||
return false
|
||||
}
|
||||
|
||||
if filter.Log && filter.LogScore != 0 && r.LogScore != filter.LogScore {
|
||||
r.addRejection("wanted: log score")
|
||||
return false
|
||||
}
|
||||
|
||||
if filter.Cue && r.HasCue != filter.Cue {
|
||||
r.addRejection("wanted: cue")
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -158,6 +158,8 @@ func TestRelease_Parse(t *testing.T) {
|
|||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Group: "",
|
||||
Audio: "FLAC",
|
||||
Format: "FLAC",
|
||||
Quality: "Lossless",
|
||||
Source: "CD",
|
||||
HasCue: true,
|
||||
HasLog: true,
|
||||
|
@ -178,9 +180,9 @@ func TestRelease_Parse(t *testing.T) {
|
|||
Tags: []string{"house, techno, tech.house, electro.house, future.house, bass.house, melodic.house"},
|
||||
ReleaseTags: "MP3 / 320 / Cassette",
|
||||
Group: "",
|
||||
Audio: "MP3",
|
||||
Format: "MP3",
|
||||
Source: "Cassette",
|
||||
Bitrate: "320",
|
||||
Quality: "320",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
|
@ -195,7 +197,8 @@ func TestRelease_Parse(t *testing.T) {
|
|||
Clean: "The artist (ザ・フリーダムユニティ) Long album name",
|
||||
ReleaseTags: "MP3 / V0 (VBR) / CD",
|
||||
Group: "",
|
||||
Audio: "MP3",
|
||||
Format: "MP3",
|
||||
Quality: "V0 (VBR)",
|
||||
Source: "CD",
|
||||
},
|
||||
wantErr: false,
|
||||
|
@ -212,6 +215,29 @@ func TestRelease_Parse(t *testing.T) {
|
|||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Group: "",
|
||||
Audio: "FLAC",
|
||||
Format: "FLAC",
|
||||
Quality: "Lossless",
|
||||
Source: "CD",
|
||||
HasCue: true,
|
||||
HasLog: true,
|
||||
LogScore: 100,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "parse_music_5",
|
||||
fields: Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD",
|
||||
},
|
||||
want: Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
Clean: "Artist Albumname",
|
||||
ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD",
|
||||
Group: "",
|
||||
Audio: "FLAC",
|
||||
Format: "FLAC",
|
||||
Quality: "24bit Lossless",
|
||||
Source: "CD",
|
||||
HasCue: true,
|
||||
HasLog: true,
|
||||
|
@ -1031,6 +1057,192 @@ func TestRelease_CheckFilter(t *testing.T) {
|
|||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "match_music_1",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / 24bit Lossless / Log / 100% / Cue / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchCategories: "Album",
|
||||
Artists: "Artist",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless"},
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "match_music_2",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "MP3 / 320 / WEB",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchCategories: "Album",
|
||||
Artists: "Artist",
|
||||
//Sources: []string{"CD"},
|
||||
//Formats: []string{"FLAC"},
|
||||
//Quality: []string{"24bit Lossless"},
|
||||
PerfectFlac: true,
|
||||
//Log: true,
|
||||
//LogScore: 100,
|
||||
//Cue: true,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "match_music_3",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchCategories: "Album",
|
||||
Artists: "Artist",
|
||||
//Sources: []string{"CD"},
|
||||
//Formats: []string{"FLAC"},
|
||||
//Quality: []string{"24bit Lossless"},
|
||||
PerfectFlac: true,
|
||||
//Log: true,
|
||||
//LogScore: 100,
|
||||
//Cue: true,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "match_music_4",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchCategories: "Album",
|
||||
Artists: "Artist",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless"},
|
||||
//PerfectFlac: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "match_music_5",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
Year: 2022,
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchReleaseTypes: []string{"Album"},
|
||||
Years: "2020-2022",
|
||||
Artists: "Artist",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless", "Lossless"},
|
||||
PerfectFlac: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "match_music_6",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchReleaseTypes: []string{"Single"},
|
||||
Artists: "Artist",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless", "Lossless"},
|
||||
PerfectFlac: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "match_music_7",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchReleaseTypes: []string{"Album"},
|
||||
Artists: "Artiiiist",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless", "Lossless"},
|
||||
PerfectFlac: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "match_music_8",
|
||||
fields: &Release{
|
||||
TorrentName: "Artist - Albumname",
|
||||
ReleaseTags: "FLAC / Lossless / Log / 100% / Cue / CD",
|
||||
Category: "Album",
|
||||
},
|
||||
args: args{
|
||||
filter: Filter{
|
||||
Enabled: true,
|
||||
MatchReleaseTypes: []string{"Album"},
|
||||
Artists: "Artist",
|
||||
Albums: "Albumname",
|
||||
Sources: []string{"CD"},
|
||||
Formats: []string{"FLAC"},
|
||||
Quality: []string{"24bit Lossless", "Lossless"},
|
||||
PerfectFlac: true,
|
||||
Log: true,
|
||||
LogScore: 100,
|
||||
Cue: true,
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
@ -211,8 +211,8 @@ func (s *service) FindAndCheckFilters(release *domain.Release) (bool, *domain.Fi
|
|||
if release.AdditionalSizeCheckRequired {
|
||||
log.Debug().Msgf("filter-service.find_and_check_filters: (%v) additional size check required", f.Name)
|
||||
|
||||
// check if indexer = btn,ptp (ggn,red later)
|
||||
if release.Indexer == "ptp" || release.Indexer == "btn" || release.Indexer == "ggn" {
|
||||
// check if indexer = btn, ptp, ggn or red
|
||||
if release.Indexer == "ptp" || release.Indexer == "btn" || release.Indexer == "ggn" || release.Indexer == "redacted" {
|
||||
// fetch torrent info from api
|
||||
// save outside of loop to check multiple filters with only one fetch
|
||||
if torrentInfo == nil {
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/autobrr/autobrr/pkg/btn"
|
||||
"github.com/autobrr/autobrr/pkg/ggn"
|
||||
"github.com/autobrr/autobrr/pkg/ptp"
|
||||
"github.com/autobrr/autobrr/pkg/red"
|
||||
)
|
||||
|
||||
type APIService interface {
|
||||
|
@ -104,6 +105,13 @@ func (s *apiService) AddClient(indexer string, settings map[string]string) error
|
|||
}
|
||||
s.apiClients[indexer] = ggn.NewClient("", key)
|
||||
|
||||
case "redacted":
|
||||
key, ok := settings["api_key"]
|
||||
if !ok || key == "" {
|
||||
return fmt.Errorf("api_service: could not initialize red client: missing var 'api_key'")
|
||||
}
|
||||
s.apiClients[indexer] = red.NewClient("", key)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("api_service: could not initialize client: unsupported indexer '%v'", indexer)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ urls:
|
|||
privacy: private
|
||||
protocol: torrent
|
||||
supports:
|
||||
- api
|
||||
- irc
|
||||
- rss
|
||||
source: gazelle
|
||||
|
@ -21,6 +22,22 @@ settings:
|
|||
type: secret
|
||||
label: Torrent pass
|
||||
help: Right click DL on a torrent and get the torrent_pass.
|
||||
- name: api_key
|
||||
type: secret
|
||||
label: API Key
|
||||
help: Settings -> Account Settings -> API Keys - Generate new api keys. Scope (User, Torrents)
|
||||
|
||||
api:
|
||||
url: https://redacted.ch/ajax.php
|
||||
type: json
|
||||
limits:
|
||||
max: 10
|
||||
per: 10 seconds
|
||||
settings:
|
||||
- name: api_key
|
||||
type: secret
|
||||
label: API Key
|
||||
help: Settings -> Account Settings -> API Keys - Generate new api keys. Scope (User, Torrents)
|
||||
|
||||
irc:
|
||||
network: Scratch-Network
|
||||
|
@ -55,12 +72,13 @@ parse:
|
|||
- test:
|
||||
- "Artist - Albumname [2008] [Single] - FLAC / Lossless / Log / 100% / Cue / CD - https://redacted.ch/torrents.php?id=0000000 / https://redacted.ch/torrents.php?action=download&id=0000000 - hip.hop,rhythm.and.blues,2000s"
|
||||
- "A really long name here - Concertos 5 and 6, Suite No 2 [1991] [Album] - FLAC / Lossless / Log / 100% / Cue / CD - https://redacted.ch/torrents.php?id=0000000 / https://redacted.ch/torrents.php?action=download&id=0000000 - classical"
|
||||
pattern: '^(.*)\s+\[(.*)\] \[(.*)\] - (.*) -\s+https?:.*[&\?]id=.*(https?\:\/\/.*)\s* -\s*(.*)'
|
||||
pattern: '(.*) (?:\[(.*)\] \[(.*)\] - (.*))? -\s+https?:.*[&\?]id=(\d+) \/ (https?\:\/\/.*)\s* -\s*(.*)'
|
||||
vars:
|
||||
- torrentName
|
||||
- year
|
||||
- category
|
||||
- releaseTags
|
||||
- torrentId
|
||||
- baseUrl
|
||||
- tags
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue