mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-07-14 18:15:56 -04:00
refactor: organize API around operation domains (#24)
* feat: reorganize API around operation domains
Restructure the oRPC contract from 14 resource-oriented routers to 8
domain-oriented routers for a cleaner public API surface.
- Consolidate 7 watch procedures into unified tracking.watch/unwatch
with scope + ids input (movie, episode, season, series)
- Split dashboard across tracking (stats, history), library
(continueWatching, upcoming), and discover (recommendations)
- Merge explore + search + discover into single discover router
- Absorb integrations into account.integrations
- Merge system.authConfig into system.publicInfo
- Collapse 6 admin setting endpoints into admin.settings.get/update
- Deduplicate platforms.list + explore.watchProviders into
discover.platforms
- Add symmetric unwatchMovie/unwatchSeries core functions
- Rename titles.detail→get, titles.recommendations→similar,
people.detail→get
BREAKING CHANGE: All client API paths have changed. REST paths now
mirror router structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address review feedback on API reorganization
- Fix updateRating invalidation in native app — was only invalidating
title queries, now calls invalidateTitleQueries() to also refresh
tracking.userInfo (drives the rating UI)
- Make unwatchMovie status revert consistent with unwatchSeries — revert
any non-watchlist status, not just "completed"
- Add sync invariant comment on handleWatch/handleUnwatch loops
- Remove unused queryClient import in native use-title-actions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: updateStatus NOT_FOUND error + rate() invalidation in native
- updateStatus now throws NOT_FOUND when quickAddTitle returns null
(title doesn't exist), instead of silently succeeding
- Add NOT_FOUND error to updateStatus contract definition
- Fix rate() in native title-actions.ts to call invalidateTitleQueries()
instead of only invalidating orpc.titles.key() — mirrors the fix
already applied to the hook-based path in d2ddc0f
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: expand mobile app docs and add Play Store badge
- Add the Google Play badge to the README alongside the App Store badge
- Expand the mobile app docs with the Android/Play Store release details
- Refresh docs site dependencies and related package versions for the updated docs stack
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+10
-6
@@ -7,10 +7,10 @@
|
||||
"dependencies": {
|
||||
"@takumi-rs/image-response": "^0.73.1",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"fumadocs-core": "^16.7.5",
|
||||
"fumadocs-core": "^16.7.6",
|
||||
"fumadocs-mdx": "^14.2.11",
|
||||
"fumadocs-openapi": "^10.4.1",
|
||||
"fumadocs-ui": "^16.7.5",
|
||||
"fumadocs-openapi": "^10.5.0",
|
||||
"fumadocs-ui": "^16.7.6",
|
||||
"lucide-react": "^0.577.0",
|
||||
"next": "16.2.1",
|
||||
"react": "19.2.4",
|
||||
@@ -568,13 +568,13 @@
|
||||
|
||||
"framer-motion": ["framer-motion@12.38.0", "", { "dependencies": { "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g=="],
|
||||
|
||||
"fumadocs-core": ["fumadocs-core@16.7.5", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.2", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^4.0.2", "@shikijs/transformers": "^4.0.2", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0 || ^1.0.0", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-qOAKQyNUCZQ0wRun2Jnw7f6VLEgCiGFomU+kQXUZI36cX9bJPiymXboKaSjCxT1PuIusjzUyxYTGapVZxzd9TA=="],
|
||||
"fumadocs-core": ["fumadocs-core@16.7.6", "", { "dependencies": { "@formatjs/intl-localematcher": "^0.8.2", "@orama/orama": "^3.1.18", "@shikijs/rehype": "^4.0.2", "@shikijs/transformers": "^4.0.2", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "image-size": "^2.0.2", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "negotiator": "^1.0.0", "npm-to-yarn": "^3.0.1", "path-to-regexp": "^8.3.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", "scroll-into-view-if-needed": "^3.1.0", "shiki": "^4.0.2", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { "@mdx-js/mdx": "*", "@mixedbread/sdk": "^0.46.0", "@orama/core": "1.x.x", "@oramacloud/client": "2.x.x", "@tanstack/react-router": "1.x.x", "@types/estree-jsx": "*", "@types/hast": "*", "@types/mdast": "*", "@types/react": "*", "algoliasearch": "5.x.x", "flexsearch": "*", "lucide-react": "*", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "7.x.x", "waku": "^0.26.0 || ^0.27.0 || ^1.0.0", "zod": "4.x.x" }, "optionalPeers": ["@mdx-js/mdx", "@mixedbread/sdk", "@orama/core", "@oramacloud/client", "@tanstack/react-router", "@types/estree-jsx", "@types/hast", "@types/mdast", "@types/react", "algoliasearch", "flexsearch", "lucide-react", "next", "react", "react-dom", "react-router", "waku", "zod"] }, "sha512-d4HtGupFpcSWQqLbWh184yoEg6D70pH68NP77Ct4mI0N61t/Uy63wYj9sbS1h/m6jlijUIXC6rz8D5JApOB9Wg=="],
|
||||
|
||||
"fumadocs-mdx": ["fumadocs-mdx@14.2.11", "", { "dependencies": { "@mdx-js/mdx": "^3.1.1", "@standard-schema/spec": "^1.1.0", "chokidar": "^5.0.0", "esbuild": "^0.27.3", "estree-util-value-to-estree": "^3.5.0", "js-yaml": "^4.1.1", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", "picocolors": "^1.1.1", "picomatch": "^4.0.3", "tinyexec": "^1.0.4", "tinyglobby": "^0.2.15", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "zod": "^4.3.6" }, "peerDependencies": { "@fumadocs/mdx-remote": "^1.4.0", "@types/mdast": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "^15.0.0 || ^16.0.0", "mdast-util-directive": "*", "next": "^15.3.0 || ^16.0.0", "react": "*", "vite": "6.x.x || 7.x.x || 8.x.x" }, "optionalPeers": ["@fumadocs/mdx-remote", "@types/mdast", "@types/mdx", "@types/react", "mdast-util-directive", "next", "react", "vite"], "bin": { "fumadocs-mdx": "dist/bin.js" } }, "sha512-j0gHKs45c62ARteE8/yBM2Nu2I8AE2Cs37ktPEdc/8EX7TL66XP74un5OpHp6itLyWTu8Jur0imOiiIDq8+rDg=="],
|
||||
|
||||
"fumadocs-openapi": ["fumadocs-openapi@10.4.1", "", { "dependencies": { "@fastify/deepmerge": "^3.2.1", "@fumari/json-schema-ts": "^0.0.2", "@fumari/stf": "1.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@scalar/json-magic": "^0.12.4", "@scalar/openapi-upgrader": "^0.2.1", "ajv": "^8.18.0", "class-variance-authority": "^0.7.1", "dereference-json-schema": "^0.2.2", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^4.1.1", "lucide-react": "^0.577.0", "next-themes": "^0.4.6", "openapi-sampler": "^1.7.2", "react-hook-form": "^7.71.2", "remark": "^15.0.1", "remark-rehype": "^11.1.2", "tailwind-merge": "^3.5.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "fumadocs-ui": "^16.7.0", "json-schema-typed": "*", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@scalar/api-client-react", "@types/react", "json-schema-typed", "shiki"] }, "sha512-iMEDyANcikDePUFlBE5sffjTbrUI5LELuIla+6kTZJYEuoQMLUK+plOnXq1iacPBMeCS7cSFadvE/Vp/h7oA+g=="],
|
||||
"fumadocs-openapi": ["fumadocs-openapi@10.5.0", "", { "dependencies": { "@fastify/deepmerge": "^3.2.1", "@fumari/json-schema-ts": "^0.0.2", "@fumari/stf": "1.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.4", "@scalar/json-magic": "^0.12.4", "@scalar/openapi-upgrader": "^0.2.2", "ajv": "^8.18.0", "class-variance-authority": "^0.7.1", "dereference-json-schema": "^0.2.2", "github-slugger": "^2.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "js-yaml": "^4.1.1", "lucide-react": "^1.6.0", "next-themes": "^0.4.6", "openapi-sampler": "^1.7.2", "react-hook-form": "^7.72.0", "remark": "^15.0.1", "remark-rehype": "^11.1.2", "tailwind-merge": "^3.5.0", "xml-js": "^1.6.11" }, "peerDependencies": { "@scalar/api-client-react": "*", "@types/react": "*", "fumadocs-core": "^16.7.0", "fumadocs-ui": "^16.7.0", "json-schema-typed": "*", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@scalar/api-client-react", "@types/react", "json-schema-typed", "shiki"] }, "sha512-EG3/U/NI/ha0xhIUiUIKiRSmLS4AOjn+4xkZXOoiLB5rlV+HyHsq9qcN+wKgbK9u4qoNuAcMzTEYUXScoSUbXA=="],
|
||||
|
||||
"fumadocs-ui": ["fumadocs-ui@16.7.5", "", { "dependencies": { "@fumadocs/tailwind": "0.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "lucide-react": "^0.577.0", "motion": "^12.38.0", "next-themes": "^0.4.6", "react-medium-image-zoom": "^5.4.1", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.5.0", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.7.5", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next", "shiki"] }, "sha512-i9zvslCFc25hxYsAmkBCJUgAwvWbKwMn4Xd5L7LcnQwW60r8VPihkfeWB/DDtHv4wJTGOPl+neb1AgNkm1Cujw=="],
|
||||
"fumadocs-ui": ["fumadocs-ui@16.7.6", "", { "dependencies": { "@fumadocs/tailwind": "0.0.3", "@radix-ui/react-accordion": "^1.2.12", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-direction": "^1.1.1", "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-presence": "^1.1.5", "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "class-variance-authority": "^0.7.1", "lucide-react": "^1.6.0", "motion": "^12.38.0", "next-themes": "^0.4.6", "react-medium-image-zoom": "^5.4.1", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", "scroll-into-view-if-needed": "^3.1.0", "tailwind-merge": "^3.5.0", "unist-util-visit": "^5.1.0" }, "peerDependencies": { "@takumi-rs/image-response": "*", "@types/mdx": "*", "@types/react": "*", "fumadocs-core": "16.7.6", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", "shiki": "*" }, "optionalPeers": ["@takumi-rs/image-response", "@types/mdx", "@types/react", "next", "shiki"] }, "sha512-wjZnm8SiX2lj5zWOlOHnzSZ0YBFwNqYGBX1u5F3mZtdIkmkDVs+3+JngCkRHNZzYJVBulXjp8t5wzBz0yDJa8w=="],
|
||||
|
||||
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
|
||||
|
||||
@@ -968,6 +968,10 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"fumadocs-openapi/lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
|
||||
|
||||
"fumadocs-ui/lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="],
|
||||
|
||||
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
|
||||
|
||||
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
|
||||
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Create a new media server integration or update an existing one. Generates a unique webhook token for the provider.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/integrations","method":"post"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/account/integrations","method":"post"}]} />
|
||||
+1
-1
@@ -14,4 +14,4 @@ _openapi:
|
||||
|
||||
Remove a media server integration and all its event history.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/integrations/{provider}","method":"delete"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/account/integrations/{provider}","method":"delete"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch all configured media server integrations for the current user, including recent webhook/sync events for each.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/integrations","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/account/integrations","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Generate a new webhook token for an integration. The old token is immediately invalidated.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/integrations/{provider}/regenerate-token","method":"post"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/account/integrations/{provider}/regenerate-token","method":"post"}]} />
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get telemetry status
|
||||
title: Get admin settings
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -8,12 +8,12 @@ _openapi:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch whether anonymous telemetry is enabled and when the last report
|
||||
was sent.
|
||||
Fetch all admin-configurable settings: registration, update checks,
|
||||
and telemetry.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch whether anonymous telemetry is enabled and when the last report was sent.
|
||||
Fetch all admin-configurable settings: registration, update checks, and telemetry.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/telemetry","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/settings","method":"get"}]} />
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Update admin settings
|
||||
full: true
|
||||
_openapi:
|
||||
method: PATCH
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Partially update admin settings. Only provided sections are changed;
|
||||
omitted sections keep their current values.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Partially update admin settings. Only provided sections are changed; omitted sections keep their current values.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/settings","method":"patch"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Toggle registration
|
||||
full: true
|
||||
_openapi:
|
||||
method: PUT
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Open or close new user registration.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Open or close new user registration.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/registration","method":"put"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Toggle telemetry
|
||||
full: true
|
||||
_openapi:
|
||||
method: PUT
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Enable or disable anonymous telemetry reporting.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Enable or disable anonymous telemetry reporting.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/telemetry","method":"put"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Toggle update checks
|
||||
full: true
|
||||
_openapi:
|
||||
method: PUT
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Enable or disable automatic update checks against the public API.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Enable or disable automatic update checks against the public API.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/update-check","method":"put"}]} />
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Get dashboard statistics
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch summary counts for the current user: movies watched this month,
|
||||
episodes this week, library size, and completed titles.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch summary counts for the current user: movies watched this month, episodes this week, library size, and completed titles.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/dashboard/stats","method":"get"}]} />
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Discover titles by genre
|
||||
title: Browse titles with filters
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Browse movies or TV shows filtered by genre, sorted by popularity. Returns user statuses and episode progress.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/browse","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch the list of TMDB genres for movies or TV shows. Used to populate genre filters for discovery.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/explore/genres","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/genres","method":"get"}]} />
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get update check status
|
||||
title: List available streaming platforms
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -8,12 +8,12 @@ _openapi:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch whether automatic update checks are enabled, and the latest
|
||||
cached check result if available.
|
||||
Fetch all available streaming platforms, ordered by popularity.
|
||||
Includes subscription status for filtering.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch whether automatic update checks are enabled, and the latest cached check result if available.
|
||||
Fetch all available streaming platforms, ordered by popularity. Includes subscription status for filtering.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/update-check","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/platforms","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch currently popular movies or TV shows from TMDB with the user's tracking statuses.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/explore/popular","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/popular","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch personalized title recommendations based on the user's library and watch history.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/dashboard/recommendations","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/recommendations","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Full-text search across movies, TV shows, and people via TMDB. Optionally filter by media type.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/search","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/search","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch today's trending movies and/or TV shows from TMDB, including a featured hero title and the user's statuses.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/explore/trending","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/discover/trending","method":"get"}]} />
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Batch mark episodes watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Record watch events for multiple episodes at once. Useful for marking
|
||||
a range of episodes as watched.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Record watch events for multiple episodes at once. Useful for marking a range of episodes as watched.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/episodes/batch-watch","method":"post"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Mark episode unwatched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Remove the watch record for a single TV episode.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Remove the watch record for a single TV episode.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/episodes/{id}/unwatch","method":"post"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Mark episode watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Record a watch event for a single TV episode.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Record a watch event for a single TV episode.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/episodes/{id}/watch","method":"post"}]} />
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: List available watch providers
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch the list of streaming providers available in the configured
|
||||
region. Used to populate provider filter dropdowns.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch the list of streaming providers available in the configured region. Used to populate provider filter dropdowns.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/explore/watch-providers","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch TV shows the user is currently watching, with the next unwatched episode and progress for each.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/dashboard/continue-watching","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/library/continue-watching","method":"get"}]} />
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get registration status
|
||||
title: Get library statistics
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -7,11 +7,11 @@ _openapi:
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Check whether new user registration is currently open or closed.
|
||||
- content: 'Fetch aggregate library counts: total titles and completed titles.'
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Check whether new user registration is currently open or closed.
|
||||
Fetch aggregate library counts: total titles and completed titles.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/admin/registration","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/library/stats","method":"get"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch upcoming episodes and movie releases for titles in the user's library, sorted by date. Supports cursor-based pagination.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/dashboard/upcoming","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/library/upcoming","method":"get"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: List all platforms
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Fetch all available streaming platforms, ordered by popularity.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch all available streaming platforms, ordered by popularity.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/platforms","method":"get"}]} />
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Mark season unwatched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Remove all watch records for episodes in a season in a single
|
||||
operation.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Remove all watch records for episodes in a season in a single operation.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/seasons/{id}/unwatch","method":"post"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Mark season watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Mark all episodes in a season as watched in a single operation.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Mark all episodes in a season as watched in a single operation.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/seasons/{id}/watch","method":"post"}]} />
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: Get authentication config
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch the authentication configuration including OIDC availability,
|
||||
password login status, and registration state. Does not require
|
||||
authentication.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch the authentication configuration including OIDC availability, password login status, and registration state. Does not require authentication.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/system/auth-config","method":"get"}]} />
|
||||
@@ -8,12 +8,12 @@ _openapi:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch public information about this Sofa instance. Does not require
|
||||
authentication. Used by the login screen to display instance details.
|
||||
Fetch public information about this Sofa instance including
|
||||
authentication configuration. Does not require authentication.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch public information about this Sofa instance. Does not require authentication. Used by the login screen to display instance details.
|
||||
Fetch public information about this Sofa instance including authentication configuration. Does not require authentication.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/system/public-info","method":"get"}]} />
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
title: Quick add title to library
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Add a title to the user's watchlist and trigger a full TMDB import if
|
||||
needed. If the title already exists in the user's library, returns
|
||||
alreadyAdded: true.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Add a title to the user's watchlist and trigger a full TMDB import if needed. If the title already exists in the user's library, returns alreadyAdded: true.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/quick-add","method":"post"}]} />
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get title recommendations
|
||||
title: Get similar titles
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch similar titles based on locally cached recommendation data, along with the user's statuses for each.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/recommendations","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/similar","method":"get"}]} />
|
||||
@@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Mark all episodes watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Mark every episode of a TV show as watched. Requires seasons to be
|
||||
hydrated first.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Mark every episode of a TV show as watched. Requires seasons to be hydrated first.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/watch-all","method":"post"}]} />
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
title: Mark movie as watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: Log a watch event for a movie. Automatically sets status to completed.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Log a watch event for a movie. Automatically sets status to completed.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/watch","method":"post"}]} />
|
||||
+1
-1
@@ -14,4 +14,4 @@ _openapi:
|
||||
|
||||
Set a 0-5 star rating for a title. Use 0 to clear the rating.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/rating","method":"put"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/titles/{id}/rating","method":"put"}]} />
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get watch history
|
||||
title: Get watch statistics
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -9,11 +9,11 @@ _openapi:
|
||||
contents:
|
||||
- content: >-
|
||||
Fetch the user's watch counts grouped by time period. Useful for
|
||||
rendering activity charts.
|
||||
rendering activity charts and dashboard counters.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Fetch the user's watch counts grouped by time period. Useful for rendering activity charts.
|
||||
Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/dashboard/watch-history","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/stats","method":"get"}]} />
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: Remove watch records
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Remove watch records for one or more items. Use scope to indicate what
|
||||
the IDs refer to: movie, episode, season, or series.
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Remove watch records for one or more items. Use scope to indicate what the IDs refer to: movie, episode, season, or series.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/unwatch","method":"post"}]} />
|
||||
+1
-1
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Set the user's tracking status for a title. Use null to remove the title from the library entirely.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/status","method":"put"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/titles/{id}/status","method":"put"}]} />
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Get user's title info
|
||||
title: Get user's tracking info for a title
|
||||
full: true
|
||||
_openapi:
|
||||
method: GET
|
||||
@@ -16,4 +16,4 @@ _openapi:
|
||||
|
||||
Fetch the current user's tracking status, rating, and watched episode IDs for a title.
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/titles/{id}/user-info","method":"get"}]} />
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/titles/{id}","method":"get"}]} />
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Mark items as watched
|
||||
full: true
|
||||
_openapi:
|
||||
method: POST
|
||||
toc: []
|
||||
structuredData:
|
||||
headings: []
|
||||
contents:
|
||||
- content: >-
|
||||
Mark one or more items as watched. Use scope to indicate what the IDs
|
||||
refer to: movie (title IDs), episode (episode IDs), season (season
|
||||
IDs), or series (title IDs to mark all episodes).
|
||||
---
|
||||
|
||||
{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
|
||||
|
||||
Mark one or more items as watched. Use scope to indicate what the IDs refer to: movie (title IDs), episode (episode IDs), season (season IDs), or series (title IDs to mark all episodes).
|
||||
|
||||
<APIPage document={"./public/openapi.json"} operations={[{"path":"/tracking/watch","method":"post"}]} />
|
||||
@@ -4,10 +4,118 @@ description: Set up the iOS and Android app.
|
||||
icon: Smartphone
|
||||
---
|
||||
|
||||
export const AppStoreBadge = (props) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 119.664 40"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M110.135 0H9.535Q8.986 0 8.44.002q-.457.004-.919.013A13 13 0 0 0 5.517.19a6.7 6.7 0 0 0-1.9.627 6.4 6.4 0 0 0-1.62 1.18A6.3 6.3 0 0 0 .82 3.617 6.6 6.6 0 0 0 .195 5.52a13 13 0 0 0-.179 2.002c-.01.307-.01.615-.015.921V31.56c.005.31.006.61.015.921a13 13 0 0 0 .18 2.002 6.6 6.6 0 0 0 .624 1.905A6.2 6.2 0 0 0 1.998 38a6.3 6.3 0 0 0 1.618 1.179 6.7 6.7 0 0 0 1.901.63 13.5 13.5 0 0 0 2.004.177c.31.007.613.011.919.011.366.002.728.002 1.095.002h100.6c.36 0 .724 0 1.084-.002.304 0 .617-.004.922-.01a13 13 0 0 0 2-.178 6.8 6.8 0 0 0 1.908-.63A6.3 6.3 0 0 0 117.666 38a6.4 6.4 0 0 0 1.182-1.614 6.6 6.6 0 0 0 .619-1.905 13.5 13.5 0 0 0 .185-2.002c.004-.31.004-.61.004-.921.008-.364.008-.725.008-1.094V9.536q.002-.549-.008-1.092.001-.46-.004-.92a13.5 13.5 0 0 0-.185-2.003 6.6 6.6 0 0 0-.62-1.903 6.47 6.47 0 0 0-2.798-2.8 6.8 6.8 0 0 0-1.908-.627 13 13 0 0 0-2-.176c-.305-.005-.618-.011-.922-.013-.36-.002-.725-.002-1.084-.002"
|
||||
style={{
|
||||
fill: "#a6a6a6",
|
||||
}}
|
||||
/>
|
||||
<path d="M8.445 39.125c-.305 0-.602-.004-.904-.01a13 13 0 0 1-1.87-.164 5.9 5.9 0 0 1-1.656-.548 5.4 5.4 0 0 1-1.397-1.016 5.3 5.3 0 0 1-1.02-1.397 5.7 5.7 0 0 1-.544-1.657 12.4 12.4 0 0 1-.166-1.875c-.007-.21-.015-.913-.015-.913v-23.1s.009-.692.015-.895a12.4 12.4 0 0 1 .165-1.872 5.8 5.8 0 0 1 .544-1.662 5.4 5.4 0 0 1 1.015-1.398 5.6 5.6 0 0 1 1.402-1.023 5.8 5.8 0 0 1 1.653-.544A12.6 12.6 0 0 1 7.543.887l.902-.012h102.769l.913.013a12.4 12.4 0 0 1 1.858.162 6 6 0 0 1 1.671.548 5.6 5.6 0 0 1 2.415 2.42 5.8 5.8 0 0 1 .535 1.649 13 13 0 0 1 .174 1.887c.003.283.003.588.003.89.008.375.008.732.008 1.092v20.929c0 .363 0 .718-.008 1.075 0 .325 0 .623-.004.93a13 13 0 0 1-.17 1.853 5.7 5.7 0 0 1-.54 1.67 5.5 5.5 0 0 1-1.016 1.386 5.4 5.4 0 0 1-1.4 1.022 5.9 5.9 0 0 1-1.668.55 12.5 12.5 0 0 1-1.869.163c-.293.007-.6.011-.897.011l-1.084.002Z" />
|
||||
<g>
|
||||
<g>
|
||||
<path
|
||||
d="M24.769 20.3a4.95 4.95 0 0 1 2.356-4.151 5.07 5.07 0 0 0-3.99-2.158c-1.68-.176-3.308 1.005-4.164 1.005-.872 0-2.19-.988-3.608-.958a5.32 5.32 0 0 0-4.473 2.728c-1.934 3.348-.491 8.269 1.361 10.976.927 1.325 2.01 2.805 3.428 2.753 1.387-.058 1.905-.885 3.58-.885 1.658 0 2.144.885 3.59.852 1.489-.025 2.426-1.332 3.32-2.67a11 11 0 0 0 1.52-3.092 4.78 4.78 0 0 1-2.92-4.4m-2.732-8.09a4.87 4.87 0 0 0 1.115-3.49 4.96 4.96 0 0 0-3.208 1.66 4.64 4.64 0 0 0-1.144 3.36 4.1 4.1 0 0 0 3.237-1.53"
|
||||
style={{
|
||||
fill: "#fff",
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M42.302 27.14H37.57l-1.137 3.356h-2.005l4.484-12.418h2.083l4.483 12.418h-2.039Zm-4.243-1.55h3.752l-1.85-5.446h-.051Zm17.101.38c0 2.813-1.506 4.62-3.779 4.62a3.07 3.07 0 0 1-2.848-1.583h-.043v4.484h-1.86V21.442h1.8v1.506h.033a3.21 3.21 0 0 1 2.883-1.6c2.298 0 3.813 1.816 3.813 4.622m-1.91 0c0-1.833-.948-3.038-2.393-3.038-1.42 0-2.375 1.23-2.375 3.038 0 1.824.955 3.046 2.375 3.046 1.445 0 2.393-1.197 2.393-3.046m11.876 0c0 2.813-1.506 4.62-3.779 4.62a3.07 3.07 0 0 1-2.848-1.583h-.043v4.484h-1.859V21.442h1.799v1.506h.034a3.21 3.21 0 0 1 2.883-1.6c2.298 0 3.813 1.816 3.813 4.622m-1.91 0c0-1.833-.948-3.038-2.393-3.038-1.42 0-2.375 1.23-2.375 3.038 0 1.824.955 3.046 2.375 3.046 1.445 0 2.392-1.197 2.392-3.046m8.496 1.066c.138 1.232 1.334 2.04 2.97 2.04 1.566 0 2.693-.808 2.693-1.919 0-.964-.68-1.54-2.29-1.936l-1.609-.388c-2.28-.55-3.339-1.617-3.339-3.348 0-2.142 1.867-3.614 4.519-3.614 2.624 0 4.423 1.472 4.483 3.614h-1.876c-.112-1.239-1.136-1.987-2.634-1.987s-2.521.757-2.521 1.858c0 .878.654 1.395 2.255 1.79l1.368.336c2.548.603 3.606 1.626 3.606 3.443 0 2.323-1.85 3.778-4.793 3.778-2.754 0-4.614-1.42-4.734-3.667ZM83.346 19.3v2.142h1.722v1.472h-1.722v4.991c0 .776.345 1.137 1.102 1.137a6 6 0 0 0 .611-.043v1.463a5 5 0 0 1-1.032.086c-1.833 0-2.548-.689-2.548-2.445v-5.189h-1.316v-1.472h1.316V19.3Zm2.719 6.67c0-2.849 1.678-4.639 4.294-4.639 2.625 0 4.295 1.79 4.295 4.639 0 2.856-1.661 4.638-4.295 4.638s-4.294-1.782-4.294-4.638m6.695 0c0-1.954-.895-3.108-2.401-3.108s-2.4 1.162-2.4 3.108c0 1.962.894 3.106 2.4 3.106s2.401-1.144 2.401-3.106m3.426-4.528h1.773v1.541h.043a2.16 2.16 0 0 1 2.177-1.635 3 3 0 0 1 .637.069v1.738a2.6 2.6 0 0 0-.835-.112 1.873 1.873 0 0 0-1.937 2.083v5.37h-1.858Zm13.198 6.395c-.25 1.643-1.85 2.771-3.898 2.771-2.634 0-4.269-1.764-4.269-4.595 0-2.84 1.644-4.682 4.19-4.682 2.506 0 4.08 1.72 4.08 4.466v.637h-6.394v.112a2.358 2.358 0 0 0 2.436 2.564 2.05 2.05 0 0 0 2.09-1.273Zm-6.282-2.702h4.526a2.177 2.177 0 0 0-2.22-2.298 2.29 2.29 0 0 0-2.306 2.298"
|
||||
style={{
|
||||
fill: "#fff",
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M37.826 8.731a2.64 2.64 0 0 1 2.808 2.965c0 1.906-1.03 3.002-2.808 3.002h-2.155V8.73Zm-1.228 5.123h1.125a1.876 1.876 0 0 0 1.967-2.146 1.88 1.88 0 0 0-1.967-2.134h-1.125Zm5.082-1.41a2.133 2.133 0 1 1 4.248 0 2.134 2.134 0 1 1-4.247 0m3.334 0c0-.976-.439-1.547-1.208-1.547-.773 0-1.207.571-1.207 1.547 0 .984.434 1.55 1.207 1.55.77 0 1.208-.57 1.208-1.55m6.558 2.254h-.922l-.93-3.317h-.07l-.927 3.317h-.913l-1.242-4.503h.902l.806 3.436h.067l.926-3.436h.852l.926 3.436h.07l.803-3.436h.889Zm2.281-4.503h.855v.715h.066a1.35 1.35 0 0 1 1.344-.802 1.465 1.465 0 0 1 1.559 1.675v2.915h-.889v-2.692c0-.724-.314-1.084-.972-1.084a1.033 1.033 0 0 0-1.075 1.141v2.635h-.888Zm5.24-1.758h.888v6.26h-.888Zm2.124 4.007a2.133 2.133 0 1 1 4.247 0 2.134 2.134 0 1 1-4.247 0m3.333 0c0-.976-.439-1.547-1.208-1.547-.773 0-1.207.571-1.207 1.547 0 .984.434 1.55 1.207 1.55.77 0 1.208-.57 1.208-1.55m1.849.98c0-.81.604-1.278 1.676-1.344l1.22-.07v-.389c0-.475-.315-.744-.922-.744-.497 0-.84.182-.939.5h-.86c.09-.773.818-1.27 1.84-1.27 1.128 0 1.765.563 1.765 1.514v3.077h-.855v-.633h-.07a1.52 1.52 0 0 1-1.353.707 1.36 1.36 0 0 1-1.501-1.348m2.895-.384v-.377l-1.1.07c-.62.042-.9.253-.9.65 0 .405.351.64.834.64a1.06 1.06 0 0 0 1.166-.983m2.052-.596c0-1.423.732-2.324 1.87-2.324a1.48 1.48 0 0 1 1.38.79h.067V8.437h.888v6.26h-.851v-.71h-.07a1.56 1.56 0 0 1-1.415.785c-1.145 0-1.869-.901-1.869-2.328m.918 0c0 .955.45 1.53 1.203 1.53.75 0 1.212-.583 1.212-1.526 0-.938-.468-1.53-1.212-1.53-.748 0-1.203.58-1.203 1.526m6.964 0a2.133 2.133 0 1 1 4.247 0 2.134 2.134 0 1 1-4.247 0m3.333 0c0-.976-.438-1.547-1.208-1.547-.772 0-1.207.571-1.207 1.547 0 .984.435 1.55 1.207 1.55.77 0 1.208-.57 1.208-1.55m2.107-2.249h.855v.715h.066a1.35 1.35 0 0 1 1.344-.802 1.465 1.465 0 0 1 1.559 1.675v2.915h-.889v-2.692c0-.724-.314-1.084-.972-1.084a1.033 1.033 0 0 0-1.075 1.141v2.635h-.889Zm8.845-1.121v1.141h.976v.749h-.976v2.315c0 .472.194.679.637.679a3 3 0 0 0 .339-.021v.74a3 3 0 0 1-.484.046c-.988 0-1.381-.348-1.381-1.216v-2.543h-.715v-.749h.715V9.074Zm2.19-.637h.88v2.481h.07a1.39 1.39 0 0 1 1.374-.806 1.483 1.483 0 0 1 1.55 1.679v2.907h-.889V12.01c0-.72-.335-1.084-.963-1.084a1.052 1.052 0 0 0-1.134 1.142v2.63h-.888Zm9.056 5.045a1.83 1.83 0 0 1-1.95 1.303 2.045 2.045 0 0 1-2.081-2.325 2.077 2.077 0 0 1 2.076-2.352c1.253 0 2.009.856 2.009 2.27v.31h-3.18v.05a1.19 1.19 0 0 0 1.2 1.29 1.08 1.08 0 0 0 1.07-.546Zm-3.126-1.451h2.275a1.086 1.086 0 0 0-1.109-1.167 1.15 1.15 0 0 0-1.166 1.167"
|
||||
style={{
|
||||
fill: "#fff",
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const GooglePlayBadge = (props) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 238.96 70.87" {...props}>
|
||||
<rect x={-0.11} width={239.17} height={70.87} rx={8.86} ry={8.86} />
|
||||
<path
|
||||
d="M230.21 1.42c4.1 0 7.44 3.34 7.44 7.44v53.15c0 4.1-3.34 7.44-7.44 7.44H8.75c-4.1 0-7.44-3.34-7.44-7.44V8.86c0-4.1 3.34-7.44 7.44-7.44zm0-1.42H8.75C3.88 0-.11 3.99-.11 8.86v53.15c0 4.87 3.99 8.86 8.86 8.86h221.46c4.87 0 8.86-3.99 8.86-8.86V8.86c0-4.87-3.99-8.86-8.86-8.86"
|
||||
style={{
|
||||
fill: "#a6a6a6",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M83.9 18.15c0 1.48-.44 2.67-1.32 3.55-1 1.05-2.3 1.57-3.9 1.57s-2.84-.53-3.91-1.59-1.61-2.38-1.61-3.96.54-2.89 1.61-3.96c1.07-1.06 2.38-1.6 3.91-1.6.76 0 1.49.15 2.18.44.69.3 1.25.69 1.66 1.19l-.93.94q-1.05-1.26-2.91-1.26c-1.12 0-2.09.39-2.9 1.18s-1.22 1.81-1.22 3.06.41 2.28 1.22 3.06c.82.79 1.78 1.18 2.9 1.18 1.19 0 2.18-.4 2.97-1.19.51-.52.81-1.23.89-2.15h-3.86v-1.28h5.15c.05.28.07.54.07.8Zm8.17-4.44h-4.84v3.37h4.37v1.28h-4.37v3.37h4.84v1.31h-6.21V12.41h6.21v1.31Zm5.76 9.32h-1.37v-9.32h-2.97V12.4h7.3v1.31h-2.97v9.32Zm8.25 0V12.4h1.36v10.63zm7.43 0h-1.37v-9.32h-2.97V12.4h7.3v1.31h-2.97v9.32Zm16.79-1.37c-1.05 1.08-2.34 1.61-3.9 1.61s-2.85-.54-3.9-1.61-1.57-2.39-1.57-3.94.52-2.87 1.57-3.94c1.04-1.08 2.34-1.61 3.9-1.61s2.84.54 3.89 1.62 1.57 2.39 1.57 3.93-.52 2.87-1.57 3.94Zm-6.78-.89c.79.8 1.75 1.19 2.89 1.19s2.1-.4 2.89-1.19c.79-.8 1.18-1.81 1.18-3.05s-.4-2.26-1.18-3.05c-.78-.8-1.75-1.19-2.89-1.19s-2.1.4-2.89 1.19c-.78.8-1.18 1.81-1.18 3.05s.4 2.26 1.18 3.05Zm10.27 2.26V12.4h1.66l5.17 8.27h.06l-.06-2.05V12.4h1.37v10.63h-1.43l-5.41-8.67h-.06l.06 2.05v6.62h-1.37Z"
|
||||
style={{
|
||||
stroke: "#fff",
|
||||
strokeMiterlimit: 10,
|
||||
strokeWidth: ".2px",
|
||||
fill: "#fff",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M120.61 38.54c-4.17 0-7.56 3.17-7.56 7.53s3.4 7.53 7.56 7.53 7.56-3.2 7.56-7.53-3.4-7.53-7.56-7.53m0 12.1c-2.28 0-4.25-1.88-4.25-4.57s1.97-4.57 4.25-4.57 4.25 1.85 4.25 4.57-1.97 4.57-4.25 4.57m-16.5-12.1c-4.17 0-7.56 3.17-7.56 7.53s3.4 7.53 7.56 7.53 7.56-3.2 7.56-7.53-3.4-7.53-7.56-7.53m0 12.1c-2.28 0-4.25-1.88-4.25-4.57s1.97-4.57 4.25-4.57 4.25 1.85 4.25 4.57-1.97 4.57-4.25 4.57m-19.63-9.79v3.2h7.65c-.23 1.8-.83 3.11-1.74 4.03-1.11 1.11-2.85 2.34-5.91 2.34-4.71 0-8.39-3.8-8.39-8.51s3.68-8.51 8.39-8.51c2.54 0 4.4 1 5.76 2.28l2.26-2.26c-1.91-1.83-4.45-3.22-8.02-3.22-6.45 0-11.87 5.25-11.87 11.7s5.42 11.7 11.87 11.7c3.48 0 6.11-1.14 8.16-3.28 2.11-2.11 2.77-5.08 2.77-7.48 0-.74-.06-1.43-.17-2H84.48Zm80.27 2.48c-.63-1.68-2.54-4.8-6.45-4.8s-7.11 3.05-7.11 7.53c0 4.22 3.2 7.53 7.48 7.53 3.45 0 5.45-2.11 6.28-3.34l-2.57-1.71c-.86 1.26-2.03 2.08-3.71 2.08s-2.88-.77-3.65-2.28l10.08-4.17-.34-.86Zm-10.28 2.51c-.09-2.91 2.26-4.4 3.94-4.4 1.31 0 2.43.66 2.8 1.6zm-8.19 7.31h3.31V31h-3.31zm-5.42-12.93h-.11c-.74-.88-2.17-1.68-3.97-1.68-3.77 0-7.22 3.31-7.22 7.56s3.45 7.51 7.22 7.51c1.8 0 3.22-.8 3.97-1.71h.11v1.08c0 2.88-1.54 4.42-4.03 4.42-2.03 0-3.28-1.46-3.8-2.68l-2.88 1.2c.83 2 3.03 4.45 6.68 4.45 3.88 0 7.16-2.28 7.16-7.85V38.99h-3.14v1.23Zm-3.8 10.42c-2.28 0-4.2-1.91-4.2-4.54s1.91-4.6 4.2-4.6 4.03 1.94 4.03 4.6-1.77 4.54-4.03 4.54M180.26 31h-7.92v22.15h3.31v-8.39h4.62c3.66 0 7.27-2.65 7.27-6.88s-3.6-6.88-7.27-6.88Zm.08 10.68h-4.7v-7.59h4.7c2.47 0 3.87 2.05 3.87 3.8s-1.4 3.8-3.87 3.8Zm20.43-3.18c-2.39 0-4.87 1.05-5.9 3.39l2.94 1.23c.63-1.23 1.79-1.62 3.02-1.62 1.71 0 3.45 1.03 3.48 2.85v.23c-.6-.34-1.88-.86-3.45-.86-3.16 0-6.38 1.74-6.38 4.99 0 2.96 2.59 4.87 5.5 4.87 2.22 0 3.45-1 4.22-2.16h.11v1.71h3.19v-8.49c0-3.93-2.94-6.13-6.73-6.13Zm-.39 12.13c-1.08 0-2.59-.54-2.59-1.88 0-1.71 1.88-2.37 3.5-2.37 1.45 0 2.14.31 3.02.74-.26 2.05-2.02 3.5-3.93 3.5Zm18.74-11.65-3.79 9.6h-.11l-3.93-9.6h-3.56l5.9 13.42-3.36 7.47h3.45l9.09-20.89h-3.68Zm-29.77 14.17h3.31V31h-3.31z"
|
||||
style={{
|
||||
fill: "#fff",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M36.6 34.41 17.74 54.43v.01a5.09 5.09 0 0 0 4.92 3.77c.94 0 1.83-.26 2.58-.7l.06-.04 21.23-12.25-9.94-10.82Z"
|
||||
style={{
|
||||
fill: "#ea4335",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M55.68 31h-.02l-9.17-5.33-10.33 9.19 10.36 10.36 9.12-5.26c1.6-.86 2.68-2.55 2.68-4.49s-1.07-3.61-2.65-4.47Z"
|
||||
style={{
|
||||
fill: "#fbbc04",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="M17.73 16.44c-.11.42-.17.86-.17 1.31v35.38c0 .45.06.89.17 1.31l19.51-19.51z"
|
||||
style={{
|
||||
fill: "#4285f4",
|
||||
}}
|
||||
/>
|
||||
<path
|
||||
d="m36.74 35.43 9.76-9.76-21.21-12.3c-.77-.46-1.67-.73-2.63-.73-2.36 0-4.34 1.6-4.92 3.78s19 19 19 19Z"
|
||||
style={{
|
||||
fill: "#34a853",
|
||||
}}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
Sofa has a native mobile app for iOS and Android built with [Expo](https://expo.dev/) and React Native. It connects to your self-hosted Sofa server and provides the same core features as the web app.
|
||||
|
||||
<div style={{ width: "100%", display: "flex", justifyContent: "center", gap: 10 }}>
|
||||
<a
|
||||
href="https://apps.apple.com/us/app/sofa-tv-movie-tracker/id6760432427"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<AppStoreBadge style={{ height: 40 }} />
|
||||
</a>
|
||||
<a
|
||||
href="https://play.google.com/store/apps/details?id=com.jakejarvis.sofa"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{ display: "block" }}
|
||||
>
|
||||
<GooglePlayBadge style={{ height: 40 }} />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<Callout type="info">
|
||||
The mobile app is not currently available on the App Store or Google Play. You'll need to build it
|
||||
The mobile app is not currently available on Google Play. You'll need to build it
|
||||
from source.
|
||||
</Callout>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const config = {
|
||||
return [
|
||||
{
|
||||
source: "/docs/api",
|
||||
destination: "/docs/api/account/account.removeAvatar",
|
||||
destination: "/docs/api/account/account.integrations.create",
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
@@ -32,6 +32,11 @@ const config = {
|
||||
destination: "https://testflight.apple.com/join/tjSddcaZ",
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: "/android",
|
||||
destination: "https://play.google.com/store/apps/details?id=com.jakejarvis.sofa",
|
||||
permanent: false,
|
||||
}
|
||||
];
|
||||
},
|
||||
async rewrites() {
|
||||
|
||||
+3
-3
@@ -16,10 +16,10 @@
|
||||
"dependencies": {
|
||||
"@takumi-rs/image-response": "^0.73.1",
|
||||
"@vercel/analytics": "^2.0.1",
|
||||
"fumadocs-core": "^16.7.5",
|
||||
"fumadocs-core": "^16.7.6",
|
||||
"fumadocs-mdx": "^14.2.11",
|
||||
"fumadocs-openapi": "^10.4.1",
|
||||
"fumadocs-ui": "^16.7.5",
|
||||
"fumadocs-openapi": "^10.5.0",
|
||||
"fumadocs-ui": "^16.7.6",
|
||||
"lucide-react": "^0.577.0",
|
||||
"next": "16.2.1",
|
||||
"react": "19.2.4",
|
||||
|
||||
+1651
-2266
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user