Skip to content

feat(downloads): offline download feature for movies and episodes#280

Open
chillpill244 wants to merge 2 commits into
ProdigyV21:mainfrom
chillpill244:downloads
Open

feat(downloads): offline download feature for movies and episodes#280
chillpill244 wants to merge 2 commits into
ProdigyV21:mainfrom
chillpill244:downloads

Conversation

@chillpill244
Copy link
Copy Markdown
Contributor

@chillpill244 chillpill244 commented May 29, 2026

Summary

Adds end-to-end offline download support for mobile users. Downloads are restricted to user-controlled local servers (Jellyfin, Emby, Plex) to comply with Play Store policy.

What's included

Core infrastructure

  • Room database (DownloadEntity, DownloadDao, ArflixDatabase) — tracks download status, progress percentage, local file path, and artwork metadata (poster, backdrop, TMDB ID, season/episode numbers)
  • DownloadsRepository — provides insert, query, pause/resume/cancel/delete/retry operations; deletes the on-disk file when a download is removed
  • DownloadWorkerCoroutineWorker that streams the file over OkHttp with Range header support for true pause/resume, posts a persistent foreground notification with live progress, and handles Android 12+ foreground service restrictions gracefully

Downloads UI

  • DownloadsTab — horizontal card rows split into Movies and Series sections; artwork respects the user's existing poster/backdrop CardLayoutMode setting
  • DownloadActionsSheet — bottom sheet offering pause, resume, cancel, delete, and retry — actions available depend on the current DownloadStatus
  • DownloadedEpisodesScreen — per-series episode list, navigable from the Downloads tab
  • Series cards show a completed/total episode badge; long-pressing opens a "Delete all downloads" sheet for the whole series

Entry points

  • Bottom bar gains a Downloads tab on mobile when at least one download exists
  • Details screen shows a Download / Downloading x% / Downloaded / Paused / Retry button below the primary Play button, styled with a subtle glass outline to match the Trailer button; tapping a completed download prompts deletion
  • Context menu on watchlist and catalog cards gains a Download option

Player

  • Chromecast cast button is hidden and the Cast handler is no-op'd for offline (file://) streams — Chromecast devices cannot access local device storage

Watchlist / Downloads tab navigation

  • Replaced the Material TabRow with an Apple TV-style animated pill segmented control — smooth background and text color transitions between tabs
  • Tab selection is now preserved with rememberSaveable so returning from Details always restores the tab the user was on

Additional improvements

  • Stream headers forwarding: Per-stream proxy headers (Referer, Authorization, etc.) from Stremio addon behaviorHints.proxyHeaders are forwarded to DownloadWorker, matching the credentials ExoPlayer uses during playback
  • Session-cookie detection: The download stream picker runs a HEAD probe on selection; streams that respond with Set-Cookie are shown as unavailable for download rather than silently failing
  • IPTV file size resolution: Two-stage probe — HEAD first, then Range: bytes=0-0 GET fallback for IPTV/Xtream Codes servers that omit Content-Length on HEAD; resolved size appears next to the Download button
  • Subtitle preferences: Download picker honours default_subtitle / secondary_subtitle from Settings, filtering the list and auto-selecting a matching track
  • Movie size badge: Completed movie cards in the Downloads tab show a file size overlay (e.g. "2.1 GB") in the top-left corner
  • formatBytes extracted to com.arflix.tv.util.DownloadUtils; subtitle matching helpers extracted to com.arflix.tv.util.SubtitleLanguageMatcher — removes identical private copies across multiple files

Play Store compliance

  • Download picker filtered to local servers onlyDownloadSheet only surfaces streams where addonId == "home_server" (Jellyfin, Emby, Plex). The user sees "Downloads are only available for your local server (Jellyfin, Emby, or Plex)" if no local server streams are present.

Test plan

  • Shows watchlist icon in navigation by default
  • Watchlist screen has two tabs - Watchlist/Downloads
  • Download a movie from Jellyfin → verify progress notification → tap play after completion
  • Download a series episode from Jellyfin → navigate to Downloaded Episodes screen
  • Shows downloads icon in navigation when downloads are present
  • Shows download progress in navigation when movie/episodes are downloading
  • Pause mid-download → resume → verify it continues from where it left off (not restarted)
  • Delete a download → verify file is removed from device storage
  • Verify Chromecast button is absent on the player screen for a downloaded file
  • Switch to watchlist tab → navigate to Details by clicking on any media → back → confirm tab stays on Watchlist
  • Open download sheet for a Stremio/IPTV source → verify "local server only" message appears and no streams are listed
  • Set a preferred subtitle language in Settings — verify matching subtitle is pre-selected in the download picker
  • Complete a movie download — verify size badge appears on the card in Downloads tab

🤖 Generated with Claude Code

@ProdigyV21
Copy link
Copy Markdown
Owner

I really like the idea, great pr! and offline downloads would be a strong feature, but I’m worried about Play Store risk.

I want ARVIO to stay the same version on Play Store and GitHub. I don’t want a “Play Store version” and a separate “GitHub version”. Because of that, we have to follow the stricter Play Store rules.

The problem is that this PR allows downloading video files from general streaming sources like IPTV/Stremio/Debrid/unknown links. Even if the user adds the source themselves, Google can still see the app as enabling users to save copyrighted movies/episodes locally without permission. Play Store policy is very strict about apps that stream or download copyrighted content without authorization, and this could risk rejection or even app suspension.

For self-owned sources like Jellyfin/local media it is much safer, because the user controls the server/content. But “download any stream” is too broad for a Play Store app.

So I don’t think we should merge this as-is. If we keep it, it should be limited to clearly self-hosted/owned sources only, or redesigned as temporary playback cache instead of a visible offline download library. Otherwise I think it is better to skip full offline downloads so ARVIO stays safe for Play Store. If you have any other suggestions about this let me know, you could also look into the policies more, it really falls into a green or grey zone we could include this feature.

@chillpill244
Copy link
Copy Markdown
Contributor Author

chillpill244 commented May 30, 2026

Hey @ProdigyV21, addressed this with two changes:

  1. The Download button on the details screen is now only shown when the user has a home server (Jellyfin, Emby, or Plex) configured — so users without a local server never see the option at all.
  2. As a defense-in-depth measure, the download stream picker also filters to local server streams only (addonId == "home_server"), so even if the button were somehow reached, IPTV/Stremio/Debrid streams would never be downloadable.

chillpill244 and others added 2 commits June 1, 2026 18:58
Adds end-to-end offline download support for mobile users. Downloaded content is persisted in a local Room database and playable without an internet connection. Tested with IPTV and Jellyfin sources.

**Core infrastructure**
- Room database (`DownloadEntity`, `DownloadDao`, `ArflixDatabase`) tracking download state, progress, file path, and metadata
- `DownloadsRepository` — insert, query, pause/resume/cancel/delete/retry helpers
- `DownloadWorker` — streams via OkHttp with `Range` header support for pause/resume, foreground notifications with live progress

**Downloads UI**
- `DownloadsTab` — horizontal card rows for movies and series with poster/backdrop layout; long-press or MoreVert opens an actions sheet
- `DownloadActionsSheet` — bottom sheet with pause, resume, cancel, delete, and retry actions
- `DownloadedEpisodesScreen` — per-series episode list reachable from the Downloads tab
- Series long-press shows a "Delete all downloads" sheet

**Entry points**
- Bottom bar shows a Downloads tab on mobile when any download exists
- Details screen shows a Download / Downloading / Downloaded / Retry button below Play
- Context menu on watchlist/catalog cards gains a Download option

**Player**
- Chromecast disabled for offline (`file://`) streams

**Watchlist tab**
- Apple TV-style animated pill segmented control (mobile-only)
- Tab selection preserved across navigation with `rememberSaveable`

**Stream headers & subtitles**
- Stream header forwarding for authenticated sources
- Subtitle track preferences persisted per profile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onfigured

Limits the download stream picker to streams from user-controlled local
servers (Jellyfin, Emby, Plex) to comply with Play Store policy. IPTV,
Stremio addon, and Debrid streams are filtered out of the picker — the
full infrastructure remains in place so support can be expanded later by
removing the isLocalServerStream() guard.

Also injects HomeServerRepository into DetailsViewModel and observes the
connections flow to surface hasHomeServer in UiState. The Download button
on the details screen and the Download option in the episode context menu
are only shown when the user has a usable Jellyfin/Emby/Plex connection
set up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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