mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-26 16:51:51 +01:00
first mvp extension
This commit is contained in:
parent
fbb5ef7115
commit
ef85c217f7
@ -31,6 +31,7 @@
|
|||||||
"@ladjs/country-language": "^1.0.3",
|
"@ladjs/country-language": "^1.0.3",
|
||||||
"@movie-web/providers": "^2.0.5",
|
"@movie-web/providers": "^2.0.5",
|
||||||
"@noble/hashes": "^1.3.3",
|
"@noble/hashes": "^1.3.3",
|
||||||
|
"@plasmohq/messaging": "^0.6.1",
|
||||||
"@react-spring/web": "^9.7.3",
|
"@react-spring/web": "^9.7.3",
|
||||||
"@scure/bip39": "^1.2.2",
|
"@scure/bip39": "^1.2.2",
|
||||||
"@sozialhelden/ietf-language-tags": "^5.4.2",
|
"@sozialhelden/ietf-language-tags": "^5.4.2",
|
||||||
|
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@ -27,6 +27,9 @@ dependencies:
|
|||||||
'@noble/hashes':
|
'@noble/hashes':
|
||||||
specifier: ^1.3.3
|
specifier: ^1.3.3
|
||||||
version: 1.3.3
|
version: 1.3.3
|
||||||
|
'@plasmohq/messaging':
|
||||||
|
specifier: ^0.6.1
|
||||||
|
version: 0.6.1(react@18.2.0)
|
||||||
'@react-spring/web':
|
'@react-spring/web':
|
||||||
specifier: ^9.7.3
|
specifier: ^9.7.3
|
||||||
version: 9.7.3(react-dom@18.2.0)(react@18.2.0)
|
version: 9.7.3(react-dom@18.2.0)(react@18.2.0)
|
||||||
@ -1980,6 +1983,18 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@plasmohq/messaging@0.6.1(react@18.2.0):
|
||||||
|
resolution: {integrity: sha512-/nn1k8SG5z++o/NnZu+byHWcC9MhPLxfmvj+AP3buqMn7uwfYDcYWURLuMW2Knw08HBg+wku2v1Ltt4evN0nzA==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^16.8.6 || ^17 || ^18
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
nanoid: 5.0.3
|
||||||
|
react: 18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@react-spring/animated@9.7.3(react@18.2.0):
|
/@react-spring/animated@9.7.3(react@18.2.0):
|
||||||
resolution: {integrity: sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==}
|
resolution: {integrity: sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -5156,6 +5171,12 @@ packages:
|
|||||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/nanoid@5.0.3:
|
||||||
|
resolution: {integrity: sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==}
|
||||||
|
engines: {node: ^18 || >=20}
|
||||||
|
hasBin: true
|
||||||
|
dev: false
|
||||||
|
|
||||||
/nanoid@5.0.4:
|
/nanoid@5.0.4:
|
||||||
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
|
resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
|
||||||
engines: {node: ^18 || >=20}
|
engines: {node: ^18 || >=20}
|
||||||
|
37
src/@types/plasmo.d.ts
vendored
Normal file
37
src/@types/plasmo.d.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
|
import "@plasmohq/messaging";
|
||||||
|
|
||||||
|
export interface PlasmoRequestBody {
|
||||||
|
ruleId: number;
|
||||||
|
domain: string;
|
||||||
|
requestHeaders?: Record<string, string>;
|
||||||
|
responseHeaders?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PlasmoResponseBody =
|
||||||
|
| {
|
||||||
|
success: true;
|
||||||
|
ruleId: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
success: false;
|
||||||
|
error: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface MmMetadata {
|
||||||
|
"declarative-net-request": {
|
||||||
|
req: PlasmoRequestBody;
|
||||||
|
res: PlasmoResponseBody;
|
||||||
|
};
|
||||||
|
"proxy-request": {
|
||||||
|
req: PlasmoRequestBody;
|
||||||
|
res: PlasmoResponseBody;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MpMetadata {}
|
||||||
|
|
||||||
|
declare module "@plasmohq/messaging" {
|
||||||
|
interface MessagesMetadata extends MmMetadata {}
|
||||||
|
interface PortsMetadata extends MpMetadata {}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
|
import { sendToBackgroundViaRelay } from "@plasmohq/messaging";
|
||||||
import fscreen from "fscreen";
|
import fscreen from "fscreen";
|
||||||
import Hls, { Level } from "hls.js";
|
import Hls, { Level } from "hls.js";
|
||||||
|
|
||||||
|
import { PlasmoRequestBody, PlasmoResponseBody } from "@/@types/plasmo";
|
||||||
import {
|
import {
|
||||||
DisplayInterface,
|
DisplayInterface,
|
||||||
DisplayInterfaceEvents,
|
DisplayInterfaceEvents,
|
||||||
@ -100,65 +102,75 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupSource(vid: HTMLVideoElement, src: LoadableSource) {
|
function setupSource(vid: HTMLVideoElement, src: LoadableSource) {
|
||||||
if (src.type === "hls") {
|
// TODO: Add check whether the extension is installed
|
||||||
if (canPlayHlsNatively(vid)) {
|
sendToBackgroundViaRelay<PlasmoRequestBody, PlasmoResponseBody>({
|
||||||
vid.src = processCdnLink(src.url);
|
name: "declarative-net-request",
|
||||||
|
body: {
|
||||||
|
ruleId: 1,
|
||||||
|
domain: src.type === "hls" ? new URL(src.url).hostname : src.url,
|
||||||
|
requestHeaders: src.preferredHeaders,
|
||||||
|
},
|
||||||
|
}).then(() => {
|
||||||
|
if (src.type === "hls") {
|
||||||
|
if (canPlayHlsNatively(vid)) {
|
||||||
|
vid.src = processCdnLink(src.url);
|
||||||
|
vid.currentTime = startAt;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Hls.isSupported()) throw new Error("HLS not supported");
|
||||||
|
if (!hls) {
|
||||||
|
hls = new Hls({
|
||||||
|
maxBufferSize: 500 * 1000 * 1000, // 500 mb of buffering, should load more fragments at once
|
||||||
|
fragLoadPolicy: {
|
||||||
|
default: {
|
||||||
|
maxLoadTimeMs: 30 * 1000, // allow it load extra long, fragments are slow if requested for the first time on an origin
|
||||||
|
maxTimeToFirstByteMs: 30 * 1000,
|
||||||
|
errorRetry: {
|
||||||
|
maxNumRetry: 2,
|
||||||
|
retryDelayMs: 1000,
|
||||||
|
maxRetryDelayMs: 8000,
|
||||||
|
},
|
||||||
|
timeoutRetry: {
|
||||||
|
maxNumRetry: 3,
|
||||||
|
maxRetryDelayMs: 0,
|
||||||
|
retryDelayMs: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
hls.on(Hls.Events.ERROR, (event, data) => {
|
||||||
|
console.error("HLS error", data);
|
||||||
|
if (data.fatal) {
|
||||||
|
emit("error", {
|
||||||
|
message: data.error.message,
|
||||||
|
stackTrace: data.error.stack,
|
||||||
|
errorName: data.error.name,
|
||||||
|
type: "hls",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hls.on(Hls.Events.MANIFEST_LOADED, () => {
|
||||||
|
if (!hls) return;
|
||||||
|
reportLevels();
|
||||||
|
setupQualityForHls();
|
||||||
|
});
|
||||||
|
hls.on(Hls.Events.LEVEL_SWITCHED, () => {
|
||||||
|
if (!hls) return;
|
||||||
|
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
|
||||||
|
emit("changedquality", quality);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hls.attachMedia(vid);
|
||||||
|
hls.loadSource(processCdnLink(src.url));
|
||||||
vid.currentTime = startAt;
|
vid.currentTime = startAt;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Hls.isSupported()) throw new Error("HLS not supported");
|
vid.src = processCdnLink(src.url);
|
||||||
if (!hls) {
|
|
||||||
hls = new Hls({
|
|
||||||
maxBufferSize: 500 * 1000 * 1000, // 500 mb of buffering, should load more fragments at once
|
|
||||||
fragLoadPolicy: {
|
|
||||||
default: {
|
|
||||||
maxLoadTimeMs: 30 * 1000, // allow it load extra long, fragments are slow if requested for the first time on an origin
|
|
||||||
maxTimeToFirstByteMs: 30 * 1000,
|
|
||||||
errorRetry: {
|
|
||||||
maxNumRetry: 2,
|
|
||||||
retryDelayMs: 1000,
|
|
||||||
maxRetryDelayMs: 8000,
|
|
||||||
},
|
|
||||||
timeoutRetry: {
|
|
||||||
maxNumRetry: 3,
|
|
||||||
maxRetryDelayMs: 0,
|
|
||||||
retryDelayMs: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
hls.on(Hls.Events.ERROR, (event, data) => {
|
|
||||||
console.error("HLS error", data);
|
|
||||||
if (data.fatal) {
|
|
||||||
emit("error", {
|
|
||||||
message: data.error.message,
|
|
||||||
stackTrace: data.error.stack,
|
|
||||||
errorName: data.error.name,
|
|
||||||
type: "hls",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
hls.on(Hls.Events.MANIFEST_LOADED, () => {
|
|
||||||
if (!hls) return;
|
|
||||||
reportLevels();
|
|
||||||
setupQualityForHls();
|
|
||||||
});
|
|
||||||
hls.on(Hls.Events.LEVEL_SWITCHED, () => {
|
|
||||||
if (!hls) return;
|
|
||||||
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
|
|
||||||
emit("changedquality", quality);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
hls.attachMedia(vid);
|
|
||||||
hls.loadSource(processCdnLink(src.url));
|
|
||||||
vid.currentTime = startAt;
|
vid.currentTime = startAt;
|
||||||
return;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
vid.src = processCdnLink(src.url);
|
|
||||||
vid.currentTime = startAt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSource() {
|
function setSource() {
|
||||||
|
@ -28,6 +28,7 @@ export function convertRunoutputToSource(out: {
|
|||||||
return {
|
return {
|
||||||
type: "hls",
|
type: "hls",
|
||||||
url: out.stream.playlist,
|
url: out.stream.playlist,
|
||||||
|
preferredHeaders: out.stream.preferredHeaders,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (out.stream.type === "file") {
|
if (out.stream.type === "file") {
|
||||||
@ -49,6 +50,7 @@ export function convertRunoutputToSource(out: {
|
|||||||
return {
|
return {
|
||||||
type: "file",
|
type: "file",
|
||||||
qualities,
|
qualities,
|
||||||
|
preferredHeaders: out.stream.preferredHeaders,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new Error("unrecognized type");
|
throw new Error("unrecognized type");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Qualities } from "@movie-web/providers";
|
import { Qualities, Stream } from "@movie-web/providers";
|
||||||
|
|
||||||
import { QualityStore } from "@/stores/quality";
|
import { QualityStore } from "@/stores/quality";
|
||||||
|
|
||||||
@ -14,16 +14,19 @@ export type SourceFileStream = {
|
|||||||
export type LoadableSource = {
|
export type LoadableSource = {
|
||||||
type: StreamType;
|
type: StreamType;
|
||||||
url: string;
|
url: string;
|
||||||
|
preferredHeaders?: Stream["preferredHeaders"];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SourceSliceSource =
|
export type SourceSliceSource =
|
||||||
| {
|
| {
|
||||||
type: "file";
|
type: "file";
|
||||||
qualities: Partial<Record<SourceQuality, SourceFileStream>>;
|
qualities: Partial<Record<SourceQuality, SourceFileStream>>;
|
||||||
|
preferredHeaders?: Stream["preferredHeaders"];
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
type: "hls";
|
type: "hls";
|
||||||
url: string;
|
url: string;
|
||||||
|
preferredHeaders?: Stream["preferredHeaders"];
|
||||||
};
|
};
|
||||||
|
|
||||||
const qualitySorting: Record<SourceQuality, number> = {
|
const qualitySorting: Record<SourceQuality, number> = {
|
||||||
|
@ -62,5 +62,7 @@ function makeLoadBalancedSimpleProxyFetcher() {
|
|||||||
export const providers = makeProviders({
|
export const providers = makeProviders({
|
||||||
fetcher: makeStandardFetcher(fetch),
|
fetcher: makeStandardFetcher(fetch),
|
||||||
proxiedFetcher: makeLoadBalancedSimpleProxyFetcher(),
|
proxiedFetcher: makeLoadBalancedSimpleProxyFetcher(),
|
||||||
target: targets.BROWSER,
|
// TODO: Add check whether the extension is installed
|
||||||
|
// target: targets.BROWSER,
|
||||||
|
target: targets.BROWSER_EXTENSION,
|
||||||
}) as any as ProviderControls;
|
}) as any as ProviderControls;
|
||||||
|
Loading…
Reference in New Issue
Block a user