feat(onboarding): guided first-run tour with background FFmpeg provisioning
Phase 5 of the 0.1.0 release prep: - FFmpeg no longer blocks startup (or crashes the app offline): provisioning runs in a background thread emitting throttled ffmpeg-progress events, with installed/downloading/failed state queryable via get_ffmpeg_status and a retry command - video jobs are invisible to claiming and tier-priority checks until FFmpeg is ready, so they wait as pending without failing — and without starving image embeddings/tagging (include_videos gating in db.rs) - 7-step show-don't-tell onboarding wizard: welcome + live FFmpeg progress, real first-folder picker, faked animating pipeline bar, placeholder gallery tiles, cycling search-syntax demo, views overview, and an AI features step with a real opt-in tagger download - skippable at every point (Escape included), persisted via settings/onboarding_completed.txt, re-runnable from Settings > General, which also gains an FFmpeg status/retry row
This commit is contained in:
+28
-2
@@ -1132,6 +1132,7 @@ pub fn get_all_folder_job_progress(conn: &Connection) -> Result<Vec<FolderJobPro
|
||||
fn get_pending_thumbnail_jobs_excluding(
|
||||
conn: &Connection,
|
||||
excluded_folder_ids: &std::collections::HashSet<i64>,
|
||||
include_videos: bool,
|
||||
limit: usize,
|
||||
) -> Result<Vec<ThumbnailJob>> {
|
||||
let sql = format!(
|
||||
@@ -1140,8 +1141,10 @@ fn get_pending_thumbnail_jobs_excluding(
|
||||
JOIN images i ON i.id = j.image_id
|
||||
WHERE j.status = 'pending'
|
||||
{}
|
||||
{}
|
||||
ORDER BY j.updated_at, j.image_id
|
||||
LIMIT ?1",
|
||||
media_kind_clause(include_videos),
|
||||
folder_exclusion_clause("i", excluded_folder_ids)
|
||||
);
|
||||
let mut stmt = conn.prepare(&sql)?;
|
||||
@@ -1156,6 +1159,17 @@ fn get_pending_thumbnail_jobs_excluding(
|
||||
Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
|
||||
}
|
||||
|
||||
/// Video jobs need FFmpeg; while it isn't provisioned they must be invisible
|
||||
/// to both claiming and the tier-priority checks, or pending video jobs would
|
||||
/// stall every lower tier indefinitely.
|
||||
fn media_kind_clause(include_videos: bool) -> &'static str {
|
||||
if include_videos {
|
||||
""
|
||||
} else {
|
||||
"AND i.media_kind = 'image'"
|
||||
}
|
||||
}
|
||||
|
||||
/// True if any claimable (pending, non-excluded) jobs exist in `job_table`.
|
||||
/// Used by lower-priority workers to defer to higher-priority queues.
|
||||
/// `extra_predicate` narrows the image join (e.g. videos only for metadata).
|
||||
@@ -1184,8 +1198,14 @@ fn has_claimable_jobs(
|
||||
pub fn has_claimable_thumbnail_jobs(
|
||||
conn: &Connection,
|
||||
excluded_folder_ids: &std::collections::HashSet<i64>,
|
||||
include_videos: bool,
|
||||
) -> Result<bool> {
|
||||
has_claimable_jobs(conn, "thumbnail_jobs", "", excluded_folder_ids)
|
||||
has_claimable_jobs(
|
||||
conn,
|
||||
"thumbnail_jobs",
|
||||
media_kind_clause(include_videos),
|
||||
excluded_folder_ids,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn has_claimable_metadata_jobs(
|
||||
@@ -1211,6 +1231,7 @@ pub fn claim_thumbnail_jobs(
|
||||
conn: &mut Connection,
|
||||
active_folder_ids: &std::collections::HashSet<i64>,
|
||||
paused_folder_ids: &std::collections::HashSet<i64>,
|
||||
include_videos: bool,
|
||||
fetch_limit: usize,
|
||||
claim_limit: usize,
|
||||
) -> Result<Vec<ThumbnailJob>> {
|
||||
@@ -1219,7 +1240,12 @@ pub fn claim_thumbnail_jobs(
|
||||
.union(paused_folder_ids)
|
||||
.copied()
|
||||
.collect::<std::collections::HashSet<_>>();
|
||||
let candidates = get_pending_thumbnail_jobs_excluding(&tx, &excluded_folder_ids, fetch_limit)?;
|
||||
let candidates = get_pending_thumbnail_jobs_excluding(
|
||||
&tx,
|
||||
&excluded_folder_ids,
|
||||
include_videos,
|
||||
fetch_limit,
|
||||
)?;
|
||||
let mut claimed = Vec::with_capacity(claim_limit);
|
||||
|
||||
for job in candidates {
|
||||
|
||||
Reference in New Issue
Block a user