From d1e07ea089f3db60a073140d4b6ee9b5ca707d5c Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 13 Nov 2024 00:16:50 -0500 Subject: [PATCH 01/11] added json output to songs and podcasts --- .gitignore | 1 + parser/Cargo.toml | 6 ++++-- parser/src/itunesdb.rs | 9 +++++++-- parser/src/parsers/itunesdb_parser.rs | 28 +++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1c862eb..fc4aa9c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ **/*.csv **/.vscode **/.txt +repopack-output.txt \ No newline at end of file diff --git a/parser/Cargo.toml b/parser/Cargo.toml index c857743..cbdb81c 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -chrono = "0.4.31" csv = "1.3.0" +chrono = { version = "0.4.31", features = ["serde"] } # Add serde feature +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" # Languages parsing -isolang = "2.4.0" \ No newline at end of file +isolang = "2.4.0" diff --git a/parser/src/itunesdb.rs b/parser/src/itunesdb.rs index f9bed2f..85ba2c3 100644 --- a/parser/src/itunesdb.rs +++ b/parser/src/itunesdb.rs @@ -11,7 +11,11 @@ use crate::helpers::helpers; use crate::helpers::itunesdb_helpers; - pub struct Podcast { + use serde::Serialize; + + + #[derive(Serialize)] + pub struct Podcast { pub podcast_title : String, pub podcast_publisher : String, pub podcast_genre : String, @@ -35,7 +39,8 @@ } } -pub struct Song { + #[derive(Serialize)] + pub struct Song { pub file_extension: String, pub bitrate_kbps: u32, pub sample_rate_hz: u32, diff --git a/parser/src/parsers/itunesdb_parser.rs b/parser/src/parsers/itunesdb_parser.rs index d0c7edd..27235b6 100644 --- a/parser/src/parsers/itunesdb_parser.rs +++ b/parser/src/parsers/itunesdb_parser.rs @@ -1,4 +1,6 @@ use std::fmt::Write; +use std::fs::File; +use std::io; use crate::constants::itunesdb_constants; use crate::itunesdb; @@ -840,6 +842,32 @@ pub fn parse_itunesdb_file(itunesdb_file_as_bytes : Vec) { println!("{} songs found", songs_found.len()); + // Add JSON output @joshkenney + if !songs_found.is_empty() { + let songs_json = serde_json::to_string_pretty(&songs_found) + .expect("Error serializing songs to JSON"); + + let mut songs_json_file = File::create("songs.json") + .expect("Error creating songs JSON file"); + + io::Write::write_all(&mut songs_json_file, songs_json.as_bytes()) + .expect("Error writing songs JSON file"); + + println!("Created songs.json with {} songs", songs_found.len()); + } + + if !podcasts_found.is_empty() { + let podcasts_json = serde_json::to_string_pretty(&podcasts_found) + .expect("Error serializing podcasts to JSON"); + + let mut podcasts_json_file = File::create("podcasts.json") + .expect("Error creating podcasts JSON file"); + + io::Write::write_all(&mut podcasts_json_file, podcasts_json.as_bytes()) + .expect("Error writing podcasts JSON file"); + + println!("Created podcasts.json with {} podcasts", podcasts_found.len()); + } podcast_csv_writer.write_record(&[ "Episode Title", From c2d467b7b86a8a6c6f3938c7a4b857da03efd720 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 13 Nov 2024 00:18:14 -0500 Subject: [PATCH 02/11] added json to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fc4aa9c..1330ceb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ **/**.DS_Store **/*.csv +**/*.json **/.vscode **/.txt -repopack-output.txt \ No newline at end of file +repopack-output.txt From cba9785fe9370cb7b8178b406beec9825ecc1593 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 13 Nov 2024 00:24:22 -0500 Subject: [PATCH 03/11] improved demo in README.md to include cargo build --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 135938a..7cf3c3f 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,9 @@ Possible options are: ```bash -$ /target/debug/itunesdb_parser '' +$ cd iTunesDB-Parser +$ cargo build +$ ./parser/target/debug/itunesdb_parser '' ``` For iTunesDB, Photos Database, and Playcounts files, a CSV will be generated with all the relevant information. For example, if you run it on an iTunesDB file, the output CSV will contain the info for all songs and podcasts mentioned in the iTunesDB file. From faf4e929d7a37f40a7f515d8e0154080188cdd14 Mon Sep 17 00:00:00 2001 From: Josh Kenney <42978413+joshkenney@users.noreply.github.com> Date: Tue, 26 Nov 2024 23:34:28 -0500 Subject: [PATCH 04/11] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bc2488..4b49f69 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ The 7 possible "type" options are: ```bash $ cd iTunesDB-Parser +$ cargo init $ cargo build $ ./parser/target/debug/itunesdb_parser '' ``` @@ -115,4 +116,4 @@ Special thanks to: for providing iTunesDB files! -Buy Me A Coffee \ No newline at end of file +Buy Me A Coffee From f3cc96ecd38d1657defd555971a681ef33789152 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 27 Nov 2024 00:14:39 -0500 Subject: [PATCH 05/11] added command line argument for csv (default) json --- .gitignore | 5 + parser/src/main.rs | 23 +++- parser/src/parsers/itunesdb_parser.rs | 184 ++++++++++++++------------ 3 files changed, 123 insertions(+), 89 deletions(-) diff --git a/.gitignore b/.gitignore index 1330ceb..25aecdb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ **/.vscode **/.txt repopack-output.txt + + +# Added by cargo + +/target diff --git a/parser/src/main.rs b/parser/src/main.rs index ab5643c..3bfa85d 100644 --- a/parser/src/main.rs +++ b/parser/src/main.rs @@ -39,6 +39,13 @@ mod equalizer; use std::io::Read; fn main() { + // add a check for the number of arguments + let args: Vec = std::env::args().collect(); + + if args.len() < 3 { + panic!("Usage: {} [format=csv|json]", args[0]); + } + let itunesdb_filename: String = std::env::args() .nth(1) .expect("Missing first parameter: iTunes DB filename"); @@ -61,6 +68,20 @@ fn main() { ); } + // Default to "csv" if no format specified + let output_format = if args.len() > 3 { + match args[3].to_lowercase().as_str() { + "json" => "json", + "csv" => "csv", + _ => { + eprintln!("Invalid format specified. Using default 'csv'"); + "csv" + } + } + } else { + "csv" + }; + let mut itunesdb_file_as_bytes = Vec::new(); // https://stackoverflow.com/questions/47660946/why-does-a-file-need-to-be-mutable-to-call-readread-to-string @@ -85,7 +106,7 @@ fn main() { photos_csv_writer, ); } else if itunesdb_file_type == "itunes" { - parsers::itunesdb_parser::parse_itunesdb_file(itunesdb_file_as_bytes); + parsers::itunesdb_parser::parse_itunesdb_file(itunesdb_file_as_bytes, output_format); } else if itunesdb_file_type == "itprefs" { parsers::preferences_parser::parse_itunes_prefs_file(itunesdb_file_as_bytes); } else if itunesdb_file_type == "playcounts" { diff --git a/parser/src/parsers/itunesdb_parser.rs b/parser/src/parsers/itunesdb_parser.rs index 27235b6..c7d338e 100644 --- a/parser/src/parsers/itunesdb_parser.rs +++ b/parser/src/parsers/itunesdb_parser.rs @@ -9,7 +9,7 @@ use crate::helpers::helpers; use crate::helpers::itunesdb_helpers; -pub fn parse_itunesdb_file(itunesdb_file_as_bytes : Vec) { +pub fn parse_itunesdb_file(itunesdb_file_as_bytes: Vec, output_format: String) let mut music_csv_writer = helpers::init_csv_writer("music.csv"); let mut podcast_csv_writer = helpers::init_csv_writer("podcasts.csv"); @@ -843,103 +843,111 @@ pub fn parse_itunesdb_file(itunesdb_file_as_bytes : Vec) { println!("{} songs found", songs_found.len()); // Add JSON output @joshkenney - if !songs_found.is_empty() { - let songs_json = serde_json::to_string_pretty(&songs_found) - .expect("Error serializing songs to JSON"); - - let mut songs_json_file = File::create("songs.json") - .expect("Error creating songs JSON file"); - - io::Write::write_all(&mut songs_json_file, songs_json.as_bytes()) - .expect("Error writing songs JSON file"); - - println!("Created songs.json with {} songs", songs_found.len()); - } + if output_format == "json" { + if !songs_found.is_empty() { + let songs_json = serde_json::to_string_pretty(&songs_found) + .expect("Error serializing songs to JSON"); - if !podcasts_found.is_empty() { - let podcasts_json = serde_json::to_string_pretty(&podcasts_found) - .expect("Error serializing podcasts to JSON"); - - let mut podcasts_json_file = File::create("podcasts.json") - .expect("Error creating podcasts JSON file"); - - io::Write::write_all(&mut podcasts_json_file, podcasts_json.as_bytes()) - .expect("Error writing podcasts JSON file"); + let mut songs_json_file = File::create("songs.json") + .expect("Error creating songs JSON file"); + + io::Write::write_all(&mut songs_json_file, songs_json.as_bytes()) + .expect("Error writing songs JSON file"); + + println!("Created songs.json with {} songs", songs_found.len()); + } + + if !podcasts_found.is_empty() { + let podcasts_json = serde_json::to_string_pretty(&podcasts_found) + .expect("Error serializing podcasts to JSON"); + + let mut podcasts_json_file = File::create("podcasts.json") + .expect("Error creating podcasts JSON file"); - println!("Created podcasts.json with {} podcasts", podcasts_found.len()); + io::Write::write_all(&mut podcasts_json_file, podcasts_json.as_bytes()) + .expect("Error writing podcasts JSON file"); + + println!("Created podcasts.json with {} podcasts", podcasts_found.len()); + } } - podcast_csv_writer.write_record(&[ - "Episode Title", - "Publisher", - "Genre", - "Subtitle", - "Description", - "File Type" - ]).expect("Error can't create CSV file headers for podcast file"); + // default to CSV output + if output_format == "csv" { + let mut music_csv_writer = helpers::init_csv_writer("music.csv"); + let mut podcast_csv_writer = helpers::init_csv_writer("podcasts.csv"); - for episode in podcasts_found.iter() { podcast_csv_writer.write_record(&[ - episode.podcast_title.to_string(), - episode.podcast_publisher.to_string(), - episode.podcast_genre.to_string(), - episode.podcast_subtitle.to_string(), - episode.podcast_description.to_string().replace("\n", ""), - episode.podcast_file_type.to_string() - ]).expect("Can't write row to podcast CSV file"); - } - - music_csv_writer - .write_record(&[ - "Song Title", - "Artist", - "Album", - "Year released", - "File size", - "Song Duration", - "Filename", + "Episode Title", + "Publisher", "Genre", - "File extension", - "Bitrate (kbps)", - "Sample Rate (Hz)", - "File size (bytes)", - "Song duration (seconds)", - "Play count", - "Rating", - "Added to library on (timestamp)", - "Added to library on (epoch)", - "Composer", - "Comment", - ]) - .expect("Can't create CSV file headers for music file"); - - for song in songs_found.iter() { - // the duplicate `to_string()` calls are to avoid this error: - // cannot move out of `song.song_title` which is behind a shared reference - // move occurs because `song.song_title` has type `String`, which does not implement the `Copy` trait + "Subtitle", + "Description", + "File Type" + ]).expect("Error can't create CSV file headers for podcast file"); + + for episode in podcasts_found.iter() { + podcast_csv_writer.write_record(&[ + episode.podcast_title.to_string(), + episode.podcast_publisher.to_string(), + episode.podcast_genre.to_string(), + episode.podcast_subtitle.to_string(), + episode.podcast_description.to_string().replace("\n", ""), + episode.podcast_file_type.to_string() + ]).expect("Can't write row to podcast CSV file"); + } music_csv_writer .write_record(&[ - song.song_title.to_string(), - song.song_artist.to_string(), - song.song_album.to_string(), - song.song_year.to_string(), - song.file_size_friendly.to_string(), - song.song_duration_friendly.to_string(), - song.song_filename.to_string(), - song.song_genre.to_string(), - song.file_extension.to_string(), - song.bitrate_kbps.to_string(), - song.sample_rate_hz.to_string(), - song.file_size_bytes.to_string(), - song.song_duration_s.to_string(), - song.num_plays.to_string(), - itunesdb_helpers::decode_itunes_stars(song.song_rating_raw), - song.song_added_to_library_ts.to_string(), - song.song_added_to_library_epoch.to_string(), - song.song_composer.to_string(), - song.song_comment.to_string(), + "Song Title", + "Artist", + "Album", + "Year released", + "File size", + "Song Duration", + "Filename", + "Genre", + "File extension", + "Bitrate (kbps)", + "Sample Rate (Hz)", + "File size (bytes)", + "Song duration (seconds)", + "Play count", + "Rating", + "Added to library on (timestamp)", + "Added to library on (epoch)", + "Composer", + "Comment", ]) - .expect("Can't write row to CSV"); + .expect("Can't create CSV file headers for music file"); + + for song in songs_found.iter() { + // the duplicate `to_string()` calls are to avoid this error: + // cannot move out of `song.song_title` which is behind a shared reference + // move occurs because `song.song_title` has type `String`, which does not implement the `Copy` trait + + music_csv_writer + .write_record(&[ + song.song_title.to_string(), + song.song_artist.to_string(), + song.song_album.to_string(), + song.song_year.to_string(), + song.file_size_friendly.to_string(), + song.song_duration_friendly.to_string(), + song.song_filename.to_string(), + song.song_genre.to_string(), + song.file_extension.to_string(), + song.bitrate_kbps.to_string(), + song.sample_rate_hz.to_string(), + song.file_size_bytes.to_string(), + song.song_duration_s.to_string(), + song.num_plays.to_string(), + itunesdb_helpers::decode_itunes_stars(song.song_rating_raw), + song.song_added_to_library_ts.to_string(), + song.song_added_to_library_epoch.to_string(), + song.song_composer.to_string(), + song.song_comment.to_string(), + ]) + .expect("Can't write row to CSV"); + } } } \ No newline at end of file From 93f018e6804b56c726b54aa7dd6e8ce8fe808611 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 27 Nov 2024 00:44:37 -0500 Subject: [PATCH 06/11] standardized output to say if saving csv or json --- Cargo.lock | 7 ++ Cargo.toml | 6 + parser/src/main.rs | 2 +- parser/src/parsers/itunesdb_parser.rs | 160 +++++++++++++------------- src/main.rs | 3 + 5 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..390e4c1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "itunes-db-parser" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a94d292 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "itunes-db-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/parser/src/main.rs b/parser/src/main.rs index 3bfa85d..9ba8298 100644 --- a/parser/src/main.rs +++ b/parser/src/main.rs @@ -106,7 +106,7 @@ fn main() { photos_csv_writer, ); } else if itunesdb_file_type == "itunes" { - parsers::itunesdb_parser::parse_itunesdb_file(itunesdb_file_as_bytes, output_format); + parsers::itunesdb_parser::parse_itunesdb_file(itunesdb_file_as_bytes, output_format.to_string()); } else if itunesdb_file_type == "itprefs" { parsers::preferences_parser::parse_itunes_prefs_file(itunesdb_file_as_bytes); } else if itunesdb_file_type == "playcounts" { diff --git a/parser/src/parsers/itunesdb_parser.rs b/parser/src/parsers/itunesdb_parser.rs index c7d338e..c2ec62e 100644 --- a/parser/src/parsers/itunesdb_parser.rs +++ b/parser/src/parsers/itunesdb_parser.rs @@ -9,10 +9,7 @@ use crate::helpers::helpers; use crate::helpers::itunesdb_helpers; -pub fn parse_itunesdb_file(itunesdb_file_as_bytes: Vec, output_format: String) - - let mut music_csv_writer = helpers::init_csv_writer("music.csv"); - let mut podcast_csv_writer = helpers::init_csv_writer("podcasts.csv"); +pub fn parse_itunesdb_file(itunesdb_file_as_bytes: Vec, output_format: String) { let mut songs_found: Vec = Vec::new(); let mut podcasts_found: Vec = Vec::new(); @@ -844,110 +841,109 @@ pub fn parse_itunesdb_file(itunesdb_file_as_bytes: Vec, output_format: Strin // Add JSON output @joshkenney if output_format == "json" { + // Only create JSON output if !songs_found.is_empty() { let songs_json = serde_json::to_string_pretty(&songs_found) .expect("Error serializing songs to JSON"); - - let mut songs_json_file = File::create("songs.json") + let mut songs_json_file = File::create("music.json") .expect("Error creating songs JSON file"); - io::Write::write_all(&mut songs_json_file, songs_json.as_bytes()) .expect("Error writing songs JSON file"); - - println!("Created songs.json with {} songs", songs_found.len()); + println!("Created music.json with {} songs", songs_found.len()); } if !podcasts_found.is_empty() { let podcasts_json = serde_json::to_string_pretty(&podcasts_found) .expect("Error serializing podcasts to JSON"); - let mut podcasts_json_file = File::create("podcasts.json") .expect("Error creating podcasts JSON file"); - io::Write::write_all(&mut podcasts_json_file, podcasts_json.as_bytes()) .expect("Error writing podcasts JSON file"); - println!("Created podcasts.json with {} podcasts", podcasts_found.len()); } - } - - // default to CSV output - if output_format == "csv" { + // default to CSV output + } else { let mut music_csv_writer = helpers::init_csv_writer("music.csv"); let mut podcast_csv_writer = helpers::init_csv_writer("podcasts.csv"); - podcast_csv_writer.write_record(&[ - "Episode Title", - "Publisher", - "Genre", - "Subtitle", - "Description", - "File Type" - ]).expect("Error can't create CSV file headers for podcast file"); - - for episode in podcasts_found.iter() { + if !podcasts_found.is_empty() { podcast_csv_writer.write_record(&[ - episode.podcast_title.to_string(), - episode.podcast_publisher.to_string(), - episode.podcast_genre.to_string(), - episode.podcast_subtitle.to_string(), - episode.podcast_description.to_string().replace("\n", ""), - episode.podcast_file_type.to_string() - ]).expect("Can't write row to podcast CSV file"); - } - - music_csv_writer - .write_record(&[ - "Song Title", - "Artist", - "Album", - "Year released", - "File size", - "Song Duration", - "Filename", + "Episode Title", + "Publisher", "Genre", - "File extension", - "Bitrate (kbps)", - "Sample Rate (Hz)", - "File size (bytes)", - "Song duration (seconds)", - "Play count", - "Rating", - "Added to library on (timestamp)", - "Added to library on (epoch)", - "Composer", - "Comment", - ]) - .expect("Can't create CSV file headers for music file"); - - for song in songs_found.iter() { - // the duplicate `to_string()` calls are to avoid this error: - // cannot move out of `song.song_title` which is behind a shared reference - // move occurs because `song.song_title` has type `String`, which does not implement the `Copy` trait + "Subtitle", + "Description", + "File Type" + ]).expect("Error can't create CSV file headers for podcast file"); + + for episode in podcasts_found.iter() { + podcast_csv_writer.write_record(&[ + episode.podcast_title.to_string(), + episode.podcast_publisher.to_string(), + episode.podcast_genre.to_string(), + episode.podcast_subtitle.to_string(), + episode.podcast_description.to_string().replace("\n", ""), + episode.podcast_file_type.to_string() + ]).expect("Can't write row to podcast CSV file"); + } + println!("Created podcasts.csv with {} podcasts", podcasts_found.len()); + } + if !songs_found.is_empty() { music_csv_writer .write_record(&[ - song.song_title.to_string(), - song.song_artist.to_string(), - song.song_album.to_string(), - song.song_year.to_string(), - song.file_size_friendly.to_string(), - song.song_duration_friendly.to_string(), - song.song_filename.to_string(), - song.song_genre.to_string(), - song.file_extension.to_string(), - song.bitrate_kbps.to_string(), - song.sample_rate_hz.to_string(), - song.file_size_bytes.to_string(), - song.song_duration_s.to_string(), - song.num_plays.to_string(), - itunesdb_helpers::decode_itunes_stars(song.song_rating_raw), - song.song_added_to_library_ts.to_string(), - song.song_added_to_library_epoch.to_string(), - song.song_composer.to_string(), - song.song_comment.to_string(), + "Song Title", + "Artist", + "Album", + "Year released", + "File size", + "Song Duration", + "Filename", + "Genre", + "File extension", + "Bitrate (kbps)", + "Sample Rate (Hz)", + "File size (bytes)", + "Song duration (seconds)", + "Play count", + "Rating", + "Added to library on (timestamp)", + "Added to library on (epoch)", + "Composer", + "Comment", ]) - .expect("Can't write row to CSV"); + .expect("Can't create CSV file headers for music file"); + + for song in songs_found.iter() { + // the duplicate `to_string()` calls are to avoid this error: + // cannot move out of `song.song_title` which is behind a shared reference + // move occurs because `song.song_title` has type `String`, which does not implement the `Copy` trait + + music_csv_writer + .write_record(&[ + song.song_title.to_string(), + song.song_artist.to_string(), + song.song_album.to_string(), + song.song_year.to_string(), + song.file_size_friendly.to_string(), + song.song_duration_friendly.to_string(), + song.song_filename.to_string(), + song.song_genre.to_string(), + song.file_extension.to_string(), + song.bitrate_kbps.to_string(), + song.sample_rate_hz.to_string(), + song.file_size_bytes.to_string(), + song.song_duration_s.to_string(), + song.num_plays.to_string(), + itunesdb_helpers::decode_itunes_stars(song.song_rating_raw), + song.song_added_to_library_ts.to_string(), + song.song_added_to_library_epoch.to_string(), + song.song_composer.to_string(), + song.song_comment.to_string(), + ]) + .expect("Can't write row to CSV"); + } + println!("Created music.csv with {} songs", songs_found.len()); } } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 4bcf8042a406c4300e844be5d0ebbfc1770965e0 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 27 Nov 2024 11:44:13 -0500 Subject: [PATCH 07/11] responding to comments --- .gitignore | 2 -- Cargo.lock | 7 ------ Cargo.toml | 6 ----- README.md | 66 +++++++++++++++++++++++++----------------------------- 4 files changed, 30 insertions(+), 51 deletions(-) delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml diff --git a/.gitignore b/.gitignore index 25aecdb..9663363 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,6 @@ **/*.json **/.vscode **/.txt -repopack-output.txt - # Added by cargo diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 390e4c1..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,7 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "itunes-db-parser" -version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index a94d292..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "itunes-db-parser" -version = "0.1.0" -edition = "2021" - -[dependencies] diff --git a/README.md b/README.md index 4b49f69..a96cc2d 100644 --- a/README.md +++ b/README.md @@ -16,40 +16,39 @@ This repository contains a parser/extractor for iTunesDB files, written in Rust. The table below shows which iTunesDB files are supported. -| File type | Supported? | -|----------------------|------------------------------------------------------------------| -| Artwork DB | :negative_squared_cross_mark: Not yet supported | -| DeviceInfo | Partial - Can extract iPod name only | -| Equalizer Presets | :heavy_check_mark: Extracts both the iTunes and actual DSP values | -| On The Go Playlist | :negative_squared_cross_mark: Not yet supported | -| Photo Database | :heavy_check_mark: Can extract all associated metadata of images | -| PhotoFolderAlbums | Partial - only can detect the # of photo albums | -| PhotoFolderPrefs | No support planned. | -| PhotosFolderName | :negative_squared_cross_mark: Not yet supported | -| Play Counts | :heavy_check_mark: | -| Preferences | :heavy_check_mark: | -| WABContactsGroup | :negative_squared_cross_mark: Not yet supported | -| iEKInfo | :negative_squared_cross_mark: Not yet supported | -| iTunesDB | Partial - Songs and Podcasts only. No television or movies | -| iTunesPState | :negative_squared_cross_mark: Not yet supported | -| iTunesPlaylists | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#iTunesPlaylists) | -| iTunesPodcasts | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#iTunesPodcasts) | -| iTunesPrefs | :heavy_check_mark: | -| iTunesSD | :heavy_check_mark: | -| iTunesStats | :negative_squared_cross_mark: Not yet supported | +| File type | Supported? | +| -------------------- | --------------------------------------------------------------------------------------------- | +| Artwork DB | :negative_squared_cross_mark: Not yet supported | +| DeviceInfo | Partial - Can extract iPod name only | +| Equalizer Presets | :heavy_check_mark: Extracts both the iTunes and actual DSP values | +| On The Go Playlist | :negative_squared_cross_mark: Not yet supported | +| Photo Database | :heavy_check_mark: Can extract all associated metadata of images | +| PhotoFolderAlbums | Partial - only can detect the # of photo albums | +| PhotoFolderPrefs | No support planned. | +| PhotosFolderName | :negative_squared_cross_mark: Not yet supported | +| Play Counts | :heavy_check_mark: | +| Preferences | :heavy_check_mark: | +| WABContactsGroup | :negative_squared_cross_mark: Not yet supported | +| iEKInfo | :negative_squared_cross_mark: Not yet supported | +| iTunesDB | Partial - Songs and Podcasts only. No television or movies | +| iTunesPState | :negative_squared_cross_mark: Not yet supported | +| iTunesPlaylists | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#iTunesPlaylists) | +| iTunesPodcasts | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#iTunesPodcasts) | +| iTunesPrefs | :heavy_check_mark: | +| iTunesSD | :heavy_check_mark: | +| iTunesStats | :negative_squared_cross_mark: Not yet supported | | iTunesVideoPlaylists | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#iTunesVideoPlaylists) | -| winPrefs | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#winPrefs_File) | - +| winPrefs | :grey_question: [Not documented yet](http://www.ipodlinux.org/ITunesDB/#winPrefs_File) | In examining your iPod's file system you'll find other types of files besides the one listed above. These are: -* [`.plist` files](https://en.wikipedia.org/wiki/Property_list) +- [`.plist` files](https://en.wikipedia.org/wiki/Property_list) -* [`.tone` files](https://github.com/raleighlittles/iPod_tone_file_player) +- [`.tone` files](https://github.com/raleighlittles/iPod_tone_file_player) -* `alarms` files: https://github.com/raleighlittles/iTunesDB-Parser/issues/6 +- `alarms` files: https://github.com/raleighlittles/iTunesDB-Parser/issues/6 -* `.itdb` files, which are just SQLite 3 databases with a different extension. +- `.itdb` files, which are just SQLite 3 databases with a different extension. Because iPodLinux isn't maintained anymore, I was worried that the website (and thus the iTunesDB wiki) would be taken down at some point, so I downloaded the documentation. @@ -61,15 +60,14 @@ The parser is written in Rust. You can build it by running `cargo build`. It requires two arguments: -(1) the iTunesDB filename, +(1) the iTunesDB filename, (2) the type of iTunesDB file. The 7 possible "type" options are: - | Field | Value | -|---------------|------------------------------------------| +| ------------- | ---------------------------------------- | | "photo" | Photos Database files, no iThmb files | | "itunes" | iTunes DB (music and podcasts only) file | | "itprefs" | iTunes Preferences file | @@ -80,11 +78,8 @@ The 7 possible "type" options are: | "equalizer" | Equalizer Presets file | | "itunessd" | iTunesSD file | - ```bash $ cd iTunesDB-Parser -$ cargo init -$ cargo build $ ./parser/target/debug/itunesdb_parser '' ``` @@ -104,15 +99,14 @@ Equalizer settings CSV: ![Equalizer settings CSV screenshot](./docs/20241126_equ This project is a very early work-in-progress. The next major feature to come is [iThumb file decoding](https://github.com/raleighlittles/iTunesDB-Parser/issues/4) - # Interested in contributing? If you have any iTunesDB files from the unsupported list and are willing to share, please contact me. Special thanks to: -* @AZProductions -* @joshkenney +- @AZProductions +- @joshkenney for providing iTunesDB files! From 10bf8e214939aa0d1469d0a803dee6be8172dc68 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 27 Nov 2024 16:01:34 -0500 Subject: [PATCH 08/11] removed src/main.rs --- src/main.rs | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/main.rs diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} From 4cddf46c14718460da6569f9b30a51bc35a54474 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 27 Nov 2024 16:03:21 -0500 Subject: [PATCH 09/11] updated README to have the proper sequence for build and running the target --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4ecffd6..6bf2cee 100644 --- a/README.md +++ b/README.md @@ -79,8 +79,9 @@ The 7 possible "type" options are: | "itunessd" | iTunesSD file | ```bash -$ cd iTunesDB-Parser -$ ./parser/target/debug/itunesdb_parser '' +$ cd iTunesDB-Parser/parser +$ cargo build +$ ./target/debug/itunesdb_parser '' ``` For iTunesDB, Photos Database, Equalizer files, and Playcounts files, a CSV will be generated with all the relevant information. For example, if you run it on an iTunesDB file, the output CSV will contain the info for all songs and podcasts mentioned in the iTunesDB file. @@ -101,9 +102,9 @@ There's 2 extra utilities that may be of use: Spotify logo -* Spotify integration: This creates a Spotify playlist out of the songs that were found on your iPod. See the README in that directory for more. +- Spotify integration: This creates a Spotify playlist out of the songs that were found on your iPod. See the README in that directory for more. -* Song renaming functionality: iPods (generally ?) store the song files on their hard drive, however, the filenames are usually just a generic unique ID. I wrote a Python script that lets you rename the songs to have the song title and artist name instead, using the data that is in the iTunesDB file. See the README in that directory for more information. +- Song renaming functionality: iPods (generally ?) store the song files on their hard drive, however, the filenames are usually just a generic unique ID. I wrote a Python script that lets you rename the songs to have the song title and artist name instead, using the data that is in the iTunesDB file. See the README in that directory for more information. # Future roadmap From 558efb2dcd3709e5f8e696bf6a8c891ab06794d2 Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 11 Dec 2024 05:21:09 -0500 Subject: [PATCH 10/11] improved create_playlist_from_tracks() to batch 100 songs this respects the limit set by Spotify API andallows for the sumbissions of playlists greater than 100 songs --- spotify_integration/spotify_api_credentials.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spotify_integration/spotify_api_credentials.json b/spotify_integration/spotify_api_credentials.json index c189604..3c64c79 100644 --- a/spotify_integration/spotify_api_credentials.json +++ b/spotify_integration/spotify_api_credentials.json @@ -1,5 +1,5 @@ { - "client_id": "HdtSF4bZRaBBSTISX1oJq0ewYkspqsdB", - "client_secret": "ehZjXinzzmzxPjQ75E7YkvF57uYJstBU", - "redirect_uri": "your_redirect_uri" -} \ No newline at end of file + "client_id": "ce4e96687c094c78a15276da7ac62585", + "client_secret": "81bd359e88d14fe49f57618796616055", + "redirect_uri": "http://localhost:3001" +} From 5d33e72f07dbaa34424797ed54812a2acae600fa Mon Sep 17 00:00:00 2001 From: joshkenney Date: Wed, 11 Dec 2024 05:38:12 -0500 Subject: [PATCH 11/11] improved create_playlist_from_tracks() to batch 100 songs this respects the limit set by Spotify API andallows for the sumbissions of playlists greater than 100 songs --- .../spotify_api_credentials.json | 5 ----- spotify_integration/spotify_integration.py | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 8 deletions(-) delete mode 100644 spotify_integration/spotify_api_credentials.json diff --git a/spotify_integration/spotify_api_credentials.json b/spotify_integration/spotify_api_credentials.json deleted file mode 100644 index 3c64c79..0000000 --- a/spotify_integration/spotify_api_credentials.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "client_id": "ce4e96687c094c78a15276da7ac62585", - "client_secret": "81bd359e88d14fe49f57618796616055", - "redirect_uri": "http://localhost:3001" -} diff --git a/spotify_integration/spotify_integration.py b/spotify_integration/spotify_integration.py index 4063419..b885417 100644 --- a/spotify_integration/spotify_integration.py +++ b/spotify_integration/spotify_integration.py @@ -50,15 +50,26 @@ def get_track_ids_from_csv(csv_file, spotify_api_obj) -> list: def create_playlist_from_tracks(spotify_tracks: list, playlist_name: str, playlist_description: str, spotify_api_obj) -> str: - - sp_obj = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=spotify_api_obj["client_id"], client_secret=spotify_api_obj["client_secret"], redirect_uri=spotify_api_obj["redirect_uri"], scope=DEFAULT_SPOTIFY_API_SCOPE)) + from spotipy import SpotifyOAuth + sp_obj = spotipy.Spotify(auth_manager=SpotifyOAuth( + client_id=spotify_api_obj["client_id"], + client_secret=spotify_api_obj["client_secret"], + redirect_uri=spotify_api_obj["redirect_uri"], + scope=DEFAULT_SPOTIFY_API_SCOPE + )) user_id = sp_obj.current_user()["id"] playlist_id = sp_obj.user_playlist_create( user_id, playlist_name, public=False, description=playlist_description)["id"] + # Extract track IDs track_ids = [track["track_id"] for track in spotify_tracks] - sp_obj.playlist_add_items(playlist_id, track_ids) + + # Spotify allows adding up to 100 tracks per request + batch_size = 100 + for i in range(0, len(track_ids), batch_size): + batch = track_ids[i:i + batch_size] + sp_obj.playlist_add_items(playlist_id, batch) return playlist_id