feat: add Amvstrm as provider for stream URL

Summary:

Test Plan:
This commit is contained in:
2024-05-30 22:48:02 -04:00
parent 7e3c818828
commit c0ef6838fc
6 changed files with 220 additions and 5 deletions

View File

@@ -0,0 +1,75 @@
import type { FetchUrlResponse } from "./responseType";
export async function getSourcesFromAmvstrm(
watchId: string,
): Promise<FetchUrlResponse | null> {
const source = await fetch(
`https://api-amvstrm.nyt92.eu.org/api/v2/stream/${watchId}`,
)
.then((res) => res.json<AmvstrmStreamResponse>())
.then(({ stream }) => {
const streamObj = stream?.multi;
if (!!streamObj) {
return streamObj.main ?? streamObj.backup;
}
})
.then((streamObj) => streamObj?.url);
if (!source) {
return null;
}
return {
source,
subtitles: [],
audio: [],
};
}
interface AmvstrmStreamResponse {
code: number;
message: string;
info: Info;
stream: Stream;
iframe: Iframe[];
plyr: Nspl;
nspl: Nspl;
}
interface Iframe {
name: string;
iframe: string;
}
interface Info {
title: string;
id: string;
episode: string;
}
interface Nspl {
main: string;
backup: string;
}
interface Stream {
multi: Multi;
tracks: Tracks;
}
interface Multi {
main: Backup;
backup: Backup;
}
interface Backup {
url: string;
label: string;
isM3U8: boolean;
quality: string;
}
interface Tracks {
file: string;
kind: string;
}

View File

@@ -90,4 +90,45 @@ describe('requests the "/episodes/:id/url" route', () => {
expect(response.json()).resolves.toEqual({ success: false });
expect(response.status).toBe(404);
});
it("with sources from Amvstrm", async () => {
const response = await app.request(
"/episodes/1/url",
{
method: "POST",
body: JSON.stringify({
provider: "amvstrm",
id: "ore-dake-level-up-na-ken-episode-2",
}),
headers: { "Content-Type": "application/json" },
},
{
ENABLE_ANIFY: "true",
},
);
expect(response.json()).resolves.toEqual({
success: true,
result: {
source:
"https://www032.vipanicdn.net/streamhls/aa804a2400535d84dd59454b28d329fb/ep.1.1712504065.m3u8",
subtitles: [],
audio: [],
},
});
});
it("with no URL from Amvstrm source", async () => {
const response = await app.request("/episodes/-1/url", {
method: "POST",
body: JSON.stringify({
provider: "amvstrm",
id: "unknown",
}),
headers: { "Content-Type": "application/json" },
});
expect(response.json()).resolves.toEqual({ success: false });
expect(response.status).toBe(404);
});
});

View File

@@ -112,6 +112,26 @@ app.openapi(route, async (c) => {
}
}
if (provider === "amvstrm") {
try {
const result = await import("./amvstrm").then(
({ getSourcesFromAmvstrm }) => getSourcesFromAmvstrm(id),
);
if (!result) {
return c.json({ success: false }, { status: 404 });
}
return c.json({
success: true,
result,
});
} catch (e) {
console.error("Failed to fetch download URL from Amvstrm", e);
return c.json(ErrorResponse, { status: 500 });
}
}
return c.json(ErrorResponse, { status: 400 });
});