diff --git a/src/components.js b/src/components.js
index 03d60eb..d8f97c9 100644
--- a/src/components.js
+++ b/src/components.js
@@ -1,5 +1,13 @@
import { S } from './state.js';
import { esc, formatDur } from './utils.js';
+
+// Exposed on window so inline onerror= handlers can call it.
+// Retries the same src up to 3 times with increasing delays, then shows fallback.
+window._imgRetry = function(img, fallback) {
+ const n = (img._r = (img._r || 0) + 1);
+ if (n > 3 || !img.src) { img.parentNode.innerHTML = `
${fallback}
`; return; }
+ setTimeout(() => { const s = img.src; img.src = ''; img.src = s; }, 1500 * n);
+};
import { coverUrl, dispTitle, dispArtistParts, dispAlbum } from './api.js';
// Circular imports โ safe (only used inside event handlers after module init)
import { playQueue, toggleStar, playPreview, parseArtists } from './player.js';
@@ -11,7 +19,7 @@ export function makeCard(title, sub, imgUrl, fallback='๐ฟ') {
const c = document.createElement('div');
c.className = 'card';
c.innerHTML = `
- ${imgUrl ? `

` : `
${fallback}
`}
+ ${imgUrl ? `

` : `
${fallback}
`}
${esc(title)}
${esc(sub||'')}
`;
@@ -45,7 +53,7 @@ export function makeSongItem(s, idx, list, { noArtistLink = false } = {}) {
? `
${s.playCount ? s.playCount.toLocaleString() + 'ร' : 'โ'}`
: '';
el.innerHTML = `
${idx+1}
-
${cv ? `

` : '
โช
'}
+
${cv ? `

` : '
โช
'}
${esc(dispTitle(s))}
${artistHtml}
diff --git a/src/main.js b/src/main.js
index eaa2797..41691b3 100644
--- a/src/main.js
+++ b/src/main.js
@@ -94,7 +94,7 @@ async function startApp() {
if (bnav) bnav.style.display = '';
absLoadSettingsLibraries();
}
- if (S.cfg.showRomaji) await fetchRomajiIndex().catch(() => {});
+ if (S.cfg.showRomaji) fetchRomajiIndex().catch(() => {}); // fire-and-forget โ don't block navigation
if(_navLoad() && S._navCursor >= 0 && S._navStack[S._navCursor]) {
const last = S._navStack[S._navCursor];
S._navJumping = true;
diff --git a/src/player.js b/src/player.js
index 1c997f0..be5d2c4 100644
--- a/src/player.js
+++ b/src/player.js
@@ -359,6 +359,11 @@ export function playQueue(songs, startIdx) {
_radioFilling = false; // cancel any in-flight radio fill for the old queue
S._origQueue = null;
_histClear();
+ // On Android with shuffle on, start at a random position so we don't always
+ // begin at index 0 (the first album/playlist track).
+ if (HAS_BRIDGE && S.shuffle && songs.length > 1) {
+ startIdx = Math.floor(Math.random() * songs.length);
+ }
if (!HAS_BRIDGE && S.shuffle && songs.length > 1) {
const start = songs[startIdx];
const rest = songs.filter((_, i) => i !== startIdx).sort(() => Math.random() - 0.5);
diff --git a/src/ui.js b/src/ui.js
index 0b0be44..718c8c4 100644
--- a/src/ui.js
+++ b/src/ui.js
@@ -536,13 +536,13 @@ export async function deleteSong() {
if (!r.ok) throw new Error(d.error || `HTTP ${r.status}`);
toast(`๐ "${s.title}" gelรถscht`);
closeInfoModal();
- // Refresh playlist views after Navidrome's background scan has a chance to finish
+ // Refresh playlist data after Navidrome's background scan has a chance to finish.
+ // Always reload playlists (even if not active) so counts are fresh when navigating back.
setTimeout(() => {
- if (document.getElementById('page-playlists')?.classList.contains('active'))
- loadPlaylists();
- else if (document.getElementById('page-playlist-detail')?.classList.contains('active') && S._currentPlaylist)
+ loadPlaylists();
+ if (document.getElementById('page-playlist-detail')?.classList.contains('active') && S._currentPlaylist)
loadPlaylistDetail(S._currentPlaylist);
- }, 2000);
+ }, 3000);
} catch (e) {
status.textContent = 'โ Fehler: ' + e.message;
}
diff --git a/src/views.js b/src/views.js
index faee608..be25d07 100644
--- a/src/views.js
+++ b/src/views.js
@@ -180,7 +180,7 @@ function _genreColor(name) {
let _allGenres = [];
let _genresExpanded = false;
-const GENRE_LIMIT = () => window.innerWidth >= 700 ? 24 : 12;
+const GENRE_LIMIT = () => window.innerWidth >= 700 ? 24 : 6;
async function loadGenreSection() {
const el = document.getElementById('home-genres');
@@ -631,7 +631,7 @@ export async function loadItunesAlbumDetail(album) {
document.getElementById('itunes-album-detail-artist').textContent = album.artistName || '';
const cw = document.getElementById('itunes-album-cover-wrap');
const art = (album.artworkUrl100 || '').replace('100x100bb','600x600bb').replace('100x100','600x600');
- cw.innerHTML = art ? `

` : '
๐ฟ
';
+ cw.innerHTML = art ? `

` : '
๐ฟ
';
const dlBtn = document.getElementById('itunes-album-dl-btn');
dlBtn.onclick = () => openDlModal({ artistName: album.artistName, trackName: album.collectionName, collectionName: album.collectionName, artworkUrl100: album.artworkUrl100||'', isAlbum: true, itunesData: album, collectionId: album.collectionId });
const sl = document.getElementById('itunes-album-songs');
@@ -657,7 +657,7 @@ export async function loadAlbumDetail(album) {
.join('
โข ') || '';
const cw = document.getElementById('album-cover-wrap');
const cv = coverUrl(album.coverArt, 512);
- cw.innerHTML = cv ? `

` : '
๐ฟ
';
+ cw.innerHTML = cv ? `

` : '
๐ฟ
';
const sl = document.getElementById('album-songs');
sl.innerHTML = '
';
try {
@@ -717,7 +717,7 @@ export async function loadPlaylistDetail(pl) {
document.getElementById('playlist-detail-name').textContent = pl.name;
const cw = document.getElementById('playlist-cover');
const cv = coverUrl(pl.coverArt, 512);
- cw.innerHTML = cv ? `

` : '
๐ถ
';
+ cw.innerHTML = cv ? `

` : '
๐ถ
';
document.getElementById('btn-edit-playlist').onclick = () => openEditPlModal(S._currentPlaylist);
const sl = document.getElementById('playlist-songs');
sl.innerHTML = '
';