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}