Auto-save every video you play for offline, with a Settings switch

This commit is contained in:
Jonathan Sykes
2026-10-11 03:26:04 +08:00
parent 933e920701
commit f84ecb5ae9
3 changed files with 27 additions and 1 deletions

View File

@@ -655,6 +655,7 @@ const DEFAULT_SETTINGS = {
quality: 'auto', volume: 1, audioOnly: false, autoPreload: true, quality: 'auto', volume: 1, audioOnly: false, autoPreload: true,
p2pShare: true, // P2P (docs/p2p-architecture.md): share my saved videos with other devices — ON by default p2pShare: true, // P2P (docs/p2p-architecture.md): share my saved videos with other devices — ON by default
p2pReceive: true, // fetch from other devices when YouTube and the server can't — ON by default p2pReceive: true, // fetch from other devices when YouTube and the server can't — ON by default
autoSavePlayed: true, // keep every video you play for offline (background save once playback starts)
saveBeforePlay: false, // download (server-muxed single file) before playing instead of streaming saveBeforePlay: false, // download (server-muxed single file) before playing instead of streaming
repeatMode: 'off', // 'off' | 'all' — repeat the playing list when it ends repeatMode: 'off', // 'off' | 'all' — repeat the playing list when it ends
loopOne: false, // repeat the single current video loopOne: false, // repeat the single current video
@@ -1977,6 +1978,20 @@ function markCardCacheState(id, state) {
}); });
} }
// Auto-save what you play: once a video starts playing, save it in the
// background (never delays playback). One attempt per video per page session.
const autoSaveTried = new Set();
document.addEventListener('play', () => {
const m = current && current.meta;
if (!m || !m.id || m.custom || current.localUrl) return;
if (data.settings.autoSavePlayed === false) return;
if (!WEB) return;
if (cachedIds.has(m.id) || downloading.has(m.id) || autoSaveTried.has(m.id)) return;
autoSaveTried.add(m.id);
preload(m, { quiet: true });
}, true);
// ============================================================================ // ============================================================================
// Video editor — cut parts out of a video and save a custom offline copy // Video editor — cut parts out of a video and save a custom offline copy
// //

View File

@@ -844,6 +844,13 @@ async function renderSettings() {
<div class="set-group"> <div class="set-group">
<div class="set-group-title">Offline cache</div> <div class="set-group-title">Offline cache</div>
<div class="set-row"><span>App &amp; features</span><small id="offlineAssetStatus" role="status">Checking offline readiness…</small></div> <div class="set-row"><span>App &amp; features</span><small id="offlineAssetStatus" role="status">Checking offline readiness…</small></div>
<label class="set-row">
<span>
Auto-save videos I play
<small>Every video you open is kept on this device in the background, so it plays offline later.</small>
</span>
<input id="setAutoSavePlayed" type="checkbox" ${data.settings.autoSavePlayed !== false ? 'checked' : ''} />
</label>
<label class="set-row"> <label class="set-row">
<span> <span>
Auto-save playlist videos Auto-save playlist videos
@@ -1116,6 +1123,10 @@ async function renderSettings() {
persist(); persist();
Notes.refreshFlags(); Notes.refreshFlags();
}); });
$('setAutoSavePlayed').addEventListener('change', (e) => {
data.settings.autoSavePlayed = e.target.checked;
persist();
});
$('setAutoPreload').addEventListener('change', (e) => { $('setAutoPreload').addEventListener('change', (e) => {
data.settings.autoPreload = e.target.checked; data.settings.autoPreload = e.target.checked;
persist(); persist();

View File

@@ -94,7 +94,7 @@ execFileSync('ffmpeg', ['-y', '-i', mp4, '-vn', '-c:a', 'copy', path.join(media,
await page.waitForFunction(key => JSON.parse(localStorage.getItem('_ytpdata')).settings[key] === true, key); await page.waitForFunction(key => JSON.parse(localStorage.getItem('_ytpdata')).settings[key] === true, key);
if (scenario === 'settings-save-first') await page.evaluate(clip => { Player.loadVideo(clip); }, clip); if (scenario === 'settings-save-first') await page.evaluate(clip => { Player.loadVideo(clip); }, clip);
} }
const expected = !['open-default', 'open-audio-only', 'open-prefer-stream', 'playlist-boot-off'].includes(scenario); const expected = !['playlist-boot-off'].includes(scenario);
if (supported && expected) await page.waitForFunction(id => cachedIds.has(id), clip.id, { timeout: 15000 }); if (supported && expected) await page.waitForFunction(id => cachedIds.has(id), clip.id, { timeout: 15000 });
else await page.waitForTimeout(1000); else await page.waitForTimeout(1000);
const state = await page.evaluate(async id => ({ cached: cachedIds.has(id), files: OPFS.isSupported() ? await OPFS.listVideos() : [], queue: SaveQueue.pending(), calls: window.__saveCalls }), clip.id); const state = await page.evaluate(async id => ({ cached: cachedIds.has(id), files: OPFS.isSupported() ? await OPFS.listVideos() : [], queue: SaveQueue.pending(), calls: window.__saveCalls }), clip.id);