v0.2.1
Download

Rust Core

Downloads, disk scans, ffmpeg jobs, and SponsorBlock fetches run in Rust. The React layer invokes and renders.

Command modules under src-tauri/src/commands/ own IO-heavy work. downloader.rs spawns yt-dlp children, parses stdout into download-progress events, and cleans up on pause or cancel. gallery.rs walks configured folders, merges playlist stacks, dedupes by yt-dlp id, and normalizes chapter arrays from sidecars.

media.rs runs ffmpeg/ffprobe sidecars with per-video async locks (with_per_video_ffmpeg_lock). That fixed a past deadlock where nested locks on the same file blocked sprite generation during playback. Auto scrub previews enqueue sprite sheets when a video download finishes (unless audio-only or the setting is off).

explorer_embed.rs positions the embedded browser: in-window child on Windows, parented surface on Linux dev builds. Bounds sync coalesces through explorerBoundsSync.ts during sidebar animation so IPC does not fire on every frame.

Windows-only niceties live here too: windows_audio_brand.rs renames WebView2 audio sessions in the volume mixer to "RuForge" with the app icon. Linux dev builds get broader asset scopes and platform path hydration from Tauri APIs.

In the repo

    #[cfg(windows)]
    {
        let app_id =
            windows_audio_brand::process_app_user_model_id(&context.config().identifier);
        windows_audio_brand::set_explicit_app_user_model_id(&app_id);
    }

    let identifier = context.config().identifier.clone();
    // WebView localStorage is unavailable at this point; read the on-disk mirror of showDebuggingSettings so Aptabase registers only when the dev gate was enabled at last quit.
    let aptabase_dev_gate = DevGateDisk::load(&identifier).show_debugging_settings;

    let mut builder = tauri::Builder::default()
        .manage(AppConfig {
            minimize_to_tray: Mutex::new(true),
        })
        .manage(DownloadJobManager::default())
        .manage(ExportBundleState::default())
        .manage(RemovableDrivesState::default())
        .manage(crate::companion::CompanionState::new())
        .plugin(tauri_plugin_process::init())
        .plugin(tauri_plugin_updater::Builder::new().build())
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_store::Builder::default().build())
        .plugin(tauri_plugin_autostart::init(tauri_plugin_autostart::MacosLauncher::LaunchAgent, Some(vec!["--silently"])))
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_opener::init())
        .plugin(tauri_plugin_clipboard_manager::init())
        .plugin(
            tauri_plugin_snap_layout::init()
                .button_id("ruforge-tb-maximize")
                .build(),
        )
        .append_invoke_initialization_script(
            r";(function(){try{var h=(window.location.hostname||'').toLowerCase();if(h==='tauri.localhost'||h==='localhost'||h==='127.0.0.1'||h.endsWith('.tauri.localhost'))return;if((window.location.protocol||'')==='tauri:')return;window.__snapLayoutInit=true;}catch(e){}})();",
        )
        .into_iter()
        .enumerate()
        .filter_map(|(i, entry)| {
            if remove_indices.contains(&i) {
                return None;
            }
            match entry {
                GalleryEntry::Playlist { mut playlist } => {
                    playlist.items = dedupe_media_files(playlist.items);
                    playlist.item_count = playlist.items.len() as u32;
                    Some(GalleryEntry::Playlist { playlist })
                }
                other => Some(other),
            }
        })
        .collect()
}

/// Sidecars and RuForge thumb dir for a media path (not the primary video file).
pub(crate) fn remove_media_sidecar_artifacts(media_path: &Path) -> Vec<String> {
    let mut warnings = Vec::new();
    for path in crate::media_bundle::collect_deletion_paths(media_path) {
        if path == media_path {
            continue;
        }
        if !path.exists() {
            continue;
        }
        if path.is_dir() {

async fn ensure_sprite_sheet(
    app: &AppHandle,
    slot: &Arc<FfmpegVideoSlot>,
    sprite_path: &Path,
) -> Result<(), String> {
    if sprite_path.is_file() {
        return Ok(());
    }
    let sprite_str = sprite_path.to_str().ok_or("Bad sprite path")?;
    run_ffmpeg_sidecar_unlocked(
        app,
        slot,
        vec![
            "-hide_banner",
            "-nostdin",
            "-loglevel",
            "error",
            "-y",
            "-f",
            "lavfi",
            "-i",
            "color=c=black:s=1600x900",

Where it shows up

  • src-tauri/src/lib.rs command registration
  • downloader.rs, gallery.rs, media.rs, sponsorblock.rs
  • explorer_embed.rs for Explorer bounds and visibility
  • Sidecar resolution for yt-dlp, ffmpeg, ffprobe under binaries/