spotify_dl/encoder/
mod.rs1mod flac;
2#[cfg(feature = "mp3")]
3mod mp3;
4pub mod tags;
5
6use std::{path::Path, str::FromStr};
7
8use anyhow::Result;
9
10use self::{flac::FlacEncoder, mp3::Mp3Encoder};
11
12#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
13pub enum Format {
14 Flac,
15 #[cfg(feature = "mp3")]
16 Mp3,
17}
18
19impl FromStr for Format {
20 type Err = anyhow::Error;
21
22 fn from_str(s: &str) -> Result<Self> {
23 match s {
24 "flac" => Ok(Format::Flac),
25 #[cfg(feature = "mp3")]
26 "mp3" => Ok(Format::Mp3),
27 _ => Err(anyhow::anyhow!("Unsupported format")),
28 }
29 }
30}
31
32impl Format {
33 pub fn extension(&self) -> &'static str {
34 match self {
35 Format::Flac => "flac",
36 #[cfg(feature = "mp3")]
37 Format::Mp3 => "mp3",
38 }
39 }
40}
41
42const FLAC_ENCODER: &FlacEncoder = &FlacEncoder;
43#[cfg(feature = "mp3")]
44const MP3_ENCODER: &Mp3Encoder = &Mp3Encoder;
45
46pub fn get_encoder(format: Format) -> &'static dyn Encoder {
47 match format {
48 Format::Flac => FLAC_ENCODER,
49 #[cfg(feature = "mp3")]
50 Format::Mp3 => MP3_ENCODER,
51 }
52}
53
54#[async_trait::async_trait]
55pub trait Encoder {
56 async fn encode(&self, samples: Samples) -> Result<EncodedStream>;
57}
58
59pub struct Samples {
60 pub samples: Vec<i32>,
61 pub sample_rate: u32,
62 pub channels: u32,
63 pub bits_per_sample: u32,
64}
65
66impl Samples {
67 pub fn new(samples: Vec<i32>, sample_rate: u32, channels: u32, bits_per_sample: u32) -> Self {
68 Samples {
69 samples,
70 sample_rate,
71 channels,
72 bits_per_sample,
73 }
74 }
75
76 pub fn to_s24(&self) -> Vec<i32> {
77 self.samples
78 .iter()
79 .map(|&sample| (sample >> 8) as i32) .collect()
81 }
82
83}
84
85impl Default for Samples {
86 fn default() -> Self {
87 Samples {
88 samples: Vec::new(),
89 sample_rate: 44100,
90 channels: 2,
91 bits_per_sample: 32,
92 }
93 }
94}
95
96pub struct EncodedStream {
97 pub stream: Vec<u8>,
98}
99
100impl EncodedStream {
101 pub fn new(stream: Vec<u8>) -> Self {
102 EncodedStream { stream }
103 }
104
105 pub async fn write_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
106 if !path.as_ref().exists() {
107 tokio::fs::create_dir_all(
108 path.as_ref()
109 .parent()
110 .ok_or(anyhow::anyhow!("Could not create path"))?,
111 )
112 .await?;
113 }
114 tokio::fs::write(path, &self.stream).await?;
115 Ok(())
116 }
117}