App v0.2.26 and gateway 0.1.20

Client: seek controls, Bazarr subtitle download and cast panel in the
player; MDBList ratings strip; episode and schedule detail pages; series
pace estimate; what's new panel; install-permission onboarding step;
synced per-profile preferences; Emby outage banner.

Gateway: rebuilt admin console (one fragment per page), preference
history and restore, merged Continue Watching, Emby health probe,
subtitle selection and Bazarr download, structured request logging with
per-request identity, and embedded build version.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ponzischeme89
2026-08-06 22:33:56 +12:00
co-authored by Claude Opus 5
parent 2675e6d82b
commit 4a4df7a73c
257 changed files with 24868 additions and 3108 deletions
+18 -8
View File
@@ -61,19 +61,25 @@ func New(baseURL string, timeout time.Duration) *Client {
}
}
// Movie resolves a movie through one of the identifier providers accepted by MDBList.
// The single-title endpoint returns every rating source available for that title in one
// request, which lets source selection happen locally without spending more API quota.
func (c *Client) Movie(
ctx context.Context, apiKey, provider, providerID string,
// Media resolves a movie or series through one of the identifier providers accepted by
// MDBList. The single-title endpoint returns every rating source in one request, which
// lets source selection happen locally without spending more API quota.
func (c *Client) Media(
ctx context.Context, apiKey, provider, providerID, mediaType string,
) ([]Rating, error) {
apiKey = strings.TrimSpace(apiKey)
provider = strings.ToLower(strings.TrimSpace(provider))
providerID = strings.TrimSpace(providerID)
if apiKey == "" || providerID == "" || (provider != "tmdb" && provider != "imdb") {
return nil, fmt.Errorf("mdblist: invalid movie lookup")
mediaType = strings.ToLower(strings.TrimSpace(mediaType))
if mediaType == "series" || mediaType == "tv" {
mediaType = "show"
}
endpoint, err := url.Parse(c.baseURL + "/" + provider + "/movie/" + url.PathEscape(providerID) + "/")
if apiKey == "" || providerID == "" ||
(provider != "tmdb" && provider != "imdb") ||
(mediaType != "movie" && mediaType != "show") {
return nil, fmt.Errorf("mdblist: invalid media lookup")
}
endpoint, err := url.Parse(c.baseURL + "/" + provider + "/" + mediaType + "/" + url.PathEscape(providerID) + "/")
if err != nil {
return nil, fmt.Errorf("mdblist: build request: %w", err)
}
@@ -111,6 +117,10 @@ func (c *Client) Movie(
return result, nil
}
func (c *Client) Movie(ctx context.Context, apiKey, provider, providerID string) ([]Rating, error) {
return c.Media(ctx, apiKey, provider, providerID, "movie")
}
func numericValue(raw json.RawMessage) (float64, bool) {
text := strings.TrimSpace(string(raw))
if text == "" || text == "null" {