First working POC
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Context for the current queue - where did the queue items come from?
|
||||
/// This is used for remote playback transfer to send album/playlist context.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum QueueContext {
|
||||
/// Playing from a specific album
|
||||
Album {
|
||||
album_id: String,
|
||||
album_name: String,
|
||||
},
|
||||
/// Playing from a specific playlist
|
||||
Playlist {
|
||||
playlist_id: String,
|
||||
playlist_name: String,
|
||||
},
|
||||
/// Custom queue (search results, manual queue, etc.)
|
||||
/// Will create a temporary playlist on remote transfer
|
||||
#[default]
|
||||
Custom,
|
||||
}
|
||||
|
||||
/// Represents a subtitle track
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct SubtitleTrack {
|
||||
/// Stream index in the media source
|
||||
pub index: i32,
|
||||
/// Subtitle URL
|
||||
pub url: String,
|
||||
/// Language code (e.g., "eng", "spa")
|
||||
pub language: Option<String>,
|
||||
/// Display title
|
||||
pub label: Option<String>,
|
||||
/// MIME type (e.g., "text/vtt", "application/x-subrip")
|
||||
pub mime_type: String,
|
||||
}
|
||||
|
||||
/// Represents a media item that can be played
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MediaItem {
|
||||
/// Unique identifier
|
||||
pub id: String,
|
||||
/// Display title
|
||||
pub title: String,
|
||||
/// Name (alias for title - for frontend compatibility)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
/// Artist name(s) for audio
|
||||
pub artist: Option<String>,
|
||||
/// Album name for audio
|
||||
pub album: Option<String>,
|
||||
/// Album name (alias - for frontend compatibility)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub album_name: Option<String>,
|
||||
/// Album ID (Jellyfin ID) for remote transfer context
|
||||
#[serde(default)]
|
||||
pub album_id: Option<String>,
|
||||
/// Artist items with IDs for clickable links
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub artist_items: Option<Vec<crate::repository::types::ArtistItem>>,
|
||||
/// Artists as array of strings (fallback when artist_items not available)
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub artists: Option<Vec<String>>,
|
||||
/// Primary image tag for artwork
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub primary_image_tag: Option<String>,
|
||||
/// Item type (Audio, Movie, Episode, etc.)
|
||||
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
|
||||
pub item_type: Option<String>,
|
||||
/// Playlist ID (Jellyfin ID) for remote transfer context
|
||||
#[serde(default)]
|
||||
pub playlist_id: Option<String>,
|
||||
/// Duration in seconds
|
||||
pub duration: Option<f64>,
|
||||
/// URL or path to artwork image
|
||||
pub artwork_url: Option<String>,
|
||||
/// Type of media
|
||||
pub media_type: MediaType,
|
||||
/// Source of the media
|
||||
pub source: MediaSource,
|
||||
/// Video codec (e.g., "h264", "hevc") for video media
|
||||
#[serde(default)]
|
||||
pub video_codec: Option<String>,
|
||||
/// Whether the video requires server-side transcoding
|
||||
#[serde(default)]
|
||||
pub needs_transcoding: bool,
|
||||
/// Video width in pixels
|
||||
#[serde(default)]
|
||||
pub video_width: Option<u32>,
|
||||
/// Video height in pixels
|
||||
#[serde(default)]
|
||||
pub video_height: Option<u32>,
|
||||
/// Available subtitle tracks
|
||||
#[serde(default)]
|
||||
pub subtitles: Vec<SubtitleTrack>,
|
||||
/// Series ID (for TV show episodes) - used for series audio preferences
|
||||
#[serde(default)]
|
||||
pub series_id: Option<String>,
|
||||
/// Server ID - used for series audio preferences
|
||||
#[serde(default)]
|
||||
pub server_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum MediaType {
|
||||
Audio,
|
||||
Video,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
pub enum MediaSource {
|
||||
/// Streaming from Jellyfin server
|
||||
Remote {
|
||||
stream_url: String,
|
||||
jellyfin_item_id: String,
|
||||
},
|
||||
/// Downloaded/cached locally
|
||||
Local {
|
||||
file_path: PathBuf,
|
||||
/// Original Jellyfin ID for sync-back
|
||||
jellyfin_item_id: Option<String>,
|
||||
},
|
||||
/// Direct URL (e.g., channel plugins)
|
||||
DirectUrl { url: String },
|
||||
}
|
||||
|
||||
impl MediaItem {
|
||||
/// Get the Jellyfin item ID if available
|
||||
pub fn jellyfin_id(&self) -> Option<&str> {
|
||||
match &self.source {
|
||||
MediaSource::Remote { jellyfin_item_id, .. } => Some(jellyfin_item_id),
|
||||
MediaSource::Local { jellyfin_item_id, .. } => jellyfin_item_id.as_deref(),
|
||||
MediaSource::DirectUrl { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the playback URL or file path
|
||||
///
|
||||
/// Only available on Android where ExoPlayer needs direct URL access
|
||||
#[cfg(target_os = "android")]
|
||||
pub fn playback_url(&self) -> String {
|
||||
match &self.source {
|
||||
MediaSource::Remote { stream_url, .. } => stream_url.clone(),
|
||||
MediaSource::Local { file_path, .. } => {
|
||||
file_path.to_string_lossy().to_string()
|
||||
}
|
||||
MediaSource::DirectUrl { url } => url.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user