Compare commits

..

20 Commits
3.2.0 ... 3.2.5

Author SHA1 Message Date
Jip Frijlink
47eba8caa4 Merge pull request #504 from movie-web/add-disallowed-ids
Add disallowed ids
2023-12-01 14:43:22 +01:00
Jip Fr
1dc957b56a Fix typo 2023-12-01 14:41:46 +01:00
Jip Fr
e653c72d87 Add comment 2023-12-01 14:41:25 +01:00
Jip Fr
c39d61cf53 Change text 2023-12-01 14:39:04 +01:00
Jip Fr
b14a73378f Bump version 2023-12-01 14:35:12 +01:00
Jip Fr
43d1e290fc Add DISALLOWED_IDS to conf 2023-12-01 14:34:52 +01:00
Jip Frijlink
1f6318360e Merge pull request #485 from movie-web/dev
Fix poster issue (into prod)
2023-11-09 15:53:54 +01:00
Jip Frijlink
791299dd43 Merge pull request #484 from movie-web/fix-posters
Fix poster issue
2023-11-09 15:50:22 +01:00
Jip Fr
2c92bbf94e Fix poster issue 2023-11-09 15:34:35 +01:00
mrjvs
e3569c7ed7 Merge pull request #479 from movie-web/dev
v3.2.3 - fixing upcloud
2023-11-07 21:14:16 +01:00
mrjvs
196a805d32 Merge branch 'master' into dev 2023-11-07 21:13:07 +01:00
William Oldham
94d6d7b37e Merge pull request #478 from movie-web/fix-upcloud
Fix upcloud
2023-11-07 20:10:22 +00:00
mrjvs
fde5f0c82e version bump 2023-11-07 21:07:39 +01:00
mrjvs
bb449d6dfb Fix upcloud 2023-11-07 21:04:43 +01:00
William Oldham
bb8b21324b Merge pull request #475 from movie-web/dev
Fix superstream
2023-11-01 14:07:45 +00:00
Jip Fr
53fe6031d1 Fix superstream 2023-11-01 15:04:52 +01:00
William Oldham
ee9400373d Merge pull request #409 from movie-web/dev
Release 3.2.1
2023-08-16 20:09:03 +01:00
William Oldham
6c8cc63cbc Merge pull request #408 from movie-web/gone-is-fast-cdn
Goodbye faster cdn
2023-08-16 19:33:22 +01:00
mrjvs
8c105e78b5 bump version 2023-08-16 19:26:43 +02:00
mrjvs
28f253c542 Remove faster cdn, as its broken 2023-08-16 19:26:29 +02:00
6 changed files with 94 additions and 25 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "movie-web", "name": "movie-web",
"version": "3.2.0", "version": "3.2.5",
"private": true, "private": true,
"homepage": "https://movie-web.app", "homepage": "https://movie-web.app",
"dependencies": { "dependencies": {

View File

@@ -29,6 +29,32 @@ function isJSON(json: string) {
} }
} }
function extractKey(script: string): [number, number][] | null {
const startOfSwitch = script.lastIndexOf("switch");
const endOfCases = script.indexOf("partKeyStartPosition");
const switchBody = script.slice(startOfSwitch, endOfCases);
const nums: [number, number][] = [];
const matches = switchBody.matchAll(
/:[a-zA-Z0-9]+=([a-zA-Z0-9]+),[a-zA-Z0-9]+=([a-zA-Z0-9]+);/g
);
for (const match of matches) {
const innerNumbers: number[] = [];
for (const varMatch of [match[1], match[2]]) {
const regex = new RegExp(`${varMatch}=0x([a-zA-Z0-9]+)`, "g");
const varMatches = [...script.matchAll(regex)];
const lastMatch = varMatches[varMatches.length - 1];
if (!lastMatch) return null;
const number = parseInt(lastMatch[1], 16);
innerNumbers.push(number);
}
nums.push([innerNumbers[0], innerNumbers[1]]);
}
return nums;
}
registerEmbedScraper({ registerEmbedScraper({
id: "upcloud", id: "upcloud",
displayName: "UpCloud", displayName: "UpCloud",
@@ -54,23 +80,31 @@ registerEmbedScraper({
let sources: { file: string; type: string } | null = null; let sources: { file: string; type: string } | null = null;
if (!isJSON(streamRes.sources)) { if (!isJSON(streamRes.sources)) {
const decryptionKey = JSON.parse( const scriptJs = await proxiedFetch<string>(
await proxiedFetch<string>( `https://rabbitstream.net/js/player/prod/e4-player.min.js`,
`https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt` {
) responseType: "text" as any,
) as [number, number][]; }
);
const decryptionKey = extractKey(scriptJs);
if (!decryptionKey) throw new Error("Key extraction failed");
let extractedKey = ""; let extractedKey = "";
const sourcesArray = streamRes.sources.split(""); let strippedSources = streamRes.sources;
for (const index of decryptionKey) { let totalledOffset = 0;
for (let i: number = index[0]; i < index[1]; i += 1) { decryptionKey.forEach(([a, b]) => {
extractedKey += streamRes.sources[i]; const start = a + totalledOffset;
sourcesArray[i] = ""; const end = start + b;
} extractedKey += streamRes.sources.slice(start, end);
} strippedSources = strippedSources.replace(
streamRes.sources.substring(start, end),
""
);
totalledOffset += b;
});
const decryptedStream = AES.decrypt( const decryptedStream = AES.decrypt(
sourcesArray.join(""), strippedSources,
extractedKey extractedKey
).toString(enc.Utf8); ).toString(enc.Utf8);
const parsedStream = JSON.parse(decryptedStream)[0]; const parsedStream = JSON.parse(decryptedStream)[0];

View File

@@ -18,12 +18,6 @@ import { compareTitle } from "@/utils/titleMatch";
const nanoid = customAlphabet("0123456789abcdef", 32); const nanoid = customAlphabet("0123456789abcdef", 32);
function makeFasterUrl(url: string) {
const fasterUrl = new URL(url);
fasterUrl.host = "mp4.shegu.net"; // this domain is faster
return fasterUrl.toString();
}
const qualityMap = { const qualityMap = {
"360p": MWStreamQuality.Q360P, "360p": MWStreamQuality.Q360P,
"480p": MWStreamQuality.Q480P, "480p": MWStreamQuality.Q480P,
@@ -154,13 +148,13 @@ registerProvider({
async scrape({ media, episode, progress }) { async scrape({ media, episode, progress }) {
// Find Superstream ID for show // Find Superstream ID for show
const searchQuery = { const searchQuery = {
module: "Search3", module: "Search4",
page: "1", page: "1",
type: "all", type: "all",
keyword: media.meta.title, keyword: media.meta.title,
pagelimit: "20", pagelimit: "20",
}; };
const searchRes = (await get(searchQuery, true)).data; const searchRes = (await get(searchQuery, true)).data.list;
progress(33); progress(33);
const superstreamEntry = searchRes.find( const superstreamEntry = searchRes.find(
@@ -205,7 +199,7 @@ registerProvider({
return { return {
embeds: [], embeds: [],
stream: { stream: {
streamUrl: makeFasterUrl(hdQuality.path), streamUrl: hdQuality.path,
quality: qualityMap[hdQuality.quality as QualityInMap], quality: qualityMap[hdQuality.quality as QualityInMap],
type: MWStreamType.MP4, type: MWStreamType.MP4,
captions: mappedCaptions, captions: mappedCaptions,
@@ -261,7 +255,7 @@ registerProvider({
quality: qualityMap[ quality: qualityMap[
hdQuality.quality as QualityInMap hdQuality.quality as QualityInMap
] as MWStreamQuality, ] as MWStreamQuality,
streamUrl: makeFasterUrl(hdQuality.path), streamUrl: hdQuality.path,
type: MWStreamType.MP4, type: MWStreamType.MP4,
captions: mappedCaptions, captions: mappedCaptions,
}, },

View File

@@ -51,7 +51,7 @@ function MediaCardContent({
> >
<div <div
className={[ className={[
"relative mb-4 aspect-[2/3] w-full overflow-hidden rounded-xl bg-denim-500 bg-cover bg-center transition-[border-radius] duration-100", "relative mb-4 w-full overflow-hidden rounded-xl bg-denim-500 bg-cover bg-center pb-[150%] transition-[border-radius] duration-100",
closable ? "" : "group-hover:rounded-lg", closable ? "" : "group-hover:rounded-lg",
].join(" ")} ].join(" ")}
style={{ style={{

View File

@@ -7,6 +7,7 @@ interface Config {
TMDB_READ_API_KEY: string; TMDB_READ_API_KEY: string;
CORS_PROXY_URL: string; CORS_PROXY_URL: string;
NORMAL_ROUTER: boolean; NORMAL_ROUTER: boolean;
DISALLOWED_IDS: string;
} }
export interface RuntimeConfig { export interface RuntimeConfig {
@@ -16,6 +17,7 @@ export interface RuntimeConfig {
TMDB_READ_API_KEY: string; TMDB_READ_API_KEY: string;
NORMAL_ROUTER: boolean; NORMAL_ROUTER: boolean;
PROXY_URLS: string[]; PROXY_URLS: string[];
DISALLOWED_IDS: string[];
} }
const env: Record<keyof Config, undefined | string> = { const env: Record<keyof Config, undefined | string> = {
@@ -25,6 +27,7 @@ const env: Record<keyof Config, undefined | string> = {
DISCORD_LINK: undefined, DISCORD_LINK: undefined,
CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL, CORS_PROXY_URL: import.meta.env.VITE_CORS_PROXY_URL,
NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER, NORMAL_ROUTER: import.meta.env.VITE_NORMAL_ROUTER,
DISALLOWED_IDS: import.meta.env.VITE_DISALLOWED_IDS,
}; };
// loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js) // loads from different locations, in order: environment (VITE_{KEY}), window (public/config.js)
@@ -61,5 +64,8 @@ export function conf(): RuntimeConfig {
.split(",") .split(",")
.map((v) => v.trim()), .map((v) => v.trim()),
NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true", NORMAL_ROUTER: getKey("NORMAL_ROUTER", "false") === "true",
DISALLOWED_IDS: getKey("DISALLOWED_IDS", "")
.split(",")
.map((v) => v.trim()), // Should be comma-seperated and contain the media type and ID, formatted like so: movie-753342,movie-753342,movie-753342
}; };
} }

View File

@@ -15,10 +15,12 @@ import {
} from "@/backend/metadata/types/mw"; } from "@/backend/metadata/types/mw";
import { IconPatch } from "@/components/buttons/IconPatch"; import { IconPatch } from "@/components/buttons/IconPatch";
import { Icons } from "@/components/Icon"; import { Icons } from "@/components/Icon";
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
import { Loading } from "@/components/layout/Loading"; import { Loading } from "@/components/layout/Loading";
import { useGoBack } from "@/hooks/useGoBack"; import { useGoBack } from "@/hooks/useGoBack";
import { useLoading } from "@/hooks/useLoading"; import { useLoading } from "@/hooks/useLoading";
import { SelectedMediaData, useScrape } from "@/hooks/useScrape"; import { SelectedMediaData, useScrape } from "@/hooks/useScrape";
import { conf } from "@/setup/config";
import { useWatchedItem } from "@/state/watched"; import { useWatchedItem } from "@/state/watched";
import { MetaController } from "@/video/components/controllers/MetaController"; import { MetaController } from "@/video/components/controllers/MetaController";
import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController"; import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController";
@@ -53,6 +55,31 @@ function MediaViewLoading(props: { onGoBack(): void }) {
); );
} }
function MediaVIewNotAllowed(props: { onGoBack(): void }) {
const { t } = useTranslation();
return (
<div className="relative flex flex-1 items-center justify-center">
<Helmet>
<title>{t("videoPlayer.got")}</title>
</Helmet>
<div className="absolute inset-x-0 top-0 px-8 py-6">
<VideoPlayerHeader onClick={props.onGoBack} />
</div>
<div className="flex flex-col items-center">
<ErrorMessage
error={{
name: "Media not allowed",
description:
"this media is no longer available due to a takedown notice or copyright claim",
path: "",
}}
/>
</div>
</div>
);
}
interface MediaViewScrapingProps { interface MediaViewScrapingProps {
onStream(stream: MWStream): void; onStream(stream: MWStream): void;
onGoBack(): void; onGoBack(): void;
@@ -240,6 +267,14 @@ export function MediaView() {
}); });
}, [exec, history, params]); }, [exec, history, params]);
const disallowedEntries = conf().DISALLOWED_IDS.map((id) => id.split("-"));
if (
disallowedEntries.find(
(entry) => meta?.tmdbId === entry[1] && meta?.meta?.type === entry[0]
)
)
return <MediaVIewNotAllowed onGoBack={goBack} />;
if (loading) return <MediaViewLoading onGoBack={goBack} />; if (loading) return <MediaViewLoading onGoBack={goBack} />;
if (error) return <MediaFetchErrorView />; if (error) return <MediaFetchErrorView />;
if (!meta || !selected) if (!meta || !selected)