Do not offer a player for a file that is not audio or video
The media-type filter stopped new image enclosures being fetched, but ones already on disk still got a play button and an <audio> element, because the UI tested for a path rather than for a playable type. Four places did this, including play() itself, which picked the first downloaded enclosure whatever it was. isPlayable() checks audio/* or video/*, falling back to the extension when a feed declares no type. A downloaded non-media file now shows as its kind with Save and Delete, so it stays available without posing as an episode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdXho5tTkjFLeUXKbEjKBh
This commit is contained in:
@@ -619,6 +619,7 @@ function renderEntries(){
|
||||
function epEl(e){
|
||||
const enc=e.enclosures[0];
|
||||
const has=!!(enc&&enc.path);
|
||||
const playable=isPlayable(enc);
|
||||
const el=document.createElement('div');
|
||||
el.className='ep'+(e.read?' read':'')+(S.sel===e.guid?' sel':'')+
|
||||
(player.guid===e.guid?' playing':'');
|
||||
@@ -643,8 +644,8 @@ function epEl(e){
|
||||
${enc&&enc.last_error?`<div class="line" style="color:var(--bad)">${esc(enc.last_error)}</div>`:''}
|
||||
</div>
|
||||
<div class="rowacts">
|
||||
${has?`<button class="iconbtn" data-a="play" title="Play">▶</button>`:
|
||||
(enc?`<button class="iconbtn" data-a="get" title="Download this ${
|
||||
${playable?`<button class="iconbtn" data-a="play" title="Play">▶</button>`:
|
||||
(enc&&!has?`<button class="iconbtn" data-a="get" title="Download this ${
|
||||
enc.state==='skipped'?kindOf(enc):'file'}">⤓</button>`:'')}
|
||||
<button class="iconbtn" data-a="flag" title="${e.flagged?'Stop keeping':'Keep (never auto-delete)'}">${e.flagged?'★':'☆'}</button>
|
||||
<button class="iconbtn" data-a="read" title="Mark ${e.read?'unread':'read'}">${e.read?'○':'●'}</button>
|
||||
@@ -652,7 +653,7 @@ function epEl(e){
|
||||
</div>`;
|
||||
|
||||
const art=$('.art',el);
|
||||
if(art&&has){
|
||||
if(art&&playable){
|
||||
art.insertAdjacentHTML('beforeend','<span class="ovl">▶</span>');
|
||||
art.onclick=ev=>{ ev.stopPropagation(); play(e); };
|
||||
}
|
||||
@@ -660,6 +661,19 @@ function epEl(e){
|
||||
$$('.rowacts .iconbtn',el).forEach(b=>b.onclick=ev=>{ev.stopPropagation();epAction(b.dataset.a,e,el)});
|
||||
return el;
|
||||
}
|
||||
/// Whether the browser can play it. Having a file is not the same as being playable:
|
||||
/// blog feeds put article images in enclosures, and an <audio> element pointed at a JPEG
|
||||
/// is just a broken player.
|
||||
function isPlayable(x){
|
||||
if(!x || !x.path) return false;
|
||||
const m=(x.mime||'').toLowerCase();
|
||||
if(m.startsWith('audio/')||m.startsWith('video/')) return true;
|
||||
if(m) return false;
|
||||
// No declared type: fall back to the file's extension.
|
||||
return /\.(mp3|m4a|m4b|aac|ogg|oga|opus|flac|wav|mp4|m4v|mov|webm|mkv)$/i
|
||||
.test((x.path||x.url||'').split('?')[0]);
|
||||
}
|
||||
|
||||
/// What an enclosure is, for a row that is not an episode: "image", "pdf", "document".
|
||||
function kindOf(enc){
|
||||
const m=(enc.mime||'').toLowerCase();
|
||||
@@ -736,6 +750,15 @@ function showDetail(e){
|
||||
/// One enclosure: a player when the file is here, otherwise what it is and a way to get it.
|
||||
function encBox(x){
|
||||
const size=x.length?mb(x.length):'';
|
||||
if(x.path && !isPlayable(x)){
|
||||
// On disk, but not audio or video: offer the file, not a player.
|
||||
return `<div class="encbox">
|
||||
<span class="chip done">${esc(kindOf(x))}</span>
|
||||
<span class="meta" style="flex:1">downloaded${size?' \u00b7 '+size:''}</span>
|
||||
<a class="btn" href="/media/${x.id}" download>Save</a>
|
||||
<button class="btn danger" data-a="del" data-enc="${x.id}">Delete file</button>
|
||||
</div>`;
|
||||
}
|
||||
if(x.path){
|
||||
return `<div class="encbox">
|
||||
<audio id="audio-${x.id}" controls preload="none" src="/media/${x.id}"></audio>
|
||||
@@ -793,8 +816,11 @@ function markPlayed(){
|
||||
}
|
||||
|
||||
function play(e){
|
||||
const enc=e.enclosures.find(x=>x.path);
|
||||
if(!enc){ toast('Not downloaded yet',true); return; }
|
||||
const enc=e.enclosures.find(isPlayable);
|
||||
if(!enc){
|
||||
toast(e.enclosures.some(x=>x.path) ? 'That file is not audio or video' : 'Not downloaded yet', true);
|
||||
return;
|
||||
}
|
||||
const resuming = player.guid===e.guid;
|
||||
if(!resuming){
|
||||
player.guid=e.guid; player.feed=e.feed_id; player.entry=e;
|
||||
|
||||
Reference in New Issue
Block a user