From d7b226025e36cd23cb1639c3dbf17d9c527cb91d Mon Sep 17 00:00:00 2001 From: 409 Date: Thu, 28 Nov 2024 03:42:36 +0100 Subject: [PATCH] improvement(indexing): cover extraction performance gains --- src/covers.rs | 25 +++++++++---------------- src/database/tracks.rs | 33 +++++++++++++++++---------------- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/src/covers.rs b/src/covers.rs index abc0f98..7cd9ca4 100644 --- a/src/covers.rs +++ b/src/covers.rs @@ -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 tower_http::services::ServeDir; -use webp::Encoder; use crate::music::metadata::CoverData; @@ -40,9 +40,7 @@ pub fn get_all_cover_hashes() -> Vec { hashes } -pub fn write_cover(hash: String, cover: CoverData) -> Result<(), Box> { - let now = Instant::now(); - +pub fn write_cover(hash: &str, cover: &CoverData, base_path: &str) -> Result<(), Box> { if cover.mime_type != "image/jpeg" && cover.mime_type != "image/png" && cover.mime_type != "image/webp" @@ -50,8 +48,6 @@ pub fn write_cover(hash: String, cover: CoverData) -> Result<(), Box Result<(), Box 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 { dynamic_image }; - let webp_encoder = Encoder::from_image(&resized_image)?; - let encoded_image = webp_encoder.encode_lossless().to_vec(); + let webp = Encoder::from_image(&resized_image)?.encode_lossless(); - fs::write(path, encoded_image)?; - - let elapsed = now.elapsed(); - - println!("Writing '{}' cover took {:.?}", hash, elapsed); + fs::write(path, webp.as_bytes())?; Ok(()) } diff --git a/src/database/tracks.rs b/src/database/tracks.rs index 23b7bc1..6b726b5 100644 --- a/src/database/tracks.rs +++ b/src/database/tracks.rs @@ -1,6 +1,7 @@ use std::{ collections::HashMap, path::{Path, PathBuf}, + sync::Arc, thread::JoinHandle, }; @@ -9,7 +10,7 @@ use r2d2_sqlite::SqliteConnectionManager; use rusqlite::{params, Row}; use crate::{ - covers::{get_all_cover_hashes, write_cover}, + covers::{get_all_cover_hashes, get_cover_base_path, write_cover}, music::metadata::TrackMetadata, proto, }; @@ -145,13 +146,25 @@ pub fn insert_tracks( // BEGIN TRANSACTION let tx = connection.transaction()?; - let mut cover_handles: Vec> = 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 = 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![ &hash, library_path_id, @@ -160,23 +173,11 @@ pub fn insert_tracks( meta.path, 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 tx.commit()?; - for handle in cover_handles { - let _ = handle.join(); - } - Ok(()) }