portable_audio_library/serialization/
mod.rs

1use crate::{
2    compression::{get_compression, CompressionType},
3    error::PortableAudioLibraryResult,
4};
5use serde::{Deserialize, Serialize};
6use std::{
7    fs::File,
8    io::{Cursor, Read, Seek, Write},
9    path::PathBuf,
10};
11
12/// Playlist name.
13pub type Playlist = String;
14
15const U64_SIZE: usize = std::mem::size_of::<u64>();
16const PROGRESS_BAR_TEMPLATE: &str = "{spinner:.green} {msg} [{wide_bar}] {pos}/{len} ({eta})";
17const PROGRESS_CHARS: &str = "=> ";
18const CHECK_GREEN: &str = "\x1b[32m✓\x1b[0m";
19
20/// Metadata of audio library.
21#[derive(Debug, Clone, Default, PartialEq, PartialOrd, Serialize, Deserialize)]
22pub struct Metadata {
23    pub name: String,
24    pub compression_type: CompressionType,
25    pub audios: Vec<AudioMetadata>,
26}
27
28/// Metadata of audio.
29#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
30pub struct AudioMetadata {
31    pub name: String,
32    pub size: u64,
33    pub playlists: Vec<Playlist>,
34
35    #[serde(skip_serializing, skip_deserializing)]
36    pub path: PathBuf,
37}
38
39impl Metadata {
40    /// Writes the metadata to the file.
41    pub fn write_to_file(&mut self, path: impl Into<PathBuf>) -> PortableAudioLibraryResult<()> {
42        let path = path.into();
43
44        let compression = get_compression(&self.compression_type);
45        let mut compressed_audio_cursors = vec![];
46
47        let progress_bar = indicatif::ProgressBar::new(self.audios.len() as u64);
48        progress_bar.set_style(
49            indicatif::ProgressStyle::default_bar()
50                .template(PROGRESS_BAR_TEMPLATE)
51                .unwrap()
52                .progress_chars(PROGRESS_CHARS),
53        );
54        progress_bar.set_message("Compressing audio files");
55
56        for audio in &mut self.audios {
57            let mut audio_file = std::fs::File::open(&audio.path)?;
58            let mut compressed_audio_cursor = Cursor::new(Vec::new());
59
60            compression.compress(&mut audio_file, &mut compressed_audio_cursor)?;
61
62            audio.size = compressed_audio_cursor.get_ref().len() as u64;
63            compressed_audio_cursors.push(compressed_audio_cursor);
64
65            progress_bar.inc(1);
66        }
67
68        progress_bar.finish_and_clear();
69        println!("{} Compression done!", CHECK_GREEN);
70
71        let progress_bar = indicatif::ProgressBar::new(compressed_audio_cursors.len() as u64 + 1);
72        progress_bar.set_style(
73            indicatif::ProgressStyle::default_bar()
74                .template(PROGRESS_BAR_TEMPLATE)
75                .unwrap()
76                .progress_chars(PROGRESS_CHARS),
77        );
78        progress_bar.set_message("Writing to file");
79
80        let mut portable_audio_library_file = std::fs::File::create(path)?;
81        let serialized_portable_audio_library = bincode::serialize(&self)?;
82        let metadata_size = serialized_portable_audio_library.len() as u64;
83        portable_audio_library_file.write_all(&metadata_size.to_be_bytes())?;
84        portable_audio_library_file.write_all(&serialized_portable_audio_library)?;
85        progress_bar.inc(1);
86
87        for compressed_audio_cursor in compressed_audio_cursors {
88            portable_audio_library_file.write_all(compressed_audio_cursor.get_ref())?;
89            progress_bar.inc(1);
90        }
91
92        progress_bar.finish_and_clear();
93        println!("{} Writing done!", CHECK_GREEN);
94
95        Ok(())
96    }
97
98    /// Reads the metadata from the file.
99    pub fn read_from_file(
100        path: impl Into<PathBuf>,
101        directory_store: impl Into<PathBuf>,
102    ) -> PortableAudioLibraryResult<Metadata> {
103        let path = path.into();
104        let directory_store: PathBuf = directory_store.into();
105
106        let mut portable_audio_library_file = std::fs::File::open(path)?;
107
108        let mut metadata_size_buf = vec![0; U64_SIZE];
109        portable_audio_library_file.read_exact(&mut metadata_size_buf)?;
110        let metadata_size = u64::from_be_bytes(metadata_size_buf.try_into().unwrap());
111
112        let mut metadata_buf = vec![0; metadata_size as usize];
113        portable_audio_library_file.read_exact(&mut metadata_buf)?;
114        let mut metadata: Metadata = bincode::deserialize(&metadata_buf)?;
115
116        let compression_type = &metadata.compression_type;
117        let compression = get_compression(compression_type);
118
119        let progress_bar = indicatif::ProgressBar::new(metadata.audios.len() as u64);
120        progress_bar.set_style(
121            indicatif::ProgressStyle::default_bar()
122                .template(PROGRESS_BAR_TEMPLATE)
123                .unwrap()
124                .progress_chars(PROGRESS_CHARS),
125        );
126        progress_bar.set_message("Decompressing audio files");
127
128        let mut offset = metadata_size + U64_SIZE as u64;
129        for audio_metadata in &mut metadata.audios {
130            let mut buf = vec![0; audio_metadata.size as usize];
131            portable_audio_library_file.seek(std::io::SeekFrom::Start(offset))?;
132            portable_audio_library_file.read_exact(&mut buf)?;
133
134            let path = directory_store.join(&audio_metadata.name);
135
136            let mut audio_file = File::create(&path)?;
137
138            compression.decompress(&mut Cursor::new(buf), &mut audio_file)?;
139
140            audio_metadata.path = path;
141
142            offset += audio_metadata.size;
143
144            progress_bar.inc(1);
145        }
146
147        progress_bar.finish_and_clear();
148        println!("{} Decompression done!", CHECK_GREEN);
149
150        Ok(metadata)
151    }
152}