Compact HE-AAC audio saves: server lane with fdkaac, shrink saved songs from Saved videos
This commit is contained in:
@@ -658,7 +658,27 @@ async function renderSaved() {
|
||||
c.innerHTML = '';
|
||||
const summary = document.createElement('div');
|
||||
summary.className = 'saved-summary saved-storage-summary';
|
||||
summary.innerHTML = `<span class="saved-storage-text">${fmtBytes(total)} · ${items.length} video${items.length === 1 ? '' : 's'} stored offline</span>`;
|
||||
const audioCount = items.filter((it) => /\.m4a$/i.test(it.name || '')).length;
|
||||
summary.innerHTML = `<span class="saved-storage-text">${fmtBytes(total)} · ${items.length} saved${audioCount ? ` · ${audioCount} as audio` : ''}</span>`;
|
||||
// Every saved YouTube song not yet in compact audio can be shrunk in place.
|
||||
const shrinkable = items.filter((it) => canShrinkSaved(it));
|
||||
if (shrinkable.length) {
|
||||
const shrinkAll = document.createElement('button');
|
||||
shrinkAll.type = 'button';
|
||||
shrinkAll.className = 'saved-clear saved-shrink-all';
|
||||
shrinkAll.textContent = `Shrink ${shrinkable.length}`;
|
||||
shrinkAll.title = 'Replace these saves with compact audio (HE-AAC): about a third of the space, no video';
|
||||
shrinkAll.setAttribute('aria-label', `Shrink ${shrinkable.length} saved songs to compact audio`);
|
||||
shrinkAll.onclick = () => {
|
||||
const before = shrinkable.reduce((n, it) => n + (it.size || 0), 0);
|
||||
showModal(`Shrink ${shrinkable.length} saved song${shrinkable.length === 1 ? '' : 's'}?`, document.createTextNode(
|
||||
`They are swapped for compact audio (HE-AAC, ~48 kbps): sound only, no video, about a third of an audio save and far less than a video (now ${fmtBytes(before)}). The server converts each song once; a song keeps playing from its current copy until the new one is in place.`), [
|
||||
{ label: 'Cancel', onClick: closeModal },
|
||||
{ label: 'Shrink', onClick: () => { closeModal(); shrinkSavedAll(shrinkable.map((it) => it.id)); } },
|
||||
]);
|
||||
};
|
||||
summary.appendChild(shrinkAll);
|
||||
}
|
||||
summary.appendChild(clearAll);
|
||||
$('listFilterBar').classList.remove('hidden');
|
||||
c.appendChild(summary);
|
||||
@@ -679,13 +699,20 @@ async function renderSaved() {
|
||||
onerror="this.classList.add('no-art')" />${isCustom ? '<span class="edit-badge" title="Edited copy">✂</span>' : ''}${isUploadId(v.id) ? `<span class="upload-badge" title="On this server">${v.kind === 'audio' ? '♪' : '📁'}</span>` : ''}</div>
|
||||
<div class="card-info">
|
||||
<div class="card-title"></div>
|
||||
<div class="card-channel saved-size">${isCustom ? '✂ edited · ' : ''}${fmtBytes(it.size)}</div>
|
||||
<div class="card-channel saved-size">${isCustom ? '✂ edited · ' : ''}${savedFormatLabel(it)} · ${fmtBytes(it.size)}</div>
|
||||
</div>`;
|
||||
row.querySelector('.card-title').textContent = v.title || it.id;
|
||||
row.addEventListener('click', (e) => {
|
||||
if (e.target.closest('.saved-file-actions')) return;
|
||||
playFromList([v], 0, 'saved');
|
||||
});
|
||||
const shrink = canShrinkSaved(it) ? async () => {
|
||||
toast(`Shrinking “${v.title || v.id}”…`);
|
||||
const r = await shrinkSaved(v);
|
||||
toast(r.ok ? (r.compact ? `Shrunk ${fmtBytes(r.before)} → ${fmtBytes(r.after)}` : 'This song has no smaller version')
|
||||
: '⚠ ' + (r.error || 'Could not shrink it'));
|
||||
if (view.type === 'saved') renderSaved();
|
||||
} : null;
|
||||
SavedPage.actions(row, v, () => exportToDevice(v), () => {
|
||||
showModal(`Delete saved file “${v.title || v.id}”?`, document.createTextNode(isCustom
|
||||
? 'This edited copy exists only on this device and will be removed permanently.'
|
||||
@@ -708,7 +735,7 @@ async function renderSaved() {
|
||||
toast('Removed saved file'); renderSaved();
|
||||
} },
|
||||
]);
|
||||
});
|
||||
}, shrink);
|
||||
c.appendChild(row);
|
||||
});
|
||||
if (!c.querySelector('.saved-card')) {
|
||||
@@ -959,6 +986,13 @@ async function renderSettings() {
|
||||
</span>
|
||||
<input id="setAutoPreload" type="checkbox" ${data.settings.autoPreload ? 'checked' : ''} />
|
||||
</label>
|
||||
<label class="set-row">
|
||||
<span>
|
||||
Save songs as compact audio
|
||||
<small>New saves keep only the sound, as HE-AAC (about a third of the space, no video). The server converts each song once. Shrink songs you already saved from Saved videos.</small>
|
||||
</span>
|
||||
<input id="setCompactAudio" type="checkbox" ${data.settings.compactAudio ? 'checked' : ''} />
|
||||
</label>
|
||||
<label class="set-row">
|
||||
<span>
|
||||
Videos saved at once
|
||||
@@ -1355,6 +1389,10 @@ async function renderSettings() {
|
||||
data.settings.autoSavePlayed = e.target.checked;
|
||||
persist();
|
||||
});
|
||||
$('setCompactAudio').addEventListener('change', (e) => {
|
||||
data.settings.compactAudio = e.target.checked;
|
||||
persist();
|
||||
});
|
||||
$('setAutoPreload').addEventListener('change', (e) => {
|
||||
data.settings.autoPreload = e.target.checked;
|
||||
persist();
|
||||
|
||||
Reference in New Issue
Block a user