Files
streamyfin/docs/tv-discovery.md
2026-05-21 13:55:26 +02:00

137 lines
5.0 KiB
Markdown

# TV Discovery
This document explains Streamyfin's platform-specific home screen discovery integrations for Apple TV and Android TV.
## Overview
Streamyfin currently publishes the same "Continue and Next Up" content to two different platform surfaces:
- `tvOS`: Apple TV Top Shelf
- `Android TV`: preview channel recommendations
Both integrations are fed by the same shared payload builder in [utils/tvDiscovery/payload.ts](../utils/tvDiscovery/payload.ts).
## Shared Data Flow
The TV home screen data starts in [components/home/Home.tv.tsx](../components/home/Home.tv.tsx), where the app fetches resume and next-up items and passes them into [utils/tvDiscovery/sync.ts](../utils/tvDiscovery/sync.ts).
The sync layer:
- builds a normalized TV discovery payload
- sends it to the tvOS Top Shelf cache writer on Apple TV
- sends it to the Android TV recommendations module on Android TV
- clears published content when server or user state changes
## Apple TV Top Shelf
Apple TV uses a Top Shelf extension target, not the main app process.
Relevant files:
- [plugins/withTVOSTopShelf.js](../plugins/withTVOSTopShelf.js)
- [targets/StreamyfinTopShelf/TopShelfProvider.swift](../targets/StreamyfinTopShelf/TopShelfProvider.swift)
- [modules/top-shelf-cache/ios/TopShelfCacheModule.swift](../modules/top-shelf-cache/ios/TopShelfCacheModule.swift)
- [utils/topshelf/cache.ts](../utils/topshelf/cache.ts)
How it works:
- the app builds a lightweight JSON payload
- the app stores that payload in the shared app group container
- the tvOS Top Shelf extension reads the cached payload
- the extension renders sections and items for Top Shelf
Why the API key is stored on tvOS:
- the Top Shelf extension runs outside the app process
- it may need authenticated image access when loading poster artwork
- the app stores the API key so the extension can build authenticated requests
## Android TV Recommendations
Android TV uses the TV provider APIs to publish a preview channel and preview programs.
Relevant files:
- [modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsPublisher.kt](../modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsPublisher.kt)
- [modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsModule.kt](../modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsModule.kt)
- [modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsReceiver.kt](../modules/tv-recommendations/android/src/main/java/expo/modules/tvrecommendations/TvRecommendationsReceiver.kt)
- [modules/tv-recommendations/android/src/main/AndroidManifest.xml](../modules/tv-recommendations/android/src/main/AndroidManifest.xml)
- [utils/tvDiscovery/sync.ts](../utils/tvDiscovery/sync.ts)
How it works:
- the app builds the shared TV discovery payload
- the Android native module creates or updates a single preview channel
- the module inserts or updates preview programs for each item
- the module stores the last payload in shared preferences
- the `INITIALIZE_PROGRAMS` receiver can replay the cached payload when requested by the system
Important differences from tvOS:
- Android TV does not use a separate extension target
- Android TV content is persisted through `TvContractCompat`
- artwork is currently published as poster URLs, not app-proxied local content
## Logging
### JavaScript logs
Look for `TVDiscovery` in Metro or app logs.
Examples:
- payload prepared
- Android sync result
- clear operations
### Native Android logs
Use `adb logcat | grep TvRecommendations`
Examples:
- channel created or updated
- preview programs inserted or updated
- stale programs deleted
- cached payload replayed
## Verifying Android TV Output
1. Launch the TV build and let the home screen load.
2. Watch `adb logcat | grep TvRecommendations`.
3. Return to the Android TV / Google TV home screen.
4. Look for the `Continue and Next Up` row.
5. If needed, enable the Streamyfin channel in `Customize home` or `Manage channels`.
Note:
- some launchers delay or hide new preview channels
- some devices expose TV provider data per user/profile
## Build Notes
This feature does not currently require a fresh `prebuild` to work in the checked-in Android project.
Why:
- the Android integration is a local Expo module
- its receiver is declared in the module manifest
- Gradle merges it during normal Android TV builds
Typical commands:
- `bun run android:tv`
- `bun run ios:tv`
## Current Limitations
- Android TV artwork may fail on authenticated Jellyfin servers because the launcher fetches poster URLs outside the app
- Android TV currently publishes a preview channel only, not Watch Next
- tvOS and Android TV both use the same payload source, so section selection is shared unless explicitly split later
## Future Improvements
- add a local image proxy or cache for Android TV artwork
- add Watch Next support for resumable content
- add a native debug dump method for querying TV provider state from inside the app process