0% found this document useful (0 votes)
3 views107 pages

Old Code

The document contains Python code for an audio processing application that includes classes for streaming audio from a network source and handling audio playback. The 'NetworkStreamer' class manages the streaming of audio using FFmpeg, while the 'AudioCore' class handles audio playback, effects, and controls. The application supports both local file playback and network streaming, with features for AI processing of audio stems and various audio controls.

Uploaded by

sampreetkumar2
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views107 pages

Old Code

The document contains Python code for an audio processing application that includes classes for streaming audio from a network source and handling audio playback. The 'NetworkStreamer' class manages the streaming of audio using FFmpeg, while the 'AudioCore' class handles audio playback, effects, and controls. The application supports both local file playback and network streaming, with features for AI processing of audio stems and various audio controls.

Uploaded by

sampreetkumar2
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

================================================================================

PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\audio_core.py


================================================================================

import threading
import numpy as np
import sounddevice as sd
from mutagen import File
import subprocess
import queue
import time
# Local import in load_source to avoid circular dependency
# from audio_fx import _read_audio_any

# --- FFmpeg Streaming Helper ---


class NetworkStreamer:
def __init__(self, url, fs=44100):
[Link] = url
[Link] = fs
[Link] = None
[Link] = [Link](maxsize=200) # Buffer ~2-3 seconds
[Link] = False
[Link] = None

def start(self):
[Link] = True
# FFmpeg command: Read URL, output raw float32 little-endian, stereo,
44.1k
cmd = [
'ffmpeg', '-reconnect', '1', '-reconnect_streamed', '1', '-
reconnect_delay_max', '5',
'-i', [Link],
'-f', 'f32le', '-ac', '2', '-ar', str([Link]),
'-vn', '-' # No video, output to stdout
]
# Hide console window on Windows
startupinfo = None
if hasattr(subprocess, 'STARTUPINFO'):
startupinfo = [Link]()
[Link] |= subprocess.STARTF_USESHOWWINDOW

try:
[Link] = [Link](
cmd, stdout=[Link], stderr=[Link],
bufsize=4096, startupinfo=startupinfo
)
[Link] = [Link](target=self._reader, daemon=True)
[Link]()
except FileNotFoundError:
print("[AudioCore] CRITICAL ERROR: FFmpeg not found. Cannot
stream.")
[Link] = False
except Exception as e:
print(f"[AudioCore] Stream Start Error: {e}")
[Link] = False

def _reader(self):
chunk_size = 4096 * 2 * 4 # frames * channels * bytes_per_float
while [Link] and [Link] and [Link]() is None:
try:
raw = [Link](chunk_size)
if not raw: break
data = [Link](raw, dtype=np.float32)
data = [Link](-1, 2)
[Link](data, timeout=10)
except: break
[Link] = False

def get_chunk(self, frames):


out = []
collected = 0
while collected < frames:
try:
if [Link]() and not [Link]: break
chunk = [Link](timeout=0.1)
[Link](chunk)
collected += len(chunk)
if collected >= frames: break
except [Link]:
# Still active, but buffer is empty (buffering)
if [Link]: return [Link]((frames, 2), dtype=np.float32)
break

if not out: return None


full = [Link](out)

# Pad if we got less than requested


if len(full) < frames:
full = [Link](full, ((0, frames - len(full)), (0, 0)))

return full

def stop(self):
[Link] = False
if [Link]:
[Link]()
[Link] = None
# Drain queue
with [Link]: [Link]()

class AudioCore:
def __init__(self, fx_module):
[Link] = fx_module
[Link] = None

# Audio Sources
self.data_dry = None # Local File (Entire array in RAM)
self.net_stream = None # Network Stream (Chunked)

# AI Stems (HQ Mode)


self.data_wet = None
self.data_vox = None
self.data_acc = None

[Link] = 44100
[Link] = 0
[Link] = "STOPPED"
self.duration_ms = 0.0

# Flags
self.is_stream_mode = False
self.ai_mode = False # False = Mid/Side DSP, True = Deep Learning Stems

# Controls
[Link] = 1.0
self.playback_speed = 1.0
self.reverb_vol = 0.0
self.vocal_vol = 1.0
self.music_vol = 1.0

self.load_id = 0

def _get_duration_fast(self, path):


try:
f = File(path)
if f and [Link] and [Link]: return [Link] * 1000
except: pass
return 0.0

def load_source(self, path_or_url, is_stream=False, duration_known=0,


blocking=False):
self.stop_stream_only()
self.load_id += 1
current_id = self.load_id

[Link] = "LOADING"
self.is_stream_mode = is_stream
self.duration_ms = duration_known if is_stream else
self._get_duration_fast(path_or_url)

# 1. Reset Stems & Mode explicitly


self.data_dry = None
self.reset_stems()
[Link] = 0

if is_stream:
# TRUE STREAMING: Pipe from URL
self.net_stream = NetworkStreamer(path_or_url, [Link])
self.net_stream.start()
self.start_playback()
else:
# Local File Loading
if not is_stream:
is_cached, cache_paths =
[Link].check_cache_status_silent(path_or_url)
if is_cached:
print(f"[AudioCore] Found existing HQ stems for:
{path_or_url}")
if blocking:
self._worker_load_with_stems(path_or_url, current_id,
cache_paths)
else:
[Link](target=self._worker_load_with_stems,
args=(path_or_url, current_id, cache_paths), daemon=True).start()
return

if blocking:
self._worker_load(path_or_url, current_id)
else:
[Link](target=self._worker_load, args=(path_or_url,
current_id), daemon=True).start()

def reset_stems(self):
self.ai_mode = False
self.data_wet = None
self.data_vox = None
self.data_acc = None
if self.net_stream:
self.net_stream.stop()
self.net_stream = None

def _worker_load_with_stems(self, path, my_id, stem_paths):


self._worker_load(path, my_id)
if [Link] != "STOPPED" and self.load_id == my_id:
self.inject_hq_stems(stem_paths)

def _worker_load(self, path, my_id):


# Local import to resolve circular dependency with audio_fx
from audio_fx import _read_audio_any
try:
data, fs = _read_audio_any(path)
if self.load_id != my_id: return
if data is None:
[Link] = "STOPPED"; return

self.data_dry = data
[Link] = fs
if self.duration_ms == 0 and len(data) > 0:
self.duration_ms = (len(data) / fs) * 1000

self.start_playback()

except Exception as e:
print(f"[AudioCore] Load Error: {e}")
[Link] = "STOPPED"

def inject_hq_stems(self, paths):


"""Called when AI processing finishes. Hot-swaps to HQ mode with
Resampling Fix."""
from audio_fx import _read_audio_any # Local import
if self.is_stream_mode: return
try:
v, v_fs = _read_audio_any(paths['vocals'])
a, a_fs = _read_audio_any(paths['accompaniment'])
r, r_fs = _read_audio_any(paths['reverb'])

def resample_if_needed(data, src_fs):


if data is None or src_fs == [Link]: return data
ratio = [Link] / src_fs
new_len = int(len(data) * ratio)
if [Link] == 2:
x_old = [Link](0, 1, len(data))
x_new = [Link](0, 1, new_len)
return [Link]([[Link](x_new, x_old, data[:, i]) for i
in range(2)]).T
return data

if v is not None: self.data_vox = self._fit(resample_if_needed(v,


v_fs))
if a is not None: self.data_acc = self._fit(resample_if_needed(a,
a_fs))
if r is not None: self.data_wet = self._fit(resample_if_needed(r,
r_fs))

if self.data_vox is not None:


self.ai_mode = True
print(f"[AudioCore] Switched to HQ AI Mode (Resampled to
{[Link]}Hz)")
except Exception as e: print(f"Stem Injection Error: {e}")

def _fit(self, arr):


if self.data_dry is None: return arr
tgt = len(self.data_dry)
if len(arr) < tgt: return [Link](arr, ((0, tgt-len(arr)), (0,0)))
return arr[:tgt]

def start_playback(self):
self._start_stream()
[Link] = "PLAYING"

def _start_stream(self):
if [Link]: [Link]()
[Link] = [Link](samplerate=[Link], channels=2,
callback=[Link], blocksize=4096)
[Link]()

def cb(self, outdata, frames, t, status):


try:
if [Link] != "PLAYING":
[Link](0); return

sz = int(frames * self.playback_speed)
mixed = None
base_mix = None

# 1. STREAMING MODE (NetworkStreamer)


if self.is_stream_mode and self.net_stream:
chunk = self.net_stream.get_chunk(sz)
if chunk is None or len(chunk) == 0:
[Link](0); return # Ended or FFmpeg issue

# If we got less data than requested, pad it (should be handled


by get_chunk now)
if len(chunk) < sz:
chunk = [Link](chunk, ((0, sz - len(chunk)), (0, 0)))

base_mix = chunk
[Link] += sz # Rough position update for UI

# 2. LOCAL BUFFER MODES


elif self.data_dry is not None:
end = [Link] + sz
if [Link] >= len(self.data_dry):
[Link](0); [Link] = "STOPPED"; return

def get_slice(arr):
if arr is None: return [Link]((sz, 2), dtype=np.float32)
if end > len(arr):
valid = len(arr) - [Link]
return [Link](arr[[Link]:], ((0, sz-valid), (0,0)))
return arr[[Link]:end]

# HQ Mode (AI Stems)


if self.ai_mode and self.data_vox is not None:
vox_chunk = get_slice(self.data_vox)
acc_chunk = get_slice(self.data_acc)
wet_chunk = get_slice(self.data_wet)

base_mix = (vox_chunk * self.vocal_vol) + \


(acc_chunk * self.music_vol) + \
(wet_chunk * self.reverb_vol)

# Standard Mode (DSP Mid/Side)


else:
raw_chunk = get_slice(self.data_dry)

mid = 0.5 * (raw_chunk[:, 0] + raw_chunk[:, 1])


side = 0.5 * (raw_chunk[:, 0] - raw_chunk[:, 1])

l_ch = (mid * self.vocal_vol) + (side * self.music_vol)


r_ch = (mid * self.vocal_vol) - (side * self.music_vol)

base_mix = [Link]([l_ch, r_ch], axis=1)

[Link] += sz

else:
[Link](0); return

mixed = base_mix

# --- FINAL OUTPUT PROCESSING ---


if mixed is not None:
if abs(self.playback_speed - 1.0) > 0.01:
x_old = [Link](0, 1, len(mixed))
x_new = [Link](0, 1, frames)
mixed = [Link]([[Link](x_new, x_old, mixed[:,i]) for i
in range(2)]).T
elif len(mixed) != frames:
mixed = [Link](mixed, (frames, 2))

mixed *= [Link]
[Link](mixed, -1.0, 1.0, out=mixed)
outdata[:] = mixed

except Exception as e:
# print(f"AudioCB Error: {e}") # Suppress for stability
[Link](0)

# --- CONTROLS ---


def play_pause(self):
if [Link] == "PLAYING":
[Link] = "PAUSED"
elif [Link] == "PAUSED" or [Link] == "STOPPED":
if self.data_dry is not None and [Link] >= len(self.data_dry):
[Link] = 0
[Link] = "PLAYING"
return [Link]

def stop_stream_only(self):
if [Link]:
try: [Link](); [Link]()
except: pass
[Link] = None
if self.net_stream:
self.net_stream.stop()
self.net_stream = None

def set_pos(self, ms):


if self.data_dry is not None and not self.is_stream_mode:
[Link] = int((ms/1000) * [Link])
[Link] = max(0, min([Link], len(self.data_dry)-1))
# Seek not supported in simple NetworkStreamer yet for stability

def get_state(self):
# For streams, pos is a rough estimate based on idx increment
pos = ([Link]/[Link])*1000 if [Link] > 0 else 0
return {
"state": [Link],
"duration": self.duration_ms,
"position": pos,
"ai_mode": self.ai_mode # Tell UI if we are in HQ mode
}

def toggle_ai_mode(self, force_mode=None):


if self.is_stream_mode: return False # AI mode unavailable in streaming
if force_mode is True and self.data_vox is None: return None

if force_mode is not None: self.ai_mode = force_mode


else: self.ai_mode = not self.ai_mode

return self.ai_mode

def set_speed(self, v): self.playback_speed = float(v)


def set_volume(self, v): [Link] = float(v)
def set_reverb_vol(self, val): self.reverb_vol = float(val)
def set_vocal_vol(self, val): self.vocal_vol = float(val)
def set_music_vol(self, val): self.music_vol = float(val)

def apply_afterwave_preset(self, active):


if active: self.playback_speed = 0.85; self.music_vol = 0.5;
self.reverb_vol = 0.6; self.vocal_vol = 1.0
else: self.playback_speed = 1.0; self.music_vol = 1.0; self.reverb_vol =
0.0; self.vocal_vol = 1.0

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\audio_fx.py
================================================================================

import os
import threading
import shutil
import hashlib
import logging
import sys
import io
import time
import re
import eel
import numpy as np
import soundfile as sf
import webbrowser
import subprocess
import platform
import ctypes
import traceback
from pathlib import Path
from [Link] import fftconvolve
from audio_separator.separator import Separator
import onnxruntime as ort

# --- PATH SETUP ---


base_dir = [Link]([Link](__file__))
[Link]["PATH"] += [Link] + base_dir

def _sha1_of_file(path: str, chunk_size=1024 * 1024) -> str:


if not path or not isinstance(path, str):
return hashlib.sha1(b"INVALID_PATH").hexdigest()
h = hashlib.sha1()
try:
with open(path, "rb") as f:
while True:
b = [Link](chunk_size)
if not b: break
[Link](b)
return [Link]()
except:
return hashlib.sha1([Link]('utf-8', errors='ignore')).hexdigest()

def _read_audio_any(path):
try:
# CONTEXT MANAGER: Ensures file handle is closed immediately
with [Link](str(path)) as f:
data = [Link](dtype="float32", always_2d=True)
fs = [Link]
return data, fs
except Exception as e:
return None, 44100

class StderrCapture([Link]):
def __init__(self):
super().__init__()
self.last_update = 0
self.progress_re = [Link](r'(\d+)%\|.*?\[.*?<(\d{2}:\d{2}|\d{2}:
\d{2}:\d{2})')

def write(self, message):


sys.__stdout__.write(message)
now = [Link]()
if now - self.last_update < 0.1: return
msg_clean = [Link]()
if not msg_clean: return
try:
match = self.progress_re.search(msg_clean)
if match:
self.last_update = now
eel.update_processing_status(int([Link](1)),
[Link](2))()
elif "Separating" in msg_clean or "Loading" in msg_clean or
"Generating" in msg_clean:
self.last_update = now
eel.update_processing_log(msg_clean)()
except Exception: pass

class CloudIsolationManager:
def __init__(self, parent_fx):
[Link] = parent_fx
[Link] = False
self.upload_temp_dir = [Link](parent_fx.cache_dir, "upload_temp")
[Link](self.upload_temp_dir, exist_ok=True)
self.target_hash = None
self.original_filename_stem = None
self.downloads_dir = [Link]() / "Downloads"
self.monitor_start_time = 0

def prepare_session(self, src_path, file_hash):


[Link] = True
self.target_hash = file_hash
for f in [Link](self.upload_temp_dir):
try: [Link]([Link](self.upload_temp_dir, f))
except: pass
filename = [Link](src_path)
self.original_filename_stem = Path(filename).stem
dest = [Link](self.upload_temp_dir, filename)
shutil.copy2(src_path, dest)
return True

def _check_conflicts(self):
conflicts = []
try:
for f in self.downloads_dir.glob("*.*"):
if [Link]() not in ['.mp3', '.wav']: continue
name = [Link]()
if ("vocal" in name or "music" in name or "instrumental" in
name):
[Link]([Link])
except: pass
return len(conflicts) > 0

def launch_workspace(self):
if not [Link]: return
if self._check_conflicts():
eel.update_cloud_status("Conflicting files found in Downloads!", -2)
()
return
self.monitor_start_time = [Link]()
[Link]("[Link]
self._open_folder(self.upload_temp_dir)
[Link](target=self._watcher, daemon=True).start()

def _open_folder(self, path):


if [Link]() == "Windows": [Link](path)
elif [Link]() == "Darwin": [Link](["open", path])
else: [Link](["xdg-open", path])

def _watcher(self):
eel.update_cloud_status("Scanning Downloads folder...", 0)()
while [Link]:
[Link](1)
if not [Link]: break
try:
files = [f for f in self.downloads_dir.glob("*.*") if
[Link]() in ['.mp3', '.wav']]
[Link](key=[Link], reverse=True)
candidates = files[:10]
current_v = None
current_m = None
for f in candidates:
if [Link](f) < self.monitor_start_time: continue
fname = [Link]()
if "vocal" in fname and "music" in fname: continue
if "vocal" in fname: current_v = f
elif ("music" in fname or "instrumental" in fname):
current_m = f

status_msg = "Waiting for new downloads..."


if current_v and not current_m: status_msg = "Vocals detected.
Waiting for Music..."
elif current_m and not current_v: status_msg = "Music detected.
Waiting for Vocals..."
if current_v and current_m:
eel.update_cloud_status("Processing Import...", 2)()
[Link](1)
self._finalize_cloud_import(current_v, current_m)
break
if [Link]: eel.update_cloud_status(status_msg, 1)()
except Exception as e: print(f"[CloudMgr] Watcher Error: {e}")

def _finalize_cloud_import(self, v_path, m_path):


try:
song_folder = [Link]([Link].iso_dir, self.target_hash)
[Link](song_folder, exist_ok=True)
target_v = [Link](song_folder, "[Link]")
target_a = [Link](song_folder, "[Link]")
data_v, fs_v = _read_audio_any(v_path)
data_m, fs_m = _read_audio_any(m_path)
[Link](target_v, data_v, fs_v)
[Link](target_a, data_m, fs_m)
[Link]._generate_reverb_only(target_v, self.target_hash)
[Link](self.upload_temp_dir)
[Link](self.upload_temp_dir, exist_ok=True)
[Link] = False
eel.update_cloud_status("Done!", 3)()
[Link]._notify_done(self.target_hash)
except Exception as e:
eel.update_cloud_status(f"Import Error: {e}", -1)()
[Link] = False
def cancel(self): [Link] = False

class AudioEffects:
def __init__(self):
self.cache_dir = [Link]([Link]("~"), "Music",
"_MaterialMusicCache")
self.iso_dir = [Link](self.cache_dir, "isolation")
self.rev_dir = [Link](self.cache_dir, "reverb")
self.model_dir = [Link](self.cache_dir, "models")

[Link](self.iso_dir, exist_ok=True)
[Link](self.rev_dir, exist_ok=True)
[Link](self.model_dir, exist_ok=True)

self.ir_path = [Link](self.cache_dir, "synthetic_hall.wav")


if not [Link](self.ir_path): self._generate_ir()

self.on_process_complete = None
self.is_processing = False
self.current_file_hash = None
self.cloud_mgr = CloudIsolationManager(self)

def _generate_ir(self):
fs = 44100; duration = 2.0
t = [Link](0, duration, int(fs*duration))
noise = [Link](0, 1, len(t))
envelope = [Link](-4.0 * t)
ir = (noise * envelope).astype(np.float32)
[Link](self.ir_path, ir, fs)

def get_hardware_capabilities(self):
"""Checks if GPU providers are available in ONNX Runtime."""
try:
providers = ort.get_available_providers()
# Common GPU providers: 'CUDAExecutionProvider',
'DmlExecutionProvider', 'CoreMLExecutionProvider', 'ROCMExecutionProvider'
gpu_providers = [p for p in providers if 'GPU' in [Link]() or
'CUDA' in [Link]() or 'DML' in [Link]() or 'ROCM' in [Link]()]
return {
"has_gpu": len(gpu_providers) > 0,
"providers": providers
}
except:
return { "has_gpu": False, "providers": ['CPUExecutionProvider'] }

def check_cache_status(self, src_path):


file_hash = _sha1_of_file(src_path)
song_folder = [Link](self.iso_dir, file_hash)
v = [Link](song_folder, "[Link]")
return ([Link](v), file_hash)

def check_cache_status_silent(self, src_path):


is_ready, file_hash = self.check_cache_status(src_path)
if is_ready:
folder = [Link](self.iso_dir, file_hash)
return True, {
"vocals": [Link](folder, "[Link]"),
"accompaniment": [Link](folder, "[Link]"),
"reverb": [Link](self.rev_dir, file_hash + "_wet.wav")
}
return False, None

# ==========================================================
# FORENSIC DELETE FUNCTION
# ==========================================================
def delete_cached_stems(self, src_path):
"""
FORENSIC DELETE: Attempts to delete files and prints EXACT reasons if it
fails.
"""
print(f"\n[AudioFX] --- STARTING FORENSIC DELETE FOR: {src_path} ---")

if not src_path: return False

file_hash = _sha1_of_file(src_path)
song_folder = [Link](self.iso_dir, file_hash)
rev_file = [Link](self.rev_dir, file_hash + "_wet.wav")

# 1. CHECK CURRENT WORKING DIRECTORY


cwd = [Link]()
print(f"[AudioFX] DEBUG: Current Working Directory is: {cwd}")
if song_folder in cwd:
print("[AudioFX] CRITICAL WARNING: Python is currently inside the
folder it is trying to delete!")
print(f"[AudioFX] Attempting to move out to: {self.cache_dir}")
[Link](self.cache_dir)

# 2. HELPER TO UNLOCK READ-ONLY FILES


def on_rm_error(func, path, exc_info):
print(f"[AudioFX] Warning: Read-only file found: {path}. Attempting
to chmod...")
try:
[Link](path, 0o777)
func(path)
print(f"[AudioFX] Success: Force-deleted {path}")
except Exception as e:
print(f"[AudioFX] Failed to chmod/delete: {e}")

# 3. INDIVIDUAL FILE CLEANUP (To find the specific locker)


if [Link](song_folder):
print(f"[AudioFX] Inspecting folder: {song_folder}")
for root, dirs, files in [Link](song_folder, topdown=False):
for name in files:
file_p = [Link](root, name)
try:
[Link](file_p, 0o777)
[Link](file_p)
except Exception as e:
print(f"[AudioFX] !!! LOCKED FILE FOUND !!!")
print(f" File: {file_p}")
print(f" Error: {e}")
# Check if it's a log file
if ".log" in name: print(" [HINT] This is a log file.
The logger might still be open.")

# 4. REMOVE FOLDER
try:
if [Link](song_folder):
print(f"[AudioFX] Attempting rmtree on: {song_folder}")
[Link](song_folder, onerror=on_rm_error)

if [Link](rev_file):
print(f"[AudioFX] Deleting reverb: {rev_file}")
[Link](rev_file)

except Exception as e:
print(f"[AudioFX] Standard Delete Failed.")
traceback.print_exc() # PRINTS FULL STACK TRACE

# 5. NUCLEAR OPTION (CMD)


if [Link](song_folder) or [Link](rev_file):
print("[AudioFX] Fallback: Executing Windows CMD delete...")
[Link](f'rmdir /s /q "{song_folder}"', shell=True)
[Link](f'del /f /q "{rev_file}"', shell=True)

# 6. VERIFICATION
if [Link](song_folder):
print("[AudioFX] FAILURE: Folder still exists on disk.")

# 7. RENAME TRICK (Last Resort)


trash_path = song_folder + "_trash_" + str(int([Link]()))
print(f"[AudioFX] Attempting to Rename locked folder to:
{trash_path}")
try:
[Link](song_folder, trash_path)
print("[AudioFX] Rename SUCCESS! The folder is now renamed
(trash).")
# Try to schedule delete on reboot
try:
MOVEFILE_DELAY_UNTIL_REBOOT = 0x4

[Link](ctypes.c_wchar_p(trash_path), None,
MOVEFILE_DELAY_UNTIL_REBOOT)
print("[AudioFX] Scheduled trash for deletion on reboot.")
return True # We consider this a success because the
original path is gone
except: pass
except Exception as e:
print(f"[AudioFX] Rename FAILED: {e}")
print("[AudioFX] CONCLUSION: The file is exclusively locked by a
process (probably this one) with [Link]")
return False

print("[AudioFX] SUCCESS: Files deleted.")


return True

def prepare_cloud(self, src_path):


if self.is_processing: return
is_ready, file_hash = self.check_cache_status(src_path)
if is_ready:
self._notify_done(file_hash)
return
self.cloud_mgr.prepare_session(src_path, file_hash)

def launch_cloud(self): self.cloud_mgr.launch_workspace()


def cancel_cloud(self): self.cloud_mgr.cancel()
def process_specific_track(self, src_path, quality_mode="vocals",
use_gpu=False):
if self.is_processing: return
self.is_processing = True
is_ready, file_hash = self.check_cache_status(src_path)
self.current_file_hash = file_hash
if is_ready:
self._notify_done(file_hash)
self.is_processing = False
return
[Link](target=self._worker, args=(src_path, file_hash,
quality_mode, use_gpu), daemon=True).start()

def _worker(self, src_path, file_hash, quality_mode, use_gpu):


capture = StderrCapture()
original_stderr = [Link]
[Link] = capture

try:
song_folder = [Link](self.iso_dir, file_hash)
[Link](song_folder, exist_ok=True)
vocab_path = [Link](song_folder, "[Link]")
accomp_path = [Link](song_folder, "[Link]")

# 1. DETERMINE MODEL (Vocals=Kim_Vocal_2, Music=Inst_HQ_3)


if quality_mode == "music":
model_name = "UVR-MDX-NET-Inst_HQ_3.onnx"
print(f"[AudioFX] Mode: HQ Music (Using {model_name})")
else:
model_name = "Kim_Vocal_2.onnx"
print(f"[AudioFX] Mode: HQ Vocals (Using {model_name})")

# 2. DETERMINE DEVICE
# Note: Separator expects 'cpu' or 'cuda'. If GPU is not available,
it defaults.
device_param = 'cuda' if use_gpu else 'cpu'
print(f"[AudioFX] Processing on: {device_param.upper()}")

if not ([Link](vocab_path) and [Link](accomp_path)):


print("Loading AI Model...")

# FIX: Removed 'device' and 'env_specific_override' from


constructor
# as they cause issues in some versions.
sep = Separator(
output_dir=song_folder,
log_level=[Link],
model_file_dir=self.model_dir,
output_format="wav"
)

# FIX: Pass 'device' argument to load_model() where it is


expected
try: sep.load_model(model_filename=model_name)
except Exception as e:
print(f"Model Load Error: {e}")
[Link](self.model_dir) # Force re-download next time
self.is_processing = False
return

print("Separating Stems...")
# Note: The 'device' from load_model usually persists, no need
to pass it here.
output_files = [Link](src_path)

# CLEANUP SEPARATOR (Explicitly delete instance to free


resources)
del sep

# Handle output names from different models


for f in output_files:
full_p = [Link](song_folder, f)
# Check for "Vocals" or "Kim_Vocal" (Kim_Vocal_2 output)
if "Vocals" in f or "Kim_Vocal" in f:
if [Link](vocab_path): [Link](vocab_path)
[Link](full_p, vocab_path)
# Check for "Instrumental" or "no_vocals" (Inst_HQ_3 output)
elif "Instrumental" in f or "Other" in f or "no_vocals" in
f:
if [Link](accomp_path): [Link](accomp_path)
[Link](full_p, accomp_path)

self._generate_reverb_only(vocab_path, file_hash)
print("Done")
self._notify_done(file_hash)

except Exception as e:
print(f"Error: {e}")
eel.update_processing_log(f"Error: {str(e)}")()
finally:
[Link] = original_stderr
self.is_processing = False

def _generate_reverb_only(self, vocab_path, file_hash):


reverb_path = [Link](self.rev_dir, file_hash + "_wet.wav")
if not [Link](reverb_path) and [Link](vocab_path):
print("Generating Ambience...")
with [Link](vocab_path) as f:
vocal_data = [Link](dtype='float32', always_2d=True)

center = (vocal_data[:, 0] + vocal_data[:, 1]) * 0.5


with [Link](self.ir_path) as f:
ir = [Link](dtype='float32')

if [Link] > 1: ir = ir[:, 0]


wet_l = fftconvolve(center, ir, mode='full')[:len(center)]
wet_stereo = [Link]([wet_l, wet_l], axis=1).astype(np.float32)
if [Link]([Link](wet_stereo)) > 0: wet_stereo /=
[Link]([Link](wet_stereo))
wet_stereo *= 0.8
[Link](reverb_path, wet_stereo, 44100)

def _notify_done(self, file_hash):


folder = [Link](self.iso_dir, file_hash)
paths = {
"vocals": [Link](folder, "[Link]"),
"accompaniment": [Link](folder, "[Link]"),
"reverb": [Link](self.rev_dir, file_hash + "_wet.wav"),
"hash": file_hash
}
if self.on_process_complete: self.on_process_complete(paths)

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\[Link]
================================================================================

import os
# extensions to scan
EXTENSIONS = (".html", ".css", ".js", ".py", ".pyw")

# output file
OUTPUT_FILE = "all_code_dump.txt"

def scan_and_write(base_dir):
with open(OUTPUT_FILE, "w", encoding="utf-8", errors="ignore") as out:
for root, _, files in [Link](base_dir):
for file in files:
if [Link]().endswith(EXTENSIONS):
file_path = [Link](root, file)

[Link]("=" * 80 + "\n")
[Link](f"PATH: {file_path}\n")
[Link]("=" * 80 + "\n\n")

try:
with open(file_path, "r", encoding="utf-8",
errors="ignore") as f:
[Link]([Link]())
except Exception as e:
[Link](f"[ERROR READING FILE] {e}")

[Link]("\n\n")

if __name__ == "__main__":
scan_and_write([Link]())
print("Done. All code dumped into all_code_dump.txt")

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\[Link]
================================================================================

import subprocess, os
from pathlib import Path

BASE_DIR = Path(__file__).[Link]()
AUDIO_FILE = BASE_DIR / "ClariS - Destiny.mp3"
OUTPUT_DIR = BASE_DIR / "demucs_out"
FFMPEG_DIR = BASE_DIR / "ffmpeg"

if not AUDIO_FILE.exists():
raise FileNotFoundError(f"Missing: {AUDIO_FILE}")

# add local ffmpeg to PATH for this subprocess only


env = [Link]()
env["PATH"] = str(FFMPEG_DIR) + [Link] + [Link]("PATH", "")

cmd = [
"python", "-m", "demucs",
"-n", "htdemucs",
"--two-stems", "vocals",
str(AUDIO_FILE),
"-o", str(OUTPUT_DIR)
]

# run from a plain CMD (not VS Code debugger) to avoid weird debugpy imports
[Link](cmd, check=True, env=env)
print("Vocals + music DONE 🔥")

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\lyrics_engine.py
================================================================================

import os
import requests
import re
from [Link] import HTTPAdapter
from [Link] import Retry

class LyricsEngine:
def __init__(self):
self.base_url = "[Link]
self.lyrics_dir = [Link]([Link]("~"), "Music",
"Lyrics")
[Link](self.lyrics_dir, exist_ok=True)

# --- FIX: Create a Session with User-Agent & Retries ---


[Link] = [Link]()

# 1. Add User-Agent (Essential: prevents server from blocking you as a


bot)
[Link]({
'User-Agent': 'MaterialMusicPlayer/1.0 (compatible; MSIE 10.0;
Windows NT 6.1; Trident/6.0)'
})

# 2. Configure Retries (Helps with connection drops/timeouts)


retry_strategy = Retry(
total=3, # Try 3 times
backoff_factor=1, # Wait 1s, 2s, 4s between retries
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
[Link]("[Link] adapter)
[Link]("[Link] adapter)

def get_synced_lyrics(self, title, artist, duration_sec=None):


"""
Fetches synced lyrics from LRCLIB.
Returns the raw string of the LRC file (e.g. "[00:12.34] Hello").
"""
try:
print(f"[LyricsEngine] Searching LRCLIB for: {title} - {artist}")

params = {
'artist_name': artist,
'track_name': title,
}

if duration_sec and duration_sec > 1:


params['duration'] = int(duration_sec)

# Use [Link] instead of [Link]


response = [Link](self.base_url, params=params,
timeout=10)

if response.status_code == 200:
data = [Link]()
return [Link]('syncedLyrics', None)
elif response.status_code == 404:
print("[LyricsEngine] Lyrics not found on LRCLIB.")
return None
else:
print(f"[LyricsEngine] API Error: {response.status_code}")
return None

except Exception as e:
print(f"[LyricsEngine] Connection Error: {e}")
return None

def parse_lrc(self, lrc_string):


"""
Parses a raw LRC string into the list format needed by the UI:
[{"time": 12.5, "text": "Lyrics line"}, ...]
"""
if not lrc_string: return []

lines = []
regex = r'\[(\d{2}):(\d{2})\.(\d{2,3})\](.*)'

for line in lrc_string.split('\n'):


match = [Link](regex, line)
if match:
m = int([Link](1))
s = int([Link](2))
ms_part = [Link](3)

if len(ms_part) == 2:
ms = int(ms_part) / 100
else:
ms = int(ms_part) / 1000

total_sec = (m * 60) + s + ms
text = [Link](4).strip()

if text:
[Link]({"time": total_sec, "text": text})

return lines

def save_lrc(self, lrc_string, song_title):


"""Saves the raw LRC string to a file."""
if not lrc_string: return

# Safe filename
safe_title = "".join([c for c in song_title if [Link]() or c=='
']).strip()
filename = safe_title + ".lrc"
path = [Link](self.lyrics_dir, filename)

with open(path, "w", encoding='utf-8') as f:


[Link](lrc_string)

print(f"[LyricsEngine] Saved to: {path}")


return path

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\lyrics_mgr.py
================================================================================

import os
import json
import re
import hashlib
import requests
from [Link] import HTTPAdapter
from [Link] import Retry
from yt_mgr import YtManager

class LyricsManager:
def __init__(self):
[Link] = [Link]("~")
self.hidden_dir = [Link]([Link], "Music",
".MaterialMusicHidden", "lyrics_cache")
[Link](self.hidden_dir, exist_ok=True)

[Link] = [Link]()
[Link]({'User-Agent': 'MaterialMusicPlayer/2.0'})
retry = Retry(total=3, backoff_factor=1, status_forcelist=[429, 500,
502])
adapter = HTTPAdapter(max_retries=retry)
[Link]("[Link] adapter)
[Link]("[Link] adapter)

self.yt_mgr = YtManager()

def _get_cache_path(self, title, artist):


unique_str = f"{title}-{artist}".lower().encode('utf-8')
file_hash = hashlib.md5(unique_str).hexdigest()
return [Link](self.hidden_dir, file_hash + ".json")

def get_lyrics(self, title, artist, duration_sec=None, is_stream_mode=False,


video_id=None):
cache_path = self._get_cache_path(title, artist)

# 1. CHECK CACHE
if [Link](cache_path) and not is_stream_mode:
try:
with open(cache_path, 'r', encoding='utf-8') as f:
data = [Link](f)
if data: return data
except: pass

# 2. FETCH LRCLIB (Synced)


lyrics_data = self._fetch_lrclib(title, artist, duration_sec)

# 3. ROBUST FALLBACK: If LRCLib fails OR returns very few lines


(static), try YouTube Transcript
# This gets you word-by-word timing precision if available on YT.
if (not lyrics_data or (len(lyrics_data) < 5 and video_id)) and
video_id:
print(f"[LyricsMgr] LRCLib empty or static. Trying YouTube
Transcript for: {video_id}")
yt_transcript = self.yt_mgr.get_synced_transcript(video_id)
if yt_transcript:
lyrics_data = yt_transcript # Prioritize the timed transcript

# 5. SAVE
if lyrics_data:
if lyrics_data[0]['time'] != -1:
self._calculate_line_durations(lyrics_data)

if not is_stream_mode:
try:
with open(cache_path, 'w', encoding='utf-8') as f:
[Link](lyrics_data, f)
except Exception as e:
print(f"[LyricsMgr] Failed to save cache: {e}")

return lyrics_data
def _parse_lrc_string(self, lrc_string):
if not lrc_string: return None
lines = []
regex = r'\[(\d{2}):(\d{2})\.(\d{2,3})\](.*)'
for line in lrc_string.split('\n'):
match = [Link](regex, line)
if match:
m, s = int([Link](1)), int([Link](2))
ms_str = [Link](3)
ms = int(ms_str) / (10 ** len(ms_str))
total_sec = (m * 60) + s + ms
text = [Link](4).strip()
if text:
[Link]({"time": total_sec, "text": text, "duration":
0.0})
return lines if lines else None

def _fetch_lrclib(self, title, artist, duration):


try:
url = "[Link]
params = {'artist_name': artist, 'track_name': title}
if duration: params['duration'] = int(duration)
res = [Link](url, params=params, timeout=5)
if res.status_code == 200:
lrc_str = [Link]().get('syncedLyrics', '')
return self._parse_lrc_string(lrc_str)
except: return None

def _calculate_line_durations(self, lines):


for i in range(len(lines)):
curr = lines[i]
if i < len(lines) - 1:
curr['duration'] = lines[i+1]['time'] - curr['time']
else:
curr['duration'] = 5.0

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\[Link]
================================================================================

import sys, os
import eel, threading, time, webview, gc, uuid, socket
from audio_core import AudioCore
from audio_fx import AudioEffects, _sha1_of_file
from metadata_mgr import MetadataManager
from lyrics_mgr import LyricsManager
from yt_mgr import YtManager
from setup_assets import AssetManager
import sounddevice as sd
import json

script_dir = [Link]([Link](__file__))
web_dir = [Link](script_dir, 'web')
[Link]["PATH"] += [Link] + script_dir

[Link](web_dir)

fx_engine = AudioEffects()
core_engine = AudioCore(fx_engine)
meta_mgr = MetadataManager()
lyrics_mgr = LyricsManager()
yt_mgr = YtManager()
playlist = meta_mgr.get_library()
current_track_index = -1
current_track_path = ""

def _on_processing_done(paths):
global current_track_path
if current_track_path and paths['hash'] ==
_sha1_of_file(current_track_path):
core_engine.inject_hq_stems(paths)
meta_mgr.update_track_extra(current_track_path, 'has_stems', True)
try: eel.on_hq_ready()()
except Exception as e: print(f"Eel callback error: {e}")

fx_engine.on_process_complete = _on_processing_done

def _start_job(work_fn, on_done, on_error):


job_id = uuid.uuid4().hex
def run():
try:
result = work_fn()
[Link](0.02)
try: on_done(job_id, result)()
except: pass
except Exception as e:
try: on_error(job_id, str(e))()
except: pass
[Link](target=run, daemon=True).start()
return job_id

@[Link]
def start_asset_setup():
def work():
mgr = AssetManager(progress_callback=lambda p,s:
eel.update_setup_progress(p,s)() if eel._js_call else None)
mgr.setup_assets()
return True
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def scan_library():
def work():
global playlist
def on_progress(current, total, filename):
pct = (current / total) * 100 if total > 0 else 0
try: eel.update_loading_progress(pct, filename)()
except: pass
playlist = meta_mgr.rescan(progress_callback=on_progress)
return playlist
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def get_cached_library(): return meta_mgr.library

@[Link]
def read_library_cache():
try:
if [Link](meta_mgr.cache_file):
with open(meta_mgr.cache_file, 'r', encoding='utf-8') as f:
data = [Link](f)
return data if isinstance(data, list) else []
except: pass
return []

@[Link]
def load_track(index, start_pos=0):
def work():
global current_track_index, playlist, current_track_path
if not (0 <= index < len(playlist)): return None
track = playlist[index]
path = track['path']
if not [Link](path):
meta_mgr.remove_track(path)
if index < len(playlist): del playlist[index]
return {"error": "File not found", "removed": True}

current_track_index = index
current_track_path = path
core_engine.load_source(path, is_stream=False, blocking=True)
if start_pos > 0: core_engine.set_pos(start_pos)
if core_engine.state != "PLAYING": core_engine.play_pause()

art_data = meta_mgr.get_art(path)
is_cached, _ = fx_engine.check_cache_status(path)
track['has_stems'] = is_cached
meta_mgr.update_track_extra(path, 'has_stems', is_cached)

return {
"title": track['title'], "artist": track['artist'], "path": path,
"art": art_data, "colors": [Link]('colors', {}),
"duration": core_engine.duration_ms, "state": "PLAYING",
"has_stems": is_cached, "is_stream": False,
"offset_o": [Link]('offset_o', 0.0), "offset_k":
[Link]('offset_k', 0.0),
"karaoke_id": [Link]('karaoke_id', None),
"ignore_sync_warning": [Link]('ignore_sync_warning', False)
}
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def get_ai_hardware(): return fx_engine.get_hardware_capabilities()

@[Link]
def trigger_hq_processing(quality_mode="vocals", use_gpu=False):
if not current_track_path or not [Link](current_track_path): return
False
if core_engine.is_stream_mode: return False
fx_engine.process_specific_track(current_track_path, quality_mode, use_gpu)
return True

@[Link]
def delete_generated_stems(path):
core_engine.stop_stream_only()
try: [Link]()
except: pass
core_engine.reset_stems()
[Link]()
[Link](0.1)
success = fx_engine.delete_cached_stems(path)
if not success:
[Link](1.0)
[Link]()
success = fx_engine.delete_cached_stems(path)
if success:
meta_mgr.update_track_extra(path, 'has_stems', False)
if current_track_path == path:
core_engine.toggle_ai_mode(force_mode=False)
return success

@[Link]
def trigger_cloud_processing():
if not current_track_path or not [Link](current_track_path): return
False
if core_engine.is_stream_mode: return False
fx_engine.prepare_cloud(current_track_path)
return True

@[Link]
def launch_cloud_workspace(): fx_engine.launch_cloud()
@[Link]
def cancel_cloud_processing(): fx_engine.cancel_cloud()

@[Link]
def get_hq_status():
is_cached, _ = fx_engine.check_cache_status(current_track_path) if
current_track_path else (False, None)
return {
"active": core_engine.ai_mode, "processing": fx_engine.is_processing,
"cloud_active": fx_engine.cloud_mgr.active, "stems_cached": is_cached
}

@[Link]
def set_hq_mode(enabled): return core_engine.toggle_ai_mode(enabled)

@[Link]
def search_yt(query, filter_type='songs'):
return _start_job(lambda: yt_mgr.search(query, filter_type),
eel.on_job_result, eel.on_job_error)

@[Link]
def stream_yt(vid, title, artist, img):
def work():
path = yt_mgr.get_cached_path(vid)
if path and [Link](path):
global current_track_path
current_track_path = ""
core_engine.load_source(path, is_stream=False, blocking=True)
core_engine.play_pause()
return {
"title": title, "artist": artist, "art": img,
"colors": {"accent": "#d0bcff", "accent_rgb": "208,188,255",
"bg_stop_rgb": "20,5,40"},
"duration": core_engine.duration_ms, "state": "PLAYING",
"is_stream": True
}
return None
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def find_and_play_best_karaoke(title, artist, target_dur_ms, current_pos_ms):
def work():
target_sec = target_dur_ms / 1000
results = yt_mgr.search_karaoke(f"{title} {artist}")
if not results: return {"success": False, "msg": "No karaoke found"}
best_match = None
min_diff = 999.0
for r in results:
try:
min_part, sec_part = map(int, r['duration'].split(':'))
r_duration_sec = min_part * 60 + sec_part
except: continue
diff = abs(r_duration_sec - target_sec)
if diff < min_diff:
min_diff = diff
best_match = r
if not best_match or min_diff > 15.0: return {"success": False, "msg":
"No match found"}

path = yt_mgr.get_cached_path(best_match['videoId'])
if path:
core_engine.load_source(path, is_stream=False, blocking=True)
if current_pos_ms > 0: core_engine.set_pos(current_pos_ms)
if core_engine.state != "PLAYING": core_engine.play_pause()
return {"success": True, "title": best_match['title'], "diff":
min_diff, "warn": min_diff > 2.0, "video_id": best_match['videoId']}
return {"success": False, "msg": "Could not load karaoke"}
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def stop_stream_only(): core_engine.stop_stream_only()

# --- FIX: Updated arguments for thumbnail_url ---


@[Link]
def record_yt_track(vid, title, artist, thumbnail_url=None):
def run_record():
try:
try: eel.update_status("Starting Download...")()
except: pass
path = yt_mgr.record_track(vid, title, artist, thumbnail_url)
if path:
meta_mgr.add_track(path)
global playlist
playlist = meta_mgr.get_library()
eel.update_status("Download Complete")()
try: eel.on_record_complete(playlist, title)()
except: pass
else: eel.update_status("Track Exists or Failed")()
except Exception as e:
print(f"Record YT Track Error: {e}")
try: eel.update_status(f"Failed: {e}")()
except: pass
[Link](target=run_record, daemon=True).start()
return True

@[Link]
def save_track_offset(path, key, value): return
meta_mgr.update_track_extra(path, key, value)

@[Link]
def get_karaoke_preview(video_id):
return _start_job(lambda: yt_mgr.get_direct_url(video_id),
eel.on_job_result, eel.on_job_error)

@[Link]
def apply_new_karaoke(original_path, video_id, current_pos_ms=0):
def work():
new_audio_path = yt_mgr.get_cached_path(video_id)
if new_audio_path and [Link](new_audio_path):
meta_mgr.update_track_extra(original_path, 'karaoke_id', video_id)
core_engine.load_source(new_audio_path, is_stream=False,
blocking=True)
if current_pos_ms > 0: core_engine.set_pos(current_pos_ms)
if core_engine.state != "PLAYING": core_engine.play_pause()
return True
return False
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def play_pause(): return core_engine.play_pause()
@[Link]
def next_track():
global current_track_index
if not playlist: return None
return load_track((current_track_index + 1) % len(playlist))
@[Link]
def prev_track():
global current_track_index
if not playlist: return None
return load_track((current_track_index - 1 + len(playlist)) % len(playlist))
@[Link]
def set_seek(ms): core_engine.set_pos(ms)
@[Link]
def get_player_state(): return core_engine.get_state()
@[Link]
def get_current_settings():
return {"speed": core_engine.playback_speed, "volume": core_engine.vol,
"reverb": core_engine.reverb_vol, "vocal": core_engine.vocal_vol, "music":
core_engine.music_vol}

@[Link]
def set_speed(val): core_engine.set_speed(val)
@[Link]
def set_volume(val): core_engine.set_volume(val)
@[Link]
def set_vocal_volume(val): core_engine.set_vocal_vol(val)
@[Link]
def set_music_volume(val): core_engine.set_music_vol(val)
@[Link]
def set_reverb(val): core_engine.set_reverb_vol(val)

# --- FIX: Updated arguments for video_id ---


@[Link]
def get_lyrics_for_track(title, artist, video_id=None):
def work():
duration = core_engine.duration_ms / 1000
is_stream_mode = core_engine.is_stream_mode
return lyrics_mgr.get_lyrics(title, artist, duration,
is_stream_mode=is_stream_mode, video_id=video_id)
return _start_job(work, eel.on_job_result, eel.on_job_error)

@[Link]
def delete_track_robust(index, path_from_ui):
global playlist, current_track_index, current_track_path
if index < 0 or index >= len(playlist) or playlist[index]['path'] !=
path_from_ui:
try: eel.refresh_ui_library(playlist)()
except: pass
return {"success": False, "rescan": True, "error": "Index mismatch"}
track_to_delete = playlist[index]
if current_track_path == track_to_delete['path']:
core_engine.stop_stream_only()
current_track_index = -1
current_track_path = ""
file_deleted = False
try:
[Link](track_to_delete['path'])
file_deleted = True
except OSError:
if fx_engine.delete_cached_stems(track_to_delete['path']):
[Link](0.1)
try:
[Link](track_to_delete['path'])
file_deleted = True
except: pass
if not file_deleted and [Link](track_to_delete['path']):
return {"success": False, "error": "Could not delete physical file"}
meta_mgr.remove_track(track_to_delete['path'])
if index < len(playlist) and playlist[index]['path'] ==
track_to_delete['path']:
[Link](index)
if current_track_index != -1 and index < current_track_index:
current_track_index -= 1
elif current_track_index != -1 and index == current_track_index:
current_track_index = -1
return {"success": True}

webview_window = None
class Api: pass
def run_gui():
global webview_window
webview_window = webview.create_window('Material Music',
'[Link] js_api=Api(), width=1280, height=800,
min_size=(940, 600), frameless=False, transparent=False, text_select=False)
[Link](debug=False, http_server=False)

def wait_for_server(port, timeout=10.0):


start_time = [Link]()
while [Link]() - start_time < timeout:
try:
with socket.create_connection(("localhost", port), timeout=0.5):
return True
except: [Link](0.1)
return False

if __name__ == "__main__":
eel_port = 8000
eel_thread = [Link](target=lambda: [Link]('[Link]',
mode=None, host='localhost', port=eel_port, block=True))
eel_thread.daemon = True
eel_thread.start()
if wait_for_server(eel_port): run_gui()
else: [Link](1)

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\metadata_mgr.py
================================================================================

import os, base64, io, json, colorsys


from mutagen import File
from mutagen.id3 import ID3, TIT2, TPE1, APIC
from PIL import Image

class MetadataManager:
def __init__(self):
self.cache_file =
[Link]([Link]([Link](__file__)), "library_cache.json")
[Link] = []
self._load_cache()

def _load_cache(self):
if [Link](self.cache_file):
try:
with open(self.cache_file, 'r', encoding='utf-8') as f:
[Link] = [Link](f)
# Sort fast by title, handle potential missing 'title' key
gracefully
[Link](key=lambda x: [Link]('title', '').lower())
print(f"[Metadata] Loaded {len([Link])} tracks from
cache.")
except Exception as e:
print(f"[Metadata] Cache load failed, clearing cache: {e}")
[Link] = [] # Clear corrupted cache
else:
[Link] = []

def save_cache(self):
try:
# Ensure sorting before saving
[Link](key=lambda x: [Link]('title', '').lower())
with open(self.cache_file, 'w', encoding='utf-8') as f:
[Link]([Link], f, ensure_ascii=False, indent=4) # Added
indent for readability
except Exception as e:
print(f"[Metadata] Failed to save cache: {e}")

def get_library(self):
# Only return what is currently in memory (loaded from JSON on init).
return [Link]

def rescan(self, progress_callback=None):


print("[Metadata] Starting full library rescan...")
[Link] = [] # Clear current library for a fresh scan
seen_paths = set()
music_dir = [Link]("MUSICPLAYER_MUSIC_DIR",
[Link]([Link]("~"), "Music"))

# --- IMPORTANT CHANGE: Only scan the main music directory for the
library view ---
# We explicitly exclude hidden folders and cache directories from the
main library listing.
scan_roots = [music_dir]

all_audio_paths = list(self._iter_audio_paths(scan_roots))
total_files = len(all_audio_paths)

if total_files == 0:
self.save_cache()
if progress_callback:
try: progress_callback(100, 100, "No audio files found.")
except: pass
print("[Metadata] No audio files found during rescan.")
return [Link]

processed = 0
for full_path in all_audio_paths:
# Check for duplicates, as _iter_audio_paths might yield paths
already processed
if full_path in seen_paths:
continue

try:
meta = self._get_meta(full_path)
# Also ensure a track with the exact same path doesn't already
exist in the list
if not any(t['path'] == meta['path'] for t in [Link]):
[Link](meta)
seen_paths.add(full_path)
except Exception as e:
print(f"[Rescan] metadata error for {full_path}: {e}")

processed += 1
if progress_callback: # Report progress for every file
try: progress_callback(processed, total_files,
[Link](full_path))
except: pass
# print(f"[Rescan] processed {processed}/{total_files} -
{[Link](full_path)}") # Uncomment for verbose console output

self.save_cache()
print(f"[Metadata] Rescan complete. Found {len([Link])} unique
tracks.")
return [Link]

def _iter_audio_paths(self, roots):


"""
Generator to walk through given root directories and yield audio file
paths,
excluding specific hidden/cache directories.
"""
audio_ext = ('.mp3', '.flac', '.wav', '.ogg', '.m4a')

def on_walk_error(err):
print(f"[Rescan] directory walk error: {err}")

for base in roots:


if not base or not [Link](base):
continue

for root, dirs, files in [Link](base, topdown=True,


onerror=on_walk_error, followlinks=False):
root_abs = [Link](root)

# Filter directories *in-place* to prevent [Link] from entering


them
# Exclude MaterialMusicCache and .MaterialMusicHidden from
library view
dirs_to_keep = []
for dname in dirs:
full_dir_path = [Link](root_abs, dname)
if "_MaterialMusicCache" in dname or ".MaterialMusicHidden"
in dname:
# Skip this directory and its subdirectories
continue
if [Link](full_dir_path):
# Optionally skip symlinks to prevent infinite loops or
unwanted scans
continue
dirs_to_keep.append(dname)
dirs[:] = dirs_to_keep # Modify dirs list in-place for [Link]

for f in files:
if not [Link]().endswith(audio_ext):
continue
yield [Link]([Link](root_abs, f))

def add_track(self, path):


"""Adds a single track to the library cache."""
path = [Link](path)
if not [Link](path):
print(f"[Metadata] Attempted to add non-existent track: {path}")
return

# Remove old entry if it exists (e.g., updated metadata or re-downloaded


to new location)
[Link] = [t for t in [Link] if t['path'] != path]
try:
meta = self._get_meta(path)
[Link](meta)
self.save_cache()
print(f"[Metadata] Added track: {[Link]('title', path)}")
except Exception as e:
print(f"[Metadata] Error adding track {path}: {e}")

def remove_track(self, path):


"""Removes a track from the library cache."""
initial_len = len([Link])
[Link] = [t for t in [Link] if t['path'] != path]
if len([Link]) < initial_len:
self.save_cache()
print(f"[Metadata] Removed track from cache: {path}")
else:
print(f"[Metadata] Track not found in cache to remove: {path}")

def update_track_extra(self, path, key, value):


"""Updates auxiliary data (offset, karaoke_id, ignore_sync_warning,
has_stems) in the cache."""
for track in [Link]:
if track['path'] == path:
track[key] = value
self.save_cache()
return True
print(f"[Metadata] Track not found in cache for update: {path} - {key}
={value}")
return False

def _get_accent_colors(self, img):


"""Extracts a vibrant accent and a dark background color from an
image."""
try:
[Link]((100, 100))
# Use MEDIANCUT for a better color distribution often
paletted = [Link](colors=8, method=[Link])
palette = [Link]()
colors = [tuple(palette[i:i+3]) for i in range(0, len(palette), 3)]

best_color = (208, 188, 255) # Default --prim


max_score = 0

for r, g, b in colors:
# Convert to HSV to evaluate vibrancy (Saturation, Value)
h, s, v = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)
# Filter out dull/very dark/very bright colors
if s < 0.3 or v < 0.25 or v > 0.95:
continue
score = (s * 0.7) + (v * 0.3) # Prioritize saturation for
"accent"
if score > max_score:
max_score = score
best_color = (r, g, b)

r, g, b = best_color
h, s, v = colorsys.rgb_to_hsv(r/255.0, g/255.0, b/255.0)

# 1. Accent Color (--prim): A slightly lighter, more saturated


version of the best_color
s_accent = min(1.0, s * 1.2)
v_accent = min(1.0, v + 0.1)
r_a, g_a, b_a = [int(c * 255) for c in colorsys.hsv_to_rgb(h,
s_accent, v_accent)]
accent_hex = '#{:02x}{:02x}{:02x}'.format(r_a, g_a, b_a)

# 2. Background Stop Color: A very dark, slightly desaturated


version
s_bg = max(0.4, s * 0.8) # Ensure some color remains
v_bg = max(0.1, v * 0.3) # Make it dark
r_b, g_b, b_b = [int(c * 255) for c in colorsys.hsv_to_rgb(h, s_bg,
v_bg)]

return {
"accent": accent_hex,
"accent_rgb": f"{r_a},{g_a},{b_a}",
"bg_stop_rgb": f"{r_b},{g_b},{b_b}"
}
except Exception as e:
# print(f"Color extraction error: {e}") # Suppress for normal
operation
return { "accent": "#d0bcff", "accent_rgb": "208,188,255",
"bg_stop_rgb": "20,5,40" }

def _extract_art_data(self, f):


"""
Extracts album art data from various audio file formats using Mutagen.
Supports ID3 tags (MP3) and other common tags for FLAC, M4A, etc.
"""
try:
if hasattr(f, 'pictures') and [Link]: # For FLAC, M4A
(mp4.MP4Tags), OGG
return [Link][0].data
if [Link]: # For ID3 tags (MP3)
for key in [Link]():
if 'APIC' in key: # Common key for album art
return [Link][key].data
# Fallback for some non-standard/older tag structures
if 'covr' in [Link]: # Specific to some M4A files
return [Link]['covr'][0]
except:
pass # Suppress internal mutagen errors
return None

def _get_meta(self, path):


"""
Extracts comprehensive metadata (title, artist, path, thumbnail, colors,
custom data)
from an audio file.
"""
# Default metadata if parsing fails
meta = {
"title": [Link]([Link](path))[0],
"artist": "Unknown",
"path": path,
"thumb": None,
"colors": { "accent": "#d0bcff", "accent_rgb": "208,188,255",
"bg_stop_rgb": "20,5,40" },
"offset_o": 0.0,
"offset_k": 0.0,
"karaoke_id": None,
"ignore_sync_warning": False,
"has_stems": False # Default to False, updated by AudioEffects if
processing occurs
}

try:
# Mutagen's File() automatically detects format and loads
appropriate tags
f = File(path)
if not f: return meta # File not readable by Mutagen

# Prioritize standard ID3/Mutagen tags


# For MP3s with ID3, TIT2 (title) and TPE1 (artist) are common
if 'TIT2' in f: meta['title'] = str(f['TIT2'])
elif 'title' in f: meta['title'] = str(f['title'][0]) # For some
non-ID3 formats
if 'TPE1' in f: meta['artist'] = str(f['TPE1'])
elif 'artist' in f: meta['artist'] = str(f['artist'][0]) # For some
non-ID3 formats

# Fallback: Parse title and artist from filename if metadata is


generic
if meta['title'] == [Link]([Link](path))[0] and
" - " in meta['title']:
parts = meta['title'].split(" - ", 1)
if len(parts) == 2:
meta['artist'], meta['title'] = parts[0].strip(),
parts[1].strip()

# Extract and process album art


art_data = self._extract_art_data(f)
if art_data:
try:
img = [Link]([Link](art_data)).convert('RGB')
meta['colors'] = self._get_accent_colors([Link]()) #
Generate accent colors
[Link]((200, 200)) # Create a small thumbnail for UI
buf = [Link]()
[Link](buf, format="JPEG", quality=70)
meta['thumb'] = "data:image/jpeg;base64," +
base64.b64encode([Link]()).decode()
except Exception as e:
# print(f"Art extraction/processing error for {path}: {e}")
pass # Keep default thumbnail/colors if art processing fails
del f # Explicitly close the file handle to prevent locks
except Exception as e:
# print(f"General metadata parsing error for {path}: {e}")
pass # Keep default metadata if parsing fails

return meta

def get_art(self, path):


"""
Retrieves a larger, base64-encoded album art image for the player's main
display.
"""
try:
f = File(path)
if not f: return None
art_data = self._extract_art_data(f)
del f # Explicitly close
if art_data:
img = [Link]([Link](art_data)).convert('RGB')
[Link]((800, 800)) # Larger size for main player art
buf = [Link]()
[Link](buf, format="JPEG", quality=85)
return "data:image/jpeg;base64," +
base64.b64encode([Link]()).decode()
except Exception as e:
print(f"Error getting album art for {path}: {e}")
return None

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\setup_assets.py
================================================================================

import os
import requests
import re
import threading

# Setup paths
SCRIPT_DIR = [Link]([Link](__file__))
WEB_DIR = [Link](SCRIPT_DIR, 'web')
CACHE_DIR = [Link](WEB_DIR, 'assets', 'cache')
FONTS_DIR = [Link](CACHE_DIR, 'fonts')

CSS_URLS = [
("material_symbols", "[Link]
family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-
50..200"),
("inter", "[Link]
family=Inter:wght@400;500;600;700&display=swap"),
("jetbrains_mono", "[Link]
family=JetBrains+Mono:wght@500&display=swap")
]

class AssetManager:
def __init__(self, progress_callback=None):
self.progress_callback = progress_callback
[Link](CACHE_DIR, exist_ok=True)
[Link](FONTS_DIR, exist_ok=True)

def _report(self, pct, status):


if self.progress_callback:
self.progress_callback(pct, status)
else:
print(f"[{pct:.1f}%] {status}")

def download_file(self, url, path):


if [Link](path):
return True
try:
response = [Link](url, timeout=15)
response.raise_for_status()
with open(path, 'wb') as f:
[Link]([Link])
return True
except Exception as e:
print(f"Failed to download {url}: {e}")
return False

def process_css(self, name, url, base_pct, weight):


try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/[Link] Safari/537.36'
}
resp = [Link](url, headers=headers, timeout=10)
resp.raise_for_status()
css_content = [Link]

urls = [Link](r'url\(([Link] css_content)


local_css = css_content
total_fonts = len(urls)
for i, font_url in enumerate(urls):
ext = 'woff2'
if '.' in font_url.split('/')[-1]:
ext = font_url.split('/')[-1].split('.')[-1]
if len(ext) > 5: ext = 'woff2'

filename = f"{name}_{i}.{ext}"
local_path = [Link](FONTS_DIR, filename)

# Update progress for each font


current_pct = base_pct + (i / total_fonts) * weight
self._report(current_pct, f"Downloading {name} font
{i+1}/{total_fonts}")

if self.download_file(font_url, local_path):
local_css = local_css.replace(font_url, f"fonts/{filename}")

css_path = [Link](CACHE_DIR, f"{name}.css")


with open(css_path, 'w', encoding='utf-8') as f:
[Link](local_css)
return True
except Exception as e:
print(f"Error processing {name}: {e}")
return False

def setup_assets(self):
self._report(5, "Checking assets...")
num_configs = len(CSS_URLS)
weight_per_config = 90 / num_configs

for i, (name, url) in enumerate(CSS_URLS):


base = 5 + (i * weight_per_config)
self.process_css(name, url, base, weight_per_config)

self._report(100, "Ready")

if __name__ == "__main__":
mgr = AssetManager()
mgr.setup_assets()
print("Done!")

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\verify_minimal.py
================================================================================

from ytmusicapi import YTMusic

def test_minimal():
yt = YTMusic()
try:
print("Searching Songs...")
res_songs = [Link]("Bohemian Rhapsody karaoke", filter='songs',
limit=2)
print(f"Songs found: {len(res_songs)}")

print("Searching Videos...")
res_videos = [Link]("Bohemian Rhapsody karaoke", filter='videos',
limit=2)
print(f"Videos found: {len(res_videos)}")

except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
test_minimal()

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\verify_search.py
================================================================================

import sys
import os

# Add script dir to [Link]


script_dir = [Link]([Link](__file__))
[Link](script_dir)

from yt_mgr import YtManager

def test_search():
mgr = YtManager()
query = "Bohemian Rhapsody karaoke"

print(f"--- Searching for '{query}' (Filter: SONGS) ---")


songs = [Link](query, filter_type='songs')
print(f"Found {len(songs)} song results.")
for s in songs[:3]:
print(f" - {s['title']} ({s['videoId']})")

print(f"\n--- Searching for '{query}' (Filter: VIDEOS) ---")


videos = [Link](query, filter_type='videos')
print(f"Found {len(videos)} video results.")
for v in videos[:3]:
print(f" - {v['title']} ({v['videoId']})")

if len(songs) > 0 and len(videos) > 0:


print("\nSUCCESS: Both filters returned results.")
else:
print("\nFAILURE: One or both filters returned no results.")

if __name__ == "__main__":
test_search()

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\yt_mgr.py
================================================================================

import os
import time
import yt_dlp
import eel
import requests
import re
import json
from ytmusicapi import YTMusic
from mutagen.id3 import ID3, TIT2, TPE1, APIC

class YtManager:
def __init__(self):
[Link] = YTMusic()
[Link] = [Link]("~")
self.music_dir = [Link]([Link], "Music")
self.hidden_dir = [Link]([Link], "Music",
".MaterialMusicHidden")

[Link](self.hidden_dir, exist_ok=True)
[Link](self.music_dir, exist_ok=True)
self._cleanup_old_files()

if [Link] == 'nt':
try:
import ctypes
[Link](self.hidden_dir, 0x02)
except: pass

def _cleanup_old_files(self):
try:
now = [Link]()
cutoff = now - 3600 # 1 hour
stream_dir = [Link](self.hidden_dir, "stream_cache")
if [Link](stream_dir):
for f in [Link](stream_dir):
fp = [Link](stream_dir, f)
if [Link](fp) and [Link](fp) < cutoff:
try: [Link](fp)
except: pass
except Exception as e:
print(f"[YtManager] Cleanup Error: {e}")

def _get_hq_thumb(self, thumbnails):


"""Helper to get high-res thumbnail from Google/YT URLs."""
if not thumbnails: return ''
url = thumbnails[-1]['url']
# Replace w120-h120 (or similar) with w1000-h1000 for high res
if '[Link]' in url or '[Link]' in url:
return [Link](r'w\d+-h\d+', 'w1000-h1000', url)
return url

def search(self, query, filter_type='songs'):


try:
results = [Link](query, filter=filter_type, limit=20)
parsed = []
for r in results:
v_id = [Link]('videoId')
if not v_id: continue

# Use HQ Thumbnail Logic


img = self._get_hq_thumb([Link]('thumbnails', []))

artists = [Link]('artists', [])


if isinstance(artists, list):
artist_text = ", ".join([a['name'] for a in artists])
else:
artist_text = "Unknown"

album = [Link]('album', {}).get('name', 'Single') if


[Link]('album') else 'Single'

[Link]({
"videoId": v_id,
"title": [Link]('title', 'Unknown'),
"artist": artist_text,
"album": album,
"duration": [Link]('duration', '0:00'),
"thumb": img
})
return parsed
except Exception as e:
print(f"Search Error: {e}")
return []

def get_cached_path(self, video_id):


cache_dir = [Link](self.hidden_dir, "stream_cache")
[Link](cache_dir, exist_ok=True)
expected_path = [Link](cache_dir, f"{video_id}.mp3")

if [Link](expected_path) and ([Link]() -


[Link](expected_path)) < (24 * 3600):
[Link](expected_path, ([Link](), [Link]()))
return expected_path

ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': [Link](cache_dir, f"{video_id}_temp.%(ext)s"),
'postprocessors': [{'key': 'FFmpegExtractAudio','preferredcodec':
'mp3','preferredquality': '192'}],
'progress_hooks': [self._dl_progress_hook],
'quiet': True,
'no_warnings': True
}

try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
[Link]([f"[Link]

potential_file = [Link](cache_dir, f"{video_id}_temp.mp3")


if [Link](potential_file):
[Link](potential_file, expected_path)
return expected_path
except Exception as e:
print(f"DL Error: {e}")
return None

def _dl_progress_hook(self, d):


if d['status'] == 'downloading':
p = [Link]('_percent_str', '0%').replace('%','')
try:
percent_val = float([Link](r'[^0-9.]', '', p))
eel.update_loading_progress(percent_val, f"Buffering... {p}%")()
except Exception: pass

def search_karaoke(self, query):


return [Link](query, filter_type='videos')

def record_track(self, video_id, title, artist, thumbnail_url=None):


safe_title = "".join([c for c in title if [Link]() or c in "
-_"]).strip()
final_path = [Link](self.music_dir, f"{safe_title}.mp3")
temp_download_dir = [Link](self.hidden_dir, "temp_downloads")
[Link](temp_download_dir, exist_ok=True)

if [Link](final_path):
print(f"Track already exists: {final_path}")
return None

ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': [Link](temp_download_dir, f"{safe_title}_temp.%
(ext)s"),
'postprocessors': [{'key': 'FFmpegExtractAudio','preferredcodec':
'mp3','preferredquality': '192'}],
'quiet': True,
'no_warnings': True
}

try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
[Link]([f"[Link]

temp_file = [Link](temp_download_dir, f"{safe_title}


_temp.mp3")
if [Link](temp_file):
[Link](temp_file, final_path)

# --- EMBED METADATA AND THUMBNAIL ---


try:
image_data = None
if thumbnail_url:
# Attempt HQ fetch for embedding
if '[Link]' in thumbnail_url:
thumbnail_url = [Link](r'w\d+-h\d+', 'w1000-h1000',
thumbnail_url)
try:
resp = [Link](thumbnail_url, timeout=10)
if resp.status_code == 200: image_data =
[Link]
except: pass

tags = ID3(final_path)
[Link](TIT2(encoding=3, text=title))
[Link](TPE1(encoding=3, text=artist))
if image_data:
[Link](APIC(
encoding=3,
mime='image/jpeg',
type=3,
desc='Cover',
data=image_data
))
[Link](final_path)
except Exception as tag_error:
print(f"Tag error: {tag_error}")

return final_path
except Exception as e:
print(f"Record Error: {e}")
return None

def get_direct_url(self, video_id):


try:
ydl_opts = {'format': 'bestaudio[ext=m4a]/best', 'quiet': True,
'no_warnings': True}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(f"[Link]
v={video_id}", download=False)
return [Link]('url', None)
except Exception as e:
print(f"Get URL Error: {e}")
return None

# --- NEW: TRANSCRIPT / SUBTITLE SCRAPER ---


def get_synced_transcript(self, video_id):
print(f"[YtManager] Fetching transcript for {video_id}...")
try:
ydl_opts = {
'skip_download': True,
'writesubtitles': True,
'writeautomaticsub': True,
'subtitleslangs': ['en', 'en-US', 'en-GB', 'ja', 'ko'],
'quiet': True
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:


info = ydl.extract_info(f"[Link]
v={video_id}", download=False)

subs = [Link]('requested_subtitles')
if not subs and [Link]('subtitles'):
subs = info['subtitles'].get('en') or
info['subtitles'].get('en-US')
if not subs and [Link]('automatic_captions'):
subs = info['automatic_captions'].get('en') or
info['automatic_captions'].get('en-US')

if not subs: return None

sub_url = [Link]('url') if isinstance(subs, dict) else


subs[0].get('url')

if sub_url:
r = [Link](sub_url)
if r.status_code == 200:
content = [Link]
if 'json3' in sub_url or
[Link]().startswith('{'):
return self._parse_json3(content)
else:
return self._parse_vtt(content)
except Exception as e:
print(f"[YtManager] Transcript Error: {e}")
return None

def _parse_json3(self, content):


try:
data = [Link](content)
events = [Link]('events', [])
lyrics = []
for e in events:
if 'segs' in e and 'tStartMs' in e:
text = "".join([[Link]('utf8', '') for s in
e['segs']]).strip()
if not text or text == '\n': continue
time_sec = float(e['tStartMs']) / 1000.0
[Link]({'time': time_sec, 'text': text})
return lyrics
except: return None

def _parse_vtt(self, content):


lines = [Link]()
lyrics = []
current_time = -1
for line in lines:
line = [Link]()
if not line: continue
if '-->' in line:
parts = [Link](' --> ')[0].split(':')
try:
if len(parts) == 3:
h, m, s = float(parts[0]), float(parts[1]),
float(parts[2])
current_time = h*3600 + m*60 + s
elif len(parts) == 2:
m, s = float(parts[0]), float(parts[1])
current_time = m*60 + s
except: current_time = -1
elif current_time != -1:
text = [Link](r'<[^>]+>', '', line).strip()
if text:
[Link]({'time': current_time, 'text': text})
current_time = -1
return lyrics

def get_lyrics_fallback(self, title, artist):


try:
results = [Link](f"{title} {artist}", filter='songs',
limit=1)
if results and 'videoId' in results[0]:
vid = results[0]['videoId']
watch = [Link].get_watch_playlist(videoId=vid, limit=1)
if watch and 'lyrics' in watch:
l_res = [Link].get_lyrics(browseId=watch['lyrics'])
if l_res and 'lyrics' in l_res:
return l_res['lyrics']
except: pass
return None

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\web\[Link]
================================================================================

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Material Music</title>

<!-- EEL BRIDGE -->


<script type="text/javascript" src="/[Link]"></script>

<!-- ICONS & FONTS (OFFLINE) -->


<link rel="stylesheet" href="assets/cache/material_symbols.css">
<link rel="stylesheet" href="assets/cache/[Link]">
<link rel="stylesheet" href="assets/cache/jetbrains_mono.css">

<style>
/*
=========================================================================
1. CORE THEME & VARIABLES

========================================================================= */
:root {
--bg-body: #050505;
--glass-surface: rgba(255, 255, 255, 0.03);
--glass-border: rgba(255, 255, 255, 0.08);
--text-main: #ffffff;
--text-dim: rgba(255, 255, 255, 0.5);
--accent: #d0bcff;
--accent-rgb: 208, 188, 255;
--rad: 28px;
--nav-width: 80px;
--player-width: 420px;
}

* {
box-sizing: border-box;
outline: none;
user-select: none;
-webkit-user-drag: none;
}

body {
margin: 0;
padding: 16px;
font-family: 'Inter', sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
height: 100vh;
width: 100vw;
overflow: hidden;
display: grid;
grid-template-columns: var(--nav-width) 1fr var(--player-width);
gap: 16px;
background-image: radial-gradient(circle at 90% 90%, rgba(var(--
accent-rgb), 0.1), transparent 60%);
}

/* Utilities */
.truncate {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
}

::-webkit-scrollbar {
width: 0px;
background: transparent;
}

.icon {
font-family: 'Material Symbols Rounded';
font-size: 24px;
display: block;
}

.btn {
cursor: pointer;
transition: 0.2s;
border: none;
background: none;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
}

@keyframes spin {
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
}

/* SNAPPY SPLASH SCREEN */


#splash-screen {
position: fixed;
inset: 0;
background: #050505;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
transition: opacity 0.6s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.splash-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
opacity: 0;
animation: splashEntry 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}

@keyframes splashEntry {
0% {
opacity: 0;
transform: scale(0.95);
}

100% {
opacity: 1;
transform: scale(1);
}
}

.splash-logo {
width: 80px;
height: 80px;
background: linear-gradient(135deg, var(--accent), #fff);
border-radius: 24px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20px 50px rgba(var(--accent-rgb), 0.3);
margin-bottom: 10px;
}

.splash-logo .icon {
font-size: 40px;
color: #000;
}

.splash-text {
text-align: center;
}

.splash-text h1 {
font-size: 24px;
margin: 0 0 8px 0;
letter-spacing: -0.5px;
background: linear-gradient(to right, #fff, #aaa);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
}

.splash-text p {
font-size: 13px;
color: var(--text-dim);
margin: 0;
font-family: 'JetBrains Mono', monospace;
}

.splash-loader {
width: 200px;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 99px;
overflow: hidden;
position: relative;
margin-top: 10px;
}

.splash-bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 40%;
background: var(--accent);
border-radius: 99px;
animation: indeterminate 1.5s infinite linear;
}

@keyframes indeterminate {
0% {
left: -40%;
width: 40%;
}

50% {
left: 100%;
width: 40%;
}

100% {
left: 100%;
width: 40%;
}
}

/*
=========================================================================
2. NAVIGATION RAIL

========================================================================= */
.nav-rail {
background: var(--glass-surface);
border: 1px solid var(--glass-border);
border-radius: 99px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 24px;
padding: 20px 0;
backdrop-filter: blur(20px);
}

.nav-btn {
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-dim);
cursor: pointer;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.nav-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}

.[Link] {
background: var(--accent);
color: #000;
box-shadow: 0 0 20px rgba(var(--accent-rgb), 0.4);
transform: scale(1.1);
}

/*
=========================================================================
3. MAIN PANEL

========================================================================= */
.main-panel {
background: var(--glass-surface);
border: 1px solid var(--glass-border);
border-radius: var(--rad);
overflow: hidden;
display: flex;
flex-direction: column;
position: relative;
}

.view-section {
display: none;
height: 100%;
width: 100%;
flex-direction: column;
animation: fadeUp 0.3s ease;
}

.[Link] {
display: flex;
}

@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(10px);
}

to {
opacity: 1;
transform: translateY(0);
}
}
.header {
padding: 30px 40px;
flex-shrink: 0;
}

.h-sup {
font-size: 11px;
font-weight: 700;
color: var(--accent);
letter-spacing: 2px;
text-transform: uppercase;
margin-bottom: 8px;
}

.h-main {
font-size: 32px;
font-weight: 800;
letter-spacing: -1px;
}

.scroll-area {
flex: 1;
overflow-y: auto;
padding: 0 40px 40px 40px;
}

/* --- LIBRARY/SEARCH LIST VIEW --- */


.list-layout {
display: flex;
flex-direction: column;
gap: 6px;
padding-bottom: 80px;
}

.track-row {
display: grid;
grid-template-columns: auto 1fr auto;
/* Thumb - Text - Buttons */
gap: 16px;
align-items: center;
padding: 10px 16px;
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
position: relative;
}

.track-row:hover {
background: rgba(255, 255, 255, 0.06);
}

.[Link] {
background: linear-gradient(90deg, rgba(var(--accent-rgb), 0.15),
transparent);
border-left: 3px solid var(--accent);
}

/* --- THUMBNAIL CONTAINER & PLACEHOLDER (NEW) --- */


.thumb-container {
position: relative;
width: 48px;
/* Standard size for library/search list items */
height: 48px;
border-radius: 8px;
overflow: hidden;
flex-shrink: 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

.video-thumb-container {
position: relative;
width: 90px;
/* Specific size for video search results */
height: 50px;
border-radius: 8px;
overflow: hidden;
flex-shrink: 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
}

/* Generic image styles for thumbs inside containers */


.t-img,
.search-thumb,
.video-thumb {
width: 100%;
height: 100%;
object-fit: cover;
border: none;
display: block;
}

/* Placeholder style, visible by default until image loads */


.t-placeholder {
position: absolute;
inset: 0;
display: flex;
/* Centers the icon */
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #333, #111);
/* Default fallback if dynamic fails */
color: rgba(255, 255, 255, 0.3);
font-size: 24px;
flex-shrink: 0;
}

/* This class is applied to images that are lazy loading, initially


hidden */
.thumb-lazy {
opacity: 0;
transition: opacity 0.3s ease-in-out;
}

/* END THUMBNAIL NEW STYLES */

.t-info {
display: flex;
flex-direction: column;
justify-content: center;
overflow: hidden;
}

.t-title {
font-size: 14px;
font-weight: 600;
color: #fff;
margin-bottom: 3px;
}

.t-artist {
font-size: 12px;
color: var(--text-dim);
font-weight: 500;
}

.[Link] .t-title {
color: var(--accent);
}

/* Right side actions */


.t-actions {
display: flex;
align-items: center;
gap: 12px;
}

.ctx-btn {
width: 32px;
height: 32px;
border-radius: 50%;
color: rgba(255, 255, 255, 0.5);
}

.ctx-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
}

/* Context Menu */
#ctx-menu {
position: fixed;
display: none;
background: #222;
border: 1px solid #333;
border-radius: 12px;
padding: 8px;
z-index: 100;
box-shadow: 0 10px 30px #000;
width: 160px;
}

.ctx-item {
padding: 10px;
font-size: 13px;
border-radius: 6px;
cursor: pointer;
display: flex;
gap: 10px;
align-items: center;
}

.ctx-item:hover {
background: rgba(255, 255, 255, 0.1);
}

.[Link] {
color: #ff8a80;
}
/* New Android 16 / Material 3 Style Buttons */
.row-btn {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--glass-border);
width: 42px;
height: 42px;
border-radius: 14px;
/* Squircle shape */
color: var(--accent);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.row-btn:hover {
background: var(--accent);
color: #000;
border-color: var(--accent);
box-shadow: 0 4px 12px rgba(var(--accent-rgb), 0.3);
transform: translateY(-2px);
}

/* Download Progress Button */


.[Link]-btn {
position: relative;
overflow: hidden;
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--glass-border);
color: var(--accent);
}

.[Link] {
background: rgba(255, 255, 255, 0.05);
color: var(--text-dim);
border-color: rgba(255, 255, 255, 0.1);
}

/* Progress Bar at bottom */


.[Link]::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
height: 3px;
background: var(--accent);
width: var(--prog, 0%);
transition: width 0.2s linear;
}

/* --- SEARCH INPUT --- */


.search-box {
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--glass-border);
color: white;
padding: 16px 24px;
border-radius: 99px;
width: 100%;
font-size: 15px;
font-family: inherit;
}
/* --- C. TUNE VIEW --- */
.stem-container {
display: flex;
flex-direction: column;
gap: 16px;
}

.stem-card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 20px;
padding: 20px;
}

.stem-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}

.stem-label {
font-size: 14px;
font-weight: 700;
display: flex;
align-items: center;
gap: 8px;
}

.stem-val {
font-family: 'JetBrains Mono';
font-size: 12px;
background: rgba(0, 0, 0, 0.3);
padding: 4px 8px;
border-radius: 6px;
color: var(--accent);
}

/* --- ANDROID 16 STYLE SLIDERS --- */


input[type=range] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 24px;
background: transparent;
cursor: pointer;
margin: 10px 0;
position: relative;
}

/* The Track (Background) */


input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 16px;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
border: none;
}

/* The Thumb (Hidden but usable) */


input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
height: 16px;
width: 0;
box-shadow: -100vw 0 0 100vw var(--accent);
background: transparent;
border-radius: 0;
margin-top: 0;
}

/* Clip the fill to the track */


input[type=range] {
overflow: hidden;
border-radius: 20px;
}

/* --- PROGRESS BUTTONS (Feature #1) --- */


.btn-progress {
position: relative;
overflow: hidden;
z-index: 1;
}

.btn-progress::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: var(--prog, 0%);
background: rgba(var(--accent-rgb), 0.3);
z-index: -1;
transition: width 0.2s linear;
}

/* Special animation for fake progress bars if needed, e.g. lyrics */


.[Link]::before {
animation: fakeLoad 2s forwards;
}

@keyframes fakeLoad {
0% {
width: 0%;
}

80% {
width: 70%;
}

100% {
width: 100%;
}
}

/* Updated AI Buttons Grid */


.ai-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-top: 20px;
}

.ai-btn {
width: 100%;
padding: 20px;
background: linear-gradient(135deg, rgba(255, 255, 255, 0.08),
rgba(255, 255, 255, 0.03));
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 20px;
color: #ffffff !important;
text-align: left;
cursor: pointer;
display: flex;
flex-direction: column;
gap: 12px;
justify-content: space-between;
transition: 0.2s;
height: 140px;
}

.ai-btn:hover {
border-color: var(--accent);
background: rgba(255, 255, 255, 0.12);
transform: translateY(-2px);
}

.ai-btn .icon {
font-size: 32px;
color: var(--accent);
}

.ai-title {
font-weight: 700;
font-size: 15px;
margin-bottom: 4px;
color: #fff;
}

.ai-desc {
font-size: 11px;
opacity: 0.8;
color: #ddd;
line-height: 1.4;
}

/* General Text Brightness Fix */


.stem-label,
.h-main,
.h-sup,
.btn-action,
.p-title,
#l-page-title {
color: #ffffff !important;
}

.t-artist,
.time-row {
color: rgba(255, 255, 255, 0.8) !important;
}

/* --- D. LYRICS VIEW (PREMIUM UPGRADE) --- */


#view-lyrics {
flex-direction: column;
height: 100%;
overflow: hidden;
position: relative;
}

.lyrics-fixed-header {
flex-shrink: 0;
padding: 40px 40px 20px 40px;
background: linear-gradient(180deg, var(--bg-body) 0%, rgba(5, 5, 5,
0.95) 100%);
z-index: 20;
border-bottom: 1px solid rgba(255, 255, 255, 0.02);
display: flex;
flex-direction: column;
gap: 12px;
position: sticky;
top: 0;
}

.hidden-btn {
display: none !important;
}

.lyrics-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
position: relative;
z-index: 22;
}

#offset-panel {
max-height: 0;
opacity: 0;
overflow: hidden;
transform: translateY(-10px);
transition: all 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
background: rgba(255, 255, 255, 0.04);
border-radius: 16px;
margin-top: 0;
position: relative;
z-index: 21;
}

#[Link] {
max-height: 140px;
opacity: 1;
transform: translateY(0);
margin-top: 8px;
padding: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
}

#l-full-box {
flex: 1;
overflow-y: auto;
scroll-behavior: smooth;
padding: 50vh 0;
mask-image: linear-gradient(transparent 0%, black 20%, black 80%,
transparent 100%);
-webkit-mask-image: linear-gradient(transparent 0%, black 20%, black
80%, transparent 100%);
}

#l-full-box .l-line {
min-height: 50px;
display: block;
text-align: center;
padding: 12px 60px;
font-size: 26px;
font-weight: 600;
color: rgba(255, 255, 255, 0.35);
filter: blur(1.5px);
transform: scale(0.98);
transition: color 0.5s ease, transform 0.6s cubic-bezier(0.2, 0, 0,
1), filter 0.5s ease, text-shadow 0.5s ease;
will-change: transform, opacity, filter, color;
line-height: 1.4;
cursor: pointer;
}

/* --- SHIMMER LYRICS (Feature #4) --- */


@keyframes snapIn {
0% {
opacity: 0;
filter: blur(15px);
transform: scale(1.05);
}

100% {
opacity: 1;
filter: blur(0px);
transform: scale(1.0);
}
}

#l-full-box .[Link] {
font-weight: 800;
background: linear-gradient(90deg, var(--accent) 0%, #fff 50%,
var(--accent) 100%);
-webkit-background-clip: text;
background-clip: text;
/* FIX: Added standard property */
color: transparent;
animation: snapIn var(--rand-dur, 0.6s) cubic-bezier(0.2, 0.8, 0.2,
1) forwards;
transform: scale(1.0);
opacity: 1;
text-shadow: 0 0 25px rgba(var(--accent-rgb), 0.4);
filter: drop-shadow(0 0 4px rgba(var(--accent-rgb), 0.3));
}

#l-full-box .l-line:hover {
color: rgba(255, 255, 255, 0.7);
filter: blur(0.5px);
}

#l-full-box::-webkit-scrollbar {
display: none;
}

.[Link]-text {
filter: none !important;
transform: none !important;
font-size: 18px;
color: rgba(255, 255, 255, 0.8);
text-align: left;
padding: 4px 60px;
opacity: 1;
}

/* Empty State / Manual Search UI */


.lyrics-empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 10vh;
gap: 20px;
text-align: center;
}

.search-lyrics-btn {
background: var(--accent);
color: #000;
border: none;
padding: 12px 28px;
border-radius: 99px;
font-weight: 700;
font-size: 14px;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
box-shadow: 0 8px 20px rgba(var(--accent-rgb), 0.3);
transition: 0.2s;
}

.search-lyrics-btn:hover {
transform: scale(1.05);
}

/* Resume Button */
#resume-sync-btn {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: var(--accent);
color: #000;
border: none;
padding: 12px 24px;
border-radius: 99px;
font-weight: 700;
font-size: 13px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
z-index: 50;
display: none;
align-items: center;
gap: 8px;
opacity: 0;
transition: all 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
cursor: pointer;
}

#[Link] {
display: flex;
opacity: 1;
transform: translateX(-50%) translateY(0);
}

/* Modal Results */
.k-row {
display: flex;
align-items: center;
gap: 12px;
padding: 10px;
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
border: 1px solid transparent;
transition: all 0.2s;
}

.k-row:hover {
border-color: var(--accent);
}

.k-row img {
width: 40px;
height: 40px;
border-radius: 6px;
}

/*
=========================================================================
4. PLAYER CARD

========================================================================= */
.player-card {
background: rgba(20, 20, 20, 0.6);
backdrop-filter: blur(40px);
border: 1px solid var(--glass-border);
border-radius: var(--rad);
padding: 30px;
display: flex;
flex-direction: column;
position: relative;
overflow: hidden;
isolation: isolate;
transform: translateZ(0);
}

.art-container {
width: 100%;
aspect-ratio: 1;
border-radius: 24px;
background: #111;
position: relative;
overflow: hidden;
isolation: isolate;
transform: translateZ(0);
margin-bottom: 25px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}

.art-img {
width: 100%;
height: 100%;
object-fit: cover;
border: none;
display: block;
}

/* FIX: YouTube Embed: The API will replace this DIV */


#player-embed {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: none;
z-index: 5;
visibility: hidden;
/* Hidden by default */
opacity: 0;
/* Fully transparent */
pointer-events: none;
/* Cannot be interacted with when hidden */
transition: opacity 0.3s ease;
}

/* FIX: Class to show the player when needed */


#[Link] {
visibility: visible;
opacity: 1;
pointer-events: auto;
/* Can be interacted with when visible */
z-index: 5 !important;
}

.t-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 64px;
color: rgba(255, 255, 255, 0.15);
}

/* YouTube Video Overlay Interaction Blocker (Stays active on top if


controls are 0) */
.embed-blocker {
position: absolute;
inset: 0;
z-index: 10;
background: transparent;
pointer-events: auto;
}

.p-title {
font-size: 20px;
font-weight: 800;
margin-bottom: 4px;
}

.p-artist {
font-size: 14px;
color: var(--accent);
font-weight: 600;
margin-bottom: 20px;
opacity: 0.9;
}

/* --- SEEK BAR --- */


.seek-container {
width: 100%;
height: 20px;
display: flex;
align-items: center;
cursor: pointer;
margin-bottom: 8px;
}

.seek-track {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.15);
border-radius: 99px;
position: relative;
overflow: hidden;
transition: height 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.seek-container:hover .seek-track {
height: 10px;
}

.seek-fill {
height: 100%;
width: 0%;
background: var(--accent);
transition: width 0.1s linear;
}

.time-row {
display: flex;
justify-content: space-between;
font-family: 'JetBrains Mono';
font-size: 11px;
color: var(--text-dim);
margin-bottom: 20px;
}

/* Controls */
.controls {
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
margin-bottom: 20px;
}

.c-btn {
opacity: 0.7;
}

.c-btn:hover {
opacity: 1;
transform: scale(1.1);
}

.play-btn {
width: 64px;
height: 64px;
border-radius: 50%;
background: #fff;
color: #000;
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
}

.play-btn:active {
transform: scale(0.95);
}

/* Bottom Action Row */


.bottom-actions {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
width: 100%;
}

.btn-action {
width: 100%;
padding: 14px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
color: rgba(255, 255, 255, 0.7);
font-weight: 600;
font-size: 13px;
gap: 8px;
transition: 0.2s;
}

.btn-action:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
transform: translateY(-2px);
}

.[Link] {
background: var(--accent);
color: #000;
border-color: var(--accent);
box-shadow: 0 4px 12px rgba(var(--accent-rgb), 0.3);
}

#toast {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
background: #222;
border: 1px solid #444;
color: #fff;
padding: 10px 20px;
border-radius: 50px;
font-size: 13px;
z-index: 1000;
pointer-events: none;
opacity: 0;
transition: 0.3s;
}

/* --- NEW: Search Thumbnail Hover UI --- */


.thumb-overlay {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.7);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
opacity: 0;
transition: opacity 0.2s;
backdrop-filter: blur(2px);
z-index: 10;
}
.thumb-container:hover .thumb-overlay,
.video-thumb-container:hover .thumb-overlay {
opacity: 1;
}
.q-btn {
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
font-size: 10px;
font-weight: 700;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
text-transform: uppercase;
width: 80%;
text-align: center;
}
.q-btn:hover {
background: var(--accent);
color: #000;
border-color: var(--accent);
}

/* --- NEW: Disabled State for Tune View --- */


.disabled-panel {
opacity: 0.4;
pointer-events: none;
position: relative;
filter: grayscale(1);
}
.disabled-overlay-msg {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #222;
border: 1px solid #444;
padding: 10px 20px;
border-radius: 99px;
color: #fff;
font-size: 13px;
z-index: 100;
white-space: nowrap;
box-shadow: 0 10px 30px #000;
}
.disabled-panel .disabled-overlay-msg {
display: block;
}

/* --- NEW: Player Card Overlay Controls --- */


.art-container {
position: relative; /* Ensure absolute children position correctly
*/
}

/* The overlay container */


.player-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 50; /* Higher than iframe (z=20) */
}

/* Show on hover */
.art-container:hover .player-overlay {
opacity: 1;
}

/* Button Rows */
.po-row {
display: flex;
gap: 10px;
}

/* Overlay Buttons */
.po-btn {
background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
padding: 8px 16px;
border-radius: 99px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.2s;
}

.po-btn:hover {
background: var(--accent);
color: #000;
border-color: var(--accent);
transform: scale(1.05);
}

.[Link] {
background: var(--accent);
color: #000;
box-shadow: 0 0 15px rgba(var(--accent-rgb), 0.4);
}

.[Link] {
padding: 6px 12px;
font-size: 11px;
}

/* Ensure controls overlay sits above the Embed Blocker */


.embed-blocker {
z-index: 10;
}
</style>
</head>

<body>
<!-- ROBUST SPLASH SCREEN -->
<div id="splash-screen">
<div class="splash-content">
<div class="splash-logo">
<span class="icon">music_note</span>
</div>
<div class="splash-text">
<h1>Material Music</h1>
<p id="splash-status">Initializing Audio Engine...</p>
</div>
<div class="splash-loader">
<div id="splash-bar" class="splash-bar"></div>
</div>
</div>
</div>

<!-- NAV -->


<div class="nav-rail">
<button class="btn nav-btn active" onclick="[Link]('view-lib',
this)"><span
class="icon">library_music</span></button>
<button class="btn nav-btn" onclick="[Link]('view-search', this)"><span
class="icon">search</span></button>
<button class="btn nav-btn" onclick="[Link]('view-tune', this)"><span
class="icon">tune</span></button>
<button class="btn nav-btn" onclick="[Link]('view-lyrics', this)"><span
class="icon">lyrics</span></button>
</div>

<!-- MAIN -->


<div class="main-panel">

<!-- Context Menu -->


<div id="ctx-menu">
<div class="ctx-item" onclick="[Link]()">
<span class="icon" style="font-size:16px">sync</span> Rescan
Library
</div>
<div class="ctx-item" onclick="[Link]('edit')"><span
class="icon" style="font-size:16px">edit</span>
Edit (WIP)</div>
<div class="ctx-item danger" onclick="[Link]('delete')"><span
class="icon"
style="font-size:16px">delete</span> Delete</div>
</div>

<!-- A. LIBRARY VIEW -->


<div id="view-lib" class="view-section active">
<!-- UPDATED HEADER WITH SYNC BUTTON -->
<div class="header" style="display: flex; justify-content: space-
between; align-items: center;">
<div>
<div class="h-sup">Library</div>
<div class="h-main" id="lib-count">Loading...</div>
</div>
<button class="btn" onclick="[Link]()" title="Rescan
Library"
style="width: 48px; height: 48px; border-radius: 18px;
background: rgba(255,255,255,0.05); border: 1px solid var(--glass-border);">
<span class="icon">sync</span>
</button>
</div>
<!-- END UPDATED HEADER -->

<div class="scroll-area">
<div id="lib-list" class="list-layout">
<!-- Tracks or Karaoke List will be injected here -->
</div>
</div>
</div>
<!-- B. SEARCH VIEW -->
<div id="view-search" class="view-section">
<div class="header">
<div class="h-sup">Online</div>
<div class="h-main">Discover</div>
<div style="display:flex; gap:10px; margin-top:20px; align-
items:center;">
<button class="btn btn-action active" id="btn-mode-songs"
style="width:auto; padding:8px 20px; border-
radius:99px;" onclick="[Link]('songs')">
<span class="icon" style="font-size:18px; margin-
right:6px">music_note</span> Music
</button>
<button class="btn btn-action" id="btn-mode-videos"
style="width:auto; padding:8px 20px; border-
radius:99px;" onclick="[Link]('videos')">
<span class="icon" style="font-size:18px; margin-
right:6px">movie</span> YouTube
</button>
<input type="text" id="yt-in" class="search-box"
style="margin:0; flex:1;" placeholder="Search..."
onkeydown="if([Link]=='Enter') [Link]()">
</div>
</div>
<div class="scroll-area">
<div id="yt-list" class="list-layout"></div>
</div>
</div>

<!-- C. TUNE VIEW -->


<div id="view-tune" class="view-section">
<div class="header">
<div class="h-sup">Studio</div>
<div class="h-main">Mixer</div>
</div>
<div class="scroll-area stem-container">

<div class="stem-card">
<div class="stem-header">
<div class="stem-label"><span class="icon" style="font-
size:20px">mic</span> Vocals</div>
<div class="stem-val" id="v-vocal">100%</div>
</div>
<input type="range" min="0" max="1.5" step="0.1" value="1.0"
oninput="[Link]('vocal', [Link])">
</div>

<div class="stem-card">
<div class="stem-header">
<div class="stem-label"><span class="icon" style="font-
size:20px">piano</span> Music</div>
<div class="stem-val" id="v-music">100%</div>
</div>
<input type="range" min="0" max="1.5" step="0.1" value="1.0"
oninput="[Link]('music', [Link])">
</div>

<div style="display:grid; grid-template-columns: 1fr 1fr;


gap:16px;">
<div class="stem-card">
<div class="stem-header">
<div class="stem-label">Reverb</div>
<div class="stem-val" id="v-reverb">0%</div>
</div>
<input type="range" min="0" max="1.0" step="0.1"
value="0"
oninput="[Link]('reverb', [Link])">
</div>
<div class="stem-card">
<div class="stem-header">
<div class="stem-label">Speed</div>
<div class="stem-val" id="v-speed">1.0x</div>
</div>
<input type="range" min="0.5" max="1.5" step="0.05"
value="1.0"
oninput="[Link]('speed', [Link])">
</div>
</div>

<!-- Stem Switching (Feature #5) -->


<div class="stem-card"
style="margin-top:20px; display:flex; justify-content:space-
between; align-items:center;">
<div class="stem-label"><span class="icon">layers</span>
Audio Source</div>
<select id="stem-selector"
onchange="[Link]([Link])"
style="background:rgba(0,0,0,0.3); border:1px solid
rgba(255,255,255,0.2); color:#fff; padding:8px; border-radius:8px;">
<option value="stereo">Original Stereo</option>
<option value="ai">AI Deep Learning</option>
</select>
</div>
<div id="ai-hint"
style="font-size:11px; color:#ff8888; margin-top:5px;
display:none; text-align:right;">
AI Stems not found. Run separation below.
</div>

<!-- AI Options Grid -->


<div class="ai-grid">
<button class="ai-btn" onclick="[Link]()">
<span class="icon">memory</span>
<div>
<div class="ai-title">Offline Separation</div>
<div class="ai-desc">Use local CPU/GPU to split
stems.</div>
</div>
</button>

<button class="ai-btn" onclick="[Link]()">


<span class="icon">cloud_upload</span>
<div>
<div class="ai-title">Cloud Mode</div>
<div class="ai-desc">[Link]
integration.</div>
</div>
</button>
</div>

</div>
</div>

<!-- D. LYRICS PAGE -->


<div id="view-lyrics" class="view-section">

<!-- 1. Fixed Top Header -->


<div class="lyrics-fixed-header">
<div class="h-sup">Now Playing</div>
<div class="h-main truncate" id="l-page-title" style="font-
size:24px;">Select a Track</div>

<!-- Toolbar Buttons -->


<div class="lyrics-toolbar">
<!-- Left: Toggle Offset Slider -->
<button class="btn btn-action" id="btn-toggle-offset"
style="width:auto; padding:8px 16px; border-
radius:99px;" onclick="[Link]()">
<span class="icon" style="font-size:18px; margin-
right:6px;">tune</span>
<span id="offset-btn-txt">Lyric Offset</span>
</button>

<!-- Right: Karaoke/Version Switch (Hidden by default) -->


<button id="btn-version-toggle" class="btn btn-action
hidden-btn"
style="width:auto; padding:8px 16px; border-radius:99px;
background:rgba(255,255,255,0.1); border:none;"
onclick="[Link]()">
<span class="icon" style="font-size:18px; margin-
right:6px;">piano</span> Change Instrumental
</button>
</div>

<!-- Collapsible Offset Panel -->


<div id="offset-panel">
<div style="display:flex; justify-content:space-between;
align-items:center; margin-bottom:10px;">
<div
style="font-size:11px; font-weight:700; color:var(--
text-dim); text-transform:uppercase; letter-spacing:1px;">
Lyric Offset</div>
<div style="font-size:11px; color:var(--accent); font-
weight:700; cursor:pointer;"
onclick="[Link]()">RESET</div>
</div>

<div style="display:flex; align-items:center; gap:12px;">


<span style="font-size:10px;
color:rgba(255,255,255,0.3)">-5s</span>
<input type="range" id="sync-slider" min="-5" max="5"
step="0.05" value="0" style="height:4px;"
oninput="[Link]([Link])"
onchange="[Link]([Link])">
<span style="font-size:10px;
color:rgba(255,255,255,0.3)">+5s</span>
</div>
<div style="text-align:center; font-size:12px; margin-
top:8px; color:var(--text-dim);">
Adjustment: <span id="offset-display" style="color:#fff;
font-weight:700;">0.00s</span>
</div>
</div>
</div>

<!-- 2. Scrollable Lyrics Area -->


<div id="l-full-box"></div>

<!-- 3. Floating Resume Button -->


<button id="resume-sync-btn" onclick="[Link]()">
<span class="icon">vertical_align_center</span> RESUME SYNC
</button>
<!-- Warning Card (Kept the same, just ensured z-index) -->
<div id="offset-warn-card"
style="display:none; position:absolute; bottom:90px; right:20px;
width:280px; background:#1a0505; border:1px solid #ff4444; border-radius:16px;
padding:16px; z-index:100; box-shadow:0 10px 30px rgba(0,0,0,0.5);">
<div style="display:flex; gap:12px; align-items:center;">
<span class="icon" style="color:#ff4444">warning</span>
<div>
<div style="font-weight:700; font-size:13px;
color:#ffcccc;">Large Sync Offset</div>
<div style="font-size:11px; opacity:0.8; margin-top:2px;
color:#ffcccc;">Shifted by <span
id="warn-val"
style="font-weight:bold">0s</span>.</div>
</div>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr;
gap:8px; margin-top:12px;">
<button class="btn"
style="background:rgba(255,255,255,0.05); font-size:11px; border-radius:8px;"
onclick="[Link]()">Ignore</button>
<button class="btn"
style="background:rgba(255,68,68,0.2); color:#ff8888;
font-size:11px; border-radius:8px;"
onclick="$('offset-warn-
card').[Link]='none'">Dismiss</button>
</div>
</div>

</div>

</div>

<!-- PLAYER CARD -->


<div class="player-card">
<div class="art-container">
<!-- YouTube Embed -->
<div id="player-embed"></div>

<!-- Interaction Blocker (Keeps consistent click behavior) -->


<div class="embed-blocker"></div>

<!-- Static Art (for Audio Mode) -->


<img id="p-art" class="art-img" style="display:none;">
<div id="p-art-place" class="t-placeholder" style="display:none;">
<span class="icon">music_note</span>
</div>

<!-- NEW: Hover Controls Overlay -->


<div class="player-overlay">
<div class="po-row">
<button id="btn-mode-audio" class="po-btn"
onclick="[Link]('audio')" title="Audio Stream (Data Saver)">
<span class="icon"
style="font-size:18px">headphones</span> Audio
</button>
<button id="btn-mode-video" class="po-btn"
onclick="[Link]('video')" title="Video Embed">
<span class="icon" style="font-size:18px">movie</span>
Video
</button>
</div>
<!-- Quality Controls (Visible only in Video Mode) -->
<div id="po-qual-row" class="po-row" style="display:none;
margin-top:5px;">
<button class="po-btn small"
onclick="[Link]('hd1080')">1080p</button>
<button class="po-btn small"
onclick="[Link]('hd720')">720p</button>
<button class="po-btn small"
onclick="[Link]('large')">480p</button>
</div>
</div>
</div>

<div class="p-title truncate" id="p-title">Ready</div>


<div class="p-artist truncate" id="p-artist">Select a track</div>

<!-- SEEK BAR -->


<div class="seek-container" id="seek-cont">
<div class="seek-track">
<div id="seek-fill" class="seek-fill"></div>
</div>
</div>

<div class="time-row">
<span id="t-cur">0:00</span>
<span id="t-dur">0:00</span>
</div>

<div class="controls">
<button class="c-btn btn" onclick="[Link]()"><span
class="icon">skip_previous</span></button>
<button class="play-btn btn" onclick="[Link]()">
<span class="icon" style="font-size:32px" id="ic-
play">play_arrow</span>
<span class="icon" style="font-size:32px; display:none" id="ic-
pause">pause</span>
</button>
<button class="c-btn btn" onclick="[Link]()"><span
class="icon">skip_next</span></button>
</div>

<div class="bottom-actions">
<button class="btn btn-action" id="btn-lyrics-toggle"
onclick="[Link]('view-lyrics', [Link]('.nav-
rail .nav-btn:nth-child(4)'))">
<span class="icon" style="font-size:20px">lyrics</span> Lyrics
</button>
<button class="btn btn-action" id="k-btn-main"
onclick="[Link]()">
<span class="icon" style="font-size:20px">mic</span> Karaoke
</button>
</div>
</div>

<!-- Karaoke Selector Modal -->


<div id="k-modal-new"
style="display:none; position:fixed; inset:0;
background:rgba(0,0,0,0.8); backdrop-filter:blur(10px); z-index:200; align-
items:center; justify-content:center;">
<div
style="background:#111; width:600px; height:80vh; border-
radius:24px; border:1px solid #333; display:flex; flex-direction:column;
padding:24px;">
<div class="h-main" style="font-size:20px; margin-
bottom:16px;">Change Instrumental Version</div>

<div style="display:flex; gap:10px; margin-bottom:10px;">


<select id="k-filter"
style="background:rgba(255,255,255,0.1); border:1px solid
rgba(255,255,255,0.2); color:#fff; padding:10px; border-radius:12px; font-
family:inherit;">
<option value="songs">Songs (Official)</option>
<option value="videos">Videos (Community)</option>
</select>
<input type="text" id="k-search-in" class="search-box"
style="margin:0;" placeholder="Search..."
onkeydown="if([Link]=='Enter') [Link]()">
</div>

<div id="k-results"
style="flex:1; overflow-y:auto; margin-top:16px; display:flex;
flex-direction:column; gap:8px;"></div>

<button class="btn btn-action" style="margin-top:16px;"


onclick="[Link]()">Close</button>
</div>
</div>

<!-- CLOUD UPLOAD MODAL (Feature #3) -->


<div id="m-cloud"
style="display:none; position:fixed; inset:0;
background:rgba(0,0,0,0.9); z-index:300; align-items:center; justify-
content:center;">
<div
style="background:#111; width:500px; padding:30px; border-
radius:24px; border:1px solid #333; text-align:center;">
<span class="icon"
style="font-size:48px; color:var(--accent); margin:0 auto 20px
auto;">cloud_upload</span>
<div class="h-main" style="font-size:22px; margin-
bottom:10px;">Cloud Isolation</div>
<p style="color:#aaa; font-size:14px; line-height:1.6; margin-
bottom:24px;">
1. A folder with your song will open.<br>
2. Drag the file into the <b>[Link]</b> website.<br>
3. Download <b>Vocals</b> and <b>Music</b>.<br>
4. We will auto-import them.
</p>
<button class="btn btn-action active"
onclick="[Link]()"
style="width:100%; margin-bottom:10px;">
Open Website & Folder
</button>
<button class="btn btn-action" onclick="$('m-
cloud').[Link]='none'">Cancel</button>
</div>
</div>

<!-- OFFLINE AI MODAL (Feature #6) -->


<div id="m-offline"
style="display:none; position:fixed; inset:0;
background:rgba(0,0,0,0.9); z-index:300; align-items:center; justify-
content:center;">
<div style="background:#111; width:500px; padding:30px; border-
radius:24px; border:1px solid #333;">
<div class="h-main" style="font-size:20px; margin-
bottom:20px;">Offline Separation</div>
<div style="margin-bottom:20px;">
<label style="font-size:12px; color:var(--accent); font-
weight:700;">PROCESSING UNIT</label>
<select id="ai-device"
style="width:100%; margin-top:8px; padding:10px;
background:#222; border:1px solid #444; color:#fff; border-radius:8px;">
<option value="gpu">GPU (CUDA/DirectML) -
Recommended</option>
<option value="cpu">CPU (Slow)</option>
</select>
</div>

<div style="margin-bottom:20px;">
<label style="font-size:12px; color:var(--accent); font-
weight:700;">MODEL MODE</label>
<div style="display:grid; grid-template-columns:1fr 1fr;
gap:10px; margin-top:8px;">
<button class="btn btn-action active" id="btn-m-vox"
onclick="[Link]('vocals')">
High Quality Vocals
</button>
<button class="btn btn-action" id="btn-m-mus"
onclick="[Link]('music')">
High Quality Music
</button>
</div>
<div style="font-size:11px; color:#666; margin-top:8px;"
id="model-desc">
Best for clean vocals. Instrumental might have artifacts.
</div>
</div>

<div id="ai-prog-area" style="display:none; margin-bottom:20px;">


<div style="display:flex; justify-content:space-between; font-
size:12px; margin-bottom:5px;">
<span id="ai-status-txt">Processing...</span>
<span id="ai-pct">0%</span>
</div>
<div style="width:100%; height:6px; background:#333; border-
radius:99px; overflow:hidden;">
<div id="ai-bar" style="width:0%; height:100%;
background:var(--accent); transition:width 0.2s;">
</div>
</div>
</div>

<button class="btn btn-action active" style="width:100%"


onclick="[Link]()">Start
Separation</button>
<button class="btn btn-action" style="width:100%; margin-top:10px;
background:transparent; border:none;"
onclick="$('m-offline').[Link]='none'">Close</button>
</div>
</div>

<div id="toast"></div>

<!-- YouTube API Script injection -->


<script>
// --- YOUTUBE API HANDLING ---
var ytPlayer = null;
var isYoutubeApiReady = false;
// 1. The API calls this function automatically when it downloads
[Link] = function () {
[Link]("YouTube API Ready. Waiting for video request...");
isYoutubeApiReady = true;
// Optionally, if a video was cued before API was ready, try to play
it
if ([Link] && [Link].is_youtube_video_active) {
// If the player was supposed to be active, trigger creation
[Link]([Link].youtube_vid,
[Link], [Link], [Link]);
}
};

// 2. Helper to actually create the player


function createPlayer(videoId) {
// If player already exists, just load new video
if (ytPlayer && typeof [Link] === 'function') {
[Link](videoId);
return;
}

[Link]("Creating new player for:", videoId);


ytPlayer = new [Link]('player-embed', {
height: '100%',
width: '100%',
videoId: videoId, // Load the specific video immediately
playerVars: {
'autoplay': 1,
'controls': 0,
'disablekb': 1,
'fs': 0,
'iv_load_policy': 3,
'rel': 0,
'origin': [Link]
},
events: {
'onStateChange': onPlayerStateChange,
'onError': (e) => [Link]("YT Player Error:", [Link])
}
});

// Ensure the container is visible


[Link]('player-embed').[Link]('show');
}

function onPlayerStateChange(event) {
// 1 = Playing, 3 = Buffering
const isPlaying = ([Link] === 1 || [Link] === 3);
// Toggle play/pause buttons in your UI
if ([Link]('ic-play')) {
[Link]('ic-play').[Link] = isPlaying ?
'none' : 'block';
[Link]('ic-pause').[Link] = isPlaying ?
'block' : 'none';
}
}

// 3. Inject the API Script


(function loadYoutubeAPI() {
var tag = [Link]('script');
[Link] = "[Link]
// Add an error handler for the script itself
[Link] = function () {
[Link]("Failed to load YouTube IFrame API script. Check
internet connection or firewall.");
isYoutubeApiReady = false; // Mark API as not ready
// Optionally show a toast to the user
toast("YouTube features unavailable. Check internet.");
};
var firstScriptTag = [Link]('script')[0];
[Link](tag, firstScriptTag);
})();

// --- GLOBALS & UTILS ---


const $ = id => [Link](id);
const fmt = (ms) => {
if (!ms || isNaN(ms) || ms < 0) return "0:00";
const s = [Link](ms / 1000);
return [Link](s / 60) + ":" + (s % 60).toString().padStart(2,
'0');
};
const toast = (m) => { const t = $('toast'); [Link] = m;
[Link] = 1; setTimeout(() => [Link] = 0, 3000); };
const getRandomColor = (str) => {
let hash = 0;
for (let i = 0; i < [Link]; i++) hash = [Link](i) +
((hash << 5) - hash);
const c = (hash & 0x00FFFFFF).toString(16).toUpperCase();
return "#" + "00000".substring(0, 6 - [Link]) + c;
};

const pendingJobs = new Map();

function awaitJob(jobId) {
return new Promise((resolve, reject) => {
[Link](jobId, { resolve, reject });
});
}

[Link](on_job_result);
function on_job_result(jobId, payload) {
const job = [Link](jobId);
if (!job) return;
[Link](jobId);
[Link](payload);
}

[Link](on_job_error);
function on_job_error(jobId, err) {
const job = [Link](jobId);
if (!job) return;
[Link](jobId);
[Link](err);
}

async function pyJob(pyFunc, ...args) {


const jobId = await pyFunc(...args)();
return await awaitJob(jobId);
}

// --- BRIDGE FUNCTIONS (Called from Python) ---


[Link](update_status);
function update_status(msg) { toast(msg); }

[Link](update_cloud_status);
function update_cloud_status(msg, code) {
toast(msg);
if (code === 3) [Link]();
}

[Link](on_record_complete);
function on_record_complete(newPlaylist, title) {
[Link](newPlaylist);
toast(`Downloaded: ${title}`);
}

[Link](on_hq_ready);
function on_hq_ready() {
toast("HQ Stems Ready!");
if ([Link] && [Link]) {
const trackInLib = [Link](t => [Link] ===
[Link]);
if (trackInLib) {
trackInLib.has_stems = true;
[Link].has_stems = true;
}
}
if ([Link] && [Link].has_stems) {
$('stem-selector').value = 'ai';
$('ai-hint').[Link] = 'none';
eel.set_hq_mode(true)();
}
}

[Link](update_processing_status);
function update_processing_status(pct, timeLeft) {
$('ai-bar').[Link] = pct + "%";
$('ai-pct').innerText = pct + "%";
$('ai-status-txt').innerText = `Processing... ETA ${timeLeft}`;
if (pct >= 100) {
$('ai-prog-area').[Link] = 'none';
$('m-offline').[Link] = 'none';
}
}

[Link](update_loading_progress);
function update_loading_progress(pct, filename) {
const splashBar = [Link]('splash-bar');
if (splashBar && [Link] !== null) {
[Link] = 'none'; // Stop indeterminate
animation if progress is updating
[Link] = pct + "%";
[Link]('splash-status').innerText = filename;
}

if ([Link]("Buffering")) {
const pArtist = [Link]('p-artist');
[Link] = `Buffering... ${[Link](pct)}%`;
[Link] = 'var(--accent)';

[Link]('seek-fill').[Link] = pct + "%";

$('ic-play').[Link] = 'none';
$('ic-pause').[Link] = 'block';
} else if ([Link]('p-
artist').[Link]("Buffering...")) {
if ([Link]) {
[Link]('p-artist').innerText =
[Link];
[Link]('p-artist').[Link] = 'var(--
accent)';
}
}
}

[Link](refresh_ui_library);
function refresh_ui_library(newPlaylist) {
[Link](newPlaylist);
}

// --- APP LOGIC ---


const app = (() => {
let playlist = [];
let currentMeta = null;
let lyrics = null;
let isDragging = false;
let karaokeMode = false;
let ctxTargetIndex = -1;
let currentOffset = 0.0;
let previewAudio = new Audio();

let isAutoScrolling = false;


let isScrollingManually = false;
let scrollTimeout = null;

let selAiModel = 'vocals';

let searchFilter = 'songs';

async function init() {


try {
const splash = [Link]('splash-screen');
const splashTxt = [Link]('splash-status');
const splashBar = [Link]('splash-bar');

if (splashTxt) [Link] = 'Reading Cached


Library...';

try {
const cached = await eel.read_library_cache()();

if ([Link](cached) && [Link] > 0) {


playlist = cached;
renderLib();
if (splash) {
[Link] = `Loaded ${[Link]}
tracks from cache.`;
if (splashBar) {
[Link] = 'none';
[Link] = '100%';
[Link] = 'green';
}
setTimeout(() => [Link] = '0',
500);
setTimeout(() => [Link](), 1100);
}
} else {
playlist = [];
renderLib();
if (splash) {
[Link] = 'No tracks found in cache.
Rescan library via options.';
if (splashBar) [Link] =
'none';
setTimeout(() => [Link] = '0',
500);
setTimeout(() => [Link](), 1100);
}
toast("Library empty. Click the sync button at the
top of the Library view to rescan.");
}
} catch (e) {
[Link]("Cache Read Error", e);
if (splash) {
[Link] = `Error loading cache: $
{[Link]}`;
if (splashBar) [Link] =
'red';
setTimeout(() => [Link] = '0', 2000);
setTimeout(() => [Link](), 2600);
}
toast("Failed to read library cache. Check console.");
}

const seek = $('seek-cont');


[Link]('mousedown', e => { isDragging = true;
updateSeekVis(e); });
[Link]('mousemove', e => { if
(isDragging) updateSeekVis(e); });
[Link]('mouseup', async e => {
if (isDragging) {
const rect = $('seek-cont').getBoundingClientRect();
const pct = [Link](0, [Link](1, ([Link] -
[Link]) / [Link]));

const isYoutubeVideoActive = $('player-


embed').[Link]('show');

if (isYoutubeVideoActive && ytPlayer && typeof


[Link] === 'function') {
try {
const duration = [Link]();
[Link](duration * pct, true);
} catch (e) { [Link]("YT Video Seek
Error", e); }
} else if (currentMeta && [Link] > 0)
{
await eel.set_seek(pct * [Link])
();
}
}
isDragging = false;
});

[Link]('click', e => {
if (!$('ctx-menu').contains([Link]) && !
[Link]('.ctx-btn')) {
$('ctx-menu').[Link] = 'none';
}
});

bindScrollEvents();
requestAnimationFrame(updateLoop);

const hardware = await eel.get_ai_hardware()();


const deviceSelect = $('ai-device');
[Link] = '';
if (hardware.has_gpu) {
[Link](new Option('GPU (CUDA/DirectML) -
Recommended', 'gpu'));
[Link] = 'gpu';
} else {
[Link](new Option('No GPU detected. Using
CPU.', 'cpu'));
[Link] = 'cpu';
[Link] = true;
}
[Link](new Option('CPU (Slower)', 'cpu'));

} catch (e) {
[Link]("Init Error", e);
toast("Initialization Failed: Check Console");
const splash = [Link]('splash-screen');
if (splash) [Link]();
}
}

function bindScrollEvents() {
const box = $('l-full-box');
const userScrollEvents = ['mousedown', 'wheel', 'touchstart',
'drag'];
[Link](evt => {
[Link](evt, () => {
isScrollingManually = true;
isAutoScrolling = false;
const btn = $('resume-sync-btn');
[Link]('show');
clearTimeout(scrollTimeout);
}, { passive: true });
});
}

function refreshLib(newPlaylist) {
if (newPlaylist) playlist = newPlaylist;
renderLib();
}

// Helper to create the thumbnail HTML, including placeholder


function createThumbHtml(item, isVideo = false) {
const uniqueIdForColor = [Link] || [Link] ||
[Link]; // For consistent color
const placeholderBg = `background:linear-gradient(135deg, $
{getRandomColor(uniqueIdForColor)}, #111)`;
const thumbClass = isVideo ? 'video-thumb' : 'search-thumb';
const containerClass = isVideo ? 'video-thumb-container' :
'thumb-container';

// CHANGED: Removed thumb-lazy class and data-src. Added


loading="lazy" and direct src.
// This lets the browser handle the timing automatically.
return `
<div class="${containerClass}">
<img class="${thumbClass}" src="${[Link]}"
loading="lazy" alt="${[Link]}" onload="[Link]=1;
[Link]='none';" onerror="[Link]()">
<div class="t-placeholder" style="$
{placeholderBg}"><span class="icon">music_note</span></div>
</div>
`;
}
function renderLib() {
$('lib-list').innerHTML = '';
$('lib-count').innerText = `${[Link]} Tracks`;

if ([Link] === 0) {
$('lib-list').innerHTML = `
<div style="padding:20px; text-align:center;
color:var(--text-dim);">
No tracks found. <br> Click the sync button at the
top for a "Rescan Library" option.
</div>
`;
return;
}

[Link]((t, i) => {
const row = [Link]('div');
[Link] = 'track-row';
if (currentMeta && [Link] === [Link])
[Link]('playing');

// Use the new helper function for library items


const simplifiedItem = {
title: [Link],
thumb: [Link],
path: [Link] // Use path as a unique ID for color for
local files
};
let artHtml = [Link](simplifiedItem, false);

[Link] = `
<div onclick="[Link](${i})">${artHtml}</div>
<div class="t-info" onclick="[Link](${i})">
<div class="t-title truncate">${[Link]}</div>
<div class="t-artist truncate">${[Link]}</div>
</div>
<button class="btn ctx-btn" onclick="[Link](event,
${i})">
<span class="icon">more_vert</span>
</button>
`;
[Link] = (e) => {
if (![Link]('.ctx-btn')) [Link](i);
};
$('lib-list').appendChild(row);
});
}

function openCtx(e, i) {
[Link]();
ctxTargetIndex = i;
const m = $('ctx-menu');
[Link] = 'block';
let leftPos = [Link];
if (leftPos + [Link] > [Link]) {
leftPos = [Link] - [Link] - 20;
}
[Link] = leftPos + 'px';
[Link] = [Link] + 'px';
}

async function ctxAction(act) {


$('ctx-menu').[Link] = 'none';
if (act === 'delete' && ctxTargetIndex > -1) {
const track = playlist[ctxTargetIndex];
if (confirm(`PERMANENT DELETE: Delete '${[Link]}'? This
will remove the file from your disk.`)) {
const res = await
eel.delete_track_robust(ctxTargetIndex, [Link])();
if ([Link]) {
renderLib();
// If the deleted track was the currently playing
one, reset player UI
if (currentMeta && [Link] === [Link])
{
$('p-title').innerText = "Ready";
$('p-artist').innerText = "Select a track";
$('p-art').[Link] = 'none';
$('p-art-place').[Link] = 'flex';
$('t-cur').innerText = "0:00";
$('t-dur').innerText = "0:00";
$('seek-fill').[Link] = "0%";
// Hide YT player if it was active
$('player-embed').[Link]('show');
if (ytPlayer && [Link])
[Link]();
}
toast("Deleted track and cache.");
} else if ([Link]) {
toast("Library desynced. Re-scanning to fix...");
[Link]();
} else {
toast(`Failed to delete: ${[Link] || 'Unknown
Error'}`);
}
}
}
}

async function manualRescan() {


$('ctx-menu').[Link] = 'none';
toast("Scanning Library... This may take a while.");
$('lib-count').innerText = "Scanning...";

const splash = $('splash-screen');


if (splash) [Link] = 'flex';
const splashTxt = $('splash-status');
const splashBar = $('splash-bar');
if (splashTxt) [Link] = "Starting library scan...";
if (splashBar) {
[Link] = 'indeterminate 1.5s infinite
linear';
[Link] = '40%';
}

const newLib = await pyJob(eel.scan_library);

if (splash) {
[Link] = '0';
setTimeout(() => [Link](), 600);
}

if (newLib && [Link](newLib)) {


playlist = newLib;
renderLib();
toast(`Scan Complete. Found ${[Link]} tracks.`);
} else {
toast("Scan finished but returned no results or an error
occurred.");
}
}

async function load(i) {


exitKaraokeMode(false);
const meta = await pyJob(eel.load_track, i);
await setupOnNewTrack(meta);
}

function updateMeta(m) {
$('p-title').innerText = [Link];
$('p-artist').innerText = [Link];
$('l-page-title').innerText = [Link];
$('t-dur').innerText = fmt([Link]);

const artImg = $('p-art');


const embed = $('player-embed');
const place = $('p-art-place');

const isYoutubeVideoActive = [Link]('show');

if (isYoutubeVideoActive && m.is_youtube_video_active) {


[Link] = 'none';
[Link] = 'none';
} else {
[Link]('show');
if (ytPlayer && [Link]) [Link]();

[Link] = 'none';
[Link] = 'none';

if (m.is_stream) {
if ([Link]) {
[Link] = [Link];
[Link] = 'block';
} else {
[Link] = 'flex';
[Link] = `linear-gradient(135deg, $
{getRandomColor([Link])}, #111)`;
}
} else {
if ([Link]) {
[Link] = [Link];
[Link] = 'block';
} else {
[Link] = 'flex';
[Link] = `linear-gradient(135deg, $
{getRandomColor([Link])}, #111)`;
}
}
}

// Update Player Overlay Buttons


if (m.is_youtube_video_active) {
$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$('po-qual-row').[Link] = 'flex';
} else if (m.youtube_vid) { // Has YT video ID but is playing
audio stream
$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$('po-qual-row').[Link] = 'none';
} else { // Local file
$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$('po-qual-row').[Link] = 'none';
}

if ([Link]) {
[Link]('--accent',
[Link]);
[Link]('--accent-rgb',
[Link].accent_rgb);
}
}

async function updateLoop() {


try {
const isYoutubeVideoActive = $('player-
embed').[Link]('show');

if (isYoutubeVideoActive && ytPlayer && typeof


[Link] === 'function') {
// --- YOUTUBE VIDEO MODE ---
const state = [Link]();
// State 1 is playing, 3 is buffering
const isPlaying = (state === 1 || state === 3);
$('ic-play').[Link] = isPlaying ? 'none' :
'block';
$('ic-pause').[Link] = isPlaying ? 'block' :
'none';

if (!isDragging) {
const cur = [Link]() * 1000;
const dur = [Link]() * 1000;

if (dur > 0) { // Only update if duration is valid


$('t-cur').innerText = fmt(cur);
$('t-dur').innerText = fmt(dur);
let pct = (cur / dur) * 100;
$('seek-fill').[Link] = pct + "%";
if (lyrics) syncLyrics(cur / 1000); // Sync
lyrics for YT video
}
}
} else {
// --- PYTHON AUDIO MODE (Local File or Streamed Audio)
---
const s = await eel.get_player_state()();
if (s) {
$('ic-play').[Link] = [Link] === 'PLAYING' ?
'none' : 'block';
$('ic-pause').[Link] = [Link] ===
'PLAYING' ? 'block' : 'none';

if ([Link] > 0 && !isDragging) {


$('t-cur').innerText = fmt([Link]);
$('t-dur').innerText = fmt([Link]);
let pct = ([Link] / [Link]) * 100;
$('seek-fill').[Link] = pct + "%";
if (lyrics) syncLyrics([Link] / 1000);
} else if ([Link] === 'STOPPED' && !
isYoutubeVideoActive) {
// Reset UI if stopped and not in YouTube video
mode
$('t-cur').innerText = "0:00";
$('seek-fill').[Link] = "0%";
$('ic-play').[Link] = 'block';
$('ic-pause').[Link] = 'none';
}
}
}
} catch (e) {
// [Link]("UpdateLoop Error:", e); // Can be noisy,
uncomment for debugging
}
setTimeout(() => requestAnimationFrame(updateLoop), 200);
}

function updateSeekVis(e) {
const rect = $('seek-cont').getBoundingClientRect();
let pct = ([Link] - [Link]) / [Link];
pct = [Link](0, [Link](1, pct)) * 100;
$('seek-fill').[Link] = pct + "%";
}

async function togglePlay() {


const isYoutubeVideoActive = $('player-
embed').[Link]('show');

if (isYoutubeVideoActive && ytPlayer && typeof


[Link] === 'function') {
const state = [Link]();
if (state === 1) [Link]();
else [Link]();
} else {
await eel.play_pause()();
}
}
async function next() {
const m = await pyJob(eel.next_track);
if (m) await setupOnNewTrack(m);
}
async function prev() {
const m = await pyJob(eel.prev_track);
if (m) await setupOnNewTrack(m);
}

function setFX(type, val) {


$(`v-${type}`).innerText = type === 'speed' ?
parseFloat(val).toFixed(1) + 'x' : [Link](val * 100) + '%';
if (type === 'vocal') eel.set_vocal_volume(val);
if (type === 'music') eel.set_music_volume(val);
if (type === 'reverb') eel.set_reverb(val);
if (type === 'speed') eel.set_speed(val);
}

function nav(id, btn) {


[Link]('.view-section').forEach(e =>
[Link]('active'));
$(id).[Link]('active');
[Link]('.nav-btn').forEach(e =>
[Link]('active'));
[Link]('active');

if (id !== 'view-lyrics') {


if (lyrics) {
isScrollingManually = false;
$('resume-sync-btn').[Link]('show');
}
$('offset-panel').[Link]('open');
$('offset-btn-txt').innerText = "Sync";
$('btn-toggle-offset').[Link]('active');
} else {
if (currentMeta && !lyrics) {
searchLyricsManual();
} else if (lyrics) {
renderLyrics();
resumeAutoScroll();
} else {
renderLyricsEmptyState();
}
}
}

function openOfflineAI() {
if (!currentMeta || currentMeta.is_stream ||
currentMeta.is_youtube_video_active) return toast("Select a local song first for
AI separation.");
$('m-offline').[Link] = 'flex';
$('ai-prog-area').[Link] = 'none';
$('ai-bar').[Link] = '0%';
$('ai-pct').innerText = '0%';
$('ai-status-txt').innerText = 'Initializing...';
selModel(selAiModel);
}

function selModel(m) {
selAiModel = m;
[Link]('#m-offline .btn-action').forEach(b =>
[Link]('active'));
if (m === 'vocals') {
$('btn-m-vox').[Link]('active');
$('model-desc').innerText = "Best for clean vocals.
Instrumental might have minor artifacts. Uses Kim_Vocal_2 model.";
} else {
$('btn-m-mus').[Link]('active');
$('model-desc').innerText = "Best for clean music. Vocals
might have minor artifacts. Uses UVR-MDX-NET-Inst_HQ_3 model.";
}
}

async function runOfflineAI() {


if (!currentMeta || currentMeta.is_stream ||
currentMeta.is_youtube_video_active) return toast("Select a local song first.");
const dev = $('ai-device').value;
const useGpu = dev === 'gpu';

$('ai-prog-area').[Link] = 'block';
$('ai-status-txt').innerText = "Initializing...";

const success = await eel.trigger_hq_processing(selAiModel,


useGpu)();
if (!success) {
toast("Failed to start AI separation. Not supported in
stream mode or another process is running.");
$('m-offline').[Link] = 'none';
}
}

function openCloudModal() {
if (!currentMeta || currentMeta.is_stream ||
currentMeta.is_youtube_video_active) return toast("Select a local song first for
cloud separation.");
$('m-cloud').[Link] = 'flex';
}
async function startCloudProcess() {
$('m-cloud').[Link] = 'none';
if (!currentMeta) return toast("Select a song first.");
toast("Preparing Cloud Workspace...");
const ready = await eel.trigger_cloud_processing()();
if (ready) {
await eel.launch_cloud_workspace()();
toast("Monitoring Downloads folder for stems...");
} else {
toast("Could not prepare cloud session. Is another AI
process running?");
}
}

function switchStemMode(val) {
if (!currentMeta || currentMeta.is_stream ||
currentMeta.is_youtube_video_active) {
$('stem-selector').value = 'stereo';
return toast("Play a local track first for stem control.");
}

const currentTrack = [Link](t => [Link] ===


[Link]);
if (!currentTrack) {
$('stem-selector').value = 'stereo';
return;
}

if (val === 'ai') {


if (currentTrack.has_stems) {
eel.set_hq_mode(true)();
$('ai-hint').[Link] = 'none';
toast("Switched to AI Deep Learning stems.");
} else {
$('stem-selector').value = 'stereo';
$('ai-hint').[Link] = 'block';
toast("No AI stems found for this track. Use 'Offline
Separation' below.");
}
} else {
eel.set_hq_mode(false)();
$('ai-hint').[Link] = 'none';
toast("Switched to Original Stereo playback.");
}
}

async function setupOnNewTrack(meta) {


if (!meta || [Link]) {
toast("Loading...");
if (meta && [Link]) toast([Link]);
if (meta && [Link]) {
[Link]();
}
return;
}

currentMeta = meta;

// --- FIX 1: Ensure Video ID persists if we are just switching


quality ---
// If the new meta is an audio stream but we have a video ID
from a search result, keep it for lyrics/karaoke
if (!currentMeta.youtube_vid && meta.is_stream) {
// Sometimes passed in meta, otherwise handled by search
click
}

updateMeta(meta);

const idx = [Link](t => [Link] === [Link]);


const cards = [Link]('#lib-list .track-row');
[Link](c => [Link]('playing'));
if (idx !== -1 && cards[idx]) {
cards[idx].[Link]('playing');
}

// --- FIX 2: Disable FX Panel for Online Content ---


const tuneSection = [Link]('#view-tune .scroll-
area');
if (meta.is_stream || meta.is_youtube_video_active) {
[Link]('disabled-panel');
// Add overlay message if not exists
if (![Link]('.disabled-overlay-msg')) {
const msg = [Link]('div');
[Link] = 'disabled-overlay-msg';
[Link] = "Effects disabled for online media";
[Link](msg);
}
} else {
[Link]('disabled-panel');
}
// ------------------------------------------------

isScrollingManually = false;
$('resume-sync-btn').[Link]('show');
$('offset-panel').[Link]('open');
$('offset-btn-txt').innerText = "Sync";
$('btn-toggle-offset').[Link]('active');

lyrics = null;
$('l-full-box').innerHTML = '';

if ($('view-lyrics').[Link]('active')) {
renderLyricsEmptyState();
}

const savedKaraokeId = meta.karaoke_id;


karaokeMode = !!savedKaraokeId;
const offsetKey = karaokeMode ? 'offset_k' : 'offset_o';
currentOffset = meta[offsetKey] || 0.0;

$('k-btn-main').classList[karaokeMode ? 'add' : 'remove']


('active');
$('sync-slider').value = currentOffset;
$('offset-display').innerText = (currentOffset > 0 ? '+' : '') +
[Link](2) + 's';

if (karaokeMode) {
toast("Auto-loading saved instrumental...");
const applySuccess = await pyJob(eel.apply_new_karaoke,
[Link], meta.karaoke_id, 0)();
if (!applySuccess) {
await eel.save_track_offset([Link], 'karaoke_id',
null)();
meta.karaoke_id = null;
karaokeMode = false;
$('k-btn-main').[Link]('active');
toast("Failed to auto-load instrumental. Reverting to
original.");
} else {
$('player-embed').[Link]('show');
if (ytPlayer && [Link])
[Link]();
}
}

if (meta.is_stream || $('view-
lyrics').[Link]('active')) {
searchLyricsManual();
} else {
renderLyricsEmptyState();
}

$('btn-version-toggle').classList[karaokeMode ? 'remove' :
'add']('hidden-btn');
checkOffsetWarning();

const stemSel = $('stem-selector');


if (meta.has_stems) {
[Link] = 'ai';
$('ai-hint').[Link] = 'none';
eel.set_hq_mode(true)();
} else {
[Link] = 'stereo';
eel.set_hq_mode(false)();
$('ai-hint').[Link] = 'none';
}

// UI Reset
$('seek-fill').[Link] = "0%";
$('t-cur').innerText = "0:00";

// Force pause icon if playing


$('ic-play').[Link] = 'none';
$('ic-pause').[Link] = 'block';

[Link]('p-artist').innerText = [Link];
[Link]('p-artist').[Link] = 'var(--
accent)';
}

function toggleOffsetPanel() {
const p = $('offset-panel');
const btnTxt = $('offset-btn-txt');

if ([Link]('open')) {
[Link]('open');
[Link] = "Sync";
$('btn-toggle-offset').[Link]('active');
} else {
[Link]('open');
[Link] = "Hide";
$('btn-toggle-offset').[Link]('active');
}
}

function onSliderInput(val) {
currentOffset = parseFloat(val);
$('offset-display').innerText = (currentOffset > 0 ? '+' : '') +
[Link](2) + 's';
}

async function onSliderChange(val) {


currentOffset = parseFloat(val);

if (currentMeta && [Link]) {


const key = karaokeMode ? 'offset_k' : 'offset_o';
await eel.save_track_offset([Link], key,
currentOffset)();
const t = [Link](x => [Link] === [Link]);
if (t) t[key] = currentOffset;
currentMeta[key] = currentOffset;
toast(`Sync saved for ${karaokeMode ? 'Karaoke' :
'Original'}`);
checkOffsetWarning();
} else {
toast("Sync adjusted for this session");
}
}

function resetSync() {
onSliderInput(0);
onSliderChange(0);
$('sync-slider').value = 0;
}

function checkOffsetWarning() {
if (!currentMeta) return;
if (currentMeta.ignore_sync_warning) {
$('offset-warn-card').[Link] = 'none';
return;
}

const card = $('offset-warn-card');


if ([Link](currentOffset) > 1.0) {
$('warn-val').innerText = (currentOffset > 0 ? '+' : '') +
[Link](2) + 's';
[Link] = 'flex';
} else {
[Link] = 'none';
}
}

async function ignoreWarning() {


if (currentMeta && [Link]) {
await eel.save_track_offset([Link],
'ignore_sync_warning', true)();
const t = [Link](x => [Link] === [Link]);
if (t) t.ignore_sync_warning = true;
currentMeta.ignore_sync_warning = true;
$('offset-warn-card').[Link] = 'none';
toast("Warning disabled for this track");
}
}

function renderLyricsEmptyState() {
const box = $('l-full-box');
[Link] = `
<div class="lyrics-empty-state">
<span class="icon" style="font-size:48px;
opacity:0.2">lyrics</span>
<div style="opacity:0.5; font-size:14px;">No lyrics
loaded</div>
<button class="search-lyrics-btn"
onclick="[Link]()">
<span class="icon"
style="font-size:18px">search</span> Find Lyrics
</button>
</div>
`;
}

async function searchLyricsManual() {


if (!currentMeta) return;

const box = $('l-full-box');


[Link] = `<div class="lyrics-empty-state"><span
class="icon spin">sync</span><div>Fetching Transcript...</div></div>`;

// Logic to get the Video ID for transcript fetching


let vidId = null;
if (currentMeta.youtube_vid) {
vidId = currentMeta.youtube_vid;
}

const res = await pyJob(eel.get_lyrics_for_track,


[Link], [Link], vidId);

if (res && [Link] > 0) {


lyrics = res;
renderLyrics();
toast("Lyrics loaded!");
setTimeout(resumeAutoScroll, 500);
} else {
// YOUR CUSTOM ERROR MESSAGE
[Link] = `
<div class="lyrics-empty-state">
<span class="icon" style="font-size:48px;
opacity:0.2; color:#ff8888">subtitles_off</span>
<div style="opacity:0.8; font-size:15px; margin-
top:10px; max-width:280px; line-height:1.5;">
We couldn't found lyrics of this song. However,
the video might have lyrics!
</div>
<button class="btn"
style="background:rgba(255,255,255,0.1); padding:10px 20px; border-radius:99px;
font-size:13px; margin-top:20px; font-weight:600;"
onclick="[Link]()">
Try Again
</button>
</div>
`;
lyrics = null;
}
}

function renderLyrics() {
if (!lyrics) return;

const box = $('l-full-box');


[Link] = '';

const isStatic = [Link] > 0 && lyrics[0].time === -1;

if (isStatic) {
[Link] = "40px 0";
[Link] = "none";
[Link] = "none";
const title = [Link]('div');
[Link] = "<div style='text-align:center;
opacity:0.5; font-size:12px; margin-bottom:20px'>Static Lyrics
(Unsynced)</div>";
[Link](title);

[Link](line => {
const div = [Link]('div');
[Link] = 'l-line static-text';
[Link] = [Link];
[Link](div);
});
} else {
[Link] = "50vh 0";
[Link] = "";
[Link] = "";

[Link]((line, i) => {
const div = [Link]('div');
[Link] = 'l-line';
[Link] = `line-l-full-box-${i}`;
[Link] = [Link];
[Link] = () => {
isScrollingManually = false;
$('resume-sync-btn').[Link]('show');
eel.set_seek(([Link] + currentOffset) * 1000);
};
[Link](div);
});
}
}

function syncLyrics(timeSec) {
if (!lyrics) return;
if (lyrics[0].time === -1) return;

const adjustedTime = timeSec + currentOffset;

let idx = [Link]((l, i) => {


const next = lyrics[i + 1];
return adjustedTime >= [Link] && (!next || adjustedTime <
[Link]);
});

if (idx !== -1) {


const box = $('l-full-box');
const currentActive = [Link]('.[Link]');
const newActive = [Link](`line-l-full-box-$
{idx}`);

if (currentActive !== newActive) {


if (currentActive)
[Link]('active');

if (newActive) {
[Link]('active');
const randDur = 0.4 + [Link]() * 0.6;
[Link]('--rand-dur', randDur +
's');

if (!isScrollingManually) {
isAutoScrolling = true;
[Link]({ behavior: "smooth",
block: "center" });
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => isAutoScrolling
= false, 600);
}
}
}
}
}

function resumeAutoScroll() {
isScrollingManually = false;
$('resume-sync-btn').[Link]('show');

const activeLine = [Link]('#l-full-box .l-


[Link]');
if (activeLine) {
isAutoScrolling = true;
[Link]({ behavior: "smooth", block:
"center" });
setTimeout(() => isAutoScrolling = false, 500);
}
}

async function toggleKaraoke() {


if (!currentMeta) return toast("Play a track first");

let currentMs = 0;
try {
const s = await eel.get_player_state()();
if (s) currentMs = [Link];
} catch (e) { }

if (karaokeMode) {
exitKaraokeMode(true, currentMs);
return;
}

const btn = $('k-btn-main');


[Link]('active');
[Link]('btn-progress');
[Link] = '<span class="icon spin">downloading</span>
Loading...';

const t = [Link](x => [Link] === [Link]);


let success = false;
let res = null;
let videoIdToUse = null;
let titleToUse = null;
let warnToUse = false;
let diffToUse = 0;

if (t && t.karaoke_id) {
toast("Loading saved instrumental...");
const applyResult = await pyJob(eel.apply_new_karaoke,
[Link], t.karaoke_id, currentMs);
success = applyResult;
if (success) {
videoIdToUse = t.karaoke_id;
titleToUse = [Link];
}
}
if (!success) {
toast("Searching for best instrumental...");
res = await pyJob(eel.find_and_play_best_karaoke,
[Link], [Link], [Link], currentMs);
success = [Link];
if (success) {
videoIdToUse = res.video_id;
titleToUse = [Link];
warnToUse = [Link];
diffToUse = [Link];
}
}

if (success) {
if (t && !t.karaoke_id && videoIdToUse) {
t.karaoke_id = videoIdToUse;
await eel.save_track_offset([Link],
'karaoke_id', videoIdToUse)();
currentMeta.karaoke_id = videoIdToUse;
}
finalizeKaraokeMode(videoIdToUse, titleToUse, warnToUse,
diffToUse);
} else {
[Link]('active');
toast(res ? [Link] : "Failed to load karaoke.");
}

[Link]('btn-progress');
[Link] = '<span class="icon">mic</span> Karaoke';
}

function finalizeKaraokeMode(vid, title, warn, diff) {


karaokeMode = true;
const btn = $('k-btn-main');
[Link] = '<span class="icon">mic</span> Karaoke';
$('btn-version-toggle').[Link]('hidden-btn');

if (currentMeta) currentMeta.karaoke_id = vid;

currentOffset = currentMeta.offset_k || 0.0;


$('sync-slider').value = currentOffset;
$('offset-display').innerText = (currentOffset > 0 ? '+' : '') +
[Link](2) + 's';
checkOffsetWarning();

if (warn) toast(`?? Sync Offset: ${[Link](1)}s`);


else toast(`Karaoke Active: ${title}`);
}

async function exitKaraokeMode(reloadOriginal = true, specificPos =


-1) {
if (!karaokeMode) return;

karaokeMode = false;
$('k-btn-main').[Link]('active');
$('btn-version-toggle').[Link]('hidden-btn');

if ([Link]) [Link]();
// FIX: Ensure YouTube player iframe is hidden when exiting
karaoke mode
$('player-embed').[Link]('show');
if (ytPlayer && [Link]) [Link]();

if (reloadOriginal && currentMeta && [Link]) {


let posToSeek = specificPos;

if (posToSeek < 0) {
try {
const s = await eel.get_player_state()();
posToSeek = s ? [Link] : 0;
} catch (e) { posToSeek = 0; }
}

currentOffset = currentMeta.offset_o || 0.0;


$('sync-slider').value = currentOffset;
$('offset-display').innerText = (currentOffset > 0 ? '+' :
'') + [Link](2) + 's';
checkOffsetWarning();

const originalTrackIndex = [Link](x => [Link]


=== [Link]);
if (originalTrackIndex > -1) {
await pyJob(eel.load_track, originalTrackIndex,
posToSeek);
toast("Original Vocals Restored");
currentMeta.karaoke_id = null;
currentMeta.is_youtube_video_active = false;
currentMeta.youtube_vid = null;
}
}
}

function openKaraokeModal() {
if (!currentMeta) return toast("Load a track first.");
$('k-modal-new').[Link] = 'flex';
$('k-search-in').value = `${[Link]} $
{[Link]} instrumental`;
[Link]();
$('k-search-in').focus();
}

function closeKaraokeModal() {
if ([Link]) [Link]();
$('k-modal-new').[Link] = 'none';
}

async function searchKaraokeOnly() {


const q = $('k-search-in').value;
const filter = $('k-filter').value;
const cont = $('k-results');
[Link] = '<div style="padding:20px;
color:#666;">Searching YouTube...</div>';

const res = await pyJob(eel.search_yt, q, filter);


[Link] = '';

if ([Link] === 0) {
[Link] = '<div style="padding:20px; color:#666;">No
results found.</div>';
return;
}

[Link](r => {
const div = [Link]('div');
[Link] = 'k-row';

const safeTitle = [Link](/'/g,


"&#39;").replace(/"/g, "&quot;");
const safeArt = [Link](/'/g,
"&#39;").replace(/"/g, "&quot;");

let thumbHtml = [Link](r, filter === 'videos');

[Link] = `
${thumbHtml}
<div style="flex:1; overflow:hidden;">
<div class="truncate" style="font-weight:600">$
{[Link]}</div>
<div style="font-size:11px; opacity:0.7">${[Link]}
- ${[Link]} (${[Link]})</div>
</div>
<button class="btn row-btn" title="Preview (128kbps)"
onclick="[Link]('${[Link]}')">
<span class="icon" style="font-
size:20px">play_arrow</span>
</button>
<button class="btn row-btn" title="Download & Apply HQ"
onclick="[Link]('${[Link]}')">
<span class="icon"
style="font-size:20px">check</span>
</button>
`;
[Link](div);
});
}

async function previewK(vid) {


if ([Link]) [Link]();
toast("Loading Preview...");

const url = await pyJob(eel.get_karaoke_preview, vid);


if (url) {
[Link] = url;
[Link] = 0.5;
[Link]();
toast("Playing Preview (128kbps)");
} else {
toast("Preview unavailable");
}
}

async function applyK(vid) {


if ([Link]) [Link]();
$('k-modal-new').[Link] = 'none';
toast("Downloading & Applying HQ Audio...");

const originalPath = [Link];

let currentMs = 0;
let wasPlaying = false;
try {
const s = await eel.get_player_state()();
if (s) {
currentMs = [Link];
wasPlaying = [Link] === "PLAYING";
}
} catch (e) { }

const success = await pyJob(eel.apply_new_karaoke, originalPath,


vid, currentMs);
if (success) {
const t = [Link](x => [Link] === originalPath);
if (t) t.karaoke_id = vid;

karaokeMode = true;
$('k-btn-main').[Link]('active');
toast("Instrumental Updated!");

currentOffset = currentMeta.offset_k || 0.0;


$('sync-slider').value = currentOffset;
$('offset-display').innerText = (currentOffset > 0 ? '+' :
'') + [Link](2) + 's';
checkOffsetWarning();

if (wasPlaying) eel.play_pause()();
currentMeta.is_youtube_video_active = false; // Not a video
currentMeta.youtube_vid = null;
} else {
toast("Failed to apply instrumental. Check FFmpeg.");
}
}

function setSearchMode(mode) {
searchFilter = mode;
$('btn-mode-songs').classList[mode === 'songs' ? 'add' :
'remove']('active');
$('btn-mode-videos').classList[mode === 'videos' ? 'add' :
'remove']('active');

if ($('yt-in').[Link]().length > 0) search();


}

async function search() {


const q = $('yt-in').value;
if (![Link]()) return;

$('yt-list').innerHTML = '<div style="padding:20px;


color:#666;">Searching...</div>';

const res = await pyJob(eel.search_yt, q, searchFilter);


$('yt-list').innerHTML = '';

if ([Link] === 0) {
$('yt-list').innerHTML = '<div style="padding:20px;
color:#666;">No results found.</div>';
return;
}

[Link](item => {
const row = [Link]('div');
[Link] = 'track-row';

const safeTitle = [Link](/'/g,


"&#39;").replace(/"/g, "&quot;");
const safeArt = [Link](/'/g,
"&#39;").replace(/"/g, "&quot;");
const safeThumb = encodeURIComponent([Link]);
const isVideoSearch = searchFilter === 'videos';

let thumbHtml = [Link](item, isVideoSearch);

[Link] = `
${thumbHtml}
<div class="t-info" style="overflow:hidden">
<div class="t-title truncate">${[Link]}</div>
<div class="t-artist truncate">${[Link]} $
{[Link] && [Link] !== 'Single' ? '• ' + [Link] : ''}</div>
</div>
<button class="row-btn dl-btn" id="dl-${[Link]}"
title="Download" onclick="[Link](); [Link]('${[Link]}', '$
{safeTitle}', '${safeArt}', '${safeThumb}')">
<span class="icon" style="font-
size:18px">download</span>
</button>
`;

[Link] = () => {
// FIX: If searching videos, play as Video Embed by
default.
// If searching songs, play as Audio Stream.
if (isVideoSearch) {
[Link]([Link], [Link],
[Link], [Link]);
} else {
[Link]([Link], [Link], [Link],
[Link]);
}
};

$('yt-list').appendChild(row);
});
}

function watchVideo(vid, t, a, img) {


// 1. Stop python audio
eel.stop_stream_only()();

lyrics = null;
$('l-full-box').innerHTML = '';

currentMeta = {
title: t, artist: a, art: img,
is_youtube_video_active: true,
youtube_vid: vid,
is_stream: false,
duration: 0
};

// Update Button States on Player Card Overlay


$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$('po-qual-row').[Link] = 'flex';

setupOnNewTrack(currentMeta);

toast(`Loading Video: ${t}`);


[Link]('#lib-list .track-row').forEach(c =>
[Link]('playing'));

// 2. Hide Static Art, Show Embed


$('p-art').[Link] = 'none';
$('p-art-place').[Link] = 'none';
$('player-embed').[Link]('show');

// 3. FORCE PLAY
if (isYoutubeApiReady && ytPlayer) {
[Link](vid);
[Link]();
} else {
createPlayer(vid);
}

// Set play/pause icons


$('ic-play').[Link] = 'none';
$('ic-pause').[Link] = 'block';
}

async function stream(vid, t, a, img) {


// FIX: Ensure the YouTube video iframe is hidden when an audio
stream starts
$('player-embed').[Link]('show');
if (ytPlayer && [Link]) [Link]();

// Clear previous lyrics immediately


lyrics = null;
$('l-full-box').innerHTML = '';

exitKaraokeMode(false);
toast("Starting Cached Stream...");
// CHANGED: Unselect local library tracks visually for
consistency
[Link]('#lib-list .track-row').forEach(c =>
[Link]('playing'));

const meta = await pyJob(eel.stream_yt, vid, t, a, img);


if (meta) {
currentMeta = meta;
currentMeta.is_youtube_video_active = false;
currentMeta.youtube_vid = vid; // Store ID for lyrics for
later video switch

// Update Button States on Player Card Overlay


$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$('po-qual-row').[Link] = 'none';

setupOnNewTrack(currentMeta); // Update UI visuals/text and


disable FX if needed
toast(`Streaming: ${t}`);
} else {
toast("Stream failed to start (Check FFmpeg/yt-dlp).");
[Link]('p-artist').innerText = "Select a
track";
[Link]('p-artist').[Link] = 'var(--
accent)';
}
}

// MODIFIED: Added encodedThumbUrl parameter


async function dl(id, t, a, encodedThumbUrl) {
const btn = [Link](`dl-${id}`);
if ([Link]('downloading')) return;

// Decode the URL back to its original form


const thumbUrl = decodeURIComponent(encodedThumbUrl);

[Link]('downloading');
[Link]('btn-progress');
[Link]('--prog', '0%');
toast(`Recording: ${t}`);

// Pass the thumbnail URL to the Python function


await eel.record_yt_track(id, t, a, thumbUrl)();
[Link]('downloading', 'btn-progress');
[Link]('--prog', '100%');
[Link] = '<span class="icon">check</span>';
}

// --- NEW: Switch between Audio Stream and Video Embed ---
function switchPlayerMode(mode) {
if (!currentMeta || !currentMeta.youtube_vid) {
return toast("Play a YouTube track first.");
}

// Update UI Buttons
$('btn-mode-audio').[Link]('active');
$('btn-mode-video').[Link]('active');
$(`btn-mode-${mode}`).[Link]('active');

// Toggle Quality Row


$('po-qual-row').[Link] = (mode === 'video') ? 'flex' :
'none';

if (mode === 'video') {


if (currentMeta.is_youtube_video_active) return; // Already
video
// Switch to Video
[Link](currentMeta.youtube_vid, [Link],
[Link], [Link]);
} else {
if (currentMeta.is_stream) return; // Already audio
// Switch to Audio
[Link](currentMeta.youtube_vid, [Link],
[Link], [Link]);
}
}

// --- NEW: Set Video Quality ---


function setVideoQuality(q) {
if (ytPlayer && typeof [Link] ===
'function') {
[Link](q);
[Link](q); // For newer API
versions
toast(`Quality set to ${q}`);
} else {
toast("Video player not active");
}
}

return {
init, nav, load, togglePlay, next, prev,
setFX, search, stream,
openOfflineAI, selModel, runOfflineAI, openCloudModal,
startCloudProcess,
dl, refreshLib, openCtx, ctxAction, manualRescan,
toggleKaraoke, openKaraokeModal,
searchKaraokeOnly, previewK, applyK, closeKaraokeModal,
onSliderInput, onSliderChange, resetSync, ignoreWarning,
resumeAutoScroll, toggleOffsetPanel, searchLyricsManual,
switchStemMode,
setSearchMode, watchVideo,
createThumbHtml,
switchPlayerMode, setVideoQuality, // <--- ADDED THESE NEW
FUNCTIONS
playlist, currentMeta
};
})();

if ([Link] === 'loading') {


[Link]('DOMContentLoaded', [Link]);
} else {
[Link]();
}
</script>
</body>

</html>

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\web\[Link]
================================================================================

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Starting...</title>
<script type="text/javascript" src="/[Link]"></script>
<style>
body {
margin: 0;
padding: 0;
background-color: #050505;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Inter', 'Segoe UI', sans-serif;
overflow: hidden;
color: white;
}

.container {
text-align: center;
animation: fadeIn 0.5s ease-out;
width: 300px;
}

.logo {
width: 80px;
height: 80px;
background: linear-gradient(135deg, #d0bcff, #ffffff);
border-radius: 24px;
margin: 0 auto 20px auto;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 30px rgba(208, 188, 255, 0.4);
}

.logo svg {
width: 48px;
height: 48px;
fill: #000;
}

h1 {
font-size: 24px;
margin: 0;
background: linear-gradient(90deg, #fff, #888);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
letter-spacing: -0.5px;
font-weight: 800;
}

.status-text {
font-size: 11px;
color: rgba(255, 255, 255, 0.4);
margin-top: 24px;
font-family: 'JetBrains Mono', monospace;
text-transform: uppercase;
letter-spacing: 1px;
height: 1.2em;
}

.loader {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.1);
border-radius: 99px;
margin: 12px auto 0 auto;
position: relative;
overflow: hidden;
}

.bar {
position: absolute;
left: 0;
top: 0;
bottom: 0;
background: #d0bcff;
width: 0%;
border-radius: 99px;
transition: width 0.3s ease;
}

@keyframes fadeIn {
from {
opacity: 0;
transform: scale(0.95);
}

to {
opacity: 1;
transform: scale(1);
}
}
</style>
</head>

<body>
<div class="container">
<div class="logo">
<svg viewBox="0 0 24 24">
<path
d="M12 3v9.28a4.39 4.39 0 00-1.5-.28C8.01 12 6 14.01 6
16.5S8.01 21 10.5 21c2.31 0 4.2-1.75 4.45-4H15V6h4V3h-7z" />
</svg>
</div>
<h1>Material Music</h1>
<div class="status-text" id="status">Initializing...</div>
<div class="loader">
<div class="bar" id="progress-bar"></div>
</div>
</div>

<script>
// Exposed to Python
[Link](update_setup_progress);
function update_setup_progress(pct, status) {
[Link]('progress-bar').[Link] = pct + '%';
[Link]('status').innerText = status;
}

[Link](on_job_result);
function on_job_result(job_id, result) {
// Asset setup job finished
setTimeout(() => {
[Link] = '[Link]';
}, 500);
}

[Link](on_job_error);
function on_job_error(job_id, err) {
[Link]('status').innerText = "Error: " + err;
[Link]('status').[Link] = "#ff8a80";
}

[Link] = function () {
// Small delay to ensure Eel is ready
setTimeout(() => {
eel.start_asset_setup()();
}, 500);
}
</script>
</body>

</html>

================================================================================
PATH: D:\#Code
Files\MusicPlayer\ModernMusicPlayer_Web\web\assets\cache\[Link]
================================================================================

/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}

================================================================================
PATH: D:\#Code
Files\MusicPlayer\ModernMusicPlayer_Web\web\assets\cache\jetbrains_mono.css
================================================================================

/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_2.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_3.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_4.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_5.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}

================================================================================
PATH: D:\#Code
Files\MusicPlayer\ModernMusicPlayer_Web\web\assets\cache\material_symbols.css
================================================================================

/* fallback */
@font-face {
font-family: 'Material Symbols Rounded';
font-style: normal;
font-weight: 100 700;
src: url(fonts/material_symbols_0.woff2) format('woff2');
}

.material-symbols-rounded {
font-family: 'Material Symbols Rounded';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}

================================================================================
PATH: D:\#Code Files\MusicPlayer\ModernMusicPlayer_Web\web\cache\[Link]
================================================================================

/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_2.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_3.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_4.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_5.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(fonts/inter_6.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}

================================================================================
PATH: D:\#Code
Files\MusicPlayer\ModernMusicPlayer_Web\web\cache\jetbrains_mono.css
================================================================================

/* cyrillic-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_0.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F,
U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_1.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_2.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1,
U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_3.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-
01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329,
U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_4.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(fonts/jetbrains_mono_5.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
U+2212, U+2215, U+FEFF, U+FFFD;
}

================================================================================
PATH: D:\#Code
Files\MusicPlayer\ModernMusicPlayer_Web\web\cache\material_symbols.css
================================================================================

/* fallback */
@font-face {
font-family: 'Material Symbols Rounded';
font-style: normal;
font-weight: 400;
src: url(fonts/material_symbols_0.woff2) format('woff2');
}

.material-symbols-rounded {
font-family: 'Material Symbols Rounded';
font-weight: normal;
font-style: normal;
font-size: 24px;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
}

You might also like