refactor!: refactored response models
doc: documented all public methods
This commit is contained in:
parent
4c1876cb55
commit
f526ab38eb
37 changed files with 600 additions and 255 deletions
|
|
@ -3,10 +3,19 @@ use super::{
|
|||
MusicPlaylistItem, PlaylistItem, TrackItem, VideoItem, YouTubeItem,
|
||||
};
|
||||
|
||||
/// Trait for casting generic YouTube/YouTube music items to a specific kind.
|
||||
///
|
||||
/// Returns [`None`] if the item does not match.
|
||||
pub trait FromYtItem: Sized {
|
||||
/// Casting from a generic YouTube item to a specific kind
|
||||
///
|
||||
/// Returns [`None`] if the item does not match.
|
||||
fn from_yt_item(_item: YouTubeItem) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
/// Casting from a generic YouTube Music item to a specific kind
|
||||
///
|
||||
/// Returns [`None`] if the item does not match.
|
||||
fn from_ytm_item(_item: MusicItem) -> Option<Self> {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
200
src/model/mod.rs
200
src/model/mod.rs
|
|
@ -1,13 +1,13 @@
|
|||
//! YouTube API request and response models
|
||||
//! YouTube API response models
|
||||
|
||||
mod convert;
|
||||
mod ordering;
|
||||
mod paginator;
|
||||
|
||||
pub mod paginator;
|
||||
pub mod richtext;
|
||||
|
||||
pub use convert::FromYtItem;
|
||||
pub use ordering::QualityOrd;
|
||||
pub use paginator::Paginator;
|
||||
pub mod traits;
|
||||
|
||||
use serde_with::serde_as;
|
||||
|
||||
use std::{collections::BTreeSet, ops::Range};
|
||||
|
|
@ -17,7 +17,7 @@ use time::{Date, OffsetDateTime};
|
|||
|
||||
use crate::{error::Error, param::Country, serializer::DateYmd, util};
|
||||
|
||||
use self::richtext::RichText;
|
||||
use self::{paginator::Paginator, richtext::RichText};
|
||||
|
||||
/*
|
||||
#COMMON
|
||||
|
|
@ -38,10 +38,36 @@ pub struct Thumbnail {
|
|||
/// Entities extracted from a YouTube URL
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum UrlTarget {
|
||||
Video { id: String, start_time: u32 },
|
||||
Channel { id: String },
|
||||
Playlist { id: String },
|
||||
Album { id: String },
|
||||
/// YouTube video
|
||||
///
|
||||
/// Example: <youtube.com/watch?v=ZeerrnuLi5E>
|
||||
Video {
|
||||
/// Unique YouTube video ID
|
||||
id: String,
|
||||
/// Video start time in seconds
|
||||
start_time: u32,
|
||||
},
|
||||
/// YouTube channel
|
||||
///
|
||||
/// Example: <https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ>
|
||||
Channel {
|
||||
/// Unique YouTube channel ID
|
||||
id: String,
|
||||
},
|
||||
/// YouTube playlist
|
||||
///
|
||||
/// Example: <https://www.youtube.com/playlist?list=PLKUA473MWUv2jmkqIxzQR3YL4kuPArj4G>
|
||||
Playlist {
|
||||
/// Unique YouTube playlist ID
|
||||
id: String,
|
||||
},
|
||||
/// YouTube Music album
|
||||
///
|
||||
/// Example: <https://music.youtube.com/browse/MPREb_nlBWQROfvjo>
|
||||
Album {
|
||||
/// Unique YouTube album ID
|
||||
id: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl ToString for UrlTarget {
|
||||
|
|
@ -51,10 +77,19 @@ impl ToString for UrlTarget {
|
|||
}
|
||||
|
||||
impl UrlTarget {
|
||||
/// Convert the URL target to a YouTube URL
|
||||
///
|
||||
/// Is equivalent to `url_target.to_string()`
|
||||
pub fn to_url(&self) -> String {
|
||||
self.to_url_yt_host("https://www.youtube.com")
|
||||
}
|
||||
|
||||
/// Convert the URL target to a YouTube URL with a specified YouTube host.
|
||||
///
|
||||
/// Used to redirect to alternative YouTube frontends like Piped or Invidious.
|
||||
///
|
||||
/// **Note:** Music album URL targets are still converted to `music.youtube.com/browse/*`,
|
||||
/// since these URLs are not supported by Piped or Invidious.
|
||||
pub fn to_url_yt_host(&self, yt_host: &str) -> String {
|
||||
match self {
|
||||
UrlTarget::Video { id, start_time, .. } => match start_time {
|
||||
|
|
@ -73,6 +108,7 @@ impl UrlTarget {
|
|||
}
|
||||
}
|
||||
|
||||
/// Validate the YouTube ID from the URL target
|
||||
pub(crate) fn validate(&self) -> Result<(), Error> {
|
||||
match self {
|
||||
UrlTarget::Video { id, .. } => {
|
||||
|
|
@ -107,11 +143,6 @@ impl UrlTarget {
|
|||
#PLAYER
|
||||
*/
|
||||
|
||||
pub trait FileFormat {
|
||||
/// Get the file extension (".xyz") of the file format
|
||||
fn extension(&self) -> &str;
|
||||
}
|
||||
|
||||
/// Video player data
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
|
|
@ -172,13 +203,17 @@ pub struct VideoStream {
|
|||
pub url: String,
|
||||
/// YouTube stream format identifier
|
||||
pub itag: u32,
|
||||
/// Stream bitrate (in bits/second)
|
||||
pub bitrate: u32,
|
||||
/// Average stream bitrate (in bits/second)
|
||||
pub average_bitrate: u32,
|
||||
/// Video file size in bytes
|
||||
pub size: Option<u64>,
|
||||
/// Index range (used for DASH streaming)
|
||||
pub index_range: Option<Range<u32>>,
|
||||
/// Init range (used for DASH streaming)
|
||||
pub init_range: Option<Range<u32>>,
|
||||
// Video duration in milliseconds
|
||||
/// Video duration in milliseconds
|
||||
pub duration_ms: Option<u32>,
|
||||
/// Video width in pixels
|
||||
pub width: u32,
|
||||
|
|
@ -209,13 +244,17 @@ pub struct AudioStream {
|
|||
pub url: String,
|
||||
/// YouTube stream format identifier
|
||||
pub itag: u32,
|
||||
/// Stream bitrate (in bits/second)
|
||||
pub bitrate: u32,
|
||||
/// Average stream bitrate (in bits/second)
|
||||
pub average_bitrate: u32,
|
||||
/// Audio file size in bytes
|
||||
pub size: u64,
|
||||
/// Index range (used for DASH streaming)
|
||||
pub index_range: Option<Range<u32>>,
|
||||
/// Init range (used for DASH streaming)
|
||||
pub init_range: Option<Range<u32>>,
|
||||
// Audio duration in milliseconds
|
||||
/// Audio duration in milliseconds
|
||||
pub duration_ms: Option<u32>,
|
||||
/// MIME file type
|
||||
pub mime: String,
|
||||
|
|
@ -241,94 +280,6 @@ pub struct AudioStream {
|
|||
pub track: Option<AudioTrack>,
|
||||
}
|
||||
|
||||
pub trait YtStream {
|
||||
fn url(&self) -> &str;
|
||||
fn itag(&self) -> u32;
|
||||
fn bitrate(&self) -> u32;
|
||||
fn averate_bitrate(&self) -> u32;
|
||||
fn size(&self) -> Option<u64>;
|
||||
fn index_range(&self) -> Option<Range<u32>>;
|
||||
fn init_range(&self) -> Option<Range<u32>>;
|
||||
fn duration_ms(&self) -> Option<u32>;
|
||||
fn mime(&self) -> &str;
|
||||
}
|
||||
|
||||
impl YtStream for VideoStream {
|
||||
fn url(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn itag(&self) -> u32 {
|
||||
self.itag
|
||||
}
|
||||
|
||||
fn bitrate(&self) -> u32 {
|
||||
self.bitrate
|
||||
}
|
||||
|
||||
fn averate_bitrate(&self) -> u32 {
|
||||
self.average_bitrate
|
||||
}
|
||||
|
||||
fn size(&self) -> Option<u64> {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn index_range(&self) -> Option<Range<u32>> {
|
||||
self.index_range.clone()
|
||||
}
|
||||
|
||||
fn init_range(&self) -> Option<Range<u32>> {
|
||||
self.init_range.clone()
|
||||
}
|
||||
|
||||
fn duration_ms(&self) -> Option<u32> {
|
||||
self.duration_ms
|
||||
}
|
||||
|
||||
fn mime(&self) -> &str {
|
||||
&self.mime
|
||||
}
|
||||
}
|
||||
|
||||
impl YtStream for AudioStream {
|
||||
fn url(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn itag(&self) -> u32 {
|
||||
self.itag
|
||||
}
|
||||
|
||||
fn bitrate(&self) -> u32 {
|
||||
self.bitrate
|
||||
}
|
||||
|
||||
fn averate_bitrate(&self) -> u32 {
|
||||
self.average_bitrate
|
||||
}
|
||||
|
||||
fn size(&self) -> Option<u64> {
|
||||
Some(self.size)
|
||||
}
|
||||
|
||||
fn index_range(&self) -> Option<Range<u32>> {
|
||||
self.index_range.clone()
|
||||
}
|
||||
|
||||
fn init_range(&self) -> Option<Range<u32>> {
|
||||
self.init_range.clone()
|
||||
}
|
||||
|
||||
fn duration_ms(&self) -> Option<u32> {
|
||||
self.duration_ms
|
||||
}
|
||||
|
||||
fn mime(&self) -> &str {
|
||||
&self.mime
|
||||
}
|
||||
}
|
||||
|
||||
/// Video codec
|
||||
#[derive(
|
||||
Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash,
|
||||
|
|
@ -336,6 +287,7 @@ impl YtStream for AudioStream {
|
|||
#[serde(rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
pub enum VideoCodec {
|
||||
/// Unknown codec
|
||||
#[default]
|
||||
Unknown,
|
||||
/// MPEG-4 Part 14 <https://en.wikipedia.org/wiki/MPEG-4_Part_14>
|
||||
|
|
@ -355,6 +307,7 @@ pub enum VideoCodec {
|
|||
#[serde(rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
pub enum AudioCodec {
|
||||
/// Unknown codec
|
||||
#[default]
|
||||
Unknown,
|
||||
/// MP4A aka AAC: <https://en.wikipedia.org/wiki/Advanced_Audio_Coding>
|
||||
|
|
@ -396,16 +349,6 @@ pub struct AudioTrack {
|
|||
pub is_default: bool,
|
||||
}
|
||||
|
||||
impl FileFormat for VideoFormat {
|
||||
fn extension(&self) -> &str {
|
||||
match self {
|
||||
VideoFormat::ThreeGp => ".3gp",
|
||||
VideoFormat::Mp4 => ".mp4",
|
||||
VideoFormat::Webm => ".webm",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Audio file type
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -417,15 +360,6 @@ pub enum AudioFormat {
|
|||
Webm,
|
||||
}
|
||||
|
||||
impl FileFormat for AudioFormat {
|
||||
fn extension(&self) -> &str {
|
||||
match self {
|
||||
AudioFormat::M4a => ".m4a",
|
||||
AudioFormat::Webm => ".webm",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// YouTube provides subtitles in different formats.
|
||||
///
|
||||
/// srv1 (XML) is the default format, to request a different format you have
|
||||
|
|
@ -1023,7 +957,9 @@ pub struct ArtistItem {
|
|||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct ArtistId {
|
||||
/// Unique YouTube channel ID
|
||||
pub id: Option<String>,
|
||||
/// Artist name
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
|
|
@ -1203,10 +1139,12 @@ pub struct MusicSearchResult {
|
|||
pub corrected_query: Option<String>,
|
||||
/// Order of the item sections of the search page, starting with
|
||||
/// the most relevant.
|
||||
pub order: Vec<MusicEntityType>,
|
||||
pub order: Vec<MusicItemType>,
|
||||
}
|
||||
|
||||
/// Generic YouTube Music item
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum MusicItem {
|
||||
Track(TrackItem),
|
||||
Album(AlbumItem),
|
||||
|
|
@ -1214,18 +1152,21 @@ pub enum MusicItem {
|
|||
Playlist(MusicPlaylistItem),
|
||||
}
|
||||
|
||||
/// YouTube Music item type
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum MusicEntityType {
|
||||
#[allow(missing_docs)]
|
||||
pub enum MusicItemType {
|
||||
Track,
|
||||
Album,
|
||||
Artist,
|
||||
Playlist,
|
||||
}
|
||||
|
||||
/// Filtered YouTube Music search result
|
||||
/// Filtered YouTube Music search result (one item type)
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct MusicSearchFiltered<T> {
|
||||
/// Search items
|
||||
pub items: Paginator<T>,
|
||||
/// Corrected search query
|
||||
///
|
||||
|
|
@ -1239,8 +1180,11 @@ pub struct MusicSearchFiltered<T> {
|
|||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct TrackDetails {
|
||||
/// Track metadata
|
||||
pub track: TrackItem,
|
||||
/// ID to fetch lyrics
|
||||
pub lyrics_id: Option<String>,
|
||||
/// ID to fetch related tracks
|
||||
pub related_id: Option<String>,
|
||||
}
|
||||
|
||||
|
|
@ -1254,7 +1198,7 @@ pub struct Lyrics {
|
|||
pub footer: String,
|
||||
}
|
||||
|
||||
/// YouTube Music entities related to a track
|
||||
/// YouTube Music items related to a track
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct MusicRelated {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ use crate::model::AudioCodec;
|
|||
|
||||
use super::{AudioStream, VideoStream};
|
||||
|
||||
/// Trait for ordering YouTube video/audio streams by quality
|
||||
///
|
||||
/// analogous to [`std::cmp::Ord`]
|
||||
pub trait QualityOrd {
|
||||
/// Compare two streams by quality
|
||||
///
|
||||
/// analogous to [`std::cmp::Ord::cmp`]
|
||||
fn quality_cmp(&self, other: &Self) -> Ordering;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
//! Wrapper model for progressively fetched items
|
||||
|
||||
use std::convert::TryInto;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::param::ContinuationEndpoint;
|
||||
|
||||
/// Wrapper around progressively fetched items
|
||||
///
|
||||
/// The paginator is a wrapper around a list of items that are fetched
|
||||
|
|
@ -34,7 +34,7 @@ pub struct Paginator<T> {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub visitor_data: Option<String>,
|
||||
/// YouTube API endpoint to fetch continuations from
|
||||
pub endpoint: ContinuationEndpoint,
|
||||
pub(crate) endpoint: ContinuationEndpoint,
|
||||
}
|
||||
|
||||
impl<T> Default for Paginator<T> {
|
||||
|
|
@ -49,6 +49,39 @@ impl<T> Default for Paginator<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// YouTube API endpoint to fetch continuations from
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[non_exhaustive]
|
||||
#[allow(missing_docs)]
|
||||
pub enum ContinuationEndpoint {
|
||||
Browse,
|
||||
Search,
|
||||
Next,
|
||||
MusicBrowse,
|
||||
MusicSearch,
|
||||
MusicNext,
|
||||
}
|
||||
|
||||
impl ContinuationEndpoint {
|
||||
pub(crate) fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
ContinuationEndpoint::Browse | ContinuationEndpoint::MusicBrowse => "browse",
|
||||
ContinuationEndpoint::Search | ContinuationEndpoint::MusicSearch => "search",
|
||||
ContinuationEndpoint::Next | ContinuationEndpoint::MusicNext => "next",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_music(self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ContinuationEndpoint::MusicBrowse
|
||||
| ContinuationEndpoint::MusicSearch
|
||||
| ContinuationEndpoint::MusicNext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Paginator<T> {
|
||||
pub(crate) fn new(count: Option<u64>, items: Vec<T>, ctoken: Option<String>) -> Self {
|
||||
Self::new_ext(count, items, ctoken, None, ContinuationEndpoint::Browse)
|
||||
|
|
|
|||
|
|
@ -6,19 +6,31 @@ use crate::util;
|
|||
|
||||
use super::UrlTarget;
|
||||
|
||||
/// Text content with links
|
||||
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub struct RichText(pub Vec<TextComponent>);
|
||||
|
||||
/// Text component forming a [`RichText`] object
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum TextComponent {
|
||||
/// Plain text
|
||||
Text(String),
|
||||
/// Web link
|
||||
Web { text: String, url: String },
|
||||
/// Link to a YouTube entity
|
||||
YouTube { text: String, target: UrlTarget },
|
||||
Web {
|
||||
/// Link text
|
||||
text: String,
|
||||
/// Link URL
|
||||
url: String,
|
||||
},
|
||||
/// Link to a YouTube item
|
||||
YouTube {
|
||||
/// Link text
|
||||
text: String,
|
||||
/// YouTube URL target
|
||||
target: UrlTarget,
|
||||
},
|
||||
}
|
||||
|
||||
/// Trait for converting rich text to plain text.
|
||||
|
|
@ -46,6 +58,7 @@ pub trait ToHtml {
|
|||
}
|
||||
|
||||
impl TextComponent {
|
||||
/// Get the text from the component
|
||||
pub fn get_text(&self) -> &str {
|
||||
match self {
|
||||
TextComponent::Text(text) => text,
|
||||
|
|
@ -54,9 +67,12 @@ impl TextComponent {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the link URL from the component
|
||||
///
|
||||
/// Returns an empty string if the component is not a link.
|
||||
pub fn get_url(&self, yt_host: &str) -> String {
|
||||
match self {
|
||||
TextComponent::Text(_) => "".to_owned(),
|
||||
TextComponent::Text(_) => String::new(),
|
||||
TextComponent::Web { url, .. } => url.to_owned(),
|
||||
TextComponent::YouTube { target, .. } => target.to_url_yt_host(yt_host),
|
||||
}
|
||||
|
|
|
|||
130
src/model/traits.rs
Normal file
130
src/model/traits.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
//! Traits for working with response models
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
pub use super::{convert::FromYtItem, ordering::QualityOrd};
|
||||
|
||||
use super::{AudioFormat, AudioStream, VideoFormat, VideoStream};
|
||||
|
||||
/// Trait for YouTube streams (video and audio)
|
||||
pub trait YtStream {
|
||||
/// Stream URL
|
||||
fn url(&self) -> &str;
|
||||
/// YouTube stream format identifier
|
||||
fn itag(&self) -> u32;
|
||||
/// Stream bitrate (in bits/second)
|
||||
fn bitrate(&self) -> u32;
|
||||
/// Average stream bitrate (in bits/second)
|
||||
fn averate_bitrate(&self) -> u32;
|
||||
/// File size in bytes
|
||||
fn size(&self) -> Option<u64>;
|
||||
/// Index range (used for DASH streaming)
|
||||
fn index_range(&self) -> Option<Range<u32>>;
|
||||
/// Init range (used for DASH streaming)
|
||||
fn init_range(&self) -> Option<Range<u32>>;
|
||||
/// Stream duration in milliseconds
|
||||
fn duration_ms(&self) -> Option<u32>;
|
||||
/// MIME file type
|
||||
fn mime(&self) -> &str;
|
||||
}
|
||||
|
||||
impl YtStream for VideoStream {
|
||||
fn url(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn itag(&self) -> u32 {
|
||||
self.itag
|
||||
}
|
||||
|
||||
fn bitrate(&self) -> u32 {
|
||||
self.bitrate
|
||||
}
|
||||
|
||||
fn averate_bitrate(&self) -> u32 {
|
||||
self.average_bitrate
|
||||
}
|
||||
|
||||
fn size(&self) -> Option<u64> {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn index_range(&self) -> Option<Range<u32>> {
|
||||
self.index_range.clone()
|
||||
}
|
||||
|
||||
fn init_range(&self) -> Option<Range<u32>> {
|
||||
self.init_range.clone()
|
||||
}
|
||||
|
||||
fn duration_ms(&self) -> Option<u32> {
|
||||
self.duration_ms
|
||||
}
|
||||
|
||||
fn mime(&self) -> &str {
|
||||
&self.mime
|
||||
}
|
||||
}
|
||||
|
||||
impl YtStream for AudioStream {
|
||||
fn url(&self) -> &str {
|
||||
&self.url
|
||||
}
|
||||
|
||||
fn itag(&self) -> u32 {
|
||||
self.itag
|
||||
}
|
||||
|
||||
fn bitrate(&self) -> u32 {
|
||||
self.bitrate
|
||||
}
|
||||
|
||||
fn averate_bitrate(&self) -> u32 {
|
||||
self.average_bitrate
|
||||
}
|
||||
|
||||
fn size(&self) -> Option<u64> {
|
||||
Some(self.size)
|
||||
}
|
||||
|
||||
fn index_range(&self) -> Option<Range<u32>> {
|
||||
self.index_range.clone()
|
||||
}
|
||||
|
||||
fn init_range(&self) -> Option<Range<u32>> {
|
||||
self.init_range.clone()
|
||||
}
|
||||
|
||||
fn duration_ms(&self) -> Option<u32> {
|
||||
self.duration_ms
|
||||
}
|
||||
|
||||
fn mime(&self) -> &str {
|
||||
&self.mime
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for file types
|
||||
pub trait FileFormat {
|
||||
/// Get the file extension (".xyz") of the file format
|
||||
fn extension(&self) -> &str;
|
||||
}
|
||||
|
||||
impl FileFormat for VideoFormat {
|
||||
fn extension(&self) -> &str {
|
||||
match self {
|
||||
VideoFormat::ThreeGp => ".3gp",
|
||||
VideoFormat::Mp4 => ".mp4",
|
||||
VideoFormat::Webm => ".webm",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileFormat for AudioFormat {
|
||||
fn extension(&self) -> &str {
|
||||
match self {
|
||||
AudioFormat::M4a => ".m4a",
|
||||
AudioFormat::Webm => ".webm",
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in a new issue