시작 시 메타 백필 + 백그라운드 처리 진행 표시(상태바)
- 메타 백필: 이전 스캔이 중단돼 meta_state=0으로 남은 파일을 시작 시 (전체 재스캔 없이) 저우선순위로 이어서 분석. 썸네일 백필(enqueue_pending)과 짝. - pending_counts 커맨드 + 상태바 표시: "썸네일 생성 중 N / 메타 분석 중 M". thumbs-ready·library-changed·시작 시 트리거하고, 남은 작업이 있으면 2초 폴링, 0이 되면 자동으로 숨김. 검증: 영상 200개 스캔 중 상태바가 200→0으로 감소 후 사라짐. 메타 도중 강제 종료 후 재실행 시 재스캔 없이(scanning=false) 메타/썸네일 백필만 재개됨. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,36 @@ pub struct SourceDto {
|
||||
pub file_count: i64,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PendingCounts {
|
||||
/// 썸네일 생성 대기(thumb_state=0) 이미지/영상 수
|
||||
pub thumbs: i64,
|
||||
/// 메타데이터 분석 대기(meta_state=0) 수
|
||||
pub meta: i64,
|
||||
}
|
||||
|
||||
/// 백그라운드 처리 진행 표시용 — 대기 중인 썸네일/메타 개수.
|
||||
#[tauri::command]
|
||||
pub fn pending_counts(state: tauri::State<'_, AppState>) -> CmdResult<PendingCounts> {
|
||||
state
|
||||
.db
|
||||
.with_read(|conn| {
|
||||
let thumbs: i64 = conn.query_row(
|
||||
"SELECT count(*) FROM files WHERE thumb_state=0 AND kind IN (0,1) AND deleted_at IS NULL",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)?;
|
||||
let meta: i64 = conn.query_row(
|
||||
"SELECT count(*) FROM files WHERE meta_state=0 AND deleted_at IS NULL",
|
||||
[],
|
||||
|r| r.get(0),
|
||||
)?;
|
||||
Ok(PendingCounts { thumbs, meta })
|
||||
})
|
||||
.map_err(err_str)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn list_sources(state: tauri::State<'_, AppState>) -> CmdResult<Vec<SourceDto>> {
|
||||
state
|
||||
|
||||
@@ -218,6 +218,34 @@ fn main() {
|
||||
.ok();
|
||||
}
|
||||
|
||||
// 시작 시 메타데이터 백필 — 이전 스캔이 중단돼 meta_state=0으로 남은 파일을
|
||||
// (전체 재스캔 없이) 이어서 분석한다. 썸네일 백필(enqueue_pending)과 짝. 저우선순위.
|
||||
{
|
||||
let state = app.state::<AppState>();
|
||||
let db = state.db.clone();
|
||||
let tools = state.tools.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("meta-backfill".into())
|
||||
.spawn(move || {
|
||||
archive_indexer::prio::set_current_thread_low();
|
||||
let cancel = std::sync::atomic::AtomicBool::new(false);
|
||||
let sources: Vec<i64> = db
|
||||
.with_read(|c| {
|
||||
let mut s = c.prepare("SELECT id FROM sources WHERE kind='local'")?;
|
||||
let rows = s.query_map([], |r| r.get(0))?;
|
||||
rows.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
for sid in sources {
|
||||
let _ = archive_indexer::meta::extract_image_meta(&db, sid, &cancel);
|
||||
if let Some(t) = &tools {
|
||||
let _ = archive_indexer::meta::extract_video_meta(&db, sid, &cancel, t);
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
// dev 편의: ARCHIVE_AUTO_SOURCE=<폴더> → 시작 시 소스 등록 + 스캔
|
||||
#[cfg(debug_assertions)]
|
||||
if let Ok(auto) = std::env::var("ARCHIVE_AUTO_SOURCE") {
|
||||
@@ -258,6 +286,7 @@ fn main() {
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::app_info,
|
||||
commands::pending_counts,
|
||||
commands::list_sources,
|
||||
commands::add_local_source,
|
||||
commands::remove_source,
|
||||
|
||||
Reference in New Issue
Block a user