mirror of
https://github.com/CDrummond/bliss-analyser.git
synced 2025-04-08 05:00:02 +03:00
Add option to write analysis results to files, and use for future scans.
Issue #4
This commit is contained in:
parent
bff4ba18b4
commit
6cc26c399e
@ -8,6 +8,7 @@
|
|||||||
4. Add ability to specify LMS JSONRPC port.
|
4. Add ability to specify LMS JSONRPC port.
|
||||||
5. If new files analysed and 'ignore' file exists then update DB's 'ignore'
|
5. If new files analysed and 'ignore' file exists then update DB's 'ignore'
|
||||||
flags.
|
flags.
|
||||||
|
6. Add option to write analysis results to files, and use for future scans.
|
||||||
|
|
||||||
0.2.3
|
0.2.3
|
||||||
-----
|
-----
|
||||||
|
@ -42,7 +42,7 @@ const MAX_ERRORS_TO_SHOW: usize = 100;
|
|||||||
const MAX_TAG_ERRORS_TO_SHOW: usize = 50;
|
const MAX_TAG_ERRORS_TO_SHOW: usize = 50;
|
||||||
const VALID_EXTENSIONS: [&str; 6] = ["m4a", "mp3", "ogg", "flac", "opus", "wv"];
|
const VALID_EXTENSIONS: [&str; 6] = ["m4a", "mp3", "ogg", "flac", "opus", "wv"];
|
||||||
|
|
||||||
fn get_file_list(db: &mut db::Db, mpath: &Path, path: &Path, track_paths: &mut Vec<String>, cue_tracks:&mut Vec<cue::CueTrack>, file_count:&mut usize, max_num_files: usize) {
|
fn get_file_list(db: &mut db::Db, mpath: &Path, path: &Path, track_paths: &mut Vec<String>, cue_tracks:&mut Vec<cue::CueTrack>, file_count:&mut usize, max_num_files: usize, use_tags: bool, tagged_file_count:&mut usize, dry_run: bool) {
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -51,21 +51,21 @@ fn get_file_list(db: &mut db::Db, mpath: &Path, path: &Path, track_paths: &mut V
|
|||||||
items.sort_by_key(|dir| dir.path());
|
items.sort_by_key(|dir| dir.path());
|
||||||
|
|
||||||
for item in items {
|
for item in items {
|
||||||
check_dir_entry(db, mpath, item, track_paths, cue_tracks, file_count, max_num_files);
|
check_dir_entry(db, mpath, item, track_paths, cue_tracks, file_count, max_num_files, use_tags, tagged_file_count, dry_run);
|
||||||
if max_num_files>0 && *file_count>=max_num_files {
|
if max_num_files>0 && *file_count>=max_num_files {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths: &mut Vec<String>, cue_tracks:&mut Vec<cue::CueTrack>, file_count:&mut usize, max_num_files: usize) {
|
fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths: &mut Vec<String>, cue_tracks:&mut Vec<cue::CueTrack>, file_count:&mut usize, max_num_files: usize, use_tags: bool, tagged_file_count:&mut usize, dry_run: bool) {
|
||||||
let pb = entry.path();
|
let pb = entry.path();
|
||||||
if pb.is_dir() {
|
if pb.is_dir() {
|
||||||
let check = pb.join(DONT_ANALYSE);
|
let check = pb.join(DONT_ANALYSE);
|
||||||
if check.exists() {
|
if check.exists() {
|
||||||
log::info!("Skipping '{}', found '{}'", pb.to_string_lossy(), DONT_ANALYSE);
|
log::info!("Skipping '{}', found '{}'", pb.to_string_lossy(), DONT_ANALYSE);
|
||||||
} else if max_num_files<=0 || *file_count<max_num_files {
|
} else if max_num_files<=0 || *file_count<max_num_files {
|
||||||
get_file_list(db, mpath, &pb, track_paths, cue_tracks, file_count, max_num_files);
|
get_file_list(db, mpath, &pb, track_paths, cue_tracks, file_count, max_num_files, use_tags, tagged_file_count, dry_run);
|
||||||
}
|
}
|
||||||
} else if pb.is_file() && (max_num_files<=0 || *file_count<max_num_files) {
|
} else if pb.is_file() && (max_num_files<=0 || *file_count<max_num_files) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
@ -106,8 +106,22 @@ fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths:
|
|||||||
} else {
|
} else {
|
||||||
if let Ok(id) = db.get_rowid(&sname) {
|
if let Ok(id) = db.get_rowid(&sname) {
|
||||||
if id<=0 {
|
if id<=0 {
|
||||||
track_paths.push(String::from(pb.to_string_lossy()));
|
let mut tags_used = false;
|
||||||
*file_count+=1;
|
if use_tags {
|
||||||
|
let meta = tags::read(&sname, true);
|
||||||
|
if !meta.is_empty() && !meta.analysis.is_none() {
|
||||||
|
if !dry_run {
|
||||||
|
db.add_track(&sname, &meta, &meta.analysis.unwrap());
|
||||||
|
}
|
||||||
|
*tagged_file_count+=1;
|
||||||
|
tags_used = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tags_used {
|
||||||
|
track_paths.push(String::from(pb.to_string_lossy()));
|
||||||
|
*file_count+=1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +130,7 @@ fn check_dir_entry(db: &mut db::Db, mpath: &Path, entry: DirEntry, track_paths:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_errors(failed: &mut Vec<String>, tag_error: &mut Vec<String>) {
|
fn show_errors(failed: &mut Vec<String>, tag_error: &mut Vec<String>) {
|
||||||
if !failed.is_empty() {
|
if !failed.is_empty() {
|
||||||
let total = failed.len();
|
let total = failed.len();
|
||||||
failed.truncate(MAX_ERRORS_TO_SHOW);
|
failed.truncate(MAX_ERRORS_TO_SHOW);
|
||||||
@ -144,7 +158,7 @@ pub fn show_errors(failed: &mut Vec<String>, tag_error: &mut Vec<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "libav")]
|
#[cfg(feature = "libav")]
|
||||||
pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize) -> Result<()> {
|
fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize, use_tags: bool) -> Result<()> {
|
||||||
let total = track_paths.len();
|
let total = track_paths.len();
|
||||||
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
||||||
ProgressStyle::default_bar()
|
ProgressStyle::default_bar()
|
||||||
@ -205,7 +219,7 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
|
|||||||
None => {
|
None => {
|
||||||
// Use lofty to read tags here, and not bliss's, so that if update
|
// Use lofty to read tags here, and not bliss's, so that if update
|
||||||
// tags is ever used they are from the same source.
|
// tags is ever used they are from the same source.
|
||||||
let mut meta = tags::read(&cpath);
|
let mut meta = tags::read(&cpath, false);
|
||||||
if meta.is_empty() {
|
if meta.is_empty() {
|
||||||
// Lofty failed? Try from bliss...
|
// Lofty failed? Try from bliss...
|
||||||
meta.title = track.title.unwrap_or_default().to_string();
|
meta.title = track.title.unwrap_or_default().to_string();
|
||||||
@ -218,10 +232,13 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
|
|||||||
if meta.is_empty() {
|
if meta.is_empty() {
|
||||||
tag_error.push(sname.clone());
|
tag_error.push(sname.clone());
|
||||||
}
|
}
|
||||||
|
if use_tags {
|
||||||
|
tags::write_analysis(&cpath, &track.analysis);
|
||||||
|
}
|
||||||
db.add_track(&sname, &meta, &track.analysis);
|
db.add_track(&sname, &meta, &track.analysis);
|
||||||
analysed += 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
analysed += 1;
|
||||||
}
|
}
|
||||||
Err(e) => { failed.push(format!("{} - {}", sname, e)); }
|
Err(e) => { failed.push(format!("{} - {}", sname, e)); }
|
||||||
};
|
};
|
||||||
@ -238,7 +255,7 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "libav"))]
|
#[cfg(not(feature = "libav"))]
|
||||||
pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize) -> Result<()> {
|
fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>, max_threads: usize, use_tags: bool) -> Result<()> {
|
||||||
let total = track_paths.len();
|
let total = track_paths.len();
|
||||||
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
||||||
ProgressStyle::default_bar()
|
ProgressStyle::default_bar()
|
||||||
@ -263,10 +280,13 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
|
|||||||
match result {
|
match result {
|
||||||
Ok(track) => {
|
Ok(track) => {
|
||||||
let cpath = String::from(path.to_string_lossy());
|
let cpath = String::from(path.to_string_lossy());
|
||||||
let meta = tags::read(&cpath);
|
let meta = tags::read(&cpath, false);
|
||||||
if meta.is_empty() {
|
if meta.is_empty() {
|
||||||
tag_error.push(sname.clone());
|
tag_error.push(sname.clone());
|
||||||
}
|
}
|
||||||
|
if use_tags {
|
||||||
|
tags::write_analysis(&cpath, &track.analysis);
|
||||||
|
}
|
||||||
db.add_track(&sname, &meta, &track.analysis);
|
db.add_track(&sname, &meta, &track.analysis);
|
||||||
analysed += 1;
|
analysed += 1;
|
||||||
}
|
}
|
||||||
@ -291,7 +311,7 @@ pub fn analyse_new_files(db: &db::Db, mpath: &PathBuf, track_paths: Vec<String>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "libav"))]
|
#[cfg(not(feature = "libav"))]
|
||||||
pub fn analyze_cue_streaming(tracks: Vec<cue::CueTrack>,) -> BlissResult<Receiver<(cue::CueTrack, BlissResult<Song>)>> {
|
fn analyze_cue_streaming(tracks: Vec<cue::CueTrack>,) -> BlissResult<Receiver<(cue::CueTrack, BlissResult<Song>)>> {
|
||||||
let num_cpus = num_cpus::get();
|
let num_cpus = num_cpus::get();
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
@ -331,7 +351,7 @@ pub fn analyze_cue_streaming(tracks: Vec<cue::CueTrack>,) -> BlissResult<Receive
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "libav"))]
|
#[cfg(not(feature = "libav"))]
|
||||||
pub fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::CueTrack>) -> Result<()> {
|
fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::CueTrack>) -> Result<()> {
|
||||||
let total = cue_tracks.len();
|
let total = cue_tracks.len();
|
||||||
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
let progress = ProgressBar::new(total.try_into().unwrap()).with_style(
|
||||||
ProgressStyle::default_bar()
|
ProgressStyle::default_bar()
|
||||||
@ -359,7 +379,8 @@ pub fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::C
|
|||||||
album_artist:track.album_artist,
|
album_artist:track.album_artist,
|
||||||
album:track.album,
|
album:track.album,
|
||||||
genre:track.genre,
|
genre:track.genre,
|
||||||
duration:if track.duration>=last_track_duration { song.duration.as_secs() as u32 } else { track.duration.as_secs() as u32 }
|
duration:if track.duration>=last_track_duration { song.duration.as_secs() as u32 } else { track.duration.as_secs() as u32 },
|
||||||
|
analysis: None
|
||||||
};
|
};
|
||||||
|
|
||||||
db.add_track(&sname, &meta, &song.analysis);
|
db.add_track(&sname, &meta, &song.analysis);
|
||||||
@ -377,7 +398,7 @@ pub fn analyse_new_cue_tracks(db:&db::Db, mpath: &PathBuf, cue_tracks:Vec<cue::C
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_old: bool, max_num_files: usize, max_threads: usize, ignore_path: &PathBuf) {
|
pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_old: bool, max_num_files: usize, max_threads: usize, ignore_path: &PathBuf, use_tags: bool) {
|
||||||
let mut db = db::Db::new(&String::from(db_path));
|
let mut db = db::Db::new(&String::from(db_path));
|
||||||
|
|
||||||
db.init();
|
db.init();
|
||||||
@ -393,18 +414,22 @@ pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_o
|
|||||||
let mut track_paths: Vec<String> = Vec::new();
|
let mut track_paths: Vec<String> = Vec::new();
|
||||||
let mut cue_tracks:Vec<cue::CueTrack> = Vec::new();
|
let mut cue_tracks:Vec<cue::CueTrack> = Vec::new();
|
||||||
let mut file_count:usize = 0;
|
let mut file_count:usize = 0;
|
||||||
|
let mut tagged_file_count:usize = 0;
|
||||||
|
|
||||||
if mpaths.len() > 1 {
|
if mpaths.len() > 1 {
|
||||||
log::info!("Looking for new files in {}", mpath.to_string_lossy());
|
log::info!("Looking for new files in {}", mpath.to_string_lossy());
|
||||||
} else {
|
} else {
|
||||||
log::info!("Looking for new files");
|
log::info!("Looking for new files");
|
||||||
}
|
}
|
||||||
get_file_list(&mut db, &mpath, &cur, &mut track_paths, &mut cue_tracks, &mut file_count, max_num_files);
|
get_file_list(&mut db, &mpath, &cur, &mut track_paths, &mut cue_tracks, &mut file_count, max_num_files, use_tags, &mut tagged_file_count, dry_run);
|
||||||
track_paths.sort();
|
track_paths.sort();
|
||||||
log::info!("Num new files: {}", track_paths.len());
|
log::info!("Num new files: {}", track_paths.len());
|
||||||
if !cue_tracks.is_empty() {
|
if !cue_tracks.is_empty() {
|
||||||
log::info!("Num new cue tracks: {}", cue_tracks.len());
|
log::info!("Num new cue tracks: {}", cue_tracks.len());
|
||||||
}
|
}
|
||||||
|
if use_tags {
|
||||||
|
log::info!("Num tagged files: {}", tagged_file_count);
|
||||||
|
}
|
||||||
|
|
||||||
if dry_run {
|
if dry_run {
|
||||||
if !track_paths.is_empty() || !cue_tracks.is_empty() {
|
if !track_paths.is_empty() || !cue_tracks.is_empty() {
|
||||||
@ -418,7 +443,7 @@ pub fn analyse_files(db_path: &str, mpaths: &Vec<PathBuf>, dry_run: bool, keep_o
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !track_paths.is_empty() {
|
if !track_paths.is_empty() {
|
||||||
match analyse_new_files(&db, &mpath, track_paths, max_threads) {
|
match analyse_new_files(&db, &mpath, track_paths, max_threads, use_tags) {
|
||||||
Ok(_) => { changes_made = true; }
|
Ok(_) => { changes_made = true; }
|
||||||
Err(e) => { log::error!("Analysis returned error: {}", e); }
|
Err(e) => { log::error!("Analysis returned error: {}", e); }
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ pub struct Metadata {
|
|||||||
pub album: String,
|
pub album: String,
|
||||||
pub genre: String,
|
pub genre: String,
|
||||||
pub duration: u32,
|
pub duration: u32,
|
||||||
|
pub analysis: Option<Analysis>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
impl Metadata {
|
||||||
@ -275,6 +276,7 @@ impl Db {
|
|||||||
album: dbtags.album.unwrap_or_default(),
|
album: dbtags.album.unwrap_or_default(),
|
||||||
genre: dbtags.genre.unwrap_or_default(),
|
genre: dbtags.genre.unwrap_or_default(),
|
||||||
duration: dbtags.duration,
|
duration: dbtags.duration,
|
||||||
|
analysis: None,
|
||||||
};
|
};
|
||||||
progress.set_message(format!("{}", dbtags.file));
|
progress.set_message(format!("{}", dbtags.file));
|
||||||
|
|
||||||
@ -282,7 +284,7 @@ impl Db {
|
|||||||
let track_path = mpath.join(&dbtags.file);
|
let track_path = mpath.join(&dbtags.file);
|
||||||
if track_path.exists() {
|
if track_path.exists() {
|
||||||
let path = String::from(track_path.to_string_lossy());
|
let path = String::from(track_path.to_string_lossy());
|
||||||
let ftags = tags::read(&path);
|
let ftags = tags::read(&path, false);
|
||||||
if ftags.is_empty() {
|
if ftags.is_empty() {
|
||||||
log::error!("Failed to read tags of '{}'", dbtags.file);
|
log::error!("Failed to read tags of '{}'", dbtags.file);
|
||||||
} else if ftags != dtags {
|
} else if ftags != dtags {
|
||||||
|
@ -41,6 +41,7 @@ fn main() {
|
|||||||
let mut max_num_files: usize = 0;
|
let mut max_num_files: usize = 0;
|
||||||
let mut music_paths: Vec<PathBuf> = Vec::new();
|
let mut music_paths: Vec<PathBuf> = Vec::new();
|
||||||
let mut max_threads: usize = 0;
|
let mut max_threads: usize = 0;
|
||||||
|
let mut use_tags = false;
|
||||||
|
|
||||||
match dirs::home_dir() {
|
match dirs::home_dir() {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
@ -74,6 +75,7 @@ fn main() {
|
|||||||
arg_parse.refer(&mut lms_json_port).add_option(&["-J", "--json"], Store, &lms_json_port_help);
|
arg_parse.refer(&mut lms_json_port).add_option(&["-J", "--json"], Store, &lms_json_port_help);
|
||||||
arg_parse.refer(&mut max_num_files).add_option(&["-n", "--numfiles"], Store, "Maximum number of files to analyse");
|
arg_parse.refer(&mut max_num_files).add_option(&["-n", "--numfiles"], Store, "Maximum number of files to analyse");
|
||||||
arg_parse.refer(&mut max_threads).add_option(&["-t", "--threads"], Store, "Maximum number of threads to use for analysis");
|
arg_parse.refer(&mut max_threads).add_option(&["-t", "--threads"], Store, "Maximum number of threads to use for analysis");
|
||||||
|
arg_parse.refer(&mut use_tags).add_option(&["-T", "--tags"], StoreTrue, "Read/write analysis results from/to source fles");
|
||||||
arg_parse.refer(&mut task).add_argument("task", Store, "Task to perform; analyse, tags, ignore, upload, stopmixer.");
|
arg_parse.refer(&mut task).add_argument("task", Store, "Task to perform; analyse, tags, ignore, upload, stopmixer.");
|
||||||
arg_parse.parse_args_or_exit();
|
arg_parse.parse_args_or_exit();
|
||||||
}
|
}
|
||||||
@ -200,7 +202,7 @@ fn main() {
|
|||||||
analyse::update_ignore(&db_path, &ignore_path);
|
analyse::update_ignore(&db_path, &ignore_path);
|
||||||
} else {
|
} else {
|
||||||
let ignore_path = PathBuf::from(&ignore_file);
|
let ignore_path = PathBuf::from(&ignore_file);
|
||||||
analyse::analyse_files(&db_path, &music_paths, dry_run, keep_old, max_num_files, max_threads, &ignore_path);
|
analyse::analyse_files(&db_path, &music_paths, dry_run, keep_old, max_num_files, max_threads, &ignore_path, use_tags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
76
src/tags.rs
76
src/tags.rs
@ -7,14 +7,44 @@
|
|||||||
**/
|
**/
|
||||||
|
|
||||||
use crate::db;
|
use crate::db;
|
||||||
use lofty::{Accessor, AudioFile, ItemKey, TaggedFileExt};
|
use lofty::{Accessor, AudioFile, ItemKey, Tag, TagExt, TaggedFileExt};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use substring::Substring;
|
use substring::Substring;
|
||||||
|
use bliss_audio::{Analysis, AnalysisIndex};
|
||||||
|
|
||||||
const MAX_GENRE_VAL: usize = 192;
|
const MAX_GENRE_VAL: usize = 192;
|
||||||
|
const NUM_ANALYSIS_VALS: usize = 20;
|
||||||
|
const ANALYSIS_TAG:ItemKey = ItemKey::Script;
|
||||||
|
const ANALYSIS_TAG_START: &str = "BLISS_ANALYSIS";
|
||||||
|
const ANALYSIS_TAG_VER: u16 = 1;
|
||||||
|
|
||||||
pub fn read(track: &String) -> db::Metadata {
|
pub fn write_analysis(track: &String, analysis: &Analysis) {
|
||||||
|
let value = format!("{},{},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24},{:.24}", ANALYSIS_TAG_START, ANALYSIS_TAG_VER,
|
||||||
|
analysis[AnalysisIndex::Tempo], analysis[AnalysisIndex::Zcr], analysis[AnalysisIndex::MeanSpectralCentroid], analysis[AnalysisIndex::StdDeviationSpectralCentroid], analysis[AnalysisIndex::MeanSpectralRolloff],
|
||||||
|
analysis[AnalysisIndex::StdDeviationSpectralRolloff], analysis[AnalysisIndex::MeanSpectralFlatness], analysis[AnalysisIndex::StdDeviationSpectralFlatness], analysis[AnalysisIndex::MeanLoudness], analysis[AnalysisIndex::StdDeviationLoudness],
|
||||||
|
analysis[AnalysisIndex::Chroma1], analysis[AnalysisIndex::Chroma2], analysis[AnalysisIndex::Chroma3], analysis[AnalysisIndex::Chroma4], analysis[AnalysisIndex::Chroma5],
|
||||||
|
analysis[AnalysisIndex::Chroma6], analysis[AnalysisIndex::Chroma7], analysis[AnalysisIndex::Chroma8], analysis[AnalysisIndex::Chroma9], analysis[AnalysisIndex::Chroma10]);
|
||||||
|
|
||||||
|
if let Ok(mut file) = lofty::read_from_path(Path::new(track)) {
|
||||||
|
let tag = match file.primary_tag_mut() {
|
||||||
|
Some(primary_tag) => primary_tag,
|
||||||
|
None => {
|
||||||
|
if let Some(first_tag) = file.first_tag_mut() {
|
||||||
|
first_tag
|
||||||
|
} else {
|
||||||
|
let tag_type = file.primary_tag_type();
|
||||||
|
file.insert_tag(Tag::new(tag_type));
|
||||||
|
file.primary_tag_mut().unwrap()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
tag.insert_text(ANALYSIS_TAG, value);
|
||||||
|
let _ = tag.save_to_path(Path::new(track));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(track: &String, read_analysis: bool) -> db::Metadata {
|
||||||
let mut meta = db::Metadata {
|
let mut meta = db::Metadata {
|
||||||
duration: 180,
|
duration: 180,
|
||||||
..db::Metadata::default()
|
..db::Metadata::default()
|
||||||
@ -71,6 +101,48 @@ pub fn read(track: &String) -> db::Metadata {
|
|||||||
}
|
}
|
||||||
|
|
||||||
meta.duration = file.properties().duration().as_secs() as u32;
|
meta.duration = file.properties().duration().as_secs() as u32;
|
||||||
|
|
||||||
|
if read_analysis {
|
||||||
|
let analysis_string = tag.get_string(&ANALYSIS_TAG).unwrap_or("");
|
||||||
|
if analysis_string.len()>(ANALYSIS_TAG_START.len()+(NUM_ANALYSIS_VALS*8)) {
|
||||||
|
let parts = analysis_string.split(",");
|
||||||
|
let mut index = 0;
|
||||||
|
let mut vals = [0.; NUM_ANALYSIS_VALS];
|
||||||
|
for part in parts {
|
||||||
|
if 0==index {
|
||||||
|
if part!=ANALYSIS_TAG_START {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if 1==index {
|
||||||
|
match part.parse::<u16>() {
|
||||||
|
Ok(ver) => {
|
||||||
|
if ver!=ANALYSIS_TAG_VER {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (index - 2) < NUM_ANALYSIS_VALS {
|
||||||
|
match part.parse::<f32>() {
|
||||||
|
Ok(val) => {
|
||||||
|
vals[index - 2] = val;
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
if index == (NUM_ANALYSIS_VALS+2) {
|
||||||
|
meta.analysis = Some(Analysis::new(vals));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
meta
|
meta
|
||||||
|
Loading…
x
Reference in New Issue
Block a user