From 35adfa7feb350f1a18ab7e1bd97cf00b47be2f81 Mon Sep 17 00:00:00 2001 From: ComputerTech Date: Sat, 28 Mar 2026 11:49:00 +0000 Subject: [PATCH] Tauri v2 migration: asset protocol, desktop CSS polish, absolutePath in library --- listener.css | 8 ++++++ script.js | 31 +++++++++++++++++------ server.py | 5 +++- src-tauri/Cargo.toml | 21 ++++++++++++++++ src-tauri/build.rs | 3 +++ src-tauri/capabilities/default.json | 11 +++++++++ src-tauri/src/lib.rs | 10 ++++++++ src-tauri/src/main.rs | 6 +++++ src-tauri/tauri.conf.json | 38 +++++++++++++++++++++++++++++ style.css | 2 ++ 10 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 src-tauri/Cargo.toml create mode 100644 src-tauri/build.rs create mode 100644 src-tauri/capabilities/default.json create mode 100644 src-tauri/src/lib.rs create mode 100644 src-tauri/src/main.rs create mode 100644 src-tauri/tauri.conf.json diff --git a/listener.css b/listener.css index 8b90220..25bb3b8 100644 --- a/listener.css +++ b/listener.css @@ -19,10 +19,18 @@ * { -webkit-overflow-scrolling: touch; box-sizing: border-box; + user-select: none; + -webkit-user-select: none; } html { scroll-behavior: smooth; + overflow: hidden; + scrollbar-width: none; +} + +html::-webkit-scrollbar { + display: none; } body { diff --git a/script.js b/script.js index 2d3d3c5..952f33b 100644 --- a/script.js +++ b/script.js @@ -1274,6 +1274,22 @@ function updateCrossfader(val) { } // Library Functions +// --------------------------------------------------------------------------- +// Tauri v2 asset-protocol helper +// When running inside Tauri (window.__TAURI__ is injected via withGlobalTauri) +// and the server has provided an absolutePath for the track, we convert it to +// an asset:// URL so the WebView reads the file directly from disk — no Flask +// round-trip, works with any folder under $HOME. +// Falls back to the ordinary server URL when not in Tauri or no absolutePath. +// --------------------------------------------------------------------------- +function tauriResolve(track) { + const cvt = window.__TAURI__?.core?.convertFileSrc; + if (cvt && track.absolutePath) { + return cvt(track.absolutePath); + } + return track.file; +} + async function fetchLibrary() { try { const res = await fetch('library.json?t=' + new Date().getTime()); @@ -1303,7 +1319,7 @@ function renderLibrary(songs) { // Drag data item.ondragstart = (e) => { - e.dataTransfer.setData('trackFile', t.file); + e.dataTransfer.setData('trackFile', tauriResolve(t)); e.dataTransfer.setData('trackTitle', t.title); e.dataTransfer.setData('source', 'library'); item.classList.add('dragging'); @@ -1324,25 +1340,25 @@ function renderLibrary(songs) { const btnA = document.createElement('button'); btnA.className = 'load-btn btn-a'; btnA.textContent = 'LOAD A'; - btnA.addEventListener('click', () => loadFromServer('A', t.file, t.title)); + btnA.addEventListener('click', () => loadFromServer('A', tauriResolve(t), t.title)); const btnB = document.createElement('button'); btnB.className = 'load-btn btn-b'; btnB.textContent = 'LOAD B'; - btnB.addEventListener('click', () => loadFromServer('B', t.file, t.title)); + btnB.addEventListener('click', () => loadFromServer('B', tauriResolve(t), t.title)); // QUEUE buttons const queueA = document.createElement('button'); queueA.className = 'load-btn queue-btn-a'; queueA.textContent = 'Q-A'; queueA.title = 'Add to Queue A'; - queueA.addEventListener('click', () => addToQueue('A', t.file, t.title)); + queueA.addEventListener('click', () => addToQueue('A', tauriResolve(t), t.title)); const queueB = document.createElement('button'); queueB.className = 'load-btn queue-btn-b'; queueB.textContent = 'Q-B'; queueB.title = 'Add to Queue B'; - queueB.addEventListener('click', () => addToQueue('B', t.file, t.title)); + queueB.addEventListener('click', () => addToQueue('B', tauriResolve(t), t.title)); loadActions.appendChild(btnA); loadActions.appendChild(queueA); @@ -1351,8 +1367,9 @@ function renderLibrary(songs) { item.appendChild(trackName); item.appendChild(loadActions); - // Add data attribute for highlighting - item.dataset.file = t.file; + // Add data attribute for highlighting — store the resolved URL so + // updateLibraryHighlighting() matches decks.X.currentFile correctly. + item.dataset.file = tauriResolve(t); list.appendChild(item); }); diff --git a/server.py b/server.py index 629b99e..400a96e 100644 --- a/server.py +++ b/server.py @@ -306,7 +306,10 @@ def setup_shared_routes(app, index_file='index.html'): rel_path = os.path.relpath(os.path.join(root, filename), MUSIC_FOLDER) library.append({ "title": os.path.splitext(filename)[0], - "file": f"music_proxy/{rel_path}" + "file": f"music_proxy/{rel_path}", + # Absolute path exposed for Tauri's asset protocol (convertFileSrc). + # The web fallback keeps using the music_proxy route above. + "absolutePath": os.path.abspath(os.path.join(root, filename)) }) break # Top-level only return jsonify(library) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..4a0bc09 --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "techdj" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-fs = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" + +# Release optimisations — keep the binary small on the 4 GB machine +[profile.release] +panic = "abort" +codegen-units = 1 +lto = true +opt-level = "s" +strip = true diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..8a5b0f5 --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://schema.tauri.app/config/2/acl/capability.json", + "identifier": "default", + "description": "TechDJ default permissions — read-only access to home directory for local audio", + "windows": ["main"], + "permissions": [ + "core:default", + "fs:read-all", + "fs:scope-home-recursive" + ] +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..fc0d76b --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,10 @@ +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + // Grant the WebView direct read access to local audio files so + // convertFileSrc() can serve tracks from $HOME without going + // through the Flask proxy. + .plugin(tauri_plugin_fs::init()) + .run(tauri::generate_context!()) + .expect("error while running TechDJ"); +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..e0fd7d0 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Hides the console window on Windows release builds; harmless on Linux. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + techdj_lib::run() +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..4730f4c --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "TechDJ", + "version": "0.1.0", + "identifier": "dev.computertech.techdj", + "build": { + "frontendDist": "../" + }, + "app": { + "withGlobalTauri": true, + "windows": [ + { + "title": "TechDJ", + "width": 1280, + "height": 800, + "minWidth": 1024, + "minHeight": 600, + "decorations": true, + "fullscreen": false, + "resizable": true + } + ], + "security": { + "assetProtocol": { + "enable": true, + "scope": [ + "$HOME/**", + "$HOME/Music/**" + ] + } + } + }, + "bundle": { + "active": true, + "targets": ["deb"], + "icon": ["../icon.png"] + } +} diff --git a/style.css b/style.css index 61990f1..ea191e7 100644 --- a/style.css +++ b/style.css @@ -20,6 +20,8 @@ /* Smooth scrolling for all scrollable elements */ * { -webkit-overflow-scrolling: touch; + user-select: none; + -webkit-user-select: none; } html {