pub struct HybridRepository {
online: Arc<OnlineRepository>,
offline: Arc<OfflineRepository>,
}Expand description
Hybrid repository combining online and offline data sources
Uses cache-first parallel racing strategy:
- Runs SQLite cache and HTTP server queries in parallel
- Cache has 100ms timeout for fast feedback
- Returns cache result if it has meaningful content
- Falls back to server result if cache is empty/stale
@req: UR-002 - Access media when online or offline @req: DR-012 - Local database for media metadata cache @req: DR-013 - Repository pattern for online/offline data access
Fields§
§online: Arc<OnlineRepository>§offline: Arc<OfflineRepository>Implementations§
Source§impl HybridRepository
impl HybridRepository
pub fn new(online: OnlineRepository, offline: OfflineRepository) -> Self
Sourcepub fn user_id(&self) -> &str
pub fn user_id(&self) -> &str
The signed-in user this repository acts for.
TRACES: UR-069 | DR-120
Sourcepub async fn download_bytes(&self, url: &str) -> Result<Vec<u8>, String>
pub async fn download_bytes(&self, url: &str) -> Result<Vec<u8>, String>
Download raw bytes from a URL using the shared authenticated HTTP client. Delegates to online repository for connection reuse and proper auth.
Sourcepub async fn prune_stale_catalog(
&self,
cutoff: &str,
item_types: &[String],
) -> Result<usize, RepoError>
pub async fn prune_stale_catalog( &self, cutoff: &str, item_types: &[String], ) -> Result<usize, RepoError>
Remove catalog entries the server no longer has. Cache-only, so it goes
straight to the offline repository. Callers must only invoke this after a
crawl in which every library succeeded — see
OfflineRepository::prune_stale_catalog for why a partial crawl must not
sweep.
TRACES: UR-065 | DR-110
Sourcepub async fn get_jray_actors(
&self,
item_id: &str,
t: f64,
) -> Result<Vec<JRayActor>, RepoError>
pub async fn get_jray_actors( &self, item_id: &str, t: f64, ) -> Result<Vec<JRayActor>, RepoError>
Query the JRay plugin for actors on screen at time t. Online-only
(the plugin lives on the Jellyfin server); empty when JRay isn’t present.
Sourcepub async fn get_video_stream_url(
&self,
item_id: &str,
media_source_id: Option<&str>,
audio_stream_index: Option<i32>,
) -> Result<String, RepoError>
pub async fn get_video_stream_url( &self, item_id: &str, media_source_id: Option<&str>, audio_stream_index: Option<i32>, ) -> Result<String, RepoError>
Get video stream URL. This method is online-only since offline playback uses local file paths.
Takes no start position: the URL is an HLS playlist spanning the whole
item, and a position on it would 400 every segment — see
OnlineRepository::get_video_stream_url. Resume by seeking after load.
Sourcepub async fn get_audio_only_stream_url_for_video(
&self,
item_id: &str,
media_source_id: Option<&str>,
start_time_seconds: Option<f64>,
audio_stream_index: Option<i32>,
) -> Result<String, RepoError>
pub async fn get_audio_only_stream_url_for_video( &self, item_id: &str, media_source_id: Option<&str>, start_time_seconds: Option<f64>, audio_stream_index: Option<i32>, ) -> Result<String, RepoError>
Get an audio-only stream URL for a video item (background-audio handoff).
Online-only, like get_video_stream_url.
TRACES: UR-040 | JA-032
Sourcepub async fn get_album_tracks(
&self,
album_id: &str,
) -> Result<Vec<MediaItem>, RepoError>
pub async fn get_album_tracks( &self, album_id: &str, ) -> Result<Vec<MediaItem>, RepoError>
Every track of an album, asked of the server rather than the cache.
Deliberately not get_items, which is cache-first: it answers from SQLite
the moment the cache has any content. That is right for browsing and wrong
for deciding what to download, because a partial or unlinked cache then
decides how much of the album gets queued while the user is told the whole
album is downloading. Downloading is the one operation that must know the
album’s complete contents.
Errors when the server cannot answer (offline); the caller falls back to the local catalog and the rows are queued either way, resolving on reconnect. Server results are written back to the cache, so browsing benefits from the round trip too.
TRACES: UR-018, UR-055 | DR-173
Sourcepub async fn get_items_unfiltered(
&self,
parent_id: &str,
options: Option<GetItemsOptions>,
) -> Result<SearchResult, RepoError>
pub async fn get_items_unfiltered( &self, parent_id: &str, options: Option<GetItemsOptions>, ) -> Result<SearchResult, RepoError>
Immediate children of a container with the user’s browsing exclusions not applied.
Exists for the exclusion picker in settings. Everything else in this repository hides what the user has hidden, which would make the setting one-way: a folder already excluded would vanish from the list of folders to exclude and could never be un-hidden. Server-first so the picker sees the real library, falling back to the cache when unreachable.
TRACES: UR-076 | DR-209
Sourcepub async fn search_cache_only(
&self,
query: &str,
options: Option<SearchOptions>,
) -> Result<SearchResult, RepoError>
pub async fn search_cache_only( &self, query: &str, options: Option<SearchOptions>, ) -> Result<SearchResult, RepoError>
Search only the local SQLite cache (downloaded content).
Fast (100ms timeout) — used to render instant results before the server responds. Returns an empty result rather than erroring on timeout so the caller can still fall through to the server.
Sourcepub async fn get_favorites_cache_only(
&self,
scope: SearchScope,
options: Option<GetItemsOptions>,
) -> Result<SearchResult, RepoError>
pub async fn get_favorites_cache_only( &self, scope: SearchScope, options: Option<GetItemsOptions>, ) -> Result<SearchResult, RepoError>
Favourites held locally, without touching the server. Backs the instant leg of the two-phase favourites read in the command layer.
TRACES: UR-067 | DR-115
Sourcepub async fn get_favorites_server_only(
&self,
scope: SearchScope,
options: Option<GetItemsOptions>,
) -> Result<SearchResult, RepoError>
pub async fn get_favorites_server_only( &self, scope: SearchScope, options: Option<GetItemsOptions>, ) -> Result<SearchResult, RepoError>
Favourites straight from the server, persisted to the cache on the way
through — which is also what mirrors their favourite flags into
user_data (DR-114), so the next offline read agrees with the server.
TRACES: UR-067 | DR-115
Sourcepub async fn cache_items_from_server(
&self,
parent_id: &str,
options: Option<GetItemsOptions>,
) -> Result<Vec<MediaItem>, RepoError>
pub async fn cache_items_from_server( &self, parent_id: &str, options: Option<GetItemsOptions>, ) -> Result<Vec<MediaItem>, RepoError>
Fetch a folder’s items from the live server and persist them to the
offline cache synchronously (unlike get_items, which saves in a
fire-and-forget background task after a 100ms cache race).
Used by the full-catalog pre-sync (sync_full_catalog) to deterministically
walk every library while online so the whole catalog is browsable — greyed
out — offline. Returns the items fetched so the caller can recurse into
containers. Server-only: errors if unreachable.
Sourcepub async fn get_downloaded_items(
&self,
parent_id: &str,
options: Option<GetItemsOptions>,
) -> Result<SearchResult, RepoError>
pub async fn get_downloaded_items( &self, parent_id: &str, options: Option<GetItemsOptions>, ) -> Result<SearchResult, RepoError>
Browse downloaded content only — the dedicated Downloads surface.
Bypasses the cache/server merge entirely and reads the offline repository directly, so an empty result is authoritative (“nothing downloaded here”) and never falls through to the server (DR-080). Available online too — a user who is reachable still wants to browse what’s on the device.
TRACES: UR-055 | DR-082, DR-083
Sourcepub async fn get_downloaded_libraries(&self) -> Result<Vec<Library>, RepoError>
pub async fn get_downloaded_libraries(&self) -> Result<Vec<Library>, RepoError>
Libraries that contain downloaded content (offline-only, authoritative).
TRACES: UR-055 | DR-082
Sourcepub async fn get_download_disk_usage(
&self,
) -> Result<DownloadDiskUsage, RepoError>
pub async fn get_download_disk_usage( &self, ) -> Result<DownloadDiskUsage, RepoError>
On-disk usage of downloaded content, for the disk-usage display.
TRACES: UR-056 | DR-085
Sourcepub async fn search_server_only(
&self,
query: &str,
options: Option<SearchOptions>,
) -> Result<SearchResult, RepoError>
pub async fn search_server_only( &self, query: &str, options: Option<SearchOptions>, ) -> Result<SearchResult, RepoError>
Search only the live Jellyfin server (full library).
Sourcepub fn merge_search_results(
cache: SearchResult,
server: SearchResult,
) -> SearchResult
pub fn merge_search_results( cache: SearchResult, server: SearchResult, ) -> SearchResult
Merge cache and server search results into a single de-duplicated list.
Ordering: local (cached/downloaded) items first, then server-only items
appended. On a duplicate id, the server’s item wins (fresher, more
complete metadata) but keeps the local item’s earlier position.
Sourceasync fn parallel_race<T, F1, F2>(
&self,
cache_future: F1,
server_future: F2,
) -> Result<T, RepoError>
async fn parallel_race<T, F1, F2>( &self, cache_future: F1, server_future: F2, ) -> Result<T, RepoError>
Cache-first query: try cache, fall back to server on miss.
- Check cache (100ms timeout applied by caller via cache_with_timeout)
- If cache has meaningful content → return immediately (fast path)
- If cache is empty/stale → query server (fresh data)
- If server fails → return cache even if empty (offline fallback)
Both legs are passed through ExcludeHidden before the “does the cache
have content?” question is asked. This is the single place the cache and
server results of a cache-first query converge, so applying the user’s
browsing exclusions here covers every query built on it at once — and
filtering before the content check is what makes a cache page holding
nothing but hidden items fall through to the server instead of being
served as an empty listing.
@req: UR-002 - Access media when online or offline @req: DR-013 - Repository pattern for online/offline data access
TRACES: UR-002, UR-076 | DR-013, DR-209
Sourceasync fn race_with_refresh<T, F1, F2, R>(
&self,
cache_future: F1,
server_future: F2,
on_cache_hit: R,
) -> Result<T, RepoError>
async fn race_with_refresh<T, F1, F2, R>( &self, cache_future: F1, server_future: F2, on_cache_hit: R, ) -> Result<T, RepoError>
Self::parallel_race, plus a callback fired on the fast path so the
caller can refresh the cache in the background.
A plain cache hit answers from data that may be arbitrarily old, which
is right for the response and wrong for what it leaves behind: per-user
state (watch positions, favourites) only reaches the local tables when a
server result is cached, so a surface that always hits cache never learns
what another device did. get_items had a bespoke version of this; this
is the same idea, reusable.
The callback runs only on a cache hit — on a miss the server result is already being fetched and cached by the normal path.
TRACES: UR-002, UR-025, UR-076 | DR-155, DR-209
Trait Implementations§
Source§impl MediaRepository for HybridRepository
impl MediaRepository for HybridRepository
Source§fn get_item<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<MediaItem, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_item<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<MediaItem, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
A single item, cache-first — and, on a cache hit, refreshed in the background so the stored copy keeps up with the server.
The background refresh is what carries per-user state home: caching an
item runs mirror_user_data, which is the only path by which a watch
position set on another device reaches the local user_data row the
resume check reads. Without it a cache hit returned this device’s own
stale position forever and cross-device resume silently did nothing —
get_items already refreshes this way, so browsing a season worked
while opening the episode directly did not.
The refreshed value lands for the next read rather than this one: the point of the cache-first race is to answer immediately.
TRACES: UR-025, UR-002 | DR-155 | UT-152
Source§fn get_favorites<'life0, 'async_trait>(
&'life0 self,
scope: SearchScope,
options: Option<GetItemsOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_favorites<'life0, 'async_trait>(
&'life0 self,
scope: SearchScope,
options: Option<GetItemsOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
TRACES: UR-067 | DR-115
Source§fn get_libraries<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<Library>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_libraries<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<Library>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn get_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: &'life1 str,
options: Option<GetItemsOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: &'life1 str,
options: Option<GetItemsOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_latest_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: &'life1 str,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_latest_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: &'life1 str,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_resume_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_resume_items<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_next_up_episodes<'life0, 'life1, 'async_trait>(
&'life0 self,
series_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_next_up_episodes<'life0, 'life1, 'async_trait>(
&'life0 self,
series_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_recently_played_audio<'life0, 'async_trait>(
&'life0 self,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_recently_played_audio<'life0, 'async_trait>(
&'life0 self,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn get_resume_movies<'life0, 'async_trait>(
&'life0 self,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_resume_movies<'life0, 'async_trait>(
&'life0 self,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn get_rediscover_albums<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_rediscover_albums<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
limit: Option<usize>,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_genres<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Genre>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_genres<'life0, 'life1, 'async_trait>(
&'life0 self,
parent_id: Option<&'life1 str>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Genre>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn search<'life0, 'life1, 'async_trait>(
&'life0 self,
query: &'life1 str,
options: Option<SearchOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn search<'life0, 'life1, 'async_trait>(
&'life0 self,
query: &'life1 str,
options: Option<SearchOptions>,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_playback_info<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<PlaybackInfo, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_playback_info<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<PlaybackInfo, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_audio_stream_url<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<String, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn get_audio_stream_url<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<String, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_audio_only_stream_url_for_video<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
media_source_id: Option<&'life2 str>,
start_time_seconds: Option<f64>,
audio_stream_index: Option<i32>,
) -> Pin<Box<dyn Future<Output = Result<String, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn get_audio_only_stream_url_for_video<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
media_source_id: Option<&'life2 str>,
start_time_seconds: Option<f64>,
audio_stream_index: Option<i32>,
) -> Pin<Box<dyn Future<Output = Result<String, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn get_live_tv_channels<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_live_tv_channels<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Vec<MediaItem>, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn get_channels<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn get_channels<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<SearchResult, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
get_items(channel_id, ...).Source§fn open_live_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<LiveStreamInfo, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn open_live_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<LiveStreamInfo, RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn report_playback_start<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn report_playback_start<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn report_playback_progress<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn report_playback_progress<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn report_playback_stopped<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn report_playback_stopped<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
position_ticks: i64,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn get_image_url(
&self,
item_id: &str,
image_type: ImageType,
options: Option<ImageOptions>,
) -> String
fn get_image_url( &self, item_id: &str, image_type: ImageType, options: Option<ImageOptions>, ) -> String
Source§fn get_subtitle_url(
&self,
item_id: &str,
media_source_id: &str,
stream_index: i32,
format: &str,
) -> String
fn get_subtitle_url( &self, item_id: &str, media_source_id: &str, stream_index: i32, format: &str, ) -> String
Source§fn get_video_download_url(
&self,
item_id: &str,
quality: &str,
media_source_id: Option<&str>,
source_audio_codec: Option<&str>,
) -> String
fn get_video_download_url( &self, item_id: &str, quality: &str, media_source_id: Option<&str>, source_audio_codec: Option<&str>, ) -> String
resolve_video_download_url rather than calling it directly. Read moreSource§fn mark_favorite<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn mark_favorite<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn unmark_favorite<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn unmark_favorite<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn clear_watch_history<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn clear_watch_history<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn mark_played<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn mark_played<'life0, 'life1, 'async_trait>(
&'life0 self,
item_id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<(), RepoError>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
clear_watch_history. Needed by the
sync-queue drain, which replays mark_played rows queued while the
server was unreachable; reporting a stop at a made-up position was the
previous stand-in and does not set the played flag reliably. Read more