From 934253a036f9bbcf33abd2892567d3b4e819f135 Mon Sep 17 00:00:00 2001 From: Honey Date: Fri, 21 Aug 2026 17:59:44 +0200 Subject: [PATCH] feat(web): merge Song Importer into Global Suche (one unified field) The importer no longer has its own sidebar/bottom-nav entry. The Global Suche field now handles both jobs: plain text runs the Deezer search; a pasted Spotify/SoundCloud/YouTube link switches into importer mode (badge + "Importieren" button + preview), matching the Android app and tidying the left nav. - New onGlobalInput/tryImporterResolve; importer DOM moved into page-discover; old page-spotify-importer removed; routing cleaned up. - Existing resolvers unchanged (still read the mirrored importer-link-input). Co-Authored-By: Claude Opus 4.8 --- index.html | 138 ++++++++++++++++++---------------------- src/main.js | 4 +- src/views/navigation.js | 5 +- src/views/spotify.js | 44 +++++++++++++ 4 files changed, 110 insertions(+), 81 deletions(-) diff --git a/index.html b/index.html index 36c6d3e..15c904f 100644 --- a/index.html +++ b/index.html @@ -85,9 +85,6 @@ - - +
-
- @@ -390,69 +443,6 @@
- -
- - - -
-
- - -
-
-
-
-
Spotify — Track, Album oder Playlist
-
SoundCloud — Track oder Playlist
-
YouTube — Video oder Playlist
-
-
Link einfügen → Metadaten werden geladen → via yt-dlp in die Navidrome Library herunterladen.
-
- - - - - - - - - - - - - -
@@ -1192,10 +1182,6 @@ Zufällig - diff --git a/src/main.js b/src/main.js index 836e9cc..2e3b067 100644 --- a/src/main.js +++ b/src/main.js @@ -24,7 +24,7 @@ import { plReorderEnter, plReorderSave, plReorderCancel, toggleAllGenres, } from './views/views.js'; import { loadSpotifyImporter, resolveSpotifyLink, clearSpotifyPreview, spotifyDlAll, - onImporterInput, resolveImporterLink } from './views/spotify.js'; + onImporterInput, resolveImporterLink, onGlobalInput, tryImporterResolve } from './views/spotify.js'; import { resolveYouTubeLink, executeYouTubeDownload } from './views/youtube.js'; import { loadSoundCloudImporter, resolveSoundCloudLink, clearSoundCloudPreview, scDlAll, scDlSingle, scDlTrack } from './views/soundcloud.js'; import { @@ -343,7 +343,7 @@ Object.assign(window, { loadAlbums, loadStarred, loadRandom, filterStarred, filterPlaylist, plReorderEnter, plReorderSave, plReorderCancel, toggleAllGenres, loadSpotifyImporter, resolveSpotifyLink, clearSpotifyPreview, spotifyDlAll, - onImporterInput, resolveImporterLink, + onImporterInput, resolveImporterLink, onGlobalInput, tryImporterResolve, loadSoundCloudImporter, resolveSoundCloudLink, clearSoundCloudPreview, scDlAll, scDlSingle, scDlTrack, executeSpotifyDownload, resolveYouTubeLink, executeYouTubeDownload, openPlModal, closePlModal, addSongsToPlaylist, createAndAddPlaylist, createPlaylistPrompt, diff --git a/src/views/navigation.js b/src/views/navigation.js index f294784..2edd777 100644 --- a/src/views/navigation.js +++ b/src/views/navigation.js @@ -62,12 +62,12 @@ export function navigate(page, data=null) { closeHeaderSearch(); document.querySelectorAll('.page').forEach(p => p.classList.remove('active')); document.querySelectorAll('.nav-item,.bnav-item').forEach(n => n.classList.remove('active','active-blue')); - const cls = ['discover','new-releases','recommendations','spotify-importer'].includes(page) ? 'active-blue' : 'active'; + const cls = ['discover','new-releases','recommendations'].includes(page) ? 'active-blue' : 'active'; document.querySelectorAll(`[data-page="${page}"]`).forEach(el => el.classList.add(cls)); const pageEl = document.getElementById(`page-${page}`); if(pageEl) { pageEl.classList.add('active'); document.getElementById('main-content').scrollTop = 0; } closeSidebar(); - if(['discover','new-releases','recommendations','random','spotify-importer'].includes(page)) switchNavMode('discover'); + if(['discover','new-releases','recommendations','random'].includes(page)) switchNavMode('discover'); else if(['home','artists','albums','starred','playlists'].includes(page)) switchNavMode('library'); if(page==='home') loadHomeData(!S._navJumping); // explicit Home click refreshes; back/forward keeps state @@ -85,7 +85,6 @@ export function navigate(page, data=null) { if(page==='playlists') loadPlaylists(); if(page==='playlist-detail' && data) loadPlaylistDetail(data); if(page==='recommendations') loadRecommendations(); - if(page==='spotify-importer') { loadSpotifyImporter(); loadSoundCloudImporter(); } if(page==='audiobooks') { import('./abs.js').then(m => { if (S.abs.currentLibraryId) m.loadAbsBooks(S.abs.currentLibraryId); diff --git a/src/views/spotify.js b/src/views/spotify.js index 28ce455..c5c23cf 100644 --- a/src/views/spotify.js +++ b/src/views/spotify.js @@ -47,6 +47,50 @@ export function onImporterInput(val) { } } +// ─── UNIFIED GLOBAL-SUCHE FIELD ─── +// One field on the Global-Suche page handles both jobs: a pasted Spotify/SoundCloud/YouTube link +// switches into the importer flow; any other text runs the Deezer library search. This replaces the +// separate "Song Importer" sidebar entry (the importer now lives inside Global Suche). +const _IMPORT_LINK_RE = /^https?:\/\//i; +const _IMPORT_HOST_RE = /(?:spotify\.com|soundcloud\.com|youtube\.com|youtu\.be)/i; +function _isImportLink(v) { return _IMPORT_LINK_RE.test(v) || _IMPORT_HOST_RE.test(v); } + +export function onGlobalInput(val) { + const v = (val || '').trim(); + const area = document.getElementById('importer-area'); + const results = document.getElementById('global-search-results'); + const badge = document.getElementById('importer-badge'); + const btn = document.getElementById('importer-btn'); + if (_isImportLink(v)) { + // Link → importer mode + if (results) results.style.display = 'none'; + if (area) area.style.display = ''; + if (badge) badge.style.display = ''; + if (btn) btn.style.display = ''; + const hidden = document.getElementById('importer-link-input'); + if (hidden) hidden.value = v; // mirror so resolveImporterLink() reads it + onImporterInput(v); // badge styling + clear stale previews + const hint = document.getElementById('importer-hint'); + if (hint) hint.style.display = ''; // keep the platform hint as a header until a preview loads + } else { + // Text → Deezer search + if (area) area.style.display = 'none'; + if (badge) badge.style.display = 'none'; + if (btn) btn.style.display = 'none'; + if (results) results.style.display = ''; + window.onGlobalSearch(v); + } +} + +// Enter in the unified field: resolve the link if it is one (search is already live via oninput). +export function tryImporterResolve() { + const v = (document.getElementById('global-search-input')?.value || '').trim(); + if (!_isImportLink(v)) return; + const hidden = document.getElementById('importer-link-input'); + if (hidden) hidden.value = v; + resolveImporterLink(); +} + export async function resolveImporterLink() { const url = document.getElementById('importer-link-input')?.value.trim(); if (!url) return;