Files
SoulStream/src-tauri/src/lib.rs
T

361 lines
15 KiB
Rust

mod commands;
mod community;
mod config;
mod cover_logo;
mod diagnostics;
mod freenet;
mod friends;
mod persona;
mod hero_crop;
mod import;
mod inbox;
mod ledger;
mod hero_detector;
mod loudness;
mod lyric_align;
mod lyric_prep;
mod lyrics;
mod paths;
mod pipeline;
mod playback;
mod profile;
mod playlist_formats;
mod playlists;
mod provider_playlist;
mod preview;
mod setlist_scan;
mod shared_playlists;
mod shazam;
mod slsk_link;
mod smtc;
mod spaces;
mod proposals;
mod overlap;
mod state;
mod thumbbar;
mod vocal_sep;
mod youtube_playlist;
mod youtube_setlist;
mod ytdlp;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
.manage(state::AppState::default())
.setup(|app| {
app.handle().plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.clear_targets()
.target(tauri_plugin_log::Target::new(
tauri_plugin_log::TargetKind::Stdout,
))
.build(),
)?;
app.handle().plugin(tauri_plugin_dialog::init())?;
// Must run before anything touches the app-data dir.
config::migrate_app_data(app.handle());
// Prepare the small local vision model while the user is still on
// the app shell, so a first artist visit pays only warm inference.
hero_detector::warm_up(app.handle().clone());
// Before playback::init: the event pump it spawns starts pushing
// transport state at the card immediately, and the card has to be
// registered by then or the first track's status is dropped.
smtc::init(app.handle());
// Only subclasses the window here; the buttons themselves go up
// when the shell says the taskbar button exists.
thumbbar::init(app.handle());
playback::init(app.handle());
playback::prune_cache(app.handle());
preview::init(app.handle());
loudness::init(app.handle());
diagnostics::init(app.handle());
// The experimental Freenet layer: applies the saved opt-in (off
// by default) and forwards adapter status to the webview. Fully
// isolated — its absence or failure touches nothing else.
freenet::init(app.handle());
if let Err(e) = pipeline::init(app.handle()) {
log::error!("library init failed: {e}");
}
Ok(())
})
.invoke_handler(tauri::generate_handler![
commands::app_status,
commands::excluded_search_phrases,
commands::get_credentials,
commands::connect_and_login,
commands::connect_stored,
commands::disconnect,
commands::start_search,
commands::stop_search,
commands::download_file,
commands::copy_stream_to_folder,
commands::save_to_library,
commands::stream_cache_len,
commands::cancel_download,
commands::show_in_folder,
commands::refresh_artist_art,
commands::play_stream,
commands::play_local,
commands::preload_local,
commands::player_control,
commands::player_state,
commands::waveform,
commands::library_albums,
commands::library_album_stats,
commands::album_tracks,
commands::album_art,
commands::library_art_prewarm,
commands::stream_embedded_art,
commands::refresh_album_art,
commands::refresh_all_album_art,
commands::get_settings,
commands::set_settings,
commands::forget_credentials,
commands::pick_folder,
commands::open_external,
commands::record_play,
commands::add_play_seconds,
commands::artist_play_stats,
commands::recent_albums,
commands::clear_play_history,
commands::play_row_count,
commands::followed_artists,
commands::follow_artist,
commands::unfollow_artist,
commands::slsk_join_room,
commands::slsk_leave_room,
commands::slsk_say_room,
commands::slsk_message_user,
commands::slsk_room_list,
commands::slsk_browse_user,
commands::slsk_user_info,
commands::slsk_user_interests,
commands::slsk_similar_users,
slsk_link::slsk_link_status,
slsk_link::slsk_link_self,
slsk_link::slsk_link_unlink,
slsk_link::slsk_link_check,
slsk_link::slsk_link_forget,
friends::friends_list,
friends::friend_request,
friends::friend_request_by_key,
friends::friend_accept,
friends::friend_decline,
friends::friend_remove,
friends::friend_forget,
shared_playlists::shared_playlists_mine,
shared_playlists::shared_playlist_publish,
shared_playlists::shared_playlist_withdraw,
shared_playlists::shared_playlist_fetch,
shared_playlists::shared_playlist_import,
shared_playlists::share_send,
shared_playlists::shares_inbox,
shared_playlists::share_dismiss,
inbox::inbox_status,
inbox::inbox_publish,
inbox::inbox_check,
spaces::spaces_list,
spaces::space_create,
spaces::space_open,
spaces::space_post,
spaces::space_invite,
spaces::space_remove_member,
spaces::space_set_pinned,
spaces::space_leave,
proposals::space_board,
proposals::space_propose,
proposals::space_decide,
overlap::overlap_list,
overlap::overlap_compute,
overlap::overlap_forget,
commands::buddy_list,
commands::buddy_add,
commands::buddy_remove,
commands::home_artist_news,
commands::home_warm,
commands::mini_open,
commands::mini_close,
commands::window_glass,
playlists::list_playlists,
playlists::create_playlist,
playlists::rename_playlist,
playlists::set_playlist_description,
playlists::set_playlist_pinned,
playlists::duplicate_playlist,
playlists::delete_playlist,
playlists::playlist_items,
playlists::playlist_add_items,
playlists::playlist_remove_items,
playlists::playlist_set_order,
playlists::playlist_relink_item,
playlists::playlist_cover,
playlists::playlist_cover_pick,
playlists::playlist_cover_clear,
playlists::playlist_import_probe,
playlists::playlist_import_folder,
playlists::playlist_import_text,
playlists::playlist_import_youtube,
playlists::playlist_import_youtube_playlist,
playlists::playlist_import_link,
playlists::playlist_import_track_links,
playlists::playlist_import_commit,
setlist_scan::playlist_scan_youtube,
setlist_scan::playlist_pick_set_file,
setlist_scan::playlist_scan_file,
setlist_scan::playlist_scan_cancel,
ytdlp::ytdlp_status,
ytdlp::ytdlp_download,
ytdlp::deno_download,
playlists::playlist_export,
playlists::library_track_search,
lyrics::lyrics_get,
lyrics::lyrics_search,
lyrics::lyrics_select,
lyrics::lyrics_set_offset,
lyrics::lyrics_set_auto_sync,
lyrics::lyrics_prepare_instrumental,
lyrics::lyrics_vocal_activity,
lyrics::lyrics_save_lines,
lyrics::lyrics_save_text,
lyrics::lyrics_clear,
commands::catalog_search,
commands::catalog_search_tracks,
commands::catalog_artist_track_candidates,
commands::catalog_track_by_isrc,
import::import_scan,
import::import_portable_root,
import::import_resolve_root,
import::import_session_get,
import::import_session_set,
import::import_session_clear,
import::import_get_dest,
import::import_set_dest,
import::import_apply,
import::import_identify,
import::import_cover_report,
import::import_local_art,
import::import_audition,
import::import_audition_missing,
import::import_audition_clear,
import::import_probe_file,
pipeline::rescan_library,
commands::catalog_artist,
commands::catalog_artist_art,
commands::catalog_artist_logos,
commands::catalog_artist_identity,
commands::catalog_artist_socials,
commands::remember_fused_artist_portrait,
commands::catalog_artist_bio,
commands::catalog_cached_cover_logo,
commands::catalog_cover_logo,
commands::catalog_cover_logo_debug,
commands::catalog_ocr_experiment,
commands::catalog_artist_hero_seed,
commands::catalog_artist_overview,
commands::catalog_artist_popular,
commands::pick_artist_hero,
commands::clear_artist_hero,
commands::catalog_related,
commands::catalog_track_listeners,
commands::catalog_artist_profile,
commands::catalog_album,
commands::catalog_album_info,
commands::catalog_art,
commands::catalog_cover,
commands::invalidate_catalog_cover,
commands::catalog_hero_preview,
commands::catalog_hero_gpu_source,
commands::catalog_cached_hero_art,
commands::catalog_hero_art,
commands::preload_stream,
commands::preload_cached_stream,
commands::stream_waveform,
commands::stream_cache_ready,
freenet::freenet_status,
freenet::freenet_set_enabled,
freenet::freenet_detect,
freenet::freenet_set_load_remote_images,
freenet::freenet_node_info,
freenet::freenet_node_start,
freenet::freenet_node_stop,
community::contacts_meta_list,
community::contact_meta_set,
community::community_pick_file,
persona::persona_status,
persona::persona_create,
persona::persona_export,
persona::persona_import,
persona::persona_rotate,
persona::persona_remove,
profile::profile_get,
profile::profile_publish,
profile::profile_set_avatar,
profile::profile_clear_avatar,
preview::preview_resolve,
preview::preview_prewarm,
preview::preview_load,
preview::preview_control,
preview::preview_dispose,
preview::player_pause_acked,
preview::player_fade_pause_acked,
preview::player_fade_resume,
diagnostics::debug_stats,
smtc::smtc_metadata,
lyric_prep::lyric_prep_request,
lyric_align::lyric_model_status,
lyric_align::lyric_model_download,
vocal_sep::vocal_sep_status,
vocal_sep::vocal_sep_download,
])
.run({
let mut context = tauri::generate_context!();
// Browser args ride the WINDOW CONFIG, nothing else: wry always sets
// AdditionalBrowserArguments programmatically, and at least WebView2
// 151.x then drops the WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS env var
// entirely — flags "delivered" that way vanish silently on some
// runtimes (they held on the home machine, gone on the work one).
// Verify in the browser process command line (Get-CimInstance
// Win32_Process), never by assumption. Explicit args replace wry's
// defaults, so those are restated. Two WebView2 presentation fixes
// live here (2026-08-26, the dark-glass work):
//
// 1. NO delegated compositing: with it, DWM composes the webview's
// layers at rest and blends semi-transparent darks in the wrong
// colorspace — the app rested washed-out LIGHT and looked correct
// only while an animation forced Chromium to compose in-process.
// 2. Force the sRGB color profile: on an HDR display the MAXIMIZED
// window's rest path takes a different SDR-white tone mapping
// than the animated path — covers blew out near-white at rest
// and snapped correct on hover. One profile, one mapping, both
// paths agree.
// 3. Force the wheel scroll animator ON: this WebView2 shipped
// wheel detents as INSTANT 120px jumps (measured: a dispatched
// detent moved scrollTop in exactly one frame) while the
// present pipeline ran a flawless 144Hz — scroll position
// updating at detent cadence reads as "choppy but the FPS
// says 144". The system animation setting is on and
// prefers-reduced-motion is off, so the snap is WebView2's
// embedded default, not the Chromium system heuristic; the
// switch pins enable_scroll_animator=true in WebPreferences.
//
// Debug builds add CDP on 9222 for instrumentation (see HANDOFF.md).
let base = "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection,DelegatedCompositing \
--autoplay-policy=no-user-gesture-required \
--enable-smooth-scrolling \
--force-color-profile=srgb";
#[cfg(debug_assertions)]
// A second instance needs its own CDP port, or it silently fails
// to bind and cannot be driven at all.
let cdp = std::env::var("SOULSTREAM_CDP_PORT").unwrap_or_else(|_| "9222".into());
let args = format!("{base} --remote-debugging-port={cdp}");
#[cfg(not(debug_assertions))]
let args = base.to_string();
for window in &mut context.config_mut().app.windows {
window.additional_browser_args = Some(args.clone());
}
context
})
.expect("error while running tauri application");
}