diff --git a/index.html b/index.html
index c03ec6d..d354e6a 100644
--- a/index.html
+++ b/index.html
@@ -435,6 +435,18 @@
+
+
+
@@ -586,7 +598,9 @@
HΓΆrbuch
-
+
diff --git a/src/abs.js b/src/abs.js
index 771a870..4cf8550 100644
--- a/src/abs.js
+++ b/src/abs.js
@@ -185,6 +185,10 @@ function _updateChapter() {
ap.chapterIdx = idx;
_renderChapterName();
_renderChapterList();
+ if (_playlistMode) {
+ const cl = document.getElementById('abs-chapter-list');
+ setTimeout(() => cl?.querySelector('.abs-ch-item.active')?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }), 30);
+ }
}
}
@@ -203,6 +207,7 @@ export async function absPlayItem(item) {
const ap = S.absPlayer;
const audio = _getAudio();
+ if (!S.audio.paused) S.audio.pause(); // stop music if playing
if (ap._sessionId) { absCloseSession(ap._sessionId); ap._sessionId = null; }
_stopSync();
audio.pause();
@@ -255,6 +260,11 @@ export function absTogglePlay() {
S.absPlayer.playing ? audio.pause() : audio.play().catch(() => {});
}
+export function absInterrupt() {
+ if (!S.absPlayer.item || !S.absPlayer.playing) return;
+ _getAudio().pause(); // triggers the pause listener which calls _doSync()
+}
+
export function absSeek(globalTime) {
const ap = S.absPlayer;
globalTime = Math.max(0, Math.min(globalTime, ap.duration || Infinity));
@@ -334,6 +344,7 @@ export function absClosePlayer() {
}
export function absToggleChapters() {
+ if (_playlistMode) return;
document.getElementById('abs-chapter-list')?.classList.toggle('open');
_renderChapterList();
}
@@ -353,6 +364,30 @@ export function absSeekToChapter(idx) {
if (ch) absSeek(ch.start);
}
+// βββ PLAYLIST MODE βββ
+let _playlistMode = localStorage.getItem('ns_abs_pl_mode') === '1';
+
+export function absTogglePlaylistMode() {
+ _playlistMode = !_playlistMode;
+ localStorage.setItem('ns_abs_pl_mode', _playlistMode ? '1' : '0');
+ _applyPlaylistMode();
+}
+
+function _applyPlaylistMode() {
+ const overlay = document.getElementById('abs-player');
+ overlay?.classList.toggle('playlist-mode', _playlistMode);
+ document.getElementById('abs-playlist-btn')?.classList.toggle('active', _playlistMode);
+ const cl = document.getElementById('abs-chapter-list');
+ if (!cl) return;
+ if (_playlistMode) {
+ cl.classList.add('open');
+ _renderChapterList();
+ setTimeout(() => cl.querySelector('.abs-ch-item.active')?.scrollIntoView({ block: 'nearest', behavior: 'smooth' }), 50);
+ } else {
+ cl.classList.remove('open');
+ }
+}
+
// βββ PROGRESS SCRUB βββ
let _scrubbing = false;
export function absProgDown(e) { _scrubbing = true; _scrub(e); }
@@ -367,6 +402,33 @@ function _scrub(e) {
}
// βββ RENDER HELPERS βββ
+function _renderMiniBar() {
+ const ap = S.absPlayer;
+ const bar = document.getElementById('abs-mini-bar');
+ if (!bar) return;
+ if (!ap.item) { bar.classList.remove('visible'); return; }
+ bar.classList.add('visible');
+ const meta = ap.item?.media?.metadata || {};
+ const cover = absCoverUrl(ap.item?.id);
+ const coverEl = document.getElementById('abs-mini-cover');
+ if (coverEl) {
+ coverEl.innerHTML = cover
+ ? `
`
+ : 'π';
+ }
+ const titleEl = document.getElementById('abs-mini-title');
+ if (titleEl) titleEl.textContent = meta.title || 'Unbekannt';
+ const ch = ap.chapters[ap.chapterIdx];
+ const chEl = document.getElementById('abs-mini-chapter');
+ if (chEl) chEl.textContent = ch?.title || meta.authorName || '';
+ const playBtn = document.getElementById('abs-mini-play');
+ if (playBtn) {
+ playBtn.innerHTML = ap.playing
+ ? ``
+ : ``;
+ }
+}
+
function _fmt(secs) {
if (!isFinite(secs) || secs < 0) secs = 0;
const h = Math.floor(secs / 3600);
@@ -393,6 +455,8 @@ function _renderPlayer() {
_renderControls();
_renderChapterName();
_renderSleepBtn();
+ _renderMiniBar();
+ _applyPlaylistMode();
}
function _renderProgress() {
@@ -417,12 +481,14 @@ function _renderControls() {
: ``;
const sv = document.getElementById('abs-speed-val');
if (sv) sv.textContent = `${ap.speed}Γ`;
+ _renderMiniBar();
}
function _renderChapterName() {
const ch = S.absPlayer.chapters[S.absPlayer.chapterIdx];
const el = document.getElementById('abs-chapter-name');
if (el) el.textContent = ch?.title || '';
+ _renderMiniBar();
}
function _renderChapterList() {
diff --git a/src/main.js b/src/main.js
index 72f22a5..8b4db3a 100644
--- a/src/main.js
+++ b/src/main.js
@@ -24,7 +24,7 @@ import {
absSetSpeed, absSleepIn, absOpenPlayer, absClosePlayer, absToggleChapters,
absToggleSpeedPicker, absToggleSleepPicker, absSeekToChapter, absProgDown, absProgMove,
absProgUp, loadAbsBooks, absOpenBook, loadAbsBookDetail, absPlayChapter,
- absLoadSettingsLibraries,
+ absLoadSettingsLibraries, absTogglePlaylistMode, absInterrupt,
} from './abs.js';
import {
openNowPlaying, closeNowPlaying, switchNpTab, npGoArtist, npGoAlbum,
@@ -126,6 +126,7 @@ window.addEventListener('load', () => {
S.audio.addEventListener('timeupdate', updateProgress);
S.audio.addEventListener('ended', onEnded);
S.audio.addEventListener('play', () => {
+ absInterrupt();
setPlayIcon(true);
document.getElementById('player-art').classList.add('spinning');
if(navigator.mediaSession) navigator.mediaSession.playbackState = 'playing';
@@ -270,7 +271,7 @@ Object.assign(window, {
absSetSpeed, absSleepIn, absOpenPlayer, absClosePlayer, absToggleChapters,
absToggleSpeedPicker, absToggleSleepPicker, absSeekToChapter,
absProgDown, absProgMove, absProgUp,
- absOpenBook, absPlayChapter, absConnectSettings, absSetLibrary, absDisconnect,
+ absOpenBook, absPlayChapter, absConnectSettings, absSetLibrary, absDisconnect, absTogglePlaylistMode,
absOnSearch: (val) => {
clearTimeout(window._absSearchTimer);
window._absSearchTimer = setTimeout(() => {
diff --git a/styles.css b/styles.css
index 7093d1f..99fe3f6 100644
--- a/styles.css
+++ b/styles.css
@@ -951,3 +951,19 @@ nav{flex:1;overflow-y:auto;padding:4px 10px}
.abs-cover-area{padding:8px 24px}
.abs-cover-wrap{width:min(240px,calc(100vw - 48px))}
}
+
+/* ββ ABS MINI BAR ββ */
+.abs-mini-bar{position:fixed;bottom:var(--player-h);left:0;right:0;background:rgba(10,10,28,.96);backdrop-filter:blur(16px);border-top:1px solid rgba(130,80,255,.25);padding:9px 16px;display:flex;align-items:center;gap:12px;z-index:99;transform:translateY(100%);transition:transform .3s;cursor:pointer}
+.abs-mini-bar.visible{transform:translateY(0)}
+.abs-mini-cover{width:40px;height:40px;border-radius:7px;background:var(--surface2);display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;overflow:hidden}
+.abs-mini-info{flex:1;min-width:0}
+.abs-mini-title{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.abs-mini-chapter{font-size:11px;color:var(--text2);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.abs-mini-play{background:none;border:none;color:var(--text);cursor:pointer;padding:6px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
+@media(max-width:768px){.abs-mini-bar{bottom:calc(var(--player-h) + 80px + env(safe-area-inset-bottom))}}
+
+/* ββ ABS PLAYLIST MODE ββ */
+.abs-hbtn.active{color:var(--accent)}
+.abs-player-overlay.playlist-mode .abs-cover-area{flex:0 0 auto;padding:4px 40px}
+.abs-player-overlay.playlist-mode .abs-cover-wrap{width:min(140px,calc(100vw - 80px))}
+.abs-player-overlay.playlist-mode .abs-chapter-list.open{flex:1;max-height:none;overflow-y:auto}