mirror of
https://github.com/sbondCo/Watcharr.git
synced 2026-08-07 07:14:44 +00:00
Games: Support searching with Year or FirstYear filters
This commit is contained in:
@@ -10,6 +10,7 @@ These changes are awaiting release:
|
|||||||
- Import(text list): Support parsing a rating (out of 10, supporting decimals) in square brackets.
|
- Import(text list): Support parsing a rating (out of 10, supporting decimals) in square brackets.
|
||||||
- Show `End Year` for TV Shows that have ended or been canceled.
|
- Show `End Year` for TV Shows that have ended or been canceled.
|
||||||
- Add full release date and last release date to their respective year elements as titles (browser tooltip).
|
- Add full release date and last release date to their respective year elements as titles (browser tooltip).
|
||||||
|
- Search: Support inline filters for Non-Multi searches (ex: You can now search `Spider Man year:2026` or `Spider Man fyear:2026` if you care strictly about the first release year).
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/sbondCo/Watcharr/config"
|
"github.com/sbondCo/Watcharr/config"
|
||||||
"github.com/sbondCo/Watcharr/database/entity"
|
"github.com/sbondCo/Watcharr/database/entity"
|
||||||
"github.com/sbondCo/Watcharr/domain"
|
"github.com/sbondCo/Watcharr/domain"
|
||||||
|
"github.com/sbondCo/Watcharr/media/igdb"
|
||||||
"github.com/sbondCo/Watcharr/media/tmdb"
|
"github.com/sbondCo/Watcharr/media/tmdb"
|
||||||
"github.com/sbondCo/Watcharr/util"
|
"github.com/sbondCo/Watcharr/util"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
@@ -80,7 +81,10 @@ func (s *Service) Search(
|
|||||||
Page: pp.Page,
|
Page: pp.Page,
|
||||||
Adult: qfilters.Adult,
|
Adult: qfilters.Adult,
|
||||||
}
|
}
|
||||||
if err := s.searchMulti(sreq, &resp); err != nil {
|
greq := igdb.SearchOptions{
|
||||||
|
Query: query,
|
||||||
|
}
|
||||||
|
if err := s.searchMulti(sreq, greq, &resp); err != nil {
|
||||||
return resp, errors.New("multi search failed")
|
return resp, errors.New("multi search failed")
|
||||||
}
|
}
|
||||||
case domain.SearchTypeMovie:
|
case domain.SearchTypeMovie:
|
||||||
@@ -119,7 +123,12 @@ func (s *Service) Search(
|
|||||||
return resp, errors.New("person search failed")
|
return resp, errors.New("person search failed")
|
||||||
}
|
}
|
||||||
case domain.SearchTypeGame:
|
case domain.SearchTypeGame:
|
||||||
if err := s.searchGame(r.Query, pp.Page, &resp); err != nil {
|
greq := igdb.SearchOptions{
|
||||||
|
Query: query,
|
||||||
|
Year: qfilters.Year,
|
||||||
|
PrimaryYear: qfilters.FirstYear,
|
||||||
|
}
|
||||||
|
if err := s.searchGame(greq, &resp); err != nil {
|
||||||
return resp, errors.New("game search failed")
|
return resp, errors.New("game search failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +141,7 @@ func (s *Service) Search(
|
|||||||
// SearchMulti is TMDB Multi search but with game data added to first page.
|
// SearchMulti is TMDB Multi search but with game data added to first page.
|
||||||
func (s *Service) searchMulti(
|
func (s *Service) searchMulti(
|
||||||
req tmdb.SearchUniversalOptions,
|
req tmdb.SearchUniversalOptions,
|
||||||
|
igdbReq igdb.SearchOptions,
|
||||||
resp *domain.SearchResponse,
|
resp *domain.SearchResponse,
|
||||||
) error {
|
) error {
|
||||||
slog.Debug("searchMulti: Running.", "req", req)
|
slog.Debug("searchMulti: Running.", "req", req)
|
||||||
@@ -149,7 +159,7 @@ func (s *Service) searchMulti(
|
|||||||
}
|
}
|
||||||
// IGDB (we will only get results for the first page)
|
// IGDB (we will only get results for the first page)
|
||||||
if req.Page == 1 && s.cfg.TwitchEnabled() {
|
if req.Page == 1 && s.cfg.TwitchEnabled() {
|
||||||
igdbRes, err := s.cfg.TWITCH.Search(req.Query)
|
igdbRes, err := s.cfg.TWITCH.Search(igdbReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("SearchMulti: Failed to search igdb!", "error", err)
|
slog.Error("SearchMulti: Failed to search igdb!", "error", err)
|
||||||
return errors.New("content request failed")
|
return errors.New("content request failed")
|
||||||
@@ -278,12 +288,11 @@ func (s *Service) searchPeople(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) searchGame(
|
func (s *Service) searchGame(
|
||||||
query string,
|
req igdb.SearchOptions,
|
||||||
page int,
|
|
||||||
resp *domain.SearchResponse,
|
resp *domain.SearchResponse,
|
||||||
) error {
|
) error {
|
||||||
slog.Debug("searchGame: Running.", "query", query, "page", page)
|
slog.Debug("searchGame: Running.", "req", req)
|
||||||
igdbRes, err := s.cfg.TWITCH.Search(query)
|
igdbRes, err := s.cfg.TWITCH.Search(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("searchGame: Failed to search igdb!", "error", err)
|
slog.Error("searchGame: Failed to search igdb!", "error", err)
|
||||||
return errors.New("content request failed")
|
return errors.New("content request failed")
|
||||||
|
|||||||
@@ -183,19 +183,68 @@ func (i *IGDB) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IGDB) Search(q string) (GameSearchResponse, error) {
|
type SearchOptions struct {
|
||||||
slog.Debug("Search:", "query", q)
|
Query string
|
||||||
|
Year int
|
||||||
|
PrimaryYear int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn supported filters from the SearchOptions struct into a `where` we can
|
||||||
|
// send to APICalypse.
|
||||||
|
func (o SearchOptions) AsWhere() string {
|
||||||
|
f := []string{}
|
||||||
|
if o.Year != 0 {
|
||||||
|
y := strconv.Itoa(o.Year)
|
||||||
|
// If `any` release of the game was on this year.
|
||||||
|
f = append(f, "release_dates.y = "+y)
|
||||||
|
}
|
||||||
|
if o.PrimaryYear != 0 {
|
||||||
|
start := strconv.FormatInt(
|
||||||
|
time.
|
||||||
|
Date(o.PrimaryYear, time.January, 1, 0, 0, 0, 0, time.UTC).
|
||||||
|
Unix(),
|
||||||
|
10)
|
||||||
|
end := strconv.FormatInt(
|
||||||
|
time.
|
||||||
|
// We add one to the year and get the first day of the next year
|
||||||
|
// BUT because day=0, we go back one day and end up with the
|
||||||
|
// very last day of `PrimaryYear`.
|
||||||
|
Date(o.PrimaryYear+1, time.January, 0, 23, 59, 59, 0, time.UTC).
|
||||||
|
Unix(),
|
||||||
|
10)
|
||||||
|
// If `first` release of the game was on this year.
|
||||||
|
f = append(f, "first_release_date > "+start)
|
||||||
|
f = append(f, "first_release_date < "+end)
|
||||||
|
}
|
||||||
|
if len(f) <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "where " + strings.Join(f, " & ") + ";"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IGDB) Search(o SearchOptions) (GameSearchResponse, error) {
|
||||||
|
slog.Debug("Search:", "query", o.Query)
|
||||||
var resp GameSearchResponse
|
var resp GameSearchResponse
|
||||||
cacheKey := cache.CreateCacheKey("Search", q)
|
cacheKey := cache.CreateCacheKey(
|
||||||
|
"Search",
|
||||||
|
o.Query,
|
||||||
|
o.Year,
|
||||||
|
o.PrimaryYear)
|
||||||
if cache.GetCache(GameStore, cacheKey, &resp) {
|
if cache.GetCache(GameStore, cacheKey, &resp) {
|
||||||
slog.Debug("Search: Returning cache.")
|
slog.Debug("Search: Returning cache.")
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
apiQuery := "fields " + fieldsForSearch + "; search \"" + o.Query + "\"; limit 40;"
|
||||||
|
whereQ := o.AsWhere()
|
||||||
|
if whereQ != "" {
|
||||||
|
apiQuery += whereQ
|
||||||
|
}
|
||||||
err := i.req(
|
err := i.req(
|
||||||
igdbHost,
|
igdbHost,
|
||||||
"/games",
|
"/games",
|
||||||
map[string]string{},
|
map[string]string{},
|
||||||
"fields "+fieldsForSearch+"; search \""+q+"\"; limit 40;",
|
apiQuery,
|
||||||
&resp,
|
&resp,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user