Skip to content

Chromecast, profiles, SubSource subtitles, more anime sources and hoster extractors - #13

Merged
Ivorisnoob merged 6 commits into
mainfrom
claude/youthful-sagan-wob3nc
Sep 29, 2026
Merged

Ivorisnoob merged 6 commits into
mainfrom
claude/youthful-sagan-wob3nc

Conversation

@Ivorisnoob

Copy link
Copy Markdown
Owner
  • Chromecast, PiP button, title watched, custom lists, skip-intro fix
  • Profiles: per-person library, "Who's watching?", kids profiles
  • Subtitles from SubSource, shared subtitle fetcher
  • Picture-in-picture: two configurable buttons beside play/pause
  • Anime sources: FourAnimo and AnimeGG
  • Native hoster extractors: Filemoon, StreamWish/VidHide, Voe, Mp4Upload, Vidmoly, ok.ru

:app:compileDebugKotlin passes on the branch tip.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MDM4TpQy8tnCD4KtetXoku

Casting
- media3-cast CastPlayer in PlaybackSession (Default Media Receiver). Connecting moves the
  current item to the TV at the phone's position; disconnecting brings it back paused. Progress,
  mini player, up next, sleep timer, TV subtitles and volume keys work while casting; a session
  that survives an app restart is re-adopted from the item's extras.
- CastMediaProxy: a LAN HTTP server the receiver streams through. It replays requests with the
  stream's headers (via the download cache, so downloads cast), rewrites HLS playlists, strips
  fake PNG segment prefixes, adds CORS, and serves subtitles as WebVTT.
- Undocumented behaviour relied on: the Default Media Receiver loading http:// media from a LAN
  address; networks with client isolation block it.

Player
- Picture-in-picture button in the top bar.
- Subtitle parsing moved to data/subtitles so the proxy can convert to WebVTT.
- ImagePrefixStrippingDataSource moved to data/streaming (shared with the proxy).

Skip intro
- MAL id and episode now come from AnimeEpisodeMapper (ani.zip + AniList), fixing split cours,
  arc-based TMDB seasons and titles whose search ranks shorts first (Frieren). Title search
  remains a fallback and prefers TV over ONA. The submission timed on the closest file length
  wins. AniSkip 404 means no times.

Library
- Mark a whole title watched/unwatched from Details.
- Custom lists (Room v7 with a real 6->7 migration): Add to list sheet on Details, lists row on
  the Saved tab, list screen with rename/delete/remove+undo, included in backups.

Docs: roadmap updated (DNS-for-playback item dropped), CLAUDE.md describes casting and lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
- profiles table (id, name, avatar key, isKids, createdAt), seeded with profile 1 on upgrade
  and on fresh installs. Active profile id lives in AppSettingsStore as a StateFlow.
- Migration(7, 8) (6 -> 7 already added custom lists): watch_later, watch_progress,
  hidden_titles and custom_lists gain profileId; primary keys include it so two profiles can
  hold the same title. SQLite can't change primary keys, so those tables are rebuilt and every
  existing row is backfilled to profile 1. Downloads stay device-wide.
- Every DAO query takes a profileId; repositories follow the active id with flatMapLatest and
  write to it (progress captures the id when recorded, so PlaybackSession and Continue
  Watching land on the right profile). Switching profile stops playback.
- UI: "Who's watching?" (large avatar tiles in Material shapes) on launch with 2+ profiles,
  avatar button on Home to switch, Settings -> Profiles to add, edit (name, avatar, kids) and
  delete with confirmation; deleting removes that profile's rows.
- Kids profiles: discover requests add certification_country=US and certification.lte (PG /
  TV-PG); lists TMDB can't filter (trending, airing, search) and shows are checked against the
  US rating via release_dates / content_ratings, unrated titles hidden. Home hides Settings
  and updates; leaving takes a 1.5 s hold on the avatar. include_adult=false is now added to
  every TMDB request.
- Backup format v2 stores each profile's library; restoring merges profiles by name, and a v1
  backup goes into the active profile.

Verified: compileDebugKotlin, unit tests (52 pass), migration SQL matches Room's generated
schema, and the 7 -> 8 statements run on a real SQLite database with sample rows.
Not run on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
- SubSourceRepository (keyless): IMDb id from TMDB external_ids -> POST /v1/movie/search ->
  the first result's link (series: /series/x -> /subtitles/x/season-N) -> GET the list per
  language (English, plus the phone's language). Drops trailers, keeps the matching language,
  requires SxxEyy/1x05/Eyy for episodes and skips multi-episode packs.
- Undocumented third-party behaviour: SubSource has no public API contract. This mirrors the
  calls its website makes to api.subsource.net/v1 (search, list, detail -> download_token,
  /subtitle/download/<token> returning a zip) and can break without notice.
- CombinedSubtitleRepository merges OpenSubtitles and SubSource in parallel, dedupes, and keeps
  each result's source name (shown under the language in the picker).
- SubtitleFetcher (data/subtitles) now does all subtitle downloads for the player and the cast
  proxy: SubSource token step, zip (largest subtitle entry), gzip, UTF-8 with Windows-1252
  fallback. The player no longer opens connections from a composable.
- Addic7ed was left out: it needs HTML scraping plus a settings toggle, so it wasn't cheap.

Verified: compile, unit tests, and the SubSource flow run live from a script outside the repo
(Breaking Bad S01E05 English: 61 season rows -> 4 episode matches -> zip with one .srt).
Not run on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
- The PiP window now shows [left] [play/pause] [right] (three is what Android allows; devices
  that allow fewer keep play/pause only). Left and right are set in Settings ->
  Picture-in-picture: Back, Forward (by the seek step), Next episode (greyed out when there is
  none) or Skip intro (the AniSkip segment playing now, else the manual 85 s jump).
- Defaults: Back and Forward. Choices are in backups.
- Own vector icons for the PiP buttons instead of the old framework media drawables.

Verified: compile. Not run on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
Probed all five requested sites with one-off scripts outside the repo first:
- FourAnimo (cdn.4animo.xyz): works. Embeds by AniList id + episode for sub and dub; the embed's
  getSources token returns HLS, 9 caption tracks and intro/outro times. Engine `fouranimo`.
- AnimeGG (animegg.org): works. Search -> /series/<slug> -> /<slug>-episode-N -> subbed/dubbed
  embeds listing /play/<id>/video.mp4 per quality (360-1080p), redirecting to vidcache.net.
  Engine `animegg`. The final vidcache hop (port 8161) isn't reachable from the build sandbox,
  so only the redirect was confirmed.
- AniNeko (vidhawk.buzz): dropped. Tickets resolve, but VidHawk's own HLS proxy answers
  502 "upstream 403, cf_waf" from hls.1embed.buzz for every server, title and audio tried.
- AniBD (animex.one): dropped. Its only stream API is download links (animepahe short links,
  already covered by the animepahe engine); playback uses third-party flixcloud embeds, and
  playeng.animeapps.top isn't referenced by the client.
- AnimeDekho (animedekho.app): dropped. Episodes sit behind a gplinks/cuty.io "24hr verify" ad
  gate; getting past it would mean bypassing their access gate.

Plumbing:
- VideoServer gains `mimeType` (HLS set explicitly for FourAnimo's /p?t= URLs, used by the
  player's MediaItem and by downloads) and `skipSegments` (source intro/outro, preferred over
  AniSkip in the player and PiP "Skip intro"). Per-source headers already reached Media3 for
  playback (session ResolvingDataSource) and downloads (DownloadRequestHeaderStore).
- SkipSegment/SkipType moved to domain/model.
- Catalog: two new entries (status Beta) in extensions/index.json, byte-identical bundled copy.

Undocumented third-party behaviour relied on: FourAnimo tokens are bound to the client IP and
want the player host as Referer; AnimeGG's play links want Referer + Origin + Chrome client
hints. Neither site documents any of this.

Verified: compile, unit tests (52, incl. OfficialCatalogTest), and both engines' request/regex
flow against the live sites (Frieren). Not played on a device.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
…d, Vidmoly, ok.ru

- HosterExtractors (data/streaming/hosters), written from scratch: reads each host's player
  config the way its page delivers it. p.a.c.k.e.r scripts via PackerUnpacker (Filemoon inner
  iframe, StreamWish/VidHide `links.hls4/hls2/hls3`, Mp4Upload `player.src`), Vidmoly inline
  jwplayer sources, Voe mirror redirect plus its `'hls'` value or obfuscated JSON config
  (rot13, junk markers, base64, shift, reverse, base64), ok.ru `data-options` metadata (HLS
  manifest, else the best MP4). Streams carry the hoster as Referer/Origin; HLS is marked.
- WebEmbedResolver now tries a native pass first (capped at 6 s): if the embed URL is a known
  hoster, or its HTML links to one, those streams are returned without starting the hidden
  WebView. Otherwise the WebView sniffing runs as before.
- Anikoto no longer drops non-megaplay servers when the host has an extractor.
- CloudStream's GPL extractors were not used or copied.

Undocumented third-party behaviour relied on: every hoster's page structure and obfuscation,
all of which change without notice; a mismatch returns nothing and falls back.

Verified: compile, unit tests; ok.ru end to end against a live video (data-options ->
metadata -> hlsManifestUrl -> master playlist 200 with ok.ru Referer). The other five hosts
were not tested against live embeds: none of the catalog's aggregators expose hoster links in
static HTML, and I didn't go looking for pirated uploads to use as samples.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H8GM7PfnBT2a3jjqSK47hS
@Ivorisnoob
Ivorisnoob merged commit aaf9bcc into main Sep 29, 2026
1 check passed
@Ivorisnoob
Ivorisnoob deleted the claude/youthful-sagan-wob3nc branch September 29, 2026 05:37
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.

2 participants