fix: Scrub lag fix and Volume control for Audiobooks
This commit is contained in:
1 parent
4b07203082
commit
7744ae85db
4 files changed
+37
-9
No files matched your search
+9
-2
@@ -429,7 +429,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="volume-wrap">
|
<div class="volume-wrap">
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
||||||
<input type="range" class="volume-slider" id="volume-slider" min="0" max="100" value="80" oninput="setVolume(this.value)">
|
<input type="range" class="volume-slider volume-slider-sync" id="volume-slider" min="0" max="100" value="50" oninput="setVolume(this.value)">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -712,6 +712,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Volume -->
|
||||||
|
<div class="abs-volume-row">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3);flex-shrink:0"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
|
||||||
|
<input type="range" class="abs-vol-slider volume-slider-sync" min="0" max="100" value="50" oninput="setVolume(this.value)">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:var(--text3);flex-shrink:0"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Chapter list -->
|
<!-- Chapter list -->
|
||||||
<div id="abs-chapter-list" class="abs-chapter-list"></div>
|
<div id="abs-chapter-list" class="abs-chapter-list"></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -872,7 +879,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="np-vol">
|
<div class="np-vol">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/></svg>
|
||||||
<input type="range" class="np-vol-slider" id="np-vol-slider" min="0" max="100" value="80" oninput="setVolume(this.value);document.getElementById('volume-slider').value=this.value">
|
<input type="range" class="np-vol-slider volume-slider-sync" id="np-vol-slider" min="0" max="100" value="50" oninput="setVolume(this.value)">
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="color:rgba(255,255,255,.45)"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<button class="np-icon-btn" id="np-radio-btn" onclick="toggleRadioMode()" title="Radio-Modus: endloser Shuffle">
|
<button class="np-icon-btn" id="np-radio-btn" onclick="toggleRadioMode()" title="Radio-Modus: endloser Shuffle">
|
||||||
|
|||||||
+1
-1
@@ -149,7 +149,7 @@ window.addEventListener('load', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
S.audio.volume = 0.65;
|
S.audio.volume = 0.5;
|
||||||
|
|
||||||
// Re-register MediaSession handlers when returning from background/screen-off/flip
|
// Re-register MediaSession handlers when returning from background/screen-off/flip
|
||||||
document.addEventListener('visibilitychange', () => {
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
|||||||
+23
-6
@@ -213,8 +213,16 @@ export function updateProgress() {
|
|||||||
|
|
||||||
export function seekTo(e) {
|
export function seekTo(e) {
|
||||||
const bar = document.getElementById('progress-bar');
|
const bar = document.getElementById('progress-bar');
|
||||||
|
if (!bar || !S.audio.duration) return;
|
||||||
const rect = bar.getBoundingClientRect();
|
const rect = bar.getBoundingClientRect();
|
||||||
S.audio.currentTime = ((e.clientX - rect.left) / rect.width) * S.audio.duration;
|
const pos = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
|
||||||
|
const fill = document.getElementById('progress-fill');
|
||||||
|
if (fill) { fill.style.transition = 'none'; fill.style.width = (pos * 100) + '%'; }
|
||||||
|
S.audio.currentTime = pos * S.audio.duration;
|
||||||
|
S._scrubbing = true;
|
||||||
|
const done = () => { S._scrubbing = false; requestAnimationFrame(() => { if (fill) fill.style.transition = ''; }); };
|
||||||
|
S.audio.addEventListener('seeked', done, { once: true });
|
||||||
|
setTimeout(done, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initProgressScrub() {
|
export function initProgressScrub() {
|
||||||
@@ -236,14 +244,15 @@ function _initBarScrub(bar, fill, onMove) {
|
|||||||
function startScrub(pos) { S._scrubbing=true; fill.style.transition='none'; fill.style.width=(pos*100)+'%'; onMove(pos); }
|
function startScrub(pos) { S._scrubbing=true; fill.style.transition='none'; fill.style.width=(pos*100)+'%'; onMove(pos); }
|
||||||
function moveScrub(pos) { fill.style.width=(pos*100)+'%'; onMove(pos); }
|
function moveScrub(pos) { fill.style.width=(pos*100)+'%'; onMove(pos); }
|
||||||
function endScrub(pos) {
|
function endScrub(pos) {
|
||||||
fill.style.transition='';
|
fill.style.width = (pos * 100) + '%'; // pin visual position before re-enabling transition
|
||||||
if(S.audio.duration) {
|
if(S.audio.duration) {
|
||||||
S.audio.currentTime = pos * S.audio.duration;
|
S.audio.currentTime = pos * S.audio.duration;
|
||||||
// Keep _scrubbing=true until seek completes so timeupdate can't snap bar back
|
const done = () => { S._scrubbing = false; requestAnimationFrame(() => { fill.style.transition = ''; }); };
|
||||||
S.audio.addEventListener('seeked', () => { S._scrubbing = false; }, { once: true });
|
S.audio.addEventListener('seeked', done, { once: true });
|
||||||
setTimeout(() => { S._scrubbing = false; }, 1500); // fallback if seeked never fires
|
setTimeout(done, 1500); // fallback if seeked never fires
|
||||||
} else {
|
} else {
|
||||||
S._scrubbing = false;
|
S._scrubbing = false;
|
||||||
|
fill.style.transition = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bar.addEventListener('touchstart', e => { startScrub(getPos(e.touches[0].clientX)); e.stopPropagation(); }, {passive:true});
|
bar.addEventListener('touchstart', e => { startScrub(getPos(e.touches[0].clientX)); e.stopPropagation(); }, {passive:true});
|
||||||
@@ -258,7 +267,15 @@ function _initBarScrub(bar, fill, onMove) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setVolume(v) { S.audio.volume = v/100; S.previewAudio.volume = v/100; }
|
export function setVolume(v) {
|
||||||
|
const vol = v / 100;
|
||||||
|
S.audio.volume = vol;
|
||||||
|
S.previewAudio.volume = vol;
|
||||||
|
const absAudio = document.getElementById('abs-audio');
|
||||||
|
if (absAudio) absAudio.volume = vol;
|
||||||
|
// sync all volume sliders
|
||||||
|
document.querySelectorAll('.volume-slider-sync').forEach(el => { el.value = v; });
|
||||||
|
}
|
||||||
|
|
||||||
// ─── MEDIA SESSION ───
|
// ─── MEDIA SESSION ───
|
||||||
export function updateMediaSession(s) {
|
export function updateMediaSession(s) {
|
||||||
|
|||||||
@@ -965,4 +965,8 @@ nav{flex:1;overflow-y:auto;padding:4px 10px}
|
|||||||
.abs-mini-open{background:none;border:none;color:var(--text2);cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
.abs-mini-open{background:none;border:none;color:var(--text2);cursor:pointer;padding:8px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
|
||||||
.abs-mini-open:hover{color:var(--text)}
|
.abs-mini-open:hover{color:var(--text)}
|
||||||
.abs-hbtn.active{color:var(--accent)}
|
.abs-hbtn.active{color:var(--accent)}
|
||||||
|
.abs-volume-row{display:flex;align-items:center;gap:10px;padding:0 24px 14px}
|
||||||
|
.abs-vol-slider{flex:1;-webkit-appearance:none;appearance:none;height:3px;border-radius:2px;background:var(--surface2);outline:none;cursor:pointer}
|
||||||
|
.abs-vol-slider::-webkit-slider-thumb{-webkit-appearance:none;width:14px;height:14px;border-radius:50%;background:var(--accent);cursor:pointer}
|
||||||
|
.abs-vol-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;background:var(--accent);cursor:pointer;border:none}
|
||||||
@media(max-width:768px){.abs-mini-bar{bottom:80px;height:var(--player-h)}}
|
@media(max-width:768px){.abs-mini-bar{bottom:80px;height:var(--player-h)}}
|
||||||
Reference in new issue
Block a user