Browse Source

first mvp extension

pull/760/head
Jorrin 2 years ago
parent
commit
ef85c217f7
  1. 1
      package.json
  2. 21
      pnpm-lock.yaml
  3. 37
      src/@types/plasmo.d.ts
  4. 118
      src/components/player/display/base.ts
  5. 2
      src/components/player/utils/convertRunoutputToSource.ts
  6. 5
      src/stores/player/utils/qualities.ts
  7. 4
      src/utils/providers.ts

1
package.json

@ -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

@ -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

@ -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 {}
}

118
src/components/player/display/base.ts

@ -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",
vid.currentTime = startAt; body: {
return; 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.isSupported()) throw new Error("HLS not supported");
if (!hls) { if (!hls) {
hls = new Hls({ hls = new Hls({
maxBufferSize: 500 * 1000 * 1000, // 500 mb of buffering, should load more fragments at once maxBufferSize: 500 * 1000 * 1000, // 500 mb of buffering, should load more fragments at once
fragLoadPolicy: { fragLoadPolicy: {
default: { default: {
maxLoadTimeMs: 30 * 1000, // allow it load extra long, fragments are slow if requested for the first time on an origin maxLoadTimeMs: 30 * 1000, // allow it load extra long, fragments are slow if requested for the first time on an origin
maxTimeToFirstByteMs: 30 * 1000, maxTimeToFirstByteMs: 30 * 1000,
errorRetry: { errorRetry: {
maxNumRetry: 2, maxNumRetry: 2,
retryDelayMs: 1000, retryDelayMs: 1000,
maxRetryDelayMs: 8000, maxRetryDelayMs: 8000,
}, },
timeoutRetry: { timeoutRetry: {
maxNumRetry: 3, maxNumRetry: 3,
maxRetryDelayMs: 0, maxRetryDelayMs: 0,
retryDelayMs: 0, retryDelayMs: 0,
},
}, },
}, },
}, });
}); hls.on(Hls.Events.ERROR, (event, data) => {
hls.on(Hls.Events.ERROR, (event, data) => { console.error("HLS error", data);
console.error("HLS error", data); if (data.fatal) {
if (data.fatal) { emit("error", {
emit("error", { message: data.error.message,
message: data.error.message, stackTrace: data.error.stack,
stackTrace: data.error.stack, errorName: data.error.name,
errorName: data.error.name, type: "hls",
type: "hls", });
}); }
} });
}); hls.on(Hls.Events.MANIFEST_LOADED, () => {
hls.on(Hls.Events.MANIFEST_LOADED, () => { if (!hls) return;
if (!hls) return; reportLevels();
reportLevels(); setupQualityForHls();
setupQualityForHls(); });
}); hls.on(Hls.Events.LEVEL_SWITCHED, () => {
hls.on(Hls.Events.LEVEL_SWITCHED, () => { if (!hls) return;
if (!hls) return; const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]);
const quality = hlsLevelToQuality(hls.levels[hls.currentLevel]); emit("changedquality", quality);
emit("changedquality", quality); });
}); }
hls.attachMedia(vid);
hls.loadSource(processCdnLink(src.url));
vid.currentTime = startAt;
return;
} }
hls.attachMedia(vid); vid.src = processCdnLink(src.url);
hls.loadSource(processCdnLink(src.url));
vid.currentTime = startAt; vid.currentTime = startAt;
return; });
}
vid.src = processCdnLink(src.url);
vid.currentTime = startAt;
} }
function setSource() { function setSource() {

2
src/components/player/utils/convertRunoutputToSource.ts

@ -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");

5
src/stores/player/utils/qualities.ts

@ -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> = {

4
src/utils/providers.ts

@ -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…
Cancel
Save