mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
Merge branch 'v3' into v3-superstream
This commit is contained in:
commit
9e8769e4c3
7 changed files with 121 additions and 2 deletions
|
@ -41,6 +41,8 @@
|
||||||
|
|
||||||
<script src="config.js"></script>
|
<script src="config.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/gh/movie-web/6C6F6C7A@d63f572f6f873bda166c2d7d3772c51d14e1c319/out.js"></script>
|
<script src="https://cdn.jsdelivr.net/gh/movie-web/6C6F6C7A@d63f572f6f873bda166c2d7d3772c51d14e1c319/out.js"></script>
|
||||||
|
<script type="text/javascript" src="https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1"></script>
|
||||||
|
|
||||||
<title>movie-web</title>
|
<title>movie-web</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -16,11 +16,11 @@ export function makeUrl(url: string, data: Record<string, string>) {
|
||||||
return parsedUrl;
|
return parsedUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mwFetch<T>(url: string, ops: P<T>[1]): R<T> {
|
export function mwFetch<T>(url: string, ops: P<T>[1] = {}): R<T> {
|
||||||
return baseFetch<T>(url, ops);
|
return baseFetch<T>(url, ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function proxiedFetch<T>(url: string, ops: P<T>[1]): R<T> {
|
export function proxiedFetch<T>(url: string, ops: P<T>[1] = {}): R<T> {
|
||||||
let combinedUrl = ops?.baseURL ?? "";
|
let combinedUrl = ops?.baseURL ?? "";
|
||||||
if (
|
if (
|
||||||
combinedUrl.length > 0 &&
|
combinedUrl.length > 0 &&
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { initializeScraperStore } from "./helpers/register";
|
||||||
import "./providers/gdriveplayer";
|
import "./providers/gdriveplayer";
|
||||||
import "./providers/flixhq";
|
import "./providers/flixhq";
|
||||||
import "./providers/superstream";
|
import "./providers/superstream";
|
||||||
|
import "./providers/gomostream";
|
||||||
|
|
||||||
// embeds
|
// embeds
|
||||||
// -- nothing here yet
|
// -- nothing here yet
|
||||||
|
|
|
@ -54,6 +54,7 @@ export async function getMetaFromId(
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(data.external_ids);
|
||||||
const imdbId = data.external_ids.find(
|
const imdbId = data.external_ids.find(
|
||||||
(v) => v.provider === "imdb_latest"
|
(v) => v.provider === "imdb_latest"
|
||||||
)?.external_id;
|
)?.external_id;
|
||||||
|
|
98
src/backend/providers/gomostream.ts
Normal file
98
src/backend/providers/gomostream.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import { unpack } from "unpacker";
|
||||||
|
import { proxiedFetch } from "../helpers/fetch";
|
||||||
|
import { registerProvider } from "../helpers/register";
|
||||||
|
import { MWStreamQuality, MWStreamType } from "../helpers/streams";
|
||||||
|
import { MWMediaType } from "../metadata/types";
|
||||||
|
import json5 from "json5";
|
||||||
|
|
||||||
|
const gomoBase = "https://gomo.to/";
|
||||||
|
|
||||||
|
registerProvider({
|
||||||
|
id: "gomostream",
|
||||||
|
displayName: "gomostream",
|
||||||
|
rank: 999,
|
||||||
|
type: [MWMediaType.MOVIE],
|
||||||
|
|
||||||
|
async scrape({ media, progress }) {
|
||||||
|
// get movie from gomostream
|
||||||
|
const contentResult = await proxiedFetch<any>(
|
||||||
|
`/${media.meta.type}/${media.imdbId}`,
|
||||||
|
{
|
||||||
|
baseURL: gomoBase,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// movie doesn't exist
|
||||||
|
if (
|
||||||
|
contentResult === "Movie not available." ||
|
||||||
|
contentResult === "Episode not available."
|
||||||
|
)
|
||||||
|
throw new Error("No watchable item found.");
|
||||||
|
|
||||||
|
// decode stream
|
||||||
|
progress(25);
|
||||||
|
|
||||||
|
const tc = contentResult.match(/var tc = '(.+)';/)?.[1] || "";
|
||||||
|
const _token = contentResult.match(/"_token": "(.+)",/)?.[1] || "";
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("tokenCode", tc);
|
||||||
|
fd.append("_token", _token);
|
||||||
|
|
||||||
|
const src = await proxiedFetch<any>(`/decoding_v3.php`, {
|
||||||
|
baseURL: gomoBase,
|
||||||
|
method: "POST",
|
||||||
|
body: fd,
|
||||||
|
headers: {
|
||||||
|
"x-token": `${tc.slice(5, 13).split("").reverse().join("")}13574199`,
|
||||||
|
},
|
||||||
|
parseResponse: JSON.parse,
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO should check all embed urls in future
|
||||||
|
const embedUrl = src.filter((url: string) => url.includes("gomo.to"))[1];
|
||||||
|
|
||||||
|
// get stream info
|
||||||
|
progress(50);
|
||||||
|
|
||||||
|
const streamRes = await proxiedFetch<any>(embedUrl);
|
||||||
|
|
||||||
|
const streamResDom = new DOMParser().parseFromString(
|
||||||
|
streamRes,
|
||||||
|
"text/html"
|
||||||
|
);
|
||||||
|
if (streamResDom.body.innerText === "File was deleted")
|
||||||
|
throw new Error("No watchable item found.");
|
||||||
|
|
||||||
|
const script = Array.from(streamResDom.querySelectorAll("script")).find(
|
||||||
|
(s: HTMLScriptElement) =>
|
||||||
|
s.innerHTML.includes("eval(function(p,a,c,k,e,d")
|
||||||
|
)?.innerHTML;
|
||||||
|
if (!script) throw new Error("Could not get packed data");
|
||||||
|
|
||||||
|
// unpack data
|
||||||
|
progress(75);
|
||||||
|
|
||||||
|
const unpacked = unpack(script);
|
||||||
|
const rawSources = /sources:(\[.*?\])/.exec(unpacked);
|
||||||
|
if (!rawSources) throw new Error("Could not get stream URL");
|
||||||
|
|
||||||
|
const sources = json5.parse(rawSources[1]);
|
||||||
|
const streamUrl = sources[0].file;
|
||||||
|
|
||||||
|
console.log(sources);
|
||||||
|
|
||||||
|
const streamType = streamUrl.split(".").at(-1);
|
||||||
|
if (streamType !== "mp4" && streamType !== "m3u8")
|
||||||
|
throw new Error("Unsupported stream type");
|
||||||
|
|
||||||
|
return {
|
||||||
|
embeds: [],
|
||||||
|
stream: {
|
||||||
|
quality: streamType,
|
||||||
|
streamUrl: streamUrl,
|
||||||
|
type: streamType,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
|
@ -3,6 +3,7 @@ import { useCallback, useRef, useState } from "react";
|
||||||
import { CSSTransition } from "react-transition-group";
|
import { CSSTransition } from "react-transition-group";
|
||||||
import { AirplayControl } from "./controls/AirplayControl";
|
import { AirplayControl } from "./controls/AirplayControl";
|
||||||
import { BackdropControl } from "./controls/BackdropControl";
|
import { BackdropControl } from "./controls/BackdropControl";
|
||||||
|
import { ChromeCastControl } from "./controls/ChromeCastControl";
|
||||||
import { FullscreenControl } from "./controls/FullscreenControl";
|
import { FullscreenControl } from "./controls/FullscreenControl";
|
||||||
import { LoadingControl } from "./controls/LoadingControl";
|
import { LoadingControl } from "./controls/LoadingControl";
|
||||||
import { MiddlePauseControl } from "./controls/MiddlePauseControl";
|
import { MiddlePauseControl } from "./controls/MiddlePauseControl";
|
||||||
|
@ -93,6 +94,7 @@ export function DecoratedVideoPlayer(
|
||||||
<LeftSideControls />
|
<LeftSideControls />
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<AirplayControl />
|
<AirplayControl />
|
||||||
|
<ChromeCastControl />
|
||||||
<FullscreenControl />
|
<FullscreenControl />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
15
src/components/video/controls/ChromeCastControl.tsx
Normal file
15
src/components/video/controls/ChromeCastControl.tsx
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||||
|
namespace JSX {
|
||||||
|
interface IntrinsicElements {
|
||||||
|
"google-cast-launcher": React.DetailedHTMLProps<
|
||||||
|
React.HTMLAttributes<HTMLElement>,
|
||||||
|
HTMLElement
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChromeCastControl() {
|
||||||
|
return <google-cast-launcher />;
|
||||||
|
}
|
Loading…
Reference in a new issue