2022-05-12 00:18:58 +02:00
|
|
|
use std::path::Path;
|
2020-10-25 01:39:20 +02:00
|
|
|
|
2024-05-10 18:33:42 +02:00
|
|
|
use audiotags::Tag;
|
|
|
|
use audiotags::TagType;
|
|
|
|
use librespot::playback::audio_backend::Sink;
|
|
|
|
use librespot::playback::audio_backend::SinkError;
|
|
|
|
use librespot::playback::convert::Converter;
|
|
|
|
use librespot::playback::decoder::AudioPacket;
|
2024-01-12 12:41:26 +01:00
|
|
|
use flac_bound::FlacEncoder;
|
2020-10-25 01:39:20 +02:00
|
|
|
|
2024-05-10 18:33:42 +02:00
|
|
|
use crate::track::TrackMetadata;
|
|
|
|
|
|
|
|
pub enum SinkEvent {
|
|
|
|
Written { bytes: usize, total: usize },
|
|
|
|
Finished,
|
|
|
|
}
|
|
|
|
pub type SinkEventChannel = tokio::sync::mpsc::UnboundedReceiver<SinkEvent>;
|
2022-05-12 00:18:58 +02:00
|
|
|
|
2020-10-25 01:39:20 +02:00
|
|
|
pub struct FileSink {
|
|
|
|
sink: String,
|
2022-05-12 00:18:58 +02:00
|
|
|
content: Vec<i32>,
|
2024-05-10 18:33:42 +02:00
|
|
|
metadata: TrackMetadata,
|
2024-01-12 12:41:26 +01:00
|
|
|
compression: u32,
|
2024-05-10 18:33:42 +02:00
|
|
|
event_sender: tokio::sync::mpsc::UnboundedSender<SinkEvent>,
|
2022-05-12 00:18:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FileSink {
|
2024-01-12 12:41:26 +01:00
|
|
|
pub fn set_compression(&mut self, compression: u32) {
|
|
|
|
self.compression = compression;
|
|
|
|
}
|
2020-10-25 01:39:20 +02:00
|
|
|
|
2024-05-10 18:33:42 +02:00
|
|
|
pub fn new(path: String, track: TrackMetadata) -> (Self, SinkEventChannel) {
|
|
|
|
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
|
|
|
|
|
|
|
(
|
|
|
|
FileSink {
|
|
|
|
sink: path,
|
|
|
|
content: Vec::new(),
|
|
|
|
metadata: track,
|
|
|
|
compression: 4,
|
|
|
|
event_sender: tx,
|
|
|
|
},
|
|
|
|
rx,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_approximate_size(&self) -> usize {
|
|
|
|
self.convert_track_duration_to_size()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn convert_track_duration_to_size(&self) -> usize {
|
|
|
|
let duration = self.metadata.duration / 1000;
|
|
|
|
let sample_rate = 44100;
|
|
|
|
let channels = 2;
|
|
|
|
let bits_per_sample = 16;
|
|
|
|
let bytes_per_sample = bits_per_sample / 8;
|
|
|
|
(duration as usize) * sample_rate * channels * bytes_per_sample * 2
|
2020-10-25 01:39:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Sink for FileSink {
|
2022-05-12 00:18:58 +02:00
|
|
|
fn start(&mut self) -> Result<(), SinkError> {
|
2020-10-25 01:39:20 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-05-12 00:18:58 +02:00
|
|
|
fn stop(&mut self) -> Result<(), SinkError> {
|
2024-05-10 18:33:42 +02:00
|
|
|
tracing::info!("Writing to file: {:?}", &self.sink);
|
2024-01-12 12:41:26 +01:00
|
|
|
let mut encoder = FlacEncoder::new()
|
2024-05-10 18:33:42 +02:00
|
|
|
.ok_or(SinkError::OnWrite(
|
|
|
|
"Failed to create flac encoder".to_string(),
|
|
|
|
))?
|
2024-01-12 12:41:26 +01:00
|
|
|
.channels(2)
|
|
|
|
.bits_per_sample(16)
|
2024-05-10 18:33:42 +02:00
|
|
|
.compression_level(self.compression)
|
2024-01-12 12:41:26 +01:00
|
|
|
.init_file(&self.sink)
|
2024-05-10 18:33:42 +02:00
|
|
|
.map_err(|e| {
|
|
|
|
SinkError::OnWrite(format!("Failed to init flac encoder: {:?}", e).to_string())
|
|
|
|
})?;
|
2024-01-12 12:41:26 +01:00
|
|
|
encoder
|
|
|
|
.process_interleaved(self.content.as_slice(), (self.content.len() / 2) as u32)
|
2024-05-10 18:33:42 +02:00
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to write flac".to_string()))?;
|
|
|
|
encoder
|
|
|
|
.finish()
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to finish encondig".to_string()))?;
|
|
|
|
|
|
|
|
let mut tag = Tag::new()
|
|
|
|
.with_tag_type(TagType::Flac)
|
|
|
|
.read_from_path(Path::new(&self.sink))
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to read metadata".to_string()))?;
|
|
|
|
|
|
|
|
tag.set_album_title(&self.metadata.album.name);
|
|
|
|
for artist in &self.metadata.artists {
|
|
|
|
tag.add_artist(&artist.name);
|
2022-05-12 00:18:58 +02:00
|
|
|
}
|
2024-05-10 18:33:42 +02:00
|
|
|
tag.set_title(&self.metadata.track_name);
|
|
|
|
tag.write_to_path(&self.sink)
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to write metadata".to_string()))?;
|
|
|
|
|
|
|
|
self.event_sender
|
|
|
|
.send(SinkEvent::Finished)
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to send finished event".to_string()))?;
|
2020-10-25 01:39:20 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-05-09 20:00:54 +02:00
|
|
|
fn write(&mut self, packet: AudioPacket, converter: &mut Converter) -> Result<(), SinkError> {
|
2024-05-10 18:33:42 +02:00
|
|
|
let data = converter.f64_to_s16(
|
|
|
|
packet
|
|
|
|
.samples()
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to get samples".to_string()))?,
|
|
|
|
);
|
2022-05-12 00:18:58 +02:00
|
|
|
let mut data32: Vec<i32> = data.iter().map(|el| i32::from(*el)).collect();
|
|
|
|
self.content.append(&mut data32);
|
2024-05-10 18:33:42 +02:00
|
|
|
|
|
|
|
self.event_sender
|
|
|
|
.send(SinkEvent::Written {
|
|
|
|
bytes: self.content.len() * std::mem::size_of::<i32>(),
|
|
|
|
total: self.convert_track_duration_to_size(),
|
|
|
|
})
|
|
|
|
.map_err(|_| SinkError::OnWrite("Failed to send event".to_string()))?;
|
|
|
|
|
2020-10-25 01:39:20 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-05-12 00:18:58 +02:00
|
|
|
}
|