From 3c007ed886fa67dd6d426db70ba1928717297570 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Sun, 10 Dec 2023 00:29:54 -0600 Subject: [PATCH 1/2] Removed MusicBee library reader --- .../db_reader/musicbee/reader.rs | 220 ------------------ src/music_storage/db_reader/musicbee/utils.rs | 29 --- 2 files changed, 249 deletions(-) delete mode 100644 src/music_storage/db_reader/musicbee/reader.rs delete mode 100644 src/music_storage/db_reader/musicbee/utils.rs diff --git a/src/music_storage/db_reader/musicbee/reader.rs b/src/music_storage/db_reader/musicbee/reader.rs deleted file mode 100644 index 0a5cac6..0000000 --- a/src/music_storage/db_reader/musicbee/reader.rs +++ /dev/null @@ -1,220 +0,0 @@ -use super::utils::get_string; -use crate::music_storage::db_reader::common::{get_bytes, get_datetime}; -use chrono::{DateTime, Utc}; -use std::fs::File; -use std::io::prelude::*; -use std::time::Duration; - -pub struct MusicBeeDatabase { - path: String, -} - -impl MusicBeeDatabase { - pub fn new(path: String) -> MusicBeeDatabase { - MusicBeeDatabase { path } - } - - /// Reads the entire MusicBee library and returns relevant values - /// as a `Vec` of `Song`s - pub fn read(&self) -> Result, Box> { - let mut f = File::open(&self.path).unwrap(); - let mut buffer = Vec::new(); - let mut retrieved_songs: Vec = Vec::new(); - - // Read the whole file - f.read_to_end(&mut buffer)?; - - let mut buf_iter = buffer.into_iter(); - - // Get the song count from the first 4 bytes - // and then right shift it by 8 for some reason - let mut database_song_count = i32::from_le_bytes(get_bytes(&mut buf_iter)); - database_song_count = database_song_count >> 8; - - let mut song_count = 0; - loop { - // If the file designation is 1, then the end of the database - // has been reached - let file_designation = match buf_iter.next() { - Some(1) => break, - Some(value) => value, - None => break, - }; - - song_count += 1; - - // Get the file status. Unknown what this means - let status = buf_iter.next().unwrap(); - - buf_iter.next(); // Read in a byte to throw it away - - // Get the play count - let play_count = u16::from_le_bytes(get_bytes(&mut buf_iter)); - - // Get the time the song was last played, stored as a signed 64 bit number of microseconds - let last_played = get_datetime(buf_iter.by_ref(), true); - - // Get the number of times the song was skipped - let skip_count = u16::from_le_bytes(get_bytes(&mut buf_iter)); - - // Get the path to the song - let path = get_string(buf_iter.by_ref()); - - // Get the file size - let file_size = i32::from_le_bytes(get_bytes(&mut buf_iter)); - - // Get the sample rate - let sample_rate = i32::from_le_bytes(get_bytes(&mut buf_iter)); - - // Get the channel count - let channel_count = buf_iter.next().unwrap(); - - // Get the bitrate type (CBR, VBR, etc.) - let bitrate_type = buf_iter.next().unwrap(); - - // Get the actual bitrate - let bitrate = i16::from_le_bytes(get_bytes(&mut buf_iter)); - - // Get the track length in milliseconds - let track_length = - Duration::from_millis(i32::from_le_bytes(get_bytes(&mut buf_iter)) as u64); - - // Get the date added and modified in the same format - let date_added = get_datetime(buf_iter.by_ref(), true); - let date_modified = get_datetime(buf_iter.by_ref(), true); - - // Gets artwork information - // - // Artworks are stored as chunks describing the type - // (embedded, file), and some other information. - let mut artwork: Vec = vec![]; - loop { - let artwork_type = buf_iter.next().unwrap(); - if artwork_type > 253 { - break; - } - - let unknown_string = get_string(buf_iter.by_ref()); - let storage_mode = buf_iter.next().unwrap(); - let storage_path = get_string(buf_iter.by_ref()); - - artwork.push(MusicBeeAlbumArt { - artwork_type, - unknown_string, - storage_mode, - storage_path, - }); - } - - buf_iter.next(); // Read in a byte to throw it away - - // Gets all the tags on the song in the database - let mut tags: Vec = vec![]; - loop { - // If the tag code is 0, the end of the block has been reached, so break. - // - // If the tag code is 255, it pertains to some CUE file values that are not known - // throw away these values - let tag_code = match buf_iter.next() { - Some(0) => break, - Some(255) => { - let repeats = u16::from_le_bytes(get_bytes(&mut buf_iter)); - for _ in 0..(repeats * 13) - 2 { - buf_iter.next().unwrap(); - } - - 255 - } - Some(value) => value, - None => panic!(), - }; - - // Get the string value of the tag - let tag_value = get_string(buf_iter.by_ref()); - tags.push(MusicBeeTag { - tag_code, - tag_value, - }); - } - - // Construct the finished song and add it to the vec - let constructed_song = MusicBeeSong { - file_designation, - status, - play_count, - last_played, - skip_count, - path, - file_size, - sample_rate, - channel_count, - bitrate_type, - bitrate, - track_length, - date_added, - date_modified, - artwork, - tags, - }; - - retrieved_songs.push(constructed_song); - } - - println!("The database claims you have: {database_song_count} songs\nThe retrieved number is: {song_count} songs"); - - match database_song_count == song_count { - true => Ok(retrieved_songs), - false => Err("Song counts do not match!".into()), - } - } -} - -#[derive(Debug)] -pub struct MusicBeeTag { - tag_code: u8, - tag_value: String, -} - -#[derive(Debug)] -pub struct MusicBeeAlbumArt { - artwork_type: u8, - unknown_string: String, - storage_mode: u8, - storage_path: String, -} - -#[derive(Debug)] -pub struct MusicBeeSong { - file_designation: u8, - status: u8, - play_count: u16, - pub last_played: DateTime, - skip_count: u16, - path: String, - file_size: i32, - sample_rate: i32, - channel_count: u8, - bitrate_type: u8, - bitrate: i16, - track_length: Duration, - date_added: DateTime, - date_modified: DateTime, - - /* Album art stuff */ - artwork: Vec, - - /* All tags */ - tags: Vec, -} - -impl MusicBeeSong { - pub fn get_tag_code(self, code: u8) -> Option { - for tag in &self.tags { - if tag.tag_code == code { - return Some(tag.tag_value.clone()); - } - } - - None - } -} diff --git a/src/music_storage/db_reader/musicbee/utils.rs b/src/music_storage/db_reader/musicbee/utils.rs deleted file mode 100644 index 65d6333..0000000 --- a/src/music_storage/db_reader/musicbee/utils.rs +++ /dev/null @@ -1,29 +0,0 @@ -use leb128; - -/// Gets a string from the MusicBee database format -/// -/// The length of the string is defined by an LEB128 encoded value at the beginning, followed by the string of that length -pub fn get_string(iterator: &mut std::vec::IntoIter) -> String { - let mut string_length = iterator.next().unwrap() as usize; - if string_length == 0 { - return String::new(); - } - - // Decode the LEB128 value - let mut leb_bytes: Vec = vec![]; - loop { - leb_bytes.push(string_length as u8); - - if string_length >> 7 != 1 { - break; - } - string_length = iterator.next().unwrap() as usize; - } - string_length = leb128::read::unsigned(&mut leb_bytes.as_slice()).unwrap() as usize; - - let mut string_bytes = vec![]; - for _ in 0..string_length { - string_bytes.push(iterator.next().unwrap()); - } - String::from_utf8(string_bytes).unwrap() -} From e5bfde684631782e53a3f44f069413dd3c6c0448 Mon Sep 17 00:00:00 2001 From: G2-Games Date: Sun, 10 Dec 2023 00:36:20 -0600 Subject: [PATCH 2/2] Revert "Removed MusicBee library reader" This reverts commit 942f12f3ebc1c557b8023ad8494375c4115404e1. --- .../db_reader/musicbee/reader.rs | 220 ++++++++++++++++++ src/music_storage/db_reader/musicbee/utils.rs | 29 +++ 2 files changed, 249 insertions(+) create mode 100644 src/music_storage/db_reader/musicbee/reader.rs create mode 100644 src/music_storage/db_reader/musicbee/utils.rs diff --git a/src/music_storage/db_reader/musicbee/reader.rs b/src/music_storage/db_reader/musicbee/reader.rs new file mode 100644 index 0000000..0a5cac6 --- /dev/null +++ b/src/music_storage/db_reader/musicbee/reader.rs @@ -0,0 +1,220 @@ +use super::utils::get_string; +use crate::music_storage::db_reader::common::{get_bytes, get_datetime}; +use chrono::{DateTime, Utc}; +use std::fs::File; +use std::io::prelude::*; +use std::time::Duration; + +pub struct MusicBeeDatabase { + path: String, +} + +impl MusicBeeDatabase { + pub fn new(path: String) -> MusicBeeDatabase { + MusicBeeDatabase { path } + } + + /// Reads the entire MusicBee library and returns relevant values + /// as a `Vec` of `Song`s + pub fn read(&self) -> Result, Box> { + let mut f = File::open(&self.path).unwrap(); + let mut buffer = Vec::new(); + let mut retrieved_songs: Vec = Vec::new(); + + // Read the whole file + f.read_to_end(&mut buffer)?; + + let mut buf_iter = buffer.into_iter(); + + // Get the song count from the first 4 bytes + // and then right shift it by 8 for some reason + let mut database_song_count = i32::from_le_bytes(get_bytes(&mut buf_iter)); + database_song_count = database_song_count >> 8; + + let mut song_count = 0; + loop { + // If the file designation is 1, then the end of the database + // has been reached + let file_designation = match buf_iter.next() { + Some(1) => break, + Some(value) => value, + None => break, + }; + + song_count += 1; + + // Get the file status. Unknown what this means + let status = buf_iter.next().unwrap(); + + buf_iter.next(); // Read in a byte to throw it away + + // Get the play count + let play_count = u16::from_le_bytes(get_bytes(&mut buf_iter)); + + // Get the time the song was last played, stored as a signed 64 bit number of microseconds + let last_played = get_datetime(buf_iter.by_ref(), true); + + // Get the number of times the song was skipped + let skip_count = u16::from_le_bytes(get_bytes(&mut buf_iter)); + + // Get the path to the song + let path = get_string(buf_iter.by_ref()); + + // Get the file size + let file_size = i32::from_le_bytes(get_bytes(&mut buf_iter)); + + // Get the sample rate + let sample_rate = i32::from_le_bytes(get_bytes(&mut buf_iter)); + + // Get the channel count + let channel_count = buf_iter.next().unwrap(); + + // Get the bitrate type (CBR, VBR, etc.) + let bitrate_type = buf_iter.next().unwrap(); + + // Get the actual bitrate + let bitrate = i16::from_le_bytes(get_bytes(&mut buf_iter)); + + // Get the track length in milliseconds + let track_length = + Duration::from_millis(i32::from_le_bytes(get_bytes(&mut buf_iter)) as u64); + + // Get the date added and modified in the same format + let date_added = get_datetime(buf_iter.by_ref(), true); + let date_modified = get_datetime(buf_iter.by_ref(), true); + + // Gets artwork information + // + // Artworks are stored as chunks describing the type + // (embedded, file), and some other information. + let mut artwork: Vec = vec![]; + loop { + let artwork_type = buf_iter.next().unwrap(); + if artwork_type > 253 { + break; + } + + let unknown_string = get_string(buf_iter.by_ref()); + let storage_mode = buf_iter.next().unwrap(); + let storage_path = get_string(buf_iter.by_ref()); + + artwork.push(MusicBeeAlbumArt { + artwork_type, + unknown_string, + storage_mode, + storage_path, + }); + } + + buf_iter.next(); // Read in a byte to throw it away + + // Gets all the tags on the song in the database + let mut tags: Vec = vec![]; + loop { + // If the tag code is 0, the end of the block has been reached, so break. + // + // If the tag code is 255, it pertains to some CUE file values that are not known + // throw away these values + let tag_code = match buf_iter.next() { + Some(0) => break, + Some(255) => { + let repeats = u16::from_le_bytes(get_bytes(&mut buf_iter)); + for _ in 0..(repeats * 13) - 2 { + buf_iter.next().unwrap(); + } + + 255 + } + Some(value) => value, + None => panic!(), + }; + + // Get the string value of the tag + let tag_value = get_string(buf_iter.by_ref()); + tags.push(MusicBeeTag { + tag_code, + tag_value, + }); + } + + // Construct the finished song and add it to the vec + let constructed_song = MusicBeeSong { + file_designation, + status, + play_count, + last_played, + skip_count, + path, + file_size, + sample_rate, + channel_count, + bitrate_type, + bitrate, + track_length, + date_added, + date_modified, + artwork, + tags, + }; + + retrieved_songs.push(constructed_song); + } + + println!("The database claims you have: {database_song_count} songs\nThe retrieved number is: {song_count} songs"); + + match database_song_count == song_count { + true => Ok(retrieved_songs), + false => Err("Song counts do not match!".into()), + } + } +} + +#[derive(Debug)] +pub struct MusicBeeTag { + tag_code: u8, + tag_value: String, +} + +#[derive(Debug)] +pub struct MusicBeeAlbumArt { + artwork_type: u8, + unknown_string: String, + storage_mode: u8, + storage_path: String, +} + +#[derive(Debug)] +pub struct MusicBeeSong { + file_designation: u8, + status: u8, + play_count: u16, + pub last_played: DateTime, + skip_count: u16, + path: String, + file_size: i32, + sample_rate: i32, + channel_count: u8, + bitrate_type: u8, + bitrate: i16, + track_length: Duration, + date_added: DateTime, + date_modified: DateTime, + + /* Album art stuff */ + artwork: Vec, + + /* All tags */ + tags: Vec, +} + +impl MusicBeeSong { + pub fn get_tag_code(self, code: u8) -> Option { + for tag in &self.tags { + if tag.tag_code == code { + return Some(tag.tag_value.clone()); + } + } + + None + } +} diff --git a/src/music_storage/db_reader/musicbee/utils.rs b/src/music_storage/db_reader/musicbee/utils.rs new file mode 100644 index 0000000..65d6333 --- /dev/null +++ b/src/music_storage/db_reader/musicbee/utils.rs @@ -0,0 +1,29 @@ +use leb128; + +/// Gets a string from the MusicBee database format +/// +/// The length of the string is defined by an LEB128 encoded value at the beginning, followed by the string of that length +pub fn get_string(iterator: &mut std::vec::IntoIter) -> String { + let mut string_length = iterator.next().unwrap() as usize; + if string_length == 0 { + return String::new(); + } + + // Decode the LEB128 value + let mut leb_bytes: Vec = vec![]; + loop { + leb_bytes.push(string_length as u8); + + if string_length >> 7 != 1 { + break; + } + string_length = iterator.next().unwrap() as usize; + } + string_length = leb128::read::unsigned(&mut leb_bytes.as_slice()).unwrap() as usize; + + let mut string_bytes = vec![]; + for _ in 0..string_length { + string_bytes.push(iterator.next().unwrap()); + } + String::from_utf8(string_bytes).unwrap() +}