use std::fmt;
use std::path::PathBuf;
use std::time::Duration;
use crate::download::DownloadPriority;
use crate::model::Video;
use crate::model::chapter::Chapter;
use crate::model::format::Format;
use crate::model::playlist::Playlist;
#[cfg(feature = "live-recording")]
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub enum RecordingMethod {
Native,
Fallback,
}
#[derive(Debug, Clone, serde::Serialize)]
#[allow(clippy::large_enum_variant)]
pub enum DownloadEvent {
VideoFetched {
url: String,
video: Box<Video>,
duration: Duration,
},
VideoFetchFailed {
url: String,
error: String,
duration: Duration,
},
DownloadQueued {
download_id: u64,
url: String,
priority: DownloadPriority,
output_path: PathBuf,
},
DownloadStarted {
download_id: u64,
url: String,
total_bytes: u64,
format_id: Option<String>,
},
DownloadProgress {
download_id: u64,
downloaded_bytes: u64,
total_bytes: u64,
speed_bytes_per_sec: f64,
eta_seconds: Option<u64>,
},
DownloadPaused { download_id: u64, reason: String },
DownloadResumed { download_id: u64 },
DownloadCompleted {
download_id: u64,
url: String,
output_path: PathBuf,
duration: Duration,
total_bytes: u64,
},
DownloadFailed {
download_id: u64,
url: String,
error: String,
retry_count: u32,
},
DownloadCanceled { download_id: u64, reason: String },
FormatSelected {
video_id: String,
format: Format,
quality: String,
},
MetadataApplied { path: PathBuf, metadata_type: MetadataType },
ChaptersEmbedded { path: PathBuf, chapters: Vec<Chapter> },
PostProcessStarted {
input_path: PathBuf,
operation: PostProcessOperation,
},
PostProcessCompleted {
input_path: PathBuf,
output_path: PathBuf,
operation: PostProcessOperation,
duration: Duration,
},
PostProcessFailed {
input_path: PathBuf,
operation: PostProcessOperation,
error: String,
},
PlaylistFetched {
url: String,
playlist: Playlist,
duration: Duration,
},
PlaylistFetchFailed {
url: String,
error: String,
duration: Duration,
},
PlaylistItemStarted {
playlist_id: String,
index: usize,
total: usize,
video_id: String,
},
PlaylistItemCompleted {
playlist_id: String,
index: usize,
total: usize,
video_id: String,
output_path: PathBuf,
},
PlaylistItemFailed {
playlist_id: String,
index: usize,
total: usize,
video_id: String,
error: String,
},
PlaylistCompleted {
playlist_id: String,
total_items: usize,
successful: usize,
failed: usize,
duration: Duration,
},
SegmentStarted {
download_id: u64,
segment_index: usize,
total_segments: usize,
},
SegmentCompleted {
download_id: u64,
segment_index: usize,
total_segments: usize,
bytes: u64,
},
#[cfg(feature = "live-recording")]
LiveRecordingStarted {
video_id: String,
url: String,
quality: String,
method: RecordingMethod,
},
#[cfg(feature = "live-recording")]
LiveRecordingProgress {
video_id: String,
elapsed: Duration,
bytes_written: u64,
segments: u64,
bitrate_bps: f64,
},
#[cfg(feature = "live-recording")]
LiveRecordingStopped {
video_id: String,
reason: String,
output_path: PathBuf,
total_bytes: u64,
total_duration: Duration,
},
#[cfg(feature = "live-recording")]
LiveRecordingFailed { video_id: String, error: String },
#[cfg(feature = "live-streaming")]
LiveStreamStarted {
video_id: String,
url: String,
quality: String,
},
#[cfg(feature = "live-streaming")]
LiveStreamProgress {
video_id: String,
elapsed: Duration,
bytes_received: u64,
segments: u64,
bitrate_bps: f64,
},
#[cfg(feature = "live-streaming")]
LiveStreamStopped {
video_id: String,
reason: String,
total_bytes: u64,
total_duration: Duration,
},
#[cfg(feature = "live-streaming")]
LiveStreamFailed { video_id: String, error: String },
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, serde::Serialize)]
pub enum MetadataType {
Mp3,
Mp4,
Ffmpeg,
}
impl fmt::Display for MetadataType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Mp3 => f.write_str("Mp3"),
Self::Mp4 => f.write_str("Mp4"),
Self::Ffmpeg => f.write_str("Ffmpeg"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub enum PostProcessOperation {
CombineStreams { audio_path: PathBuf, video_path: PathBuf },
ConvertAudio { target_format: String },
EmbedSubtitles { subtitle_path: PathBuf },
EmbedThumbnail { thumbnail_path: PathBuf },
Custom { description: String },
SplitChapters { source_path: PathBuf, chapter_count: usize },
}
impl fmt::Display for PostProcessOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CombineStreams { .. } => f.write_str("CombineStreams"),
Self::ConvertAudio { target_format } => {
write!(f, "ConvertAudio(format={})", target_format)
}
Self::EmbedSubtitles { .. } => f.write_str("EmbedSubtitles"),
Self::EmbedThumbnail { .. } => f.write_str("EmbedThumbnail"),
Self::Custom { description } => write!(f, "Custom(description={})", description),
Self::SplitChapters { chapter_count, .. } => write!(f, "SplitChapters(chapters={})", chapter_count),
}
}
}
#[cfg(feature = "live-recording")]
impl fmt::Display for RecordingMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Native => f.write_str("Native"),
Self::Fallback => f.write_str("Fallback"),
}
}
}
impl DownloadEvent {
pub fn download_id(&self) -> Option<u64> {
match self {
Self::DownloadQueued { download_id, .. }
| Self::DownloadStarted { download_id, .. }
| Self::DownloadProgress { download_id, .. }
| Self::DownloadPaused { download_id, .. }
| Self::DownloadResumed { download_id, .. }
| Self::DownloadCompleted { download_id, .. }
| Self::DownloadFailed { download_id, .. }
| Self::DownloadCanceled { download_id, .. }
| Self::SegmentStarted { download_id, .. }
| Self::SegmentCompleted { download_id, .. } => Some(*download_id),
_ => None,
}
}
pub fn is_terminal(&self) -> bool {
matches!(
self,
Self::DownloadCompleted { .. } | Self::DownloadFailed { .. } | Self::DownloadCanceled { .. }
)
}
pub fn is_progress(&self) -> bool {
match self {
Self::DownloadProgress { .. } => true,
#[cfg(feature = "live-recording")]
Self::LiveRecordingProgress { .. } => true,
#[cfg(feature = "live-streaming")]
Self::LiveStreamProgress { .. } => true,
_ => false,
}
}
pub fn event_type(&self) -> &'static str {
match self {
Self::VideoFetched { .. } => "video_fetched",
Self::VideoFetchFailed { .. } => "video_fetch_failed",
Self::DownloadQueued { .. } => "download_queued",
Self::DownloadStarted { .. } => "download_started",
Self::DownloadProgress { .. } => "download_progress",
Self::DownloadPaused { .. } => "download_paused",
Self::DownloadResumed { .. } => "download_resumed",
Self::DownloadCompleted { .. } => "download_completed",
Self::DownloadFailed { .. } => "download_failed",
Self::DownloadCanceled { .. } => "download_canceled",
Self::FormatSelected { .. } => "format_selected",
Self::MetadataApplied { .. } => "metadata_applied",
Self::ChaptersEmbedded { .. } => "chapters_embedded",
Self::PostProcessStarted { .. } => "post_process_started",
Self::PostProcessCompleted { .. } => "post_process_completed",
Self::PostProcessFailed { .. } => "post_process_failed",
Self::PlaylistFetched { .. } => "playlist_fetched",
Self::PlaylistFetchFailed { .. } => "playlist_fetch_failed",
Self::PlaylistItemStarted { .. } => "playlist_item_started",
Self::PlaylistItemCompleted { .. } => "playlist_item_completed",
Self::PlaylistItemFailed { .. } => "playlist_item_failed",
Self::PlaylistCompleted { .. } => "playlist_completed",
Self::SegmentStarted { .. } => "segment_started",
Self::SegmentCompleted { .. } => "segment_completed",
#[cfg(feature = "live-recording")]
Self::LiveRecordingStarted { .. } => "live_recording_started",
#[cfg(feature = "live-recording")]
Self::LiveRecordingProgress { .. } => "live_recording_progress",
#[cfg(feature = "live-recording")]
Self::LiveRecordingStopped { .. } => "live_recording_stopped",
#[cfg(feature = "live-recording")]
Self::LiveRecordingFailed { .. } => "live_recording_failed",
#[cfg(feature = "live-streaming")]
Self::LiveStreamStarted { .. } => "live_stream_started",
#[cfg(feature = "live-streaming")]
Self::LiveStreamProgress { .. } => "live_stream_progress",
#[cfg(feature = "live-streaming")]
Self::LiveStreamStopped { .. } => "live_stream_stopped",
#[cfg(feature = "live-streaming")]
Self::LiveStreamFailed { .. } => "live_stream_failed",
}
}
}
impl fmt::Display for DownloadEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DownloadProgress {
download_id,
downloaded_bytes,
total_bytes,
..
} => {
write!(
f,
"DownloadProgress(id={}, downloaded={}, total={})",
download_id, downloaded_bytes, total_bytes
)
}
_ => {
let event_type = self.event_type();
if let Some(id) = self.download_id() {
write!(f, "{}(id={})", event_type, id)
} else {
f.write_str(event_type)
}
}
}
}
}