improvement(indexing): cover extraction performance gains

This commit is contained in:
2024-11-28 03:42:36 +01:00
parent 05b2e11ab4
commit d7b226025e
2 changed files with 26 additions and 32 deletions

View File

@@ -1,8 +1,8 @@
use std::{fs, path::Path, time::Instant}; use std::{fs, path::Path};
use image::EncodableLayout;
use webp::Encoder;
use axum::Router; use axum::Router;
use tower_http::services::ServeDir; use tower_http::services::ServeDir;
use webp::Encoder;
use crate::music::metadata::CoverData; use crate::music::metadata::CoverData;
@@ -40,9 +40,7 @@ pub fn get_all_cover_hashes() -> Vec<String> {
hashes hashes
} }
pub fn write_cover(hash: String, cover: CoverData) -> Result<(), Box<dyn std::error::Error>> { pub fn write_cover(hash: &str, cover: &CoverData, base_path: &str) -> Result<(), Box<dyn std::error::Error>> {
let now = Instant::now();
if cover.mime_type != "image/jpeg" if cover.mime_type != "image/jpeg"
&& cover.mime_type != "image/png" && cover.mime_type != "image/png"
&& cover.mime_type != "image/webp" && cover.mime_type != "image/webp"
@@ -50,8 +48,6 @@ pub fn write_cover(hash: String, cover: CoverData) -> Result<(), Box<dyn std::er
return Err(format!("Invalid cover MIME type: {}", cover.mime_type).into()); return Err(format!("Invalid cover MIME type: {}", cover.mime_type).into());
} }
let base_path = get_cover_base_path();
let path = Path::new(&base_path).join(format!("{hash}.webp")); let path = Path::new(&base_path).join(format!("{hash}.webp"));
let dynamic_image = image::load_from_memory_with_format( let dynamic_image = image::load_from_memory_with_format(
@@ -65,19 +61,16 @@ pub fn write_cover(hash: String, cover: CoverData) -> Result<(), Box<dyn std::er
)?; )?;
let resized_image = if dynamic_image.width() > 640 || dynamic_image.height() > 640 { let resized_image = if dynamic_image.width() > 640 || dynamic_image.height() > 640 {
dynamic_image.resize_to_fill(640, 640, image::imageops::FilterType::Lanczos3) let a = dynamic_image.resize_to_fill(640, 640, image::imageops::FilterType::Lanczos3);
drop(dynamic_image);
a
} else { } else {
dynamic_image dynamic_image
}; };
let webp_encoder = Encoder::from_image(&resized_image)?; let webp = Encoder::from_image(&resized_image)?.encode_lossless();
let encoded_image = webp_encoder.encode_lossless().to_vec();
fs::write(path, encoded_image)?; fs::write(path, webp.as_bytes())?;
let elapsed = now.elapsed();
println!("Writing '{}' cover took {:.?}", hash, elapsed);
Ok(()) Ok(())
} }

View File

@@ -1,6 +1,7 @@
use std::{ use std::{
collections::HashMap, collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc,
thread::JoinHandle, thread::JoinHandle,
}; };
@@ -9,7 +10,7 @@ use r2d2_sqlite::SqliteConnectionManager;
use rusqlite::{params, Row}; use rusqlite::{params, Row};
use crate::{ use crate::{
covers::{get_all_cover_hashes, write_cover}, covers::{get_all_cover_hashes, get_cover_base_path, write_cover},
music::metadata::TrackMetadata, music::metadata::TrackMetadata,
proto, proto,
}; };
@@ -145,13 +146,25 @@ pub fn insert_tracks(
// BEGIN TRANSACTION // BEGIN TRANSACTION
let tx = connection.transaction()?; let tx = connection.transaction()?;
let mut cover_handles: Vec<JoinHandle<()>> = Vec::new();
{ {
let base_path = get_cover_base_path();
std::thread::scope(|s| {
for (hash, meta) in tracks.iter() {
if let Some(ref cover) = &meta.cover {
if !existing_covers.contains(&hash) {
s.spawn(|| {
let _ = write_cover(hash, cover, &base_path);
});
}
}
}
});
let mut statement = let mut statement =
tx.prepare("INSERT OR REPLACE INTO tracks (hash, library_path_id, name, artist_id, path, duration) VALUES (?1, ?2, ?3, ?4, ?5, ?6)")?; tx.prepare("INSERT OR REPLACE INTO tracks (hash, library_path_id, name, artist_id, path, duration) VALUES (?1, ?2, ?3, ?4, ?5, ?6)")?;
for (hash, meta) in tracks { for (hash, meta) in tracks.iter() {
statement.execute(params![ statement.execute(params![
&hash, &hash,
library_path_id, library_path_id,
@@ -160,23 +173,11 @@ pub fn insert_tracks(
meta.path, meta.path,
meta.total_seconds * 1000 meta.total_seconds * 1000
])?; ])?;
if let Some(cover) = meta.cover {
if !existing_covers.contains(&hash) {
cover_handles.push(std::thread::spawn(|| {
let _ = write_cover(hash, cover);
}));
}
}
} }
} }
// COMMIT // COMMIT
tx.commit()?; tx.commit()?;
for handle in cover_handles {
let _ = handle.join();
}
Ok(()) Ok(())
} }