Skip to main content

player_plugin/
processor.rs

1use std::collections::BTreeMap;
2use std::path::{Path, PathBuf};
3
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7use crate::ProcessorCapabilities;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum ContentFormatKind {
11    HlsSegments,
12    DashSegments,
13    FlvSegments,
14    SingleFile,
15    Unknown,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
19pub enum OutputFormat {
20    Mp4,
21    Mkv,
22    Original,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub enum StreamKind {
27    Combined,
28    Video,
29    Audio,
30    SecondaryAudio,
31    Subtitle,
32    Auxiliary,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
36pub enum AssemblyMode {
37    #[default]
38    Single,
39    SeparateAudioVideo,
40    MultiAudio,
41    WithSubtitles,
42    Generic,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
46pub struct DownloadMetadata {
47    pub source_uri: Option<String>,
48    pub manifest_uri: Option<String>,
49    pub total_bytes: Option<u64>,
50    pub version: Option<String>,
51    pub etag: Option<String>,
52    pub checksum: Option<String>,
53    pub mime_type: Option<String>,
54    pub custom: BTreeMap<String, String>,
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
58pub struct CompletedDownloadInfo {
59    pub asset_id: String,
60    pub task_id: Option<String>,
61    pub content_format: CompletedContentFormat,
62    pub metadata: DownloadMetadata,
63    #[serde(default)]
64    pub streams: Vec<CompletedStream>,
65    #[serde(default)]
66    pub assembly_mode: AssemblyMode,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct CompletedStream {
71    pub stream_id: Option<String>,
72    pub kind: StreamKind,
73    pub content_format: CompletedContentFormat,
74    pub language: Option<String>,
75    pub codec: Option<String>,
76    pub label: Option<String>,
77    pub metadata: DownloadMetadata,
78    pub quality_rank: Option<u32>,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
82pub enum CompletedContentFormat {
83    HlsSegments {
84        manifest_path: PathBuf,
85        segment_paths: Vec<PathBuf>,
86    },
87    DashSegments {
88        manifest_path: PathBuf,
89        segment_paths: Vec<PathBuf>,
90    },
91    FlvSegments {
92        manifest_path: PathBuf,
93        segment_paths: Vec<PathBuf>,
94    },
95    SingleFile {
96        path: PathBuf,
97    },
98}
99
100impl CompletedContentFormat {
101    pub fn kind(&self) -> ContentFormatKind {
102        match self {
103            Self::HlsSegments { .. } => ContentFormatKind::HlsSegments,
104            Self::DashSegments { .. } => ContentFormatKind::DashSegments,
105            Self::FlvSegments { .. } => ContentFormatKind::FlvSegments,
106            Self::SingleFile { .. } => ContentFormatKind::SingleFile,
107        }
108    }
109}
110
111pub trait ProcessorProgress: Send + Sync {
112    fn on_progress(&self, ratio: f32);
113
114    fn is_cancelled(&self) -> bool {
115        false
116    }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub enum ProcessorOutput {
121    MuxedFile { path: PathBuf, format: OutputFormat },
122    Skipped,
123}
124
125#[derive(Debug, Error, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub enum ProcessorError {
127    #[error("unsupported input format: {0:?}")]
128    UnsupportedFormat(ContentFormatKind),
129    #[error("dynamic input stream {stream_index} appeared after the output header")]
130    UnsupportedDynamicStream { stream_index: u32 },
131    #[error("payload codec error: {0}")]
132    PayloadCodec(String),
133    #[error("plugin ABI violation: {0}")]
134    AbiViolation(String),
135    #[error("mux failed: {0}")]
136    MuxFailed(String),
137    #[error("output path error: {0}")]
138    OutputPath(String),
139    #[error("cancelled")]
140    Cancelled,
141}
142
143pub trait PostDownloadProcessor: Send + Sync {
144    fn name(&self) -> &str;
145
146    fn supported_input_formats(&self) -> &[ContentFormatKind];
147
148    fn capabilities(&self) -> ProcessorCapabilities {
149        ProcessorCapabilities {
150            supported_input_formats: self.supported_input_formats().to_vec(),
151            output_formats: Vec::new(),
152            supports_cancellation: true,
153            supports_assembly: false,
154            supported_assembly_modes: Vec::new(),
155        }
156    }
157
158    fn process(
159        &self,
160        input: &CompletedDownloadInfo,
161        output_path: &Path,
162        progress: &dyn ProcessorProgress,
163    ) -> Result<ProcessorOutput, ProcessorError>;
164
165    fn supports_assembly(&self) -> bool {
166        self.capabilities().supports_assembly
167    }
168
169    fn assemble(
170        &self,
171        input: &CompletedDownloadInfo,
172        _output_path: &Path,
173        _progress: &dyn ProcessorProgress,
174    ) -> Result<ProcessorOutput, ProcessorError> {
175        Err(ProcessorError::UnsupportedFormat(
176            input.content_format.kind(),
177        ))
178    }
179}