1use serde::{Deserialize, Serialize};
2use strum_macros::EnumString;
3
4use crate::{PluginCredential, RsRequest};
5
6#[cfg(feature = "rusqlite")]
7pub mod rusqlite;
8
9fn text_contains(text: &str, contains: &str) -> bool {
10 text.contains(&format!(".{}.", contains)) || text.starts_with(contains) || text.ends_with(contains)
11}
12
13#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display, strum_macros::EnumString, Default)]
14#[serde(rename_all = "lowercase")]
15#[strum(serialize_all = "lowercase")]
16pub enum RsVideoFormat {
17 Mp4,
18 M4v,
19 Mov,
20 Mkv,
21 WebM,
22 Wmv,
23 Avi,
24 #[default]
25 Other
26}
27
28impl RsVideoFormat {
29
30 pub fn from_filename(filename: &str) -> Self {
31 let filename = filename.to_lowercase();
32 if filename.ends_with(".mkv") {
33 Self::Mkv
34 } else if filename.ends_with(".mp4") {
35 Self::Mp4
36 } else if filename.ends_with(".m4v") {
37 Self::M4v
38 } else if filename.ends_with(".mov") {
39 Self::Mov
40 } else if filename.ends_with(".webm") {
41 Self::WebM
42 } else if filename.ends_with(".wmv") {
43 Self::Wmv
44 } else if filename.ends_with(".avi") {
45 Self::Avi
46 } else {
47 Self::Other
48 }
49 }
50
51 pub fn as_mime(&self) -> &str {
52 match self {
53 RsVideoFormat::Mp4 => "video/mp4",
54 RsVideoFormat::M4v => "video/x-m4v",
55 RsVideoFormat::Mov => "video/quicktime",
56 RsVideoFormat::Mkv => "application/x-matroska",
57 RsVideoFormat::WebM => "video/webm",
58 RsVideoFormat::Wmv => "video/x-ms-wmv",
59 RsVideoFormat::Avi => "video/x-msvideo",
60 RsVideoFormat::Other => "application/octet-stream",
61 }
62 }
63 pub fn from_mime(mime: &str) -> Option<Self> {
64 match mime {
65 "video/mp4" => Some(RsVideoFormat::Mp4),
66 "video/x-m4v" => Some(RsVideoFormat::M4v),
67 "video/quicktime" => Some(RsVideoFormat::Mov),
68 "application/x-matroska" => Some(RsVideoFormat::Mkv),
69 "video/webm" => Some(RsVideoFormat::WebM),
70 "video/x-ms-wmv" => Some(RsVideoFormat::Wmv),
71 "video/x-msvideo" => Some(RsVideoFormat::Avi),
72 "application/octet-stream" => Some(RsVideoFormat::Other),
73 _ => None
74 }
75 }
76}
77
78
79#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
80pub enum RsResolution {
81 #[strum(serialize = "4K")]
82 UHD,
83 #[strum(serialize = "1080p")]
84 FullHD,
85 #[strum(serialize = "720p")]
86 HD,
87 #[strum(default)]
88 Custom(String),
89 #[default]
90 Unknown,
91}
92
93impl RsResolution {
94 pub fn from_filename(filename: &str) -> Self {
95 let modified_filename = filename.replace([' ', '-', '_'], ".").to_lowercase();
96 if text_contains(&modified_filename, "1080p") {
97 RsResolution::FullHD
98 } else if text_contains(&modified_filename, "720p") {
99 RsResolution::HD
100 } else if text_contains(&modified_filename, "4k") || text_contains(&modified_filename, "2160p") {
101 RsResolution::UHD
102 } else {
103 RsResolution::Unknown
104 }
105 }
106}
107
108#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
109pub enum RsVideoCodec {
110 H265,
111 H264,
112 AV1,
113 XVID,
114 #[strum(default)]
115 Custom(String),
116 #[default]
117 Unknown,
118}
119
120impl RsVideoCodec {
121 pub fn from_filename(filename: &str) -> Self {
122 let modified_filename = filename.replace([' ', '-', '_'], ".").to_lowercase();
123 if text_contains(&modified_filename, "x265") || text_contains(&modified_filename, "x.265") || text_contains(&modified_filename, "hevc") || text_contains(&modified_filename, "h265") || text_contains(&modified_filename, "h.265") {
124 RsVideoCodec::H265
125 } else if text_contains(&modified_filename, "h264")|| text_contains(&modified_filename, "h.264") || text_contains(&modified_filename, "x.264") || text_contains(&modified_filename, "x264"){
126 RsVideoCodec::H264
127 } else if text_contains(&modified_filename, "av1"){
128 RsVideoCodec::AV1
129 } else if text_contains(&modified_filename, "xvid"){
130 RsVideoCodec::XVID
131 } else {
132 RsVideoCodec::Unknown
133 }
134 }
135}
136
137#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
138pub enum RsAudio {
139 #[strum(serialize = "Atmos")]
140 Atmos,
141 #[strum(serialize = "DDP5.1")]
142 DDP51,
143 #[strum(serialize = "DTSHD")]
144 DTSHD,
145 #[strum(serialize = "DTSX")]
146 DTSX,
147 #[strum(serialize = "DTS")]
148 DTS,
149 #[strum(serialize = "AC35.1")]
150 AC351,
151 #[strum(serialize = "AAC5.1")]
152 AAC51,
153 AAC,
154 MP3,
155 #[strum(default)]
156 Custom(String),
157 #[default]
158 Unknown,
159}
160
161
162impl RsAudio {
163 pub fn from_filename(filename: &str) -> Self {
164 let modified_filename = filename.replace([' ', '-', '_'], ".").to_lowercase();
165 if text_contains(&modified_filename, "atmos") {
166 RsAudio::Atmos
167 } else if text_contains(&modified_filename, "ddp5.1") || text_contains(&modified_filename, "ddp51") || text_contains(&modified_filename, "dolby.digital.plus.5.1") || text_contains(&modified_filename, "dd51") {
168 RsAudio::DDP51
169 } else if text_contains(&modified_filename, "dtshd") {
170 RsAudio::DTSHD
171 } else if text_contains(&modified_filename, "dtsx") {
172 RsAudio::DTSX
173 } else if text_contains(&modified_filename, "dts") {
174 RsAudio::DTS
175 } else if text_contains(&modified_filename, "ac35.1") || text_contains(&modified_filename, "ac3.5.1") {
176 RsAudio::AC351
177 } else if text_contains(&modified_filename, "aac5.1") || text_contains(&modified_filename, "aac51") {
178 RsAudio::AAC51
179 } else if text_contains(&modified_filename, "aac") {
180 RsAudio::AAC
181 } else if text_contains(&modified_filename, "mp3") {
182 RsAudio::MP3
183 } else {
184 RsAudio::Unknown
185 }
186 }
187
188 pub fn list_from_filename(filename: &str) -> Vec<Self> {
189 let mut result = vec![];
190 let modified_filename = filename.replace([' ', '-', '_'], ".").to_lowercase();
191 if text_contains(&modified_filename, "atmos") {
192 result.push(RsAudio::Atmos);
193 }
194 if text_contains(&modified_filename, "ddp5.1") {
195 result.push(RsAudio::DDP51);
196 }
197 if text_contains(&modified_filename, "dtshd") {
198 result.push(RsAudio::DTSHD);
199 }
200 if text_contains(&modified_filename, "dtsx") {
201 result.push(RsAudio::DTSX);
202 }
203 if text_contains(&modified_filename, "dts") {
204 result.push(RsAudio::DTS);
205 }
206 if text_contains(&modified_filename, "ac35.1") || text_contains(&modified_filename, "ac3.5.1") {
207 result.push(RsAudio::AC351);
208 }
209 result
210 }
211}
212
213
214
215#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
216#[serde(rename_all = "camelCase")]
217#[strum(serialize_all = "camelCase")]
218pub enum VideoOverlayPosition {
219 TopLeft,
220 #[default]
221 TopRight,
222 BottomLeft,
223 BottomRight,
224 BottomCenter,
225 TopCenter,
226 Center
227}
228
229#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
230#[serde(rename_all = "camelCase")]
231#[strum(serialize_all = "camelCase")]
232pub enum VideoAlignment {
233 #[default]
234 Center,
235 Left,
236 Right,
237}
238
239#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display,EnumString, Default)]
240#[serde(rename_all = "camelCase")]
241#[strum(serialize_all = "camelCase")]
242pub enum VideoOverlayType {
243 #[default]
244 Watermark,
245
246 File,
247}
248
249impl VideoOverlayPosition {
250 pub fn as_filter(&self, margin: f64) -> String {
251 match self {
252 VideoOverlayPosition::TopLeft => format!("main_w*{}:main_h*{}",margin, margin),
253 VideoOverlayPosition::TopRight => format!("(main_w-w):min(main_h,main_w)*{}", margin),
254 VideoOverlayPosition::BottomLeft => format!("main_w*{}:(main_h-h)", margin),
255 VideoOverlayPosition::BottomRight => "(main_w-w):(main_h-h)".to_string(),
256 VideoOverlayPosition::BottomCenter => format!("main_w*{}:(main_h-h)", margin),VideoOverlayPosition::TopCenter => format!("main_w*{}:main_h*{}",margin, margin), VideoOverlayPosition::Center => format!("main_w*{}:main_h*{}",margin, margin), }
260 }
261 pub fn as_ass_alignment(&self) -> String {
262 match self {
263 VideoOverlayPosition::TopLeft => String::from("7"),
264 VideoOverlayPosition::TopCenter => String::from("8"),
265 VideoOverlayPosition::TopRight => String::from("9"),
266 VideoOverlayPosition::Center => String::from("5"),
267 VideoOverlayPosition::BottomLeft => String::from("1"),
268 VideoOverlayPosition::BottomCenter => String::from("2"),
269 VideoOverlayPosition::BottomRight => String::from("3"),
270 }
271 }
272
273
274}
275
276
277#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
278pub struct VideoConvertInterval {
279 pub start: f64,
280 pub duration: Option<f64>,
281 pub input: Option<String>,
283}
284
285#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
286
287pub struct VideoOverlay {
288 #[serde(rename = "type")]
289 pub kind: VideoOverlayType,
290 pub path: String,
291 #[serde(default)]
292 pub position: VideoOverlayPosition,
293 pub margin: Option<f64>,
294 pub ratio: f32,
295 pub opacity: f32,
296}
297
298#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
299#[serde(rename_all = "camelCase")]
300pub struct VideoTextOverlay {
301 pub text: String,
302 pub font_color: Option<String>,
303 pub font: Option<String>,
304 #[serde(default)]
305 pub position: VideoOverlayPosition,
306 pub margin_vertical: Option<i32>,
307 pub margin_horizontal: Option<i32>,
308 pub margin_right: Option<i32>,
309 pub margin_bottom: Option<i32>,
310 pub font_size: i32,
311 pub opacity: Option<f32>,
312 pub shadow_color: Option<String>,
313 pub shado_opacity: Option<f32>,
314 pub start: Option<u32>,
315 pub end: Option<u32>,
316}
317
318#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
319#[serde(rename_all = "camelCase")]
320pub struct VideoConvertRequest {
321 pub id: String,
322 pub format: RsVideoFormat,
323 pub codec: Option<RsVideoCodec>,
324 pub crf: Option<u16>,
325 #[serde(default)]
326 pub no_audio: bool,
327 pub width: Option<String>,
328 pub height: Option<String>,
329 pub framerate: Option<u16>,
330 pub crop_width: Option<u16>,
331 pub crop_height: Option<u16>,
332 pub aspect_ratio: Option<String>,
333 pub aspect_ratio_alignment: Option<VideoAlignment>,
334 pub overlay: Option<VideoOverlay>,
335 pub texts: Option<Vec<VideoTextOverlay>>,
336 #[serde(default)]
337 pub intervals: Vec<VideoConvertInterval>,
338}
339
340
341#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
342#[serde(rename_all = "camelCase")]
343pub struct RsVideoTranscodeJob {
344 pub source: RsRequest,
345 pub request: VideoConvertRequest,
346}
347#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
348#[serde(rename_all = "camelCase")]
349pub struct RsVideoTranscodeJobPluginRequest {
350 pub job: RsVideoTranscodeJob,
351 pub credentials: PluginCredential,
352}
353
354#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
355#[serde(rename_all = "camelCase")]
356pub struct RsVideoTranscodeJobPluginAction {
357 pub job_id: String,
358 pub credentials: PluginCredential,
359}
360
361#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
362#[serde(rename_all = "camelCase")]
363pub struct RsVideoTranscodeJobStatus {
364 pub id: String,
365 pub status: RsVideoTranscodeStatus,
366 pub progress: f32,
367}
368
369
370#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display, strum_macros::EnumString, Default)]
371#[serde(rename_all = "lowercase")]
372#[strum(serialize_all = "lowercase")]
373pub enum RsVideoTranscodeStatus {
374 #[default]
375 Pending,
376 Downloading,
377 Queued,
378 Processing,
379 Completed,
380 Failed,
381 Canceled,
382}
383
384#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
385#[serde(rename_all = "camelCase")]
386pub struct RsVideoCapabilities {
387 pub video_codecs: Vec<RsVideoCodec>,
388 pub video_codecs_hw: Vec<RsVideoCodec>,
389 pub audio_codecs: Vec<RsAudio>,
390 pub video_formats: Vec<RsVideoFormat>,
391 pub max_duration: Option<u32>,
392 pub max_concurrent_jobs: Option<u16>,
393}
394
395#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, strum_macros::Display, strum_macros::EnumString, Default)]
396#[serde(rename_all = "lowercase")]
397#[strum(serialize_all = "lowercase")]
398pub enum RsVideoTranscodeCancelResponse {
399 #[default]
400 NotFound,
401 Cancelled,
402 Error,
403}