Gallery App (Mobile) — Feature Audit vs. Jellyfin and Plex
Audit date: 2026-10-07 (gaps table revised 2026-10-10 after the podcast played/unplayed indicator shipped). Built by reading the actual
gallery-appsource (not commit messages or assumptions) — see the inline file references. The Jellyfin and Plex columns come from general product knowledge of their official mobile apps, so treat them as a checklist to verify rather than as a spec.
This audit compares the gallery-app mobile app (iOS/Android, Expo/React Native) with the official Jellyfin and Plex mobile clients. It lists what Gallery App already does, and which features the other two offer that Gallery App lacks. It supports the broader Gallery vs. Plex and Gallery vs. Jellyfin comparisons and complements the Gallery TV feature audit — Watch Together, closed on TV, is still open on mobile, noted explicitly below. Favorites/likes, mark watched/unwatched, a Home dashboard, and playlists have since been ported to mobile as well. Since the previous pass, podcast resume/progress tracking, a podcast played/unplayed/in-progress indicator, cinema library sort/genre/year filtering, video autoplay-next-episode, and a movie/episode multi-version picker have also shipped — see below.
Gallery App is unusual among the three in one structural way: it started as a photo-library app first (Albums was the landing tab, with a Timeline and a Map view) that also happens to stream movies, music, podcasts, and audiobooks. Plex and Jellyfin’s mobile apps are built the other way around — a media-server dashboard that also has a secondary photo feature (Plex) or none at all (Jellyfin). A Home tab now leads instead of Albums (see below), closing most of that gap, but several other gaps below still trace back to that origin.
Table of Contents#
- Platform Scope
- What Gallery App Has Today
- Gaps vs. Jellyfin and Plex
- Cross-Client Gaps (Gallery TV Has It, Mobile Doesn’t)
- Server Features With No Mobile Screen Yet
- Out of Scope
- Suggested Order of Work
Platform Scope#
gallery-app is built on Expo/React Native (not -tvos), targeting standard iOS and Android phones/tablets. Both platforms are reachable from one codebase; this audit does not split iOS vs. Android behavior except where a native capability is declared for only one (noted inline, e.g. UIBackgroundModes).
What Gallery App Has Today#
| Area | Feature |
|---|---|
| Sign-in | Hands off to the server’s own web login page via expo-web-browser (openAuthSessionAsync), returning a token through the galleryapp://auth deep link (contexts/RegisterPage.tsx) — whatever auth methods the server offers (password, SSO, OTP) work, but there is no native in-app form for them. Full multi-account support: add/switch/remove accounts, per-account PIN lock, per-account cache/download isolation with purge-on-removal, legacy single-account migration (contexts/AppContext.tsx, app/accounts.tsx). Separately, the phone can approve a TV/other device’s sign-in by scanning or entering its pairing code and reviewing requested scopes (app/profile/connect-device.tsx). |
| Landing / navigation | Home is the landing tab: Continue Watching, Next Up, and per-library Recently Added rows (app/(tabs)/home.tsx). Other tabs are Albums (photo grid), Timeline (date-grouped), Map (geotagged, react-native-maps), Cinema, Music, Podcasts, Audiobooks (app/(tabs)/). |
| Cinema | Library grid with a sort (title/date/rating/date-added, asc/desc) and genre/year filter sheet (components/cinema/LibraryFilterSheet.tsx, app/cinema/library.tsx). Movie/series/season/episode detail with TMDB cast & crew, play button, progress bar, a mark watched/unwatched toggle for movies, episodes, seasons, and series, and a version picker when a title has more than one file version (app/cinema/, components/cinema/CinemaDetailHeader.tsx, components/cinema/WatchedToggleButton.tsx, components/cinema/VersionPickerButton.tsx). |
| Music | Release grid and detail, queue with shuffle and repeat (off/all/one), persistent mini player (components/player/AudioPlayerBar.tsx). |
| Podcasts | Library, paginated episode list, per-episode and bulk “Download All Unplayed”, resume position and progress reporting via a dedicated PodcastClient sharing the same resume/progress-report path as other media types. Each episode row shows an unplayed/in-progress/played status with a progress bar and remaining time when in progress, and a tap toggle to mark an episode played/unplayed (app/podcasts/detail.tsx, components/player/AudioPlayerBar.tsx). |
| Books & audiobooks | Library, detail with file list, per-file and bulk download, played as a queue (app/audiobooks/book.tsx). |
| Albums (photos & videos) | Grid with hidden-albums toggle, full-screen Lightbox with pinch-zoom, swipe gestures, and a details panel (camera/lens/EXIF) (components/lightbox/Lightbox.tsx). |
| Playlists | List/detail/create/rename/delete/toggle-visibility, plus an “Add to Playlist” dialog reachable from the photo/video lightbox and the music mini player. Scoped to photos/videos and music — the two content types Gallery TV’s own playlist UI supports today (app/playlists/, components/AddToPlaylistDialog.tsx). |
| Video/audio playback | Shared transcoder engine with gallery-tv (@gallery/player-core): direct-play/remux/transcode decision, device capability probing, quality/audio/subtitle pickers, styled WebVTT subtitles, 0.5–2× speed, resume-or-restart prompt for online playback, trickplay scrub thumbnails, autoplay next episode with a countdown/cancel overlay (user-togglable in Settings) (components/player/VideoPlayerModal.tsx, components/player/useTranscoderPlayer.tsx, components/player/NextEpisodeBanner.tsx). Chromecast via react-native-google-cast, cast button in the player and the global header (hooks/useCast.ts). |
| Offline downloads | Comprehensive: photos/video, movies, episodes, music tracks, podcast episodes, audiobook files. Quality picker with remembered per-type preference, download queue with progress/retry, downloaded-content library with a degraded-quality indicator, full Storage Manager (per-category breakdown, cache size limit, clear-all), account-scoped purge (services/OfflineSyncService.ts, components/offline/, app/downloads/, components/StorageManager.tsx). |
| Search | Simple search (debounced, type-filtered, show-hidden toggle) and a separate advanced search (include/exclude terms, tag/album include-exclude, content-type filter), recent searches persisted per account (components/SearchModal.tsx). |
| Settings | Account/profile fields, units, theme, hidden-content default, API token management, offline-mode toggle, PIN management, Storage Manager, debug panel, device-pairing approval (app/profile/settings.tsx). |
| Notifications | No OS push. In-app server-driven banner polling every 5 minutes, dismissed/seen state tracked per account (hooks/useBanners.ts). |
Gaps vs. Jellyfin and Plex#
High priority#
No high-priority gaps remain. Podcast resume/progress tracking (the previous sole entry here) has shipped — network playback now goes through a PodcastClient that implements the same PlayerController interface and resume/progress-report path as movies/music/books, not a bare startLocal bypass (components/player/useTranscoderPlayer.tsx, packages/player-core/src/controllers/PodcastClient.ts). The remaining half of that old gap — a played/unplayed indicator in the episode list UI — has also since shipped (see the Podcasts row above and app/podcasts/detail.tsx).
Medium priority#
| Gap | Gallery App today | Jellyfin / Plex |
|---|---|---|
| Skip intro / credits | No markers or UI for this on mobile, even though the server now exposes getCinemaItemMarkers/chapter data consumed by Gallery TV. |
Both support this (Plex: Pass feature; Jellyfin: plugin-backed, client-rendered). |
| AirPlay | Native player controls are disabled (nativeControls={false}), so no AirPlay route button is exposed anywhere in the custom UI. |
Both support AirPlay out of the box on iOS. |
| Camera upload / photo backup | No expo-media-library dependency, no background camera-roll upload anywhere in the app. |
Plex’s mobile app has Camera Upload as a marquee feature. Worth weighing more heavily than a typical “Plex-only” gap, since Gallery App’s own landing tab is photos — this is closer to a core-identity gap than a nice-to-have. |
| Biometric app lock | App lock is PIN-only (server-verified); no Face ID/Touch ID path exists anywhere (grepped for biometric/FaceID/LocalAuthentication — zero hits), and no expo-local-authentication-style dependency is present. |
Plex supports biometric lock on its mobile app. |
| Sharing / invite flow | No share-album, share-item, or invite-link flow anywhere in the photo, cinema, music, podcast, or audiobook screens. expo-sharing is a declared dependency but only referenced from the debug panel (log export). |
Both let a user share a link to an item or invite someone to a library from mobile. |
| Push notifications | None — expo-notifications is not a dependency. The in-app banner system (useBanners.ts) is server-driven polling, not OS push, and only shows server-authored messages, not per-user events (new content added, download ready, etc.). |
Both send push notifications for new content and account events. |
| Wi-Fi-only downloads toggle has no UI | isWifiOnly/setWifiOnly exist and are wired into OfflineSyncContext.tsx, but no settings screen exposes the toggle — it’s permanently defaulted on and un-editable by the user today. This is close to a one-screen fix, not a build-from-scratch gap. |
Both expose this as a user-facing setting. |
| Lyrics | Not supported in the mobile player. | Both show lyrics during music playback (Jellyfin via plugin, Plex natively). |
| Accessibility | No deliberate accessibilityLabel/accessibilityRole pass found outside of incidental props from heroui-native component defaults (e.g. Avatar alt=). |
Both work with VoiceOver and TalkBack. |
| Localization | English only, no i18n library in package.json. |
Both ship in dozens of languages. |
Low priority#
| Gap | Notes |
|---|---|
| Widgets / Siri shortcuts / Android shortcuts / share-extension | None present — no widget, Siri, or Android App Shortcuts code, and no share-extension target to receive photos shared from another app into Gallery. |
| Now Playing / media-session surfaces | Unclear whether expo-video wires into iOS Now Playing / Android media session metadata by default; not deliberately implemented. Worth a quick check before counting as a real gap. |
| Collections and trailers/extras | Not a Gallery concept anywhere yet (server or any client) — same note as the TV and web comparison docs. Not mobile-specific. |
Cross-Client Gaps (Gallery TV Has It, Mobile Doesn’t)#
These are features the TV app already ships that the mobile app does not — worth closing for its own sake, separate from the Plex/Jellyfin comparison, since the underlying server capability and UI pattern already exist in the sibling codebase:
| Feature | Gallery TV | Gallery App |
|---|---|---|
| Mark watched/unwatched | ✅ (updateViewHistory from an action sheet) |
✅ (WatchedToggleButton on movie/episode/season/series screens) |
| Favorites/likes (movies, series, music, albums) | ✅ (useLikeStatus) |
✅ (useLikeStatus, LikeToggleButton) |
| Playlists | ✅ (full list/detail/create/edit/add-to) | ✅ (list/detail/create/rename/delete/add-to, scoped to photos/videos and music — the two content types Gallery TV’s own playlist UI supports today) |
| Watch Together | ✅ (createWatchSession/joinWatchSession, SSE sync) |
❌ |
| Skip Intro/Credits banners | ✅ (getCinemaItemMarkers) |
❌ |
| Autoplay next episode | ✅ (countdown banner, Settings toggle) | ✅ (video and audio — NextEpisodeBanner.tsx, Settings toggle) |
Server Features With No Mobile Screen Yet#
| Server capability | API operations | Mobile app |
|---|---|---|
| Likes | addLikeItem, deleteLikeItem, getAllLikeItemsForUser, isItemLiked |
Used — movie/series detail screens, the music mini player, and the photo lightbox all have a heart toggle (hooks/useLikeStatus.ts, components/LikeToggleButton.tsx). |
| Playlists | getAllPlaylistsForUser, getPlaylist, addPlaylistItem, and related |
Used — list/detail screens under app/playlists/, with “Add to Playlist” entry points on the photo/video lightbox and the music mini player (hooks/usePlaylists.ts, components/AddToPlaylistDialog.tsx). |
| Watch Together | createWatchSession, joinWatchSession, syncWatchSession, and related |
Unused. |
| Video chapters / markers | getCinemaChapters, getCinemaItemMarkers |
Unused. |
| View history toggling | updateViewHistory, dismissContinueWatching |
Used — the mark watched/unwatched toggle on movie/episode/season/series screens. |
| Continue watching/listening/reading | getContinueWatching, getContinueListening, getContinueReading |
getContinueWatching is used by the Home tab’s Continue Watching/Next Up rows; getContinueListening/getContinueReading remain unused. |
| Server-side advanced search | advancedSearch |
Used (the mobile advanced-search panel already calls this — ahead of Gallery TV here). |
Out of Scope#
These exist in Jellyfin or Plex but do not fit this app, this stack, or Gallery as a whole — consistent with the positioning already established in the Plex/Jellyfin/TV comparison docs:
| Item | Reason |
|---|---|
| Sub-profiles / managed users | Not planned, by design. Gallery uses server-level accounts only; the multi-account switcher with per-account PIN lock is the intended model (same note as the TV audit). |
| Live TV and DVR | Gallery has no live TV backend. See the Plex/Jellyfin comparison docs. |
| Collections and trailers/extras | Not a Gallery concept yet, anywhere in the product. |
Suggested Order of Work#
Home dashboard, mark watched/unwatched, background audio on iOS, PiP, playlists, podcast resume/progress tracking, a podcast played/unplayed indicator, cinema library sort/filter, video autoplay-next-episode, trickplay thumbnails, and the multi-version picker have all since shipped (see above). Remaining order:
- Wi-Fi-only downloads setting — the logic exists; it needs one settings-screen control.
- Medium-priority items in rough impact order: skip intro/credits, camera upload/photo backup, sharing/invite flow, push notifications, AirPlay, biometric app lock.
- Lower priority / broad cross-cutting efforts: accessibility labeling pass, localization, lyrics, widgets/shortcuts/share-extension.