From ef85c217f7a6288321eb8c785c8bb33f23b582df Mon Sep 17 00:00:00 2001
From: Jorrin <jorrinkievit@hotmail.com>
Date: Mon, 8 Jan 2024 17:06:27 +0100
Subject: [PATCH] first mvp extension

---
 package.json                                  |   1 +
 pnpm-lock.yaml                                |  21 +++
 src/@types/plasmo.d.ts                        |  37 ++++++
 src/components/player/display/base.ts         | 120 ++++++++++--------
 .../player/utils/convertRunoutputToSource.ts  |   2 +
 src/stores/player/utils/qualities.ts          |   5 +-
 src/utils/providers.ts                        |   4 +-
 7 files changed, 134 insertions(+), 56 deletions(-)
 create mode 100644 src/@types/plasmo.d.ts

diff --git a/package.json b/package.json
index 388054df..3148f860 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
     "@ladjs/country-language": "^1.0.3",
     "@movie-web/providers": "^2.0.5",
     "@noble/hashes": "^1.3.3",
+    "@plasmohq/messaging": "^0.6.1",
     "@react-spring/web": "^9.7.3",
     "@scure/bip39": "^1.2.2",
     "@sozialhelden/ietf-language-tags": "^5.4.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6d6bb9bd..5c1d1c9e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -27,6 +27,9 @@ dependencies:
   '@noble/hashes':
     specifier: ^1.3.3
     version: 1.3.3
+  '@plasmohq/messaging':
+    specifier: ^0.6.1
+    version: 0.6.1(react@18.2.0)
   '@react-spring/web':
     specifier: ^9.7.3
     version: 9.7.3(react-dom@18.2.0)(react@18.2.0)
@@ -1980,6 +1983,18 @@ packages:
       tslib: 2.6.2
     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):
     resolution: {integrity: sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==}
     peerDependencies:
@@ -5156,6 +5171,12 @@ packages:
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     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:
     resolution: {integrity: sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig==}
     engines: {node: ^18 || >=20}
diff --git a/src/@types/plasmo.d.ts b/src/@types/plasmo.d.ts
new file mode 100644
index 00000000..4570cae4
--- /dev/null
+++ b/src/@types/plasmo.d.ts
@@ -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 {}
+}
diff --git a/src/components/player/display/base.ts b/src/components/player/display/base.ts
index 7a9ce041..11a0799c 100644
--- a/src/components/player/display/base.ts
+++ b/src/components/player/display/base.ts
@@ -1,6 +1,8 @@
+import { sendToBackgroundViaRelay } from "@plasmohq/messaging";
 import fscreen from "fscreen";
 import Hls, { Level } from "hls.js";
 
+import { PlasmoRequestBody, PlasmoResponseBody } from "@/@types/plasmo";
 import {
   DisplayInterface,
   DisplayInterfaceEvents,
@@ -100,65 +102,75 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
   }
 
   function setupSource(vid: HTMLVideoElement, src: LoadableSource) {
-    if (src.type === "hls") {
-      if (canPlayHlsNatively(vid)) {
-        vid.src = processCdnLink(src.url);
+    // TODO: Add check whether the extension is installed
+    sendToBackgroundViaRelay<PlasmoRequestBody, PlasmoResponseBody>({
+      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;
         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.src = processCdnLink(src.url);
       vid.currentTime = startAt;
-      return;
-    }
-
-    vid.src = processCdnLink(src.url);
-    vid.currentTime = startAt;
+    });
   }
 
   function setSource() {
diff --git a/src/components/player/utils/convertRunoutputToSource.ts b/src/components/player/utils/convertRunoutputToSource.ts
index fba59e63..f54c5396 100644
--- a/src/components/player/utils/convertRunoutputToSource.ts
+++ b/src/components/player/utils/convertRunoutputToSource.ts
@@ -28,6 +28,7 @@ export function convertRunoutputToSource(out: {
     return {
       type: "hls",
       url: out.stream.playlist,
+      preferredHeaders: out.stream.preferredHeaders,
     };
   }
   if (out.stream.type === "file") {
@@ -49,6 +50,7 @@ export function convertRunoutputToSource(out: {
     return {
       type: "file",
       qualities,
+      preferredHeaders: out.stream.preferredHeaders,
     };
   }
   throw new Error("unrecognized type");
diff --git a/src/stores/player/utils/qualities.ts b/src/stores/player/utils/qualities.ts
index dbd84b5c..e5140d53 100644
--- a/src/stores/player/utils/qualities.ts
+++ b/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";
 
@@ -14,16 +14,19 @@ export type SourceFileStream = {
 export type LoadableSource = {
   type: StreamType;
   url: string;
+  preferredHeaders?: Stream["preferredHeaders"];
 };
 
 export type SourceSliceSource =
   | {
       type: "file";
       qualities: Partial<Record<SourceQuality, SourceFileStream>>;
+      preferredHeaders?: Stream["preferredHeaders"];
     }
   | {
       type: "hls";
       url: string;
+      preferredHeaders?: Stream["preferredHeaders"];
     };
 
 const qualitySorting: Record<SourceQuality, number> = {
diff --git a/src/utils/providers.ts b/src/utils/providers.ts
index e5c8503c..73c95662 100644
--- a/src/utils/providers.ts
+++ b/src/utils/providers.ts
@@ -62,5 +62,7 @@ function makeLoadBalancedSimpleProxyFetcher() {
 export const providers = makeProviders({
   fetcher: makeStandardFetcher(fetch),
   proxiedFetcher: makeLoadBalancedSimpleProxyFetcher(),
-  target: targets.BROWSER,
+  // TODO: Add check whether the extension is installed
+  // target: targets.BROWSER,
+  target: targets.BROWSER_EXTENSION,
 }) as any as ProviderControls;