feat(filters): support daily shows (#1462)

* feat(database): Add month, day columns to release table

* feat(database): Add month, day columns to postgres release table

* feat(filters): support daily show format

* feat(filters): check smart episode daily

* fix(tests): rss

* feat(filters): add daily shows elements to form

* enhancement(web): minimize html in MoviesAndTV tab

* feat(filters): smart episode check proper and repack

* feat(filters): smart episode do not allow multiple latest

* feat(filters): smart episode check group with repack

* feat(filters): smart episode allow multiple current releases

---------

Co-authored-by: s0up4200 <soup@r4tio.dev>
Co-authored-by: ze0s <43699394+zze0s@users.noreply.github.com>
Co-authored-by: martylukyy <35452459+martylukyy@users.noreply.github.com>
This commit is contained in:
kenstir 2024-05-15 10:38:10 -04:00 committed by GitHub
parent 2a3dcfbf05
commit 4fceccd611
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 270 additions and 56 deletions

View file

@ -215,6 +215,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
"f.match_other",
"f.except_other",
"f.years",
"f.months",
"f.days",
"f.artists",
"f.albums",
"f.release_types_match",
@ -265,7 +267,7 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
var f domain.Filter
// filter
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, months, days, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
var delay, maxDownloads, logScore sql.NullInt32
@ -306,6 +308,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
pq.Array(&f.MatchOther),
pq.Array(&f.ExceptOther),
&years,
&months,
&days,
&artists,
&albums,
pq.Array(&f.MatchReleaseTypes),
@ -361,6 +365,8 @@ func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter
f.Seasons = seasons.String
f.Episodes = episodes.String
f.Years = years.String
f.Months = months.String
f.Days = days.String
f.Artists = artists.String
f.Albums = albums.String
f.LogScore = int(logScore.Int32)
@ -426,6 +432,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
"f.match_other",
"f.except_other",
"f.years",
"f.months",
"f.days",
"f.artists",
"f.albums",
"f.release_types_match",
@ -480,7 +488,7 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
for rows.Next() {
var f domain.Filter
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var minSize, maxSize, maxDownloadsUnit, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, matchReleaseTags, exceptReleaseTags, matchDescription, exceptDescription, freeleechPercent, shows, seasons, episodes, years, months, days, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags, tagsMatchLogic, exceptTagsMatchLogic sql.NullString
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
var delay, maxDownloads, logScore sql.NullInt32
@ -521,6 +529,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
pq.Array(&f.MatchOther),
pq.Array(&f.ExceptOther),
&years,
&months,
&days,
&artists,
&albums,
pq.Array(&f.MatchReleaseTypes),
@ -572,6 +582,8 @@ func (r *FilterRepo) findByIndexerIdentifier(ctx context.Context, indexer string
f.Seasons = seasons.String
f.Episodes = episodes.String
f.Years = years.String
f.Months = months.String
f.Days = days.String
f.Artists = artists.String
f.Albums = albums.String
f.LogScore = int(logScore.Int32)
@ -722,6 +734,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter *domain.Filter) error {
"match_other",
"except_other",
"years",
"months",
"days",
"match_categories",
"except_categories",
"match_uploaders",
@ -785,6 +799,8 @@ func (r *FilterRepo) Store(ctx context.Context, filter *domain.Filter) error {
pq.Array(filter.MatchOther),
pq.Array(filter.ExceptOther),
filter.Years,
filter.Months,
filter.Days,
filter.MatchCategories,
filter.ExceptCategories,
filter.MatchUploaders,
@ -866,6 +882,8 @@ func (r *FilterRepo) Update(ctx context.Context, filter *domain.Filter) error {
Set("match_other", pq.Array(filter.MatchOther)).
Set("except_other", pq.Array(filter.ExceptOther)).
Set("years", filter.Years).
Set("months", filter.Months).
Set("days", filter.Days).
Set("match_categories", filter.MatchCategories).
Set("except_categories", filter.ExceptCategories).
Set("match_uploaders", filter.MatchUploaders).
@ -1024,6 +1042,12 @@ func (r *FilterRepo) UpdatePartial(ctx context.Context, filter domain.FilterUpda
if filter.Years != nil {
q = q.Set("years", filter.Years)
}
if filter.Months != nil {
q = q.Set("months", filter.Months)
}
if filter.Days != nil {
q = q.Set("days", filter.Days)
}
if filter.MatchCategories != nil {
q = q.Set("match_categories", filter.MatchCategories)
}

View file

@ -52,6 +52,8 @@ func getMockFilter() *domain.Filter {
MatchOther: []string{"Atmos"},
ExceptOther: []string{"Atmos"},
Years: "2023",
Months: "",
Days: "",
Artists: "",
Albums: "",
MatchReleaseTypes: []string{"Remux"},

View file

@ -106,6 +106,8 @@ CREATE TABLE filter
match_other TEXT [] DEFAULT '{}',
except_other TEXT [] DEFAULT '{}',
years TEXT,
months TEXT,
days TEXT,
artists TEXT,
albums TEXT,
release_types_match TEXT [] DEFAULT '{}',
@ -245,6 +247,8 @@ CREATE TABLE "release"
season INTEGER,
episode INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
resolution TEXT,
source TEXT,
codec TEXT,
@ -884,5 +888,17 @@ ADD COLUMN first_last_piece_prio BOOLEAN DEFAULT false;
UPDATE indexer
SET identifier_external = name;
`,
`ALTER TABLE "release"
ADD COLUMN month INTEGER;
ALTER TABLE "release"
ADD COLUMN day INTEGER;
ALTER TABLE filter
ADD COLUMN months TEXT;
ALTER TABLE filter
ADD COLUMN days TEXT;
`,
}

View file

@ -38,8 +38,8 @@ func (repo *ReleaseRepo) Store(ctx context.Context, r *domain.Release) error {
queryBuilder := repo.db.squirrel.
Insert("release").
Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "info_url", "download_url", "torrent_name", "size", "title", "category", "season", "episode", "year", "resolution", "source", "codec", "container", "hdr", "release_group", "proper", "repack", "website", "type", "origin", "tags", "uploader", "pre_time", "filter_id").
Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer.Identifier, r.FilterName, r.Protocol, r.Implementation, r.Timestamp.Format(time.RFC3339), r.GroupID, r.TorrentID, r.InfoURL, r.DownloadURL, r.TorrentName, r.Size, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Resolution, r.Source, codecStr, r.Container, hdrStr, r.Group, r.Proper, r.Repack, r.Website, r.Type, r.Origin, pq.Array(r.Tags), r.Uploader, r.PreTime, r.FilterID).
Columns("filter_status", "rejections", "indexer", "filter", "protocol", "implementation", "timestamp", "group_id", "torrent_id", "info_url", "download_url", "torrent_name", "size", "title", "category", "season", "episode", "year", "month", "day", "resolution", "source", "codec", "container", "hdr", "release_group", "proper", "repack", "website", "type", "origin", "tags", "uploader", "pre_time", "filter_id").
Values(r.FilterStatus, pq.Array(r.Rejections), r.Indexer.Identifier, r.FilterName, r.Protocol, r.Implementation, r.Timestamp.Format(time.RFC3339), r.GroupID, r.TorrentID, r.InfoURL, r.DownloadURL, r.TorrentName, r.Size, r.Title, r.Category, r.Season, r.Episode, r.Year, r.Month, r.Day, r.Resolution, r.Source, codecStr, r.Container, hdrStr, r.Group, r.Proper, r.Repack, r.Website, r.Type, r.Origin, pq.Array(r.Tags), r.Uploader, r.PreTime, r.FilterID).
Suffix("RETURNING id").RunWith(repo.db.handler)
// return values
@ -668,44 +668,49 @@ func (repo *ReleaseRepo) Delete(ctx context.Context, req *domain.DeleteReleaseRe
return nil
}
func (repo *ReleaseRepo) CanDownloadShow(ctx context.Context, title string, season int, episode int) (bool, error) {
// TODO support non season episode shows
// if rls.Day > 0 {
// // Maybe in the future
// // SELECT '' FROM release WHERE Title LIKE %q AND ((Year == %d AND Month == %d AND Day > %d) OR (Year == %d AND Month > %d) OR (Year > %d))"
// qs := sql.Query("SELECT torrent_name FROM release WHERE Title LIKE %q AND Year >= %d", rls.Title, rls.Year)
//
// for q := range qs.Rows() {
// r := rls.ParseTitle(q)
// if r.Year > rls.Year {
// return false, fmt.Errorf("stale release year")
// }
//
// if r.Month > rls.Month {
// return false, fmt.Errorf("stale release month")
// }
//
// if r.Month == rls.Month && r.Day > rls.Day {
// return false, fmt.Errorf("stale release day")
// }
// }
//}
func (repo *ReleaseRepo) CheckSmartEpisodeCanDownload(ctx context.Context, p *domain.SmartEpisodeParams) (bool, error) {
queryBuilder := repo.db.squirrel.
Select("COUNT(*)").
From("release").
Where(repo.db.ILike("title", title+"%"))
From("release r").
LeftJoin("release_action_status ras ON r.id = ras.release_id").
Where(sq.And{
repo.db.ILike("r.title", p.Title+"%"),
sq.Eq{"ras.status": "PUSH_APPROVED"},
})
if season > 0 && episode > 0 {
if p.Proper {
queryBuilder = queryBuilder.Where(sq.Eq{"r.proper": p.Proper})
}
if p.Repack {
queryBuilder = queryBuilder.Where(sq.And{
sq.Eq{"r.repack": p.Repack},
repo.db.ILike("r.release_group", p.Group),
})
}
if p.Season > 0 && p.Episode > 0 {
queryBuilder = queryBuilder.Where(sq.Or{
sq.And{
sq.Eq{"season": season},
sq.Gt{"episode": episode},
sq.Eq{"r.season": p.Season},
sq.Gt{"r.episode": p.Episode},
},
sq.Gt{"season": season},
sq.Gt{"r.season": p.Season},
})
} else if p.Season > 0 && p.Episode == 0 {
queryBuilder = queryBuilder.Where(sq.Gt{"r.season": p.Season})
} else if p.Year > 0 && p.Month > 0 && p.Day > 0 {
queryBuilder = queryBuilder.Where(sq.Or{
sq.And{
sq.Eq{"r.year": p.Year},
sq.Eq{"r.month": p.Month},
sq.Gt{"r.day": p.Day},
},
sq.And{
sq.Eq{"r.year": p.Year},
sq.Gt{"r.month": p.Month},
},
sq.Gt{"r.year": p.Year},
})
} else if season > 0 && episode == 0 {
queryBuilder = queryBuilder.Where(sq.Gt{"season": season})
} else {
/* No support for this scenario today. Specifically multi-part specials.
* The Database presently does not have Subtitle as a field, but is coming at a future date. */
@ -717,6 +722,8 @@ func (repo *ReleaseRepo) CanDownloadShow(ctx context.Context, title string, seas
return false, errors.Wrap(err, "error building query")
}
repo.log.Trace().Str("method", "CheckSmartEpisodeCanDownload").Str("query", query).Interface("args", args).Msgf("executing query")
row := repo.db.handler.QueryRowContext(ctx, query, args...)
if err := row.Err(); err != nil {
return false, err

View file

@ -582,7 +582,7 @@ func TestReleaseRepo_Delete(t *testing.T) {
}
}
func TestReleaseRepo_CanDownloadShow(t *testing.T) {
func TestReleaseRepo_CheckSmartEpisodeCanDownloadShow(t *testing.T) {
for dbType, db := range testDBs {
log := setupLoggerForTest()
@ -595,7 +595,7 @@ func TestReleaseRepo_CanDownloadShow(t *testing.T) {
releaseActionMockData := getMockReleaseActionStatus()
actionMockData := getMockAction()
t.Run(fmt.Sprintf("Delete_Succeeds [%s]", dbType), func(t *testing.T) {
t.Run(fmt.Sprintf("Check_Smart_Episode_Can_Download [%s]", dbType), func(t *testing.T) {
// Setup
createdClient, err := downloadClientRepo.Store(context.Background(), getMockDownloadClient())
assert.NoError(t, err)
@ -624,8 +624,17 @@ func TestReleaseRepo_CanDownloadShow(t *testing.T) {
err = repo.StoreReleaseActionStatus(context.Background(), releaseActionMockData)
assert.NoError(t, err)
params := &domain.SmartEpisodeParams{
Title: "Example.Torrent.Name",
Season: 1,
Episode: 2,
Year: 0,
Month: 0,
Day: 0,
}
// Execute
canDownload, err := repo.CanDownloadShow(context.Background(), "Example.Torrent.Name", 1, 2)
canDownload, err := repo.CheckSmartEpisodeCanDownload(context.Background(), params)
// Verify
assert.NoError(t, err)

View file

@ -106,6 +106,8 @@ CREATE TABLE filter
match_other TEXT [] DEFAULT '{}',
except_other TEXT [] DEFAULT '{}',
years TEXT,
months TEXT,
days TEXT,
artists TEXT,
albums TEXT,
release_types_match TEXT [] DEFAULT '{}',
@ -244,6 +246,8 @@ CREATE TABLE "release"
season INTEGER,
episode INTEGER,
year INTEGER,
month INTEGER,
day INTEGER,
resolution TEXT,
source TEXT,
codec TEXT,
@ -1522,5 +1526,17 @@ ALTER TABLE filter
UPDATE indexer
SET identifier_external = name;
`,
`ALTER TABLE "release"
ADD COLUMN month INTEGER;
ALTER TABLE "release"
ADD COLUMN day INTEGER;
ALTER TABLE filter
ADD COLUMN months TEXT;
ALTER TABLE filter
ADD COLUMN days TEXT;
`,
}