Skip to content

feat: Infuse mode — external-player subtitles for Apple TV#987

Open
aziz66 wants to merge 1 commit into
Viren070:mainfrom
aziz66:feat/infuse-mode
Open

feat: Infuse mode — external-player subtitles for Apple TV#987
aziz66 wants to merge 1 commit into
Viren070:mainfrom
aziz66:feat/infuse-mode

Conversation

@aziz66

@aziz66 aziz66 commented May 31, 2026

Copy link
Copy Markdown

What & why

When playing via Infuse on Apple TV, Infuse plays embedded (mostly English) subtitle tracks but never requests subtitles the way Stremio's built-in player does — so non-embedded languages mean manual downloads every time. This adds an opt-in way to resolve a subtitle up front and bake it into the Infuse launch link.

How it works

  • Per-user, opt-in (UserData.infuse { enabled, topN, candidates }), off by default → no behaviour change unless enabled.
  • Rewrites playable (http/debrid/usenet) streams into infuse://x-callback-url/play?url=…&sub=…&filename=….
  • Subtitle language reuses the user's Preferred Subtitles (defaults to English; multiple tried in priority order); provider priority follows the user's subtitle-addon order — no new hardcoded provider/language.
  • A small .srt subtitle proxy serves the chosen upstream with the right extension/content-type + language-labelled filename so Infuse recognises/labels the track; N candidates give silent fallback.
  • Pure helpers in packages/core/src/main/infuse.ts with unit tests; SPA control under Miscellaneous → Playback; docs guide added.

Scope / safety

Default off; movie/series only; skipped during precaching; keeps stream.url so preload still works. Stremio app only (Fusion/Omni build the Infuse launch themselves — documented). One subtitle per video (Infuse scheme limit).

Testing

Unit tests + full build (tsc + rsbuild) green; validated end-to-end on Apple TV.

Happy to adjust the config approach (per-user vs env/instance), commit layout, or test placement to match project conventions.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Infuse (External Player) integration: launch streams in Infuse with automatically selected, deduplicated subtitles and optional filename inclusion.
    • New Infuse settings: enable/disable Infuse, configure filename-matched stream limit (0–50) and subtitle candidates (1–5).
  • Behaviour

    • Streams are rewritten to Infuse launch URLs when enabled; only top-N streams resolve per-file subtitles, others reuse fallback candidates.
  • Documentation

    • New guide describing setup, selection rules and platform limitations.
  • Infrastructure

    • Internal subtitle proxy serves selected subtitle files via a stable URL.

@coderabbitai

coderabbitai Bot commented May 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: c750e14f-67b1-4b05-a4da-936897a7bb30

📥 Commits

Reviewing files that changed from the base of the PR and between 708b769 and d652252.

📒 Files selected for processing (10)
  • packages/core/src/db/schemas.ts
  • packages/core/src/main/infuse.test.ts
  • packages/core/src/main/infuse.ts
  • packages/core/src/main/resources.ts
  • packages/core/src/utils/fieldMeta.ts
  • packages/core/tsconfig.json
  • packages/docs/content/docs/guides/infuse.mdx
  • packages/docs/content/docs/guides/meta.json
  • packages/frontend/src/components/menu/miscellaneous/_components/playback-behavior.tsx
  • packages/server/src/app.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/frontend/src/components/menu/miscellaneous/_components/playback-behavior.tsx
  • packages/core/src/utils/fieldMeta.ts
  • packages/docs/content/docs/guides/meta.json
  • packages/core/tsconfig.json
  • packages/core/src/db/schemas.ts
  • packages/core/src/main/infuse.ts
  • packages/core/src/main/infuse.test.ts
  • packages/server/src/app.ts
  • packages/core/src/main/resources.ts

Walkthrough

This pull request introduces Infuse mode, an external player integration that rewrites qualifying playable streams into infuse:// launch URLs with embedded subtitle selection and proxying. The feature spans schema definition, pure helper functions, stream transformation logic, a subtitle proxy endpoint, frontend configuration UI, and user documentation.

Changes

Infuse Mode—External Player Subtitle Injection

Layer / File(s) Summary
Data schema and configuration
packages/core/src/db/schemas.ts, packages/core/src/utils/fieldMeta.ts, packages/core/tsconfig.json
UserDataSchema gains an optional infuse object with enabled, topN (0–50), and candidates (1–5) fields; field metadata registers it for playback UI navigation; TypeScript is configured to exclude test files from compilation.
Core Infuse helper functions and tests
packages/core/src/main/infuse.ts, packages/core/src/main/infuse.test.ts
Pure functions resolve target languages from user preferences, select and deduplicate subtitles by priority, build subtitle proxy URLs with base64url-encoded candidate lists, map languages to ISO 639-1 codes, and construct infuse://x-callback-url/play launch URLs. Unit tests validate language normalisation, subtitle selection, ISO code mapping, proxy path encoding, and play URL assembly.
Stream transformation and subtitle proxy endpoint
packages/core/src/main/resources.ts, packages/server/src/app.ts
resources.ts collects subtitle candidates from configured addons, selects per-file subtitles for the top N playable streams with fallback reuse, and rewrites eligible streams to infuse:// external URLs; app.ts adds /infuse-sub/.../*.srt route that decodes base64url candidate lists and serves the first successful upstream SRT with a 24-hour cache, returning 400/502/500 on error.
Frontend settings UI
packages/frontend/src/components/menu/miscellaneous/_components/playback-behavior.tsx
New "Infuse (External Player)" settings card adds an enable toggle and two numeric inputs for filename-matched stream count (0–50) and subtitle candidate limit (1–5); inputs are disabled when the feature is off, and updates are sanitised to respect bounds.
User guide and documentation
packages/docs/content/docs/guides/infuse.mdx, packages/docs/content/docs/guides/meta.json
Adds MDX guide explaining Infuse mode, selection rules, proxy endpoint, UI configuration, and limitations; registers the guide in docs metadata.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • Viren070

Poem

🐰 A rabbit hops through streams with glee,
Infuse mode brings subtitles spree!
From ISO codes to proxy flight,
External players now shine bright,
With fallback logic, tested right. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Infuse mode — external-player subtitles for Apple TV' accurately and specifically describes the main feature added: opt-in Infuse mode enabling external-player subtitles for Apple TV.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@aziz66 aziz66 marked this pull request as ready for review May 31, 2026 12:39

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
packages/docs/content/docs/guides/infuse.mdx (1)

38-38: ⚡ Quick win

Consider clarifying how to access Advanced mode.

The line mentions "(Advanced mode)" but doesn't explain how users should enable or access it. Users unfamiliar with the interface might not know where to find this toggle.

📝 Suggested clarification
-In the configure UI: **Miscellaneous → Playback → Infuse (External Player)** (Advanced mode).
+In the configure UI: **Miscellaneous → Playback → Infuse (External Player)** (enable Advanced mode first to see this section).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/docs/content/docs/guides/infuse.mdx` at line 38, Update the sentence
"In the configure UI: **Miscellaneous → Playback → Infuse (External Player)**
(Advanced mode)" to explicitly tell users how to enable or access Advanced mode
— e.g., add a short parenthetical or following clause such as "— enable Advanced
mode via the top-right gear/menu or by toggling 'Advanced' in the UI header" or
the exact steps/button name shown in the app; reference the same UI path
(Miscellaneous → Playback → Infuse (External Player)) so the location remains
clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/frontend/src/components/menu/miscellaneous/_components/playback-behavior.tsx`:
- Around line 227-235: The onValueChange handler for updating infuse.topN
(inside setUserData) only clamps the lower bound; update it to clamp both bounds
so topN = Math.max(0, Math.min(50, Number(value ?? 5))) (or equivalent) before
storing in userData.infuse.topN to enforce the schema's max 50 in the UI; locate
the onValueChange block that currently updates setUserData and replace the
sanitisation with a two-sided clamp referencing infuse.topN and setUserData.

In `@packages/server/src/app.ts`:
- Around line 294-304: The fetch to the upstream subtitle URL (the await
fetch(upstream, { redirect: 'follow' }) call that produces upstreamRes and then
Buffer.from(await upstreamRes.arrayBuffer())) needs a request timeout and a
maximum allowed response size: wrap the fetch with an AbortController and cancel
the request after a configurable timeout, and before buffering validate the size
using upstreamRes.headers.get('content-length') (reject if > MAX_BYTES) and, if
content-length is absent, read the body as a stream and accumulate bytes up to
MAX_BYTES, aborting and throwing if the limit is exceeded; update the logic
around upstreamRes, arrayBuffer/Buffer creation, and the handler that logs and
returns the subtitle to use these checks and ensure the fetch is aborted on
timeout or size breach.

---

Nitpick comments:
In `@packages/docs/content/docs/guides/infuse.mdx`:
- Line 38: Update the sentence "In the configure UI: **Miscellaneous → Playback
→ Infuse (External Player)** (Advanced mode)" to explicitly tell users how to
enable or access Advanced mode — e.g., add a short parenthetical or following
clause such as "— enable Advanced mode via the top-right gear/menu or by
toggling 'Advanced' in the UI header" or the exact steps/button name shown in
the app; reference the same UI path (Miscellaneous → Playback → Infuse (External
Player)) so the location remains clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: fa0a7779-60d2-4aa1-a8a8-a59fda7d9edd

📥 Commits

Reviewing files that changed from the base of the PR and between d8538b0 and bfa7eb3.

📒 Files selected for processing (10)
  • packages/core/src/db/schemas.ts
  • packages/core/src/main/infuse.test.ts
  • packages/core/src/main/infuse.ts
  • packages/core/src/main/resources.ts
  • packages/core/src/utils/fieldMeta.ts
  • packages/core/tsconfig.json
  • packages/docs/content/docs/guides/infuse.mdx
  • packages/docs/content/docs/guides/meta.json
  • packages/frontend/src/components/menu/miscellaneous/_components/playback-behavior.tsx
  • packages/server/src/app.ts

Comment thread packages/server/src/app.ts Outdated
@aziz66 aziz66 force-pushed the feat/infuse-mode branch from bfa7eb3 to 708b769 Compare June 3, 2026 13:44
Adds an opt-in, per-user "Infuse mode". When enabled, playable streams are
rewritten into direct `infuse://x-callback-url/play?url=…&sub=…&filename=…`
launch links with a subtitle resolved and baked in, so playback opens in Infuse
(Apple TV) with subtitles already loaded — Infuse never requests subtitles the
way Stremio's built-in player does.

- Per-user config (UserData.infuse { enabled, topN, candidates }); off by
  default → no behaviour change unless a user enables it.
- Subtitle language reuses the user's Preferred Subtitles (defaults to English);
  multiple languages are tried in priority order. Provider priority follows the
  user's subtitle-addon order. No new hardcoded provider/language.
- A small `.srt` subtitle proxy serves the chosen upstream with the right
  extension/content-type and a language-labelled filename so Infuse recognises
  and labels the track; N candidates per stream give silent fallback.
- Pure helpers in packages/core/src/main/infuse.ts with unit tests; SPA control
  under Miscellaneous → Playback; docs guide added.

Notes: Stremio app only (other clients build the Infuse launch themselves);
Infuse supports one subtitle per video; torrent/p2p streams are skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aziz66 aziz66 force-pushed the feat/infuse-mode branch from 708b769 to d652252 Compare June 3, 2026 13:50
@aziz66

aziz66 commented Jun 3, 2026

Copy link
Copy Markdown
Author

Thanks for the review! Addressed all three points in d6522522:

  • app.ts — subtitle-proxy fetch hardening (major): the upstream fetch now uses a 10s AbortController timeout and a 5 MB size cap (content-length pre-check + post-buffer check), skipping to the next candidate on breach and clearing the timer in finally. Guards against slow/hanging or oversized upstream subtitle providers.
  • playback-behavior.tsxtopN input clamp (minor): now clamps both bounds (Math.min(50, Math.max(0, …))) to match the schema's 0–50 range in the UI.
  • infuse.mdx — Advanced mode (nit): clarified how to reach it (toggle Basic→Advanced at the top of the configure page).

Also rebased onto the latest main and re-validated: tsc + rsbuild build clean, unit tests pass, and the proxy happy-path + dead-candidate fallback were re-tested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant