From bc64e651bc32eb76643e2bdec1012d83559d5389 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Fri, 24 Jan 2025 15:10:39 +1300 Subject: [PATCH] Removed ffmpeg downloading completely --- README.md | 13 +- materialious/package-lock.json | 34 +--- materialious/package.json | 4 +- materialious/src/lib/videoDownload.ts | 178 ------------------ materialious/src/routes/(app)/+layout.svelte | 4 +- .../routes/(app)/watch/[slug]/+page.svelte | 72 ------- .../src/routes/(app)/watch/[slug]/+page.ts | 11 -- materialious/static/ffmpeg/ffmpeg-core.js | 16 -- materialious/static/ffmpeg/ffmpeg-core.wasm | Bin 32609891 -> 0 bytes .../static/ffmpeg/ffmpeg-core.worker.js | 1 - materialious/static/ffmpeg/ffmpeg-worker.js | 146 -------------- 11 files changed, 12 insertions(+), 467 deletions(-) delete mode 100644 materialious/src/lib/videoDownload.ts delete mode 100644 materialious/static/ffmpeg/ffmpeg-core.js delete mode 100644 materialious/static/ffmpeg/ffmpeg-core.wasm delete mode 100644 materialious/static/ffmpeg/ffmpeg-core.worker.js delete mode 100644 materialious/static/ffmpeg/ffmpeg-worker.js diff --git a/README.md b/README.md index 41baea99..8ab474c1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ - Watch sync parties! - Mini player. - Silence skipper (Experimental.) -- ffmpeg integration for downloading videos with audio at any quality ([Configuration required](./docs/DOCKER.md#step-7-optional-enabling-downloads)). - [YouTube.js](https://github.com/LuanRT/YouTube.js) fallback if Invidious fails loading videos for Desktop & Android. - Preview video on hover. - Sponsorblock built-in. @@ -48,11 +47,13 @@ - YT path redirects (So your redirect plugins should still work!) # Support table -| | Dash | HLS | Local video fallback | API-Extended | ffmpeg download merging | Dearrow | RYD | -|---------|------|-----|----------------------|--------------|-------------------------|---------|-----| -| Web | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | -| Desktop | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | -| Android | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | +Here’s the updated table with the "ffmpeg download merging" column completely removed: + +| | Dash | HLS | Local video fallback | API-Extended | Dearrow | RYD | +|---------|------|-----|----------------------|--------------|---------|-----| +| Web | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | +| Desktop | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| Android | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | # Public instances [Public instances are listed here](./docs/INSTANCES.md). diff --git a/materialious/package-lock.json b/materialious/package-lock.json index ba7b7c2d..a025e44a 100644 --- a/materialious/package-lock.json +++ b/materialious/package-lock.json @@ -1,12 +1,12 @@ { "name": "materialious", - "version": "1.6.28", + "version": "1.6.30", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "materialious", - "version": "1.6.28", + "version": "1.6.30", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor/android": "^6.1.2", @@ -18,7 +18,6 @@ "@capacitor/screen-orientation": "^6.0.2", "@capacitor/status-bar": "^6.0.1", "@capgo/inappbrowser": "^6.6.8", - "@ffmpeg/ffmpeg": "^0.12.10", "@hugotomazi/capacitor-navigation-bar": "^4.0.1", "@mediagrid/capacitor-native-audio": "^1.0.0", "beercss": "^3.8.0", @@ -45,7 +44,6 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", - "@ffmpeg/util": "^0.12.1", "@sveltejs/adapter-auto": "^3.2.5", "@sveltejs/adapter-static": "^3.0.5", "@sveltejs/kit": "^2.9.1", @@ -2084,34 +2082,6 @@ "node": ">=14" } }, - "node_modules/@ffmpeg/ffmpeg": { - "version": "0.12.10", - "resolved": "https://registry.npmjs.org/@ffmpeg/ffmpeg/-/ffmpeg-0.12.10.tgz", - "integrity": "sha512-lVtk8PW8e+NUzGZhPTWj2P1J4/NyuCrbDD3O9IGpSeLYtUZKBqZO8CNj1WYGghep/MXoM8e1qVY1GztTkf8YYQ==", - "dependencies": { - "@ffmpeg/types": "^0.12.2" - }, - "engines": { - "node": ">=18.x" - } - }, - "node_modules/@ffmpeg/types": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@ffmpeg/types/-/types-0.12.2.tgz", - "integrity": "sha512-NJtxwPoLb60/z1Klv0ueshguWQ/7mNm106qdHkB4HL49LXszjhjCCiL+ldHJGQ9ai2Igx0s4F24ghigy//ERdA==", - "engines": { - "node": ">=16.x" - } - }, - "node_modules/@ffmpeg/util": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/@ffmpeg/util/-/util-0.12.1.tgz", - "integrity": "sha512-10jjfAKWaDyb8+nAkijcsi9wgz/y26LOc1NKJradNMyCIl6usQcBbhkjX5qhALrSBcOy6TOeksunTYa+a03qNQ==", - "dev": true, - "engines": { - "node": ">=18.x" - } - }, "node_modules/@floating-ui/core": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", diff --git a/materialious/package.json b/materialious/package.json index 303f0dbe..2a57b7b4 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -13,7 +13,6 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", - "@ffmpeg/util": "^0.12.1", "@sveltejs/adapter-auto": "^3.2.5", "@sveltejs/adapter-static": "^3.0.5", "@sveltejs/kit": "^2.9.1", @@ -52,7 +51,6 @@ "@capacitor/screen-orientation": "^6.0.2", "@capacitor/status-bar": "^6.0.1", "@capgo/inappbrowser": "^6.6.8", - "@ffmpeg/ffmpeg": "^0.12.10", "@hugotomazi/capacitor-navigation-bar": "^4.0.1", "@mediagrid/capacitor-native-audio": "^1.0.0", "beercss": "^3.8.0", @@ -77,4 +75,4 @@ "vidstack": "^1.12.9", "youtubei.js": "^12.2.0" } -} \ No newline at end of file +} diff --git a/materialious/src/lib/videoDownload.ts b/materialious/src/lib/videoDownload.ts deleted file mode 100644 index 1a8dd1d6..00000000 --- a/materialious/src/lib/videoDownload.ts +++ /dev/null @@ -1,178 +0,0 @@ -interface MediaQuality { - bandwidth: string; - videoUrl: string; - audioUrl: string; - resolution?: string; - frameRate?: string; - audioChannels?: string; - audioBandwidth?: string; -} - -// https://github.com/ffmpegwasm/ffmpeg.wasm/issues/603#issuecomment-1982289892 -// Ripped from https://github.com/ffmpegwasm/ffmpeg.wasm/blob/ae1cdac7db79c5315f9a1b716fcbd9bcfe27c902/packages/util/src/index.ts#L49 -export async function fetchFile( - file: string, - onProgress?: (progress: number) => void -): Promise { - const response = await fetch(file); - - const contentLength = response.headers.get('content-length'); - - // If content-length is missing, download without progress tracking - if (!contentLength) { - const data = await response.arrayBuffer(); - return new Uint8Array(data); - } - - const totalBytes = parseInt(contentLength, 10); - const data = new Uint8Array(totalBytes); - let position = 0; - let downloadBytes = 0; - - const reader = response.body!.getReader(); - while (true) { - const { done, value } = await reader.read(); - if (done) break; - - data.set(value, position); - position += value.length; - downloadBytes += value.byteLength; - - if (onProgress) { - onProgress((downloadBytes / totalBytes) * 100); - } - } - - return data; -} - -export async function toBlobURL( - url: string, - mimeType: string, - onProgress?: (progress: number) => void -): Promise { - return URL.createObjectURL(new Blob([await fetchFile(url, onProgress)], { type: mimeType })); -} - - -// Fetch and parse the DASH manifest -async function fetchDASHManifest(manifestUrl: string) { - const response = await fetch(manifestUrl); - const manifestText = await response.text(); - const parser = new DOMParser(); - const manifest = parser.parseFromString(manifestText, "text/xml"); - return manifest; -} - - -export async function listCombinedQualities(manifestUrl: string): Promise { - const manifest = await fetchDASHManifest(`${manifestUrl}?local=true`); - const qualities: MediaQuality[] = []; - - // Fetch all AdaptationSets (both video and audio) - const adaptationSets = manifest.getElementsByTagName('AdaptationSet'); - - let videoRepresentations: Element[] = []; - let audioRepresentations: Element[] = []; - - // Separate video and audio AdaptationSets based on mimeType - for (let i = 0; i < adaptationSets.length; i++) { - const mimeType = adaptationSets[i].getAttribute('mimeType'); - const representations = adaptationSets[i].getElementsByTagName('Representation'); - - if (mimeType && mimeType === 'video/mp4') { - videoRepresentations = [...videoRepresentations, ...Array.from(representations)]; - } else if (mimeType && mimeType === 'audio/mp4') { - audioRepresentations = [...audioRepresentations, ...Array.from(representations)]; - } - } - - // Combine video and audio representations - videoRepresentations.forEach((videoRep) => { - const videoBandwidth = videoRep.getAttribute('bandwidth'); - const videoUrl = videoRep.getElementsByTagName('BaseURL')[0].textContent as string; - const width = videoRep.getAttribute('width'); - const height = videoRep.getAttribute('height'); - const resolution = width && height ? `${width}x${height}` : undefined; - const frameRate = videoRep.getAttribute('frameRate') || ''; - - // Pair this video representation with each audio representation - audioRepresentations.forEach((audioRep) => { - const audioUrl = audioRep.getElementsByTagName('BaseURL')[0].textContent as string; - const audioChannels = audioRep.getElementsByTagName('AudioChannelConfiguration')[0]?.getAttribute('value') || ''; - const audioBandwidth = audioRep.getAttribute('bandwidth') || ''; - - qualities.push({ - bandwidth: videoBandwidth || '', - videoUrl, - audioUrl, - resolution, - frameRate, - audioChannels, - audioBandwidth - }); - }); - }); - - return qualities; -} - - -export async function mergeMediaFromDASH( - selectedQuality: MediaQuality, - fileName: string, - progressCallbacks?: { - video?: (progress: number) => void, - audio?: (progress: number) => void, - merging?: (progress: number) => void, - loadingFfmpeg?: (completed: boolean) => void; - classWorker?: (progress: number) => void, - core?: (progress: number) => void; - wasm?: (progress: number) => void; - worker?: (progress: number) => void; - } -) { - if (progressCallbacks?.loadingFfmpeg) { - progressCallbacks.loadingFfmpeg(false); - } - - const FFmpeg = await import('@ffmpeg/ffmpeg'); - - const ffmpeg = new FFmpeg.FFmpeg(); - - ffmpeg.on('progress', ({ progress }) => { - if (progressCallbacks?.merging) { - progressCallbacks.merging(progress * 100); - } - }); - - await ffmpeg.load({ - classWorkerURL: await toBlobURL('/ffmpeg/ffmpeg-worker.js', 'text/javascript', progressCallbacks?.classWorker), - coreURL: await toBlobURL('/ffmpeg/ffmpeg-core.js', 'text/javascript', progressCallbacks?.core), - wasmURL: await toBlobURL('/ffmpeg/ffmpeg-core.wasm', 'application/wasm', progressCallbacks?.wasm), - workerURL: await toBlobURL('/ffmpeg/ffmpeg-core.worker.js', 'text/javascript', progressCallbacks?.worker) - }); - - if (progressCallbacks?.loadingFfmpeg) { - progressCallbacks.loadingFfmpeg(true); - } - - const { videoUrl, audioUrl } = selectedQuality; - - ffmpeg.writeFile('input.mp4', await fetchFile(videoUrl, progressCallbacks?.video)); - ffmpeg.writeFile('input.mp3', await fetchFile(audioUrl, progressCallbacks?.audio)); - - await ffmpeg.exec(['-i', 'input.mp4', '-i', 'input.mp3', '-c:v', 'copy', '-c:a', 'aac', 'output.mp4']); - - const data = await ffmpeg.readFile('output.mp4'); - const mergedFile = new Blob([data], { type: 'video/mp4' }); - const url = URL.createObjectURL(mergedFile); - const a = document.createElement('a'); - a.href = url; - a.download = `${fileName} - ${selectedQuality.resolution}.mp4`; - document.body.appendChild(a); - a.click(); - - URL.revokeObjectURL(url); - document.body.removeChild(a); -} diff --git a/materialious/src/routes/(app)/+layout.svelte b/materialious/src/routes/(app)/+layout.svelte index feb9ebfb..0d6123f6 100644 --- a/materialious/src/routes/(app)/+layout.svelte +++ b/materialious/src/routes/(app)/+layout.svelte @@ -321,9 +321,9 @@ {@html $_('invidiousBlockWarning', { values: { android: - 'Android', + 'Android', desktop: - 'Desktop' + 'Desktop' } })}

diff --git a/materialious/src/routes/(app)/watch/[slug]/+page.svelte b/materialious/src/routes/(app)/watch/[slug]/+page.svelte index ca65f296..ba01f852 100644 --- a/materialious/src/routes/(app)/watch/[slug]/+page.svelte +++ b/materialious/src/routes/(app)/watch/[slug]/+page.svelte @@ -41,7 +41,6 @@ } from '$lib/store'; import Thumbnail from '$lib/Thumbnail.svelte'; import Transcript from '$lib/Transcript.svelte'; - import { mergeMediaFromDASH } from '$lib/videoDownload'; import ui from 'beercss'; import type { DataConnection } from 'peerjs'; import { type Segment } from 'sponsorblock-api'; @@ -434,50 +433,6 @@ let downloadStage: string | undefined; let downloadProgress: number = 0; - - function onVideoDownloadProgress(progress: number) { - downloadStage = $_('player.downloadSteps.video'); - downloadProgress = progress; - } - - function onAudioDownloadProgress(progress: number) { - downloadStage = $_('player.downloadSteps.audio'); - downloadProgress = progress; - } - - function onMergingProgress(progress: number) { - downloadStage = $_('player.downloadSteps.merging'); - downloadProgress = progress; - - if (progress >= 100) { - downloadStage = undefined; - } - } - - function onLoadingFFmpeg(completed: boolean) { - downloadProgress = 0; - downloadStage = $_('player.downloadSteps.ffmpeg'); - } - - function onClassWorkerProgress(progress: number) { - downloadStage = $_('player.downloadSteps.classWorker'); - downloadProgress = progress; - } - - function onCoreProgress(progress: number) { - downloadStage = $_('player.downloadSteps.core'); - downloadProgress = progress; - } - - function onWasmProgress(progress: number) { - downloadStage = $_('player.downloadSteps.wasm'); - downloadProgress = progress; - } - - function onWorkerProgress(progress: number) { - downloadStage = $_('player.downloadSteps.worker'); - downloadProgress = progress; - } @@ -621,33 +576,6 @@ - {#await data.streamed.downloadQualitiesDash then downloadQualitiesDash} - {#if downloadQualitiesDash} - - {/if} - {/await} {#if personalPlaylists}