feature, not a bug

This commit is contained in:
Tejas Panchal
2026-03-01 12:39:28 +05:30
commit a6184c63d4
91 changed files with 6797 additions and 0 deletions

6
.env.example Normal file
View File

@@ -0,0 +1,6 @@
# Origins you want to allow, use * to allow all origins (not recommended for production)
ALLOWED_ORIGINS=<https://site1.com>,<https://site2.com>,...
# Port the server will run on [OPTIONAL - default: 4444]
PORT=4444

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
.vscode
node_modules
.env
dist
package-lock.json
.idea
yarn.lock
pnpm-lock.yaml

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM node:20-alpine
WORKDIR /app
# Install system dependencies
RUN apk add --no-cache git netcat-openbsd
# Copy package files
COPY package.json ./
RUN npm install
# Copy the rest
COPY . .
# Start app
CMD ["npm", "start"]

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 JustAnime
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1517
README.md Normal file

File diff suppressed because it is too large Load Diff

33
package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "anime-api",
"version": "1.0.0",
"description": "A restful api to provide all information about anime",
"main": "./server.js",
"scripts": {
"start": "node ./server.js",
"dev": "nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Sayan Das",
"license": "MIT",
"dependencies": {
"axios": "^1.11.0",
"bcrypt": "^6.0.0",
"cheerio": "^1.0.0-rc.12",
"cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"crypto-js": "^4.2.0",
"dotenv": "^17.2.0",
"express": "^5.2.1",
"image-pixels": "^2.2.2",
"jsonwebtoken": "^9.0.2"
},
"type": "module",
"devDependencies": {
"nodemon": "^3.1.10"
},
"repository": {
"type": "git",
"url": "https://github.com/itzzzme/anime-api.git"
}
}

91
public/404.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>404 not found!</title>
<style>
@import url("https://fonts.googleapis.com/css?family=Poppins:400,500,600,700&display=swap");
* {
margin: 0;
padding: 0;
outline: none;
box-sizing: border-box;
font-family: "Poppins", sans-serif;
}
body {
height: 100vh;
background-color: #1e1e1e;
color: #ce9178;
}
#error-page {
position: absolute;
top: 10%;
left: 15%;
right: 15%;
bottom: 10%;
display: flex;
align-items: center;
justify-content: center;
}
#error-page .content {
max-width: 600px;
text-align: center;
}
.content img.header {
width: 250px;
height: 250px;
position: relative;
}
.content h4 {
font-size: 1.5em;
margin-bottom: 20px;
text-transform: uppercase;
font-size: 2em;
max-width: 600px;
position: relative;
}
.content p {
font-size: 1.2em;
}
.content .btns {
margin: 25px 0;
display: inline-flex;
}
.content .btns a {
display: inline-block;
margin: 0 10px;
text-decoration: none;
border: 2px solid #69a6ce;
color: #69a6ce;
font-weight: 500;
padding: 10px 25px;
border-radius: 25px;
text-transform: uppercase;
transition: all 0.3s ease;
}
.content .btns a:hover {
background: #69a6ce;
color: #fff;
}
</style>
</head>
<body>
<div id="error-page">
<div class="content">
<img class="header" data-text="404" src="https://media.tenor.com/TPvxRCmycL4AAAAi/gray-hair-big-eyes.gif"/>
<h4 data-text="Opps! Page not found">404! Page not found :&#40;</h4>
<p>
Sorry, the page you're looking for doesn't exist. If you think
something is broken, report a problem.
</p>
<div class="btns">
<a href="/">return home</a>
<a href="https://github.com/itzzzme/anime-api" target="_blank"
>report problem</a
>
</div>
</div>
</div>
</body>
</html>

BIN
public/anya.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 MiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/image.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

132
public/index.html Normal file
View File

@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<title>Anime Api</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body,
html {
height: 100%;
width: 100%;
max-height: 100vh;
max-width: 100vw;
background-color: #1e1e1e;
color: #ce9178;
}
.container {
height: 100%;
width: 100%;
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 1fr;
justify-content: space-between;
align-items: center;
position: relative;
}
.image-container {
width: 100%;
display: flex;
justify-content: space-between;
background-size: cover;
position: relative;
z-index: 1;
}
h1 {
font-size: 2rem;
text-align: center;
font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS",
sans-serif;
z-index: 2;
}
#gif,
#mirrored-gif {
height: 300px;
width: 300px;
}
#mirrored-gif {
transform: scaleX(-1);
}
#pic {
width: 250px;
height: 250px;
border-radius: 50%;
object-position: center;
object-fit: cover;
}
.profile {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 2;
}
.github {
display: flex;
justify-content: center;
gap: 10px;
align-items: center;
}
.fa-github {
color: grey;
}
a {
font-size: 1.3rem;
color: #7cdcfe;
}
@media (max-width: 700px) {
.image-container {
display: none;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Welcome to API 🎉</h1>
<div class="profile">
<img id="pic" src="image.webp" alt="" />
<div class="github">
<i class="fa fa-github" style="font-size: 36px"></i>
<a href="https://github.com/itzzzme/anime-api" target="_blank"
>Click here to refer docs</a
>
</div>
</div>
<div class="image-container">
<img
id="gif"
src="./wlc.gif"
alt=""
/>
<img
id="mirrored-gif"
src="./wlc.gif"
alt=""
/>
</div>
</div>
</body>
</html>

BIN
public/pic.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
public/wlc.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

13
render.yaml Normal file
View File

@@ -0,0 +1,13 @@
services:
- type: web
name: anime-api
env: node
buildCommand: npm install
startCommand: npm start
branch: main
plan: free
envVars:
- key: NODE_ENV
value: production
- key: PORT
value: 6969

64
server.js Normal file
View File

@@ -0,0 +1,64 @@
import dotenv from "dotenv";
import express from "express";
import cors from "cors";
import path from "path";
import fs from "fs";
import { fileURLToPath } from "url";
import { dirname } from "path";
import { createApiRoutes } from "./src/routes/apiRoutes.js";
dotenv.config();
const app = express();
const PORT = process.env.PORT || 4444;
const __filename = fileURLToPath(import.meta.url);
const publicDir = path.join(dirname(__filename), "public");
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",");
app.use(
cors({
origin: allowedOrigins?.includes("*") ? "*" : allowedOrigins || [],
methods: ["GET"],
})
);
// Custom CORS middleware
app.use((req, res, next) => {
const origin = req.headers.origin;
if (
!allowedOrigins ||
allowedOrigins.includes("*") ||
(origin && allowedOrigins.includes(origin))
) {
res.setHeader("Access-Control-Allow-Origin", origin || "*");
res.setHeader("Access-Control-Allow-Methods", "GET");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
return next();
}
res
.status(403)
.json({ success: false, message: "Forbidden: Origin not allowed" });
});
app.use(express.static(publicDir, { redirect: false }));
const jsonResponse = (res, data, status = 200) =>
res.status(status).json({ success: true, results: data });
const jsonError = (res, message = "Internal server error", status = 500) =>
res.status(status).json({ success: false, message });
createApiRoutes(app, jsonResponse, jsonError);
app.use((req, res) => {
const filePath = path.join(publicDir, "404.html");
if (fs.existsSync(filePath)) {
res.status(404).sendFile(filePath);
} else {
res.status(500).send("Error loading 404 page.");
}
});
app.listen(PORT, () => {
console.info(`Listening at ${PORT}`);
});

2
src/configs/dataUrl.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
export const DEFAULT_HEADERS = {
"User-Agent":
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
};

View File

@@ -0,0 +1,2 @@
export const PLAYER_SCRIPT_URL =
"https://megacloud.club/js/player/a/e1-player.min.js";

View File

@@ -0,0 +1,2 @@
export const PLAYER_SCRIPT_URL =
"https://rapid-cloud.co/js/player/prod/e6-player-v2.min.js";

View File

@@ -0,0 +1,20 @@
import extractVoiceActor from "../extractors/actors.extractor.js";
const getVoiceActor = async (req, res) => {
const id = req.params.id;
try {
const voiceActorData = await extractVoiceActor(id);
// Ensure the data is structured correctly
if (!voiceActorData || voiceActorData.results.data.length === 0) {
return res.status(404).json({ error: "No voice actor found." });
}
return res.json(voiceActorData); // Return the desired structure
} catch (e) {
console.error(e);
return res.status(500).json({ error: "An error occurred" });
}
};
export default getVoiceActor;

View File

@@ -0,0 +1,27 @@
import extractAnimeInfo from "../extractors/animeInfo.extractor.js";
import extractSeasons from "../extractors/seasons.extractor.js";
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
export const getAnimeInfo = async (req, res) => {
const { id } = req.query;
// const cacheKey = `animeInfo_${id}`;
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0) {
// return cachedResponse;
// }
const [seasons, data] = await Promise.all([
extractSeasons(id),
extractAnimeInfo(id),
]);
const responseData = { data: data, seasons: seasons };
// setCachedData(cacheKey, responseData).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return responseData;
} catch (e) {
console.error(e);
return res.status(500).json({ error: "An error occurred" });
}
};

View File

@@ -0,0 +1,29 @@
import { extractor } from "../extractors/category.extractor.js";
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
export const getCategory = async (req, res, routeType) => {
if (routeType === "genre/martial-arts") {
routeType = "genre/marial-arts";
}
const requestedPage = parseInt(req.query.page) || 1;
// const cacheKey = `${routeType.replace(/\//g, "_")}_page_${requestedPage}`;
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0)
// return cachedResponse;
const { data, totalPages } = await extractor(routeType, requestedPage);
if (requestedPage > totalPages) {
const error = new Error("Requested page exceeds total available pages.");
error.status = 404;
throw error;
}
const responseData = { totalPages: totalPages, data: data };
// setCachedData(cacheKey, responseData).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return responseData;
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,20 @@
import extractCharacter from "../extractors/characters.extractor.js";
const getCharacter = async (req, res) => {
const id = req.params.id;
try {
const characterData = await extractCharacter(id);
// Ensure the data is structured correctly
if (!characterData || characterData.results.data.length === 0) {
return res.status(404).json({ error: "Character not found." });
}
return res.json(characterData); // Return the desired structure
} catch (e) {
console.error(e);
return res.status(500).json({ error: "An error occurred" });
}
};
export default getCharacter;

View File

@@ -0,0 +1,21 @@
import extractEpisodesList from "../extractors/episodeList.extractor.js";
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
export const getEpisodes = async (req,res) => {
const { id } = req.params;
// const cacheKey = `episodes_${id}`;
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0) {
// return cachedResponse;
// }
const data = await extractEpisodesList(encodeURIComponent(id));
// setCachedData(cacheKey, data).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return data;
} catch (e) {
console.error("Error fetching episodes:", e);
return e;
}
};

View File

@@ -0,0 +1,66 @@
import extractFilterResults from "../extractors/filter.extractor.js";
export const filter = async (req) => {
try {
// Extract all possible query parameters
const {
type,
status,
rated,
score,
season,
language,
genres,
sort,
sy, // Start year
sm, // Start month
sd, // Start day
ey, // End year
em, // End month
ed, // End day
keyword,
page = 1
} = req.query;
// Convert page to number
const pageNum = parseInt(page);
// Create params object only with provided values
const params = {};
if (type) params.type = type;
if (status) params.status = status;
if (rated) params.rated = rated;
if (score) params.score = score;
if (season) params.season = season;
if (language) params.language = language;
if (genres) params.genres = genres;
if (sort) params.sort = sort;
if (sy) params.sy = sy;
if (sm) params.sm = sm;
if (sd) params.sd = sd;
if (ey) params.ey = ey;
if (em) params.em = em;
if (ed) params.ed = ed;
if (keyword) params.keyword = keyword;
if (pageNum > 1) params.page = pageNum;
// Log params for debugging
// console.log("Controller params:", params);
const [totalPage, data, currentPage, hasNextPage] = await extractFilterResults(params);
if (pageNum > totalPage) {
const error = new Error("Requested page exceeds total available pages.");
error.status = 404;
throw error;
}
return { data, totalPage, currentPage, hasNextPage };
} catch (e) {
console.error(e);
if (e.status === 404) {
throw e;
}
throw new Error("An error occurred while processing your request.");
}
};

View File

@@ -0,0 +1,68 @@
import getSpotlights from "../extractors/spotlight.extractor.js";
import getTrending from "../extractors/trending.extractor.js";
import extractPage from "../helper/extractPages.helper.js";
import extractTopTen from "../extractors/topten.extractor.js";
import { routeTypes } from "../routes/category.route.js";
import extractSchedule from "../extractors/schedule.extractor.js";
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
const genres = routeTypes
.slice(0, 41)
.map((genre) => genre.replace("genre/", ""));
export const getHomeInfo = async (req,res) => {
// const cacheKey = "homeInfo";
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0) {
// return cachedResponse;
// }
const [
spotlights,
trending,
topTen,
schedule,
topAiring,
mostPopular,
mostFavorite,
latestCompleted,
latestEpisode,
topUpcoming,
recentlyAdded,
] = await Promise.all([
getSpotlights(),
getTrending(),
extractTopTen(),
extractSchedule(new Date().toISOString().split("T")[0]),
extractPage(1, "top-airing"),
extractPage(1, "most-popular"),
extractPage(1, "most-favorite"),
extractPage(1, "completed"),
extractPage(1, "recently-updated"),
extractPage(1, "top-upcoming"),
extractPage(1, "recently-added"),
]);
const responseData = {
spotlights,
trending,
topTen,
today: { schedule },
topAiring: topAiring[0],
mostPopular: mostPopular[0],
mostFavorite: mostFavorite[0],
latestCompleted: latestCompleted[0],
latestEpisode: latestEpisode[0],
topUpcoming: topUpcoming[0],
recentlyAdded: recentlyAdded[0],
genres,
};
// setCachedData(cacheKey, responseData).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return responseData;
} catch (fetchError) {
console.error("Error fetching fresh data:", fetchError);
return fetchError;
}
};

View File

@@ -0,0 +1,12 @@
import extractNextEpisodeSchedule from "../extractors/getNextEpisodeSchedule.extractor.js";
export const getNextEpisodeSchedule = async (req) => {
const { id } = req.params;
try {
const nextEpisodeSchedule = await extractNextEpisodeSchedule(id);
return { nextEpisodeSchedule: nextEpisodeSchedule };
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,32 @@
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
import extractPage from "../helper/extractPages.helper.js";
export const getProducer = async (req) => {
const { id } = req.params;
const routeType = `producer/${id}`;
const requestedPage = parseInt(req.query.page) || 1;
// const cacheKey = `${routeType.replace(/\//g, "_")}_page_${requestedPage}`;
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0) {
// return cachedResponse;
// }
const [data, totalPages] = await extractPage(requestedPage, routeType);
if (requestedPage > totalPages) {
const error = new Error("Requested page exceeds total available pages.");
error.status = 404;
throw error;
}
const responseData = { totalPages: totalPages, data: data };
// setCachedData(cacheKey, responseData).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return { data, totalPages };
} catch (e) {
console.error(e);
if (e.status === 404) {
throw e;
}
throw new Error("An error occurred while processing your request.");
}
};

View File

@@ -0,0 +1,12 @@
import extractQtip from "../extractors/qtip.extractor.js";
export const getQtip = async (req) => {
try {
const { id } = req.params;
const data = await extractQtip(id);
return data;
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,11 @@
import extractRandom from "../extractors/random.extractor.js";
export const getRandom = async (req,res) => {
try {
const data = await extractRandom();
return data;
} catch (error) {
console.error("Error getting random anime:", error.message);
return e;
}
};

View File

@@ -0,0 +1,11 @@
import extractRandomId from "../extractors/randomId.extractor.js";
export const getRandomId = async (req,res) => {
try {
const data = await extractRandomId();
return data;
} catch (error) {
console.error("Error getting random anime ID:", error.message);
return e;
}
};

View File

@@ -0,0 +1,13 @@
import extractSchedule from "../extractors/schedule.extractor.js";
export const getSchedule = async (req) => {
const date = req.query.date;
const tzOffset = req.query.tzOffset || -330;
try {
const data = await extractSchedule(date, tzOffset);
return data;
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,43 @@
import extractSearchResults from "../extractors/search.extractor.js";
import convertForeignLanguage from "../helper/foreignInput.helper.js";
export const search = async (req) => {
try {
let { keyword, type, status, rated, score, season, language, genres, sort, sy, sm, sd, ey, em, ed } = req.query;
let page = parseInt(req.query.page) || 1;
// Check if the search keyword is in a foreign language and if it can be converted
keyword = await convertForeignLanguage(keyword);
const [totalPage, data] = await extractSearchResults({
keyword: keyword,
type: type,
status: status,
rated: rated,
score: score,
season: season,
language: language,
genres: genres,
sort: sort,
page: page,
sy: sy,
sm: sm,
sd: sd,
ey: ey,
em: em,
ed: ed,
});
if (page > totalPage) {
const error = new Error("Requested page exceeds total available pages.");
error.status = 404;
throw error;
}
return { data, totalPage };
} catch (e) {
console.error(e);
if (e.status === 404) {
throw e;
}
throw new Error("An error occurred while processing your request.");
}
};

View File

@@ -0,0 +1,12 @@
import { extractServers } from "../extractors/streamInfo.extractor.js";
export const getServers = async (req) => {
try {
const { ep } = req.query;
const servers = await extractServers(ep);
return servers;
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,17 @@
import { extractStreamingInfo } from "../extractors/streamInfo.extractor.js";
export const getStreamInfo = async (req, res, fallback = false) => {
try {
const input = req.query.id;
const server = req.query.server;
const type = req.query.type;
const match = input.match(/ep=(\d+)/);
if (!match) throw new Error("Invalid URL format");
const finalId = match[1];
const streamingInfo = await extractStreamingInfo(finalId, server, type, fallback);
return streamingInfo;
} catch (e) {
console.error(e);
return { error: e.message };
}
};

View File

@@ -0,0 +1,17 @@
import getSuggestion from "../extractors/suggestion.extractor.js";
import convertForeignLanguage from "../helper/foreignInput.helper.js";
export const getSuggestions = async (req) => {
let { keyword } = req.query;
// Check if the search keyword is in a foreign language and if it can be converted
keyword = await convertForeignLanguage(keyword);
try {
const data = await getSuggestion(encodeURIComponent(keyword));
return data;
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,13 @@
import extractTopSearch from "../extractors/topsearch.extractor.js";
const getTopSearch = async () => {
try {
const data = await extractTopSearch();
return data;
} catch (e) {
console.error(e);
return e;
}
};
export default getTopSearch;

View File

@@ -0,0 +1,22 @@
import extractTopTen from "../extractors/topten.extractor.js";
import { getCachedData, setCachedData } from "../helper/cache.helper.js";
export const getTopTen = async (req,res) => {
// const cacheKey = "topTen";
try {
// const cachedResponse = await getCachedData(cacheKey);
// if (cachedResponse && Object.keys(cachedResponse).length > 0) {
// return cachedResponse;
// }
const topTen = await extractTopTen();
// await setCachedData(cacheKey, topTen).catch((err) => {
// console.error("Failed to set cache:", err);
// });
return topTen;
} catch (e) {
console.error(e);
return c
.status(500)
.json({ success: false, error: "Internal Server Error" });
}
};

View File

@@ -0,0 +1,16 @@
import extractVoiceActor from "../extractors/voiceactor.extractor.js";
export const getVoiceActors = async (req, res) => {
const requestedPage = parseInt(req.query.page) || 1;
const id = req.params.id;
try {
const { totalPages, charactersVoiceActors: data } = await extractVoiceActor(
id,
requestedPage
);
return { currentPage: requestedPage, totalPages, data };
} catch (e) {
console.error(e);
return e;
}
};

View File

@@ -0,0 +1,40 @@
import extractWatchlist from "../extractors/watchlist.extractor.js";
export const getWatchlist = async (req, res) => {
const { userId, page = 1 } = req.params;
try {
const { watchlist, totalPages } = await extractWatchlist(userId, page);
// Restructuring the response
return res.json({
success: true,
results: {
totalPages, // Include total pages in the response
data: watchlist.map(item => ({
id: item.id,
data_id: item.data_id,
poster: item.poster,
title: item.title,
japanese_title: item.japanese_title,
description: item.description,
tvInfo: {
showType: item.tvInfo.showType,
duration: item.tvInfo.duration,
sub: item.tvInfo.sub,
dub: item.tvInfo.dub,
// Include eps if it exists
...(item.tvInfo.eps && { eps: item.tvInfo.eps })
},
adultContent: item.adultContent,
}))
}
});
} catch (error) {
console.error("Error getting watchlist:", error.message);
if (!res.headersSent) {
return res.status(500).json({ error: "An error occurred while fetching the watchlist." });
}
}
};

View File

@@ -0,0 +1,91 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export async function extractVoiceActor(id) {
try {
const response = await axios.get(`https://${v1_base_url}/people/${id}`);
const $ = cheerio.load(response.data);
// Extract basic information
const name = $(".apw-detail .name").text().trim();
const japaneseName = $(".apw-detail .sub-name").text().trim();
// Extract profile image
const profile = $(".avatar-circle img").attr("src"); // Extracting the profile image URL
// Extract about information as a full bio description
const bioText = $("#bio .bio").text().trim();
const bioHtml = $("#bio .bio").html(); // Capture the raw HTML
const about = {
description: bioText, // Store the full bio as a single description
style: bioHtml, // Store the full HTML structure
};
// Extract voice acting roles
const roles = [];
$(".bac-list-wrap .bac-item").each((_, element) => {
const animeElement = $(element).find(".per-info.anime-info.ltr");
const characterElement = $(element).find(".per-info.rtl");
const role = {
anime: {
id: animeElement.find(".pi-name a").attr("href")?.split("/").pop(),
title: animeElement.find(".pi-name a").text().trim(),
poster:
animeElement.find(".pi-avatar img").attr("data-src") ||
animeElement.find(".pi-avatar img").attr("src"),
type: animeElement
.find(".pi-cast")
.text()
.trim()
.split(",")[0]
.trim(),
year: animeElement
.find(".pi-cast")
.text()
.trim()
.split(",")[1]
?.trim(),
},
character: {
id: characterElement
.find(".pi-name a")
.attr("href")
?.split("/")
.pop(),
name: characterElement.find(".pi-name a").text().trim(),
profile:
characterElement.find(".pi-avatar img").attr("data-src") ||
characterElement.find(".pi-avatar img").attr("src"),
role: characterElement.find(".pi-cast").text().trim(),
},
};
roles.push(role);
});
// Construct the final response
const voiceActorData = {
success: true,
results: {
data: [
{
id,
name,
profile,
japaneseName,
about,
roles,
},
],
},
};
return voiceActorData;
} catch (error) {
console.error("Error extracting voice actor data:", error);
throw new Error("Failed to extract voice actor information");
}
}
export default extractVoiceActor;

View File

@@ -0,0 +1,194 @@
import axios from "axios";
import * as cheerio from "cheerio";
import formatTitle from "../helper/formatTitle.helper.js";
import { v1_base_url } from "../utils/base_v1.js";
import extractRecommendedData from "./recommend.extractor.js";
import extractRelatedData from "./related.extractor.js";
import extractPopularData from "./popular.extractor.js";
async function extractAnimeInfo(id) {
try {
const [resp, characterData] = await Promise.all([
axios.get(`https://${v1_base_url}/${id}`),
axios.get(
`https://${v1_base_url}/ajax/character/list/${id.split("-").pop()}`
),
]);
const characterHtml = characterData.data?.html || "";
const $1 = cheerio.load(characterHtml);
const $ = cheerio.load(resp.data);
const data_id = id.split("-").pop();
const titleElement = $("#ani_detail .film-name");
const showType = $("#ani_detail .prebreadcrumb ol li")
.eq(1)
.find("a")
.text()
.trim();
const posterElement = $("#ani_detail .film-poster");
const tvInfoElement = $("#ani_detail .film-stats");
const tvInfo = {};
tvInfoElement.find(".tick-item, span.item").each((_, element) => {
const el = $(element);
const text = el.text().trim();
if (el.hasClass("tick-quality")) tvInfo.quality = text;
else if (el.hasClass("tick-sub")) tvInfo.sub = text;
else if (el.hasClass("tick-dub")) tvInfo.dub = text;
else if (el.hasClass("tick-eps")) tvInfo.eps = text;
else if (el.hasClass("tick-pg")) tvInfo.rating = text;
else if (el.is("span.item")) {
if (!tvInfo.showType) tvInfo.showType = text;
else if (!tvInfo.duration) tvInfo.duration = text;
}
});
const element = $(
"#ani_detail > .ani_detail-stage > .container > .anis-content > .anisc-info-wrap > .anisc-info > .item"
);
const overviewElement = $("#ani_detail .film-description .text");
const title = titleElement.text().trim();
const japanese_title = titleElement.attr("data-jname");
const synonyms = $('.item.item-title:has(.item-head:contains("Synonyms")) .name').text().trim();
const poster = posterElement.find("img").attr("src");
const syncDataScript = $("#syncData").html();
let anilistId = null;
let malId = null;
if (syncDataScript) {
try {
const syncData = JSON.parse(syncDataScript);
anilistId = syncData.anilist_id || null;
malId = syncData.mal_id || null;
} catch (error) {
console.error("Error parsing syncData:", error);
}
}
const animeInfo = {};
element.each((_, el) => {
const key = $(el).find(".item-head").text().trim().replace(":", "");
const value =
key === "Genres" || key === "Producers"
? $(el)
.find("a")
.map((_, a) => $(a).text().split(" ").join("-").trim())
.get()
: $(el).find(".name").text().split(" ").join("-").trim();
animeInfo[key] = value;
});
const trailers = [];
$('.block_area-promotions-list .screen-items .item').each((_, element) => {
const el = $(element);
const title = el.attr('data-title');
const url = el.attr('data-src');
if (url) {
const fullUrl = url.startsWith('//') ? `https:${url}` : url;
let videoId = null;
const match = fullUrl.match(/\/embed\/([^?&]+)/);
if (match && match[1]) {
videoId = match[1];
}
trailers.push({
title: title || null,
url: fullUrl,
thumbnail: videoId ? `https://img.youtube.com/vi/${videoId}/hqdefault.jpg` : null
});
}
});
animeInfo.trailers = trailers;
const season_id = formatTitle(title, data_id);
animeInfo["Overview"] = overviewElement.text().trim();
animeInfo["tvInfo"] = tvInfo;
let adultContent = false;
const tickRateText = $(".tick-rate", posterElement).text().trim();
if (tickRateText.includes("18+")) {
adultContent = true;
}
const [recommended_data, related_data, popular_data] = await Promise.all([
extractRecommendedData($),
extractRelatedData($),
extractPopularData($),
]);
let charactersVoiceActors = [];
if (characterHtml) {
charactersVoiceActors = $1(".bac-list-wrap .bac-item")
.map((index, el) => {
const character = {
id:
$1(el)
.find(".per-info.ltr .pi-avatar")
.attr("href")
?.split("/")[2] || "",
poster:
$1(el).find(".per-info.ltr .pi-avatar img").attr("data-src") ||
"",
name: $1(el).find(".per-info.ltr .pi-detail a").text(),
cast: $1(el).find(".per-info.ltr .pi-detail .pi-cast").text(),
};
let voiceActors = [];
const rtlVoiceActors = $1(el).find(".per-info.rtl");
const xxVoiceActors = $1(el).find(
".per-info.per-info-xx .pix-list .pi-avatar"
);
if (rtlVoiceActors.length > 0) {
voiceActors = rtlVoiceActors
.map((_, actorEl) => ({
id: $1(actorEl).find("a").attr("href")?.split("/").pop() || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name:
$1(actorEl).find(".pi-detail .pi-name a").text().trim() || "",
}))
.get();
} else if (xxVoiceActors.length > 0) {
voiceActors = xxVoiceActors
.map((_, actorEl) => ({
id: $1(actorEl).attr("href")?.split("/").pop() || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name: $1(actorEl).attr("title") || "",
}))
.get();
}
if (voiceActors.length === 0) {
voiceActors = $1(el)
.find(".per-info.per-info-xx .pix-list .pi-avatar")
.map((_, actorEl) => ({
id: $1(actorEl).attr("href")?.split("/")[2] || "",
poster: $1(actorEl).find("img").attr("data-src") || "",
name: $1(actorEl).attr("title") || "",
}))
.get();
}
return { character, voiceActors };
})
.get();
}
return {
adultContent,
data_id,
id: season_id,
anilistId,
malId,
title,
japanese_title,
synonyms,
poster,
showType,
animeInfo,
charactersVoiceActors,
recommended_data,
related_data,
popular_data,
};
} catch (e) {
console.error("Error extracting anime info:", e);
}
}
export default extractAnimeInfo;

View File

@@ -0,0 +1,14 @@
import extractPage from "../helper/extractPages.helper.js";
export async function extractor(path, page) {
try {
const [data, totalPages] = await extractPage(page, path);
return { data, totalPages };
} catch (error) {
console.error(
`Error extracting data for ${path} from page ${page}:`,
error.message
);
throw error;
}
}

View File

@@ -0,0 +1,91 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export async function extractCharacter(id) {
try {
const response = await axios.get(`https://${v1_base_url}//character/${id}`);
const $ = cheerio.load(response.data);
// Extract basic information
const name = $(".apw-detail .name").text().trim();
const japaneseName = $(".apw-detail .sub-name").text().trim();
// Extract profile image
const profile = $(".avatar-circle img").attr("src");
// Extract about information
const bioText = $("#bio .bio").text().trim();
const bioHtml = $("#bio .bio").html();
const about = {
description: bioText,
style: bioHtml,
};
// Extract voice actors
const voiceActors = [];
$("#voiactor .per-info").each((_, element) => {
const voiceActorElement = $(element);
const voiceActor = {
name: voiceActorElement.find(".pi-name a").text().trim(),
profile: voiceActorElement.find(".pi-avatar img").attr("src"),
language: voiceActorElement.find(".pi-cast").text().trim(),
id: voiceActorElement.find(".pi-name a").attr("href")?.split("/").pop(),
};
if (voiceActor.name && voiceActor.id) {
voiceActors.push(voiceActor);
}
});
// Extract animeography
const animeography = [];
$(".anif-block-ul li").each((_, el) => {
const item = $(el);
const anchor = item.find(".film-name a.dynamic-name");
const title = anchor.text().trim();
const japanese_title = anchor.attr("data-jname")?.trim();
const id = anchor.attr("href")?.split("/").pop();
const role = item.find(".fdi-item").first().text().trim();
const type = item.find(".fdi-item").last().text().trim();
const poster = item.find(".film-poster img").attr("src");
if (title && id) {
animeography.push({
title,
japanese_title,
id,
role: role.replace(" (Role)", ""),
type,
poster,
});
}
});
const characterData = {
success: true,
results: {
data: [
{
id,
name,
profile,
japaneseName,
about,
voiceActors,
animeography,
},
],
},
};
return characterData;
} catch (error) {
console.error("Error extracting character data:", error);
throw new Error("Failed to extract character information");
}
}
export default extractCharacter;

View File

@@ -0,0 +1,39 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function extractEpisodesList(id) {
try {
const showId = id.split("-").pop();
const response = await axios.get(
`https://${v1_base_url}/ajax/v2/episode/list/${showId}`,
{
headers: {
"X-Requested-With": "XMLHttpRequest",
Referer: `https://${v1_base_url}/watch/${id}`,
},
}
);
if (!response.data.html) return [];
const $ = cheerio.load(response.data.html);
const res = {
totalEpisodes: 0,
episodes: [],
};
res.totalEpisodes = Number($(".detail-infor-content .ss-list a").length);
$(".detail-infor-content .ss-list a").each((_, el) => {
res.episodes.push({
episode_no: Number($(el).attr("data-number")),
id: $(el)?.attr("href")?.split("/")?.pop() || null,
title: $(el)?.attr("title")?.trim() || null,
japanese_title: $(el).find(".ep-name").attr("data-jname"),
filler: $(el).hasClass("ssl-item-filler"),
});
});
return res;
} catch (error) {
console.error(error);
return [];
}
}
export default extractEpisodesList;

View File

@@ -0,0 +1,174 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
import { v1_base_url } from "../utils/base_v1.js";
import {
FILTER_LANGUAGE_MAP,
GENRE_MAP,
FILTER_TYPES,
FILTER_STATUS,
FILTER_RATED,
FILTER_SCORE,
FILTER_SEASON,
FILTER_SORT,
} from "../routes/filter.maping.js";
async function extractFilterResults(params = {}) {
try {
const normalizeParam = (param, mapping) => {
if (!param) return undefined;
if (typeof param === "string") {
const isAlreadyId = Object.values(mapping).includes(param);
if (isAlreadyId) {
return param;
}
const key = param.trim().toUpperCase();
return mapping.hasOwnProperty(key) ? mapping[key] : undefined;
}
return param;
};
const typeParam = normalizeParam(params.type, FILTER_TYPES);
const statusParam = normalizeParam(params.status, FILTER_STATUS);
const ratedParam = normalizeParam(params.rated, FILTER_RATED);
const scoreParam = normalizeParam(params.score, FILTER_SCORE);
const seasonParam = normalizeParam(params.season, FILTER_SEASON);
const sortParam = normalizeParam(params.sort, FILTER_SORT);
let languageParam = params.language;
if (languageParam != null) {
languageParam = String(languageParam).trim().toUpperCase();
languageParam = FILTER_LANGUAGE_MAP[languageParam] ?? (Object.values(FILTER_LANGUAGE_MAP).includes(languageParam) ? languageParam : undefined);
}
let genresParam = params.genres;
if (typeof genresParam === "string") {
genresParam = genresParam
.split(",")
.map((genre) => GENRE_MAP[genre.trim().toUpperCase()] || genre.trim())
.join(",");
}
const filteredParams = {
type: typeParam,
status: statusParam,
rated: ratedParam,
score: scoreParam,
season: seasonParam,
language: languageParam,
genres: genresParam,
sort: sortParam,
page: params.page || 1,
sy: params.sy || undefined,
sm: params.sm || undefined,
sd: params.sd || undefined,
ey: params.ey || undefined,
em: params.em || undefined,
ed: params.ed || undefined,
keyword: params.keyword || undefined,
};
Object.keys(filteredParams).forEach((key) => {
if (filteredParams[key] === undefined) {
delete filteredParams[key];
}
});
const queryParams = new URLSearchParams(filteredParams).toString();
let apiUrl = `https://${v1_base_url}/filter?${queryParams}`;
if (filteredParams.keyword) {
apiUrl = `https://${v1_base_url}/search?${queryParams}`;
}
const resp = await axios.get(apiUrl, {
headers: {
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": DEFAULT_HEADERS,
},
});
const $ = cheerio.load(resp.data);
const elements = ".flw-item";
const result = [];
$(elements).each((_, el) => {
const $el = $(el);
const href = $el.find(".film-poster-ahref").attr("href");
const data_id = Number($el.find(".film-poster-ahref").attr("data-id"));
result.push({
id: href ? href.slice(1) : null,
data_id: data_id ? `${data_id}` : null,
poster:
$el.find(".film-poster .film-poster-img").attr("data-src") ||
$el.find(".film-poster .film-poster-img").attr("src") ||
null,
title: $el.find(".film-name .dynamic-name").text().trim(),
japanese_title:
$el.find(".film-name .dynamic-name").attr("data-jname") || null,
tvInfo: {
showType:
$el.find(".fd-infor .fdi-item:first-child").text().trim() ||
"Unknown",
duration: $el.find(".fd-infor .fdi-duration").text().trim() || null,
sub:
Number(
$el
.find(".tick-sub")
.text()
.replace(/[^0-9]/g, "")
) || null,
dub:
Number(
$el
.find(".tick-dub")
.text()
.replace(/[^0-9]/g, "")
) || null,
eps:
Number(
$el
.find(".tick-eps")
.text()
.replace(/[^0-9]/g, "")
) || null,
},
adultContent: $el.find(".tick-rate").text().trim() || null,
});
});
const totalPage = Number(
$('.pre-pagination nav .pagination > .page-item a[title="Last"]')
?.attr("href")
?.split("=")
.pop() ||
$('.pre-pagination nav .pagination > .page-item a[title="Next"]')
?.attr("href")
?.split("=")
.pop() ||
$(".pre-pagination nav .pagination > .page-item.active a")
?.text()
?.trim() ||
1
);
return [
parseInt(totalPage, 10),
result.length > 0 ? result : [],
parseInt(params.page, 10) || 1,
parseInt(params.page, 10) < parseInt(totalPage, 10),
];
} catch (e) {
console.error("Error fetching data:", e);
throw e;
}
}
export { extractFilterResults as default };

View File

@@ -0,0 +1,16 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export default async function extractNextEpisodeSchedule(id) {
try {
const { data } = await axios.get(`https://${v1_base_url}/watch/${id}`);
const $ = cheerio.load(data);
const nextEpisodeSchedule = $(
".schedule-alert > .alert.small > span:last"
).attr("data-value");
return nextEpisodeSchedule;
} catch (error) {
console.error(error);
}
}

View File

@@ -0,0 +1,49 @@
export default async function extractPopularData($) {
const popularSection = $('#main-sidebar .block_area:has(.cat-heading:contains("Most Popular"))');
const popularElements = popularSection.find(".anif-block-ul .ulclear li");
return await Promise.all(
popularElements
.map(async (index, element) => {
const $el = $(element);
const id = $el.find(".film-detail .film-name a").attr("href")?.split("/").pop();
const data_id = $el.find(".film-poster").attr("data-id");
const title = $el.find(".film-detail .film-name a").text().trim();
const japanese_title = $el.find(".film-detail .film-name a").attr("data-jname")?.trim();
const poster = $el.find(".film-poster img").attr("data-src") || $el.find(".film-poster img").attr("src");
// Extract show type like "TV", "Movie", etc.
const showTypeText = $el.find(".tick").text().toLowerCase();
const showTypeMatch = ["TV", "ONA", "Movie", "OVA", "Special"].find(type =>
showTypeText.toLowerCase().includes(type.toLowerCase())
);
const tvInfo = {
showType: showTypeMatch || "Unknown"
};
// Extract tick items like sub, dub, eps
["sub", "dub", "eps"].forEach((type) => {
const value = $el.find(`.tick-item.tick-${type}`).text().trim();
if (value) {
tvInfo[type] = value;
}
});
// Adult content check
const tickRateText = $el.find(".film-poster > .tick-rate").text().trim();
const adultContent = tickRateText.includes("18+");
return {
data_id,
id,
title,
japanese_title,
poster,
tvInfo,
adultContent,
};
})
.get()
);
}

View File

@@ -0,0 +1,65 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export default async function extractQtip(id) {
try {
const { data } = await axios.get(
`https://${v1_base_url}/ajax/movie/qtip/${id}`,
{
headers: {
"x-requested-with": "XMLHttpRequest",
},
}
);
const $ = cheerio.load(data);
const title = $(".pre-qtip-title").text();
const rating = $(".pqd-li i.fas.fa-star").parent().text().trim();
const quality = $(".tick-item.tick-quality").text();
const subCount = $(".tick-item.tick-sub").text().trim();
const dubCount = $(".tick-item.tick-dub").text().trim();
const episodeCount = $(".tick-item.tick-eps").text().trim();
const type = $(".badge.badge-quality").text();
const description = $(".pre-qtip-description").text().trim();
const japaneseTitle = $(".pre-qtip-line:contains('Japanese:') .stick-text")
.text()
.trim();
const airedDate = $(".pre-qtip-line:contains('Aired:') .stick-text")
.text()
.trim();
const status = $(".pre-qtip-line:contains('Status:') .stick-text")
.text()
.trim();
const Synonyms = $(".pre-qtip-line:contains('Synonyms:') .stick-text")
.text()
.trim();
const genres = [];
$(".pre-qtip-line:contains('Genres:') a").each((i, elem) => {
genres.push($(elem).text().trim().split(" ").join("-"));
});
const watchLink = $(".pre-qtip-button a.btn.btn-play").attr("href");
const extractedData = {
title,
rating,
quality,
subCount,
dubCount,
episodeCount,
type,
description,
japaneseTitle,
Synonyms,
airedDate,
status,
genres,
watchLink,
};
return extractedData;
} catch (error) {
console.error("Error extracting data:", error);
return error;
}
}

View File

@@ -0,0 +1,18 @@
import axios from "axios";
import { v1_base_url } from "../utils/base_v1.js";
import extractAnimeInfo from "./animeInfo.extractor.js";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
const axiosInstance = axios.create({ headers: DEFAULT_HEADERS });
export default async function extractRandom() {
try {
const resp = await axiosInstance.get(`https://${v1_base_url}/random`);
const redirectedUrl = resp.request.res.responseUrl;
const id = redirectedUrl.split("/").pop();
const animeInfo = await extractAnimeInfo(id);
return animeInfo;
} catch (error) {
console.error("Error extracting random anime info:", error);
}
}

View File

@@ -0,0 +1,16 @@
import axios from "axios";
import { v1_base_url } from "../utils/base_v1.js";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
const axiosInstance = axios.create({ headers: DEFAULT_HEADERS });
export default async function extractRandomId() {
try {
const resp = await axiosInstance.get(`https://${v1_base_url}/random`);
const redirectedUrl = resp.request.res.responseUrl;
const id = redirectedUrl.split("/").pop();
return id;
} catch (error) {
console.error("Error extracting random anime info:", error);
}
}

View File

@@ -0,0 +1,65 @@
export default async function extractRecommendedData($) {
const recommendedElements = $(
"#main-content .block_area_category .tab-content .block_area-content .film_list-wrap .flw-item"
);
return await Promise.all(
recommendedElements
.map(async (index, element) => {
const id = $(element)
.find(".film-detail .film-name a")
.attr("href")
.split("/")
.pop();
const data_id = $(element).find(".film-poster a").attr("data-id");
const title = $(element)
.find(".film-detail .film-name a")
.text()
.trim();
const japanese_title = $(element)
.find(".film-detail .film-name a")
.attr("data-jname")
.trim();
const poster = $(element).find(".film-poster img").attr("data-src");
const $fdiItems = $(".film-detail .fd-infor .fdi-item", element);
const showType = $fdiItems
.filter((_, item) => {
const text = $(item).text().trim().toLowerCase();
return ["tv", "ona", "movie", "ova", "special"].some((type) =>
text.includes(type)
);
})
.first();
const tvInfo = {
showType: showType ? showType.text().trim() : "Unknown",
duration: $(".film-detail .fd-infor .fdi-duration", element)
.text()
.trim(),
};
["sub", "dub", "eps"].forEach((property) => {
const value = $(`.tick .tick-${property}`, element).text().trim();
if (value) {
tvInfo[property] = value;
}
});
let adultContent = false;
const tickRateText = $(".film-poster>.tick-rate", element)
.text()
.trim();
if (tickRateText.includes("18+")) {
adultContent = true;
}
return {
data_id,
id,
title,
japanese_title,
poster,
tvInfo,
adultContent,
};
})
.get()
);
}

View File

@@ -0,0 +1,51 @@
export default async function extractRelatedData($) {
const relatedSection = $('#main-sidebar .block_area:has(.cat-heading:contains("Related Anime"))');
const relatedElements = relatedSection.find(
".anif-block-ul .ulclear li"
);
return await Promise.all(
relatedElements
.map(async (index, element) => {
const $el = $(element);
const id = $el.find(".film-detail .film-name a").attr("href")?.split("/").pop();
const data_id = $el.find(".film-poster").attr("data-id");
const title = $el.find(".film-detail .film-name a").text().trim();
const japanese_title = $el.find(".film-detail .film-name a").attr("data-jname")?.trim();
const poster = $el.find(".film-poster img").attr("data-src") || $el.find(".film-poster img").attr("src");
// Extract show type like "TV", "Movie", etc.
const showTypeText = $el.find(".tick").text().toLowerCase();
const showTypeMatch = ["TV", "ONA", "Movie", "OVA", "Special"].find(type =>
showTypeText.toLowerCase().includes(type.toLowerCase())
);
const tvInfo = {
showType: showTypeMatch || "Unknown"
};
// Extract tick items like sub, dub, eps
["sub", "dub", "eps"].forEach((type) => {
const value = $el.find(`.tick-item.tick-${type}`).text().trim();
if (value) {
tvInfo[type] = value;
}
});
// Adult content check
const tickRateText = $el.find(".film-poster > .tick-rate").text().trim();
const adultContent = tickRateText.includes("18+");
return {
data_id,
id,
title,
japanese_title,
poster,
tvInfo,
adultContent,
};
})
.get()
);
}

View File

@@ -0,0 +1,51 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export default async function extractSchedule(date, tzOffset) {
try {
tzOffset = tzOffset ?? -330;
const resp = await axios.get(
`https://${v1_base_url}/ajax/schedule/list?tzOffset=${tzOffset}&date=${date}`
);
const $ = cheerio.load(resp.data.html);
const results = [];
$("li").each((i, element) => {
const id = $(element)
?.find("a")
.attr("href")
.split("?")[0]
.replace("/", "");
const data_id = id?.split("-").pop();
const title = $(element).find(".film-name").text().trim();
const japanese_title = $(element)
.find(".film-name")
.attr("data-jname")
?.trim();
const releaseDate = date;
const time = $(element).find(".time").text().trim();
const episode_no = $(element)
?.find(".btn-play")
.text()
.trim()
.split(" ")
.pop();
results.push({
id,
data_id,
title,
japanese_title,
releaseDate,
time,
episode_no,
});
});
return results;
} catch (error) {
console.log(error.message);
return [];
}
}

View File

@@ -0,0 +1,184 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
import { v1_base_url } from "../utils/base_v1.js";
import {
FILTER_LANGUAGE_MAP,
GENRE_MAP,
FILTER_TYPES,
FILTER_STATUS,
FILTER_RATED,
FILTER_SCORE,
FILTER_SEASON,
FILTER_SORT,
} from "../routes/filter.maping.js";
async function extractSearchResults(params = {}) {
try {
const normalizeParam = (param, mapping) => {
if (!param) return undefined;
if (typeof param === "string") {
const isAlreadyId = Object.values(mapping).includes(param);
if (isAlreadyId) {
return param;
}
const key = param.trim().toUpperCase();
return mapping.hasOwnProperty(key) ? mapping[key] : undefined;
}
return param;
};
const typeParam = normalizeParam(params.type, FILTER_TYPES);
const statusParam = normalizeParam(params.status, FILTER_STATUS);
const ratedParam = normalizeParam(params.rated, FILTER_RATED);
const scoreParam = normalizeParam(params.score, FILTER_SCORE);
const seasonParam = normalizeParam(params.season, FILTER_SEASON);
const sortParam = normalizeParam(params.sort, FILTER_SORT);
let languageParam = params.language;
if (languageParam != null) {
languageParam = String(languageParam).trim().toUpperCase();
languageParam = FILTER_LANGUAGE_MAP[languageParam] ?? (Object.values(FILTER_LANGUAGE_MAP).includes(languageParam) ? languageParam : undefined);
}
let genresParam = params.genres;
if (typeof genresParam === "string") {
genresParam = genresParam
.split(",")
.map((genre) => GENRE_MAP[genre.trim().toUpperCase()] || genre.trim())
.join(",");
}
const filteredParams = {
type: typeParam,
status: statusParam,
rated: ratedParam,
score: scoreParam,
season: seasonParam,
language: languageParam,
genres: genresParam,
sort: sortParam,
page: params.page || 1,
sy: params.sy || undefined,
sm: params.sm || undefined,
sd: params.sd || undefined,
ey: params.ey || undefined,
em: params.em || undefined,
ed: params.ed || undefined,
keyword: params.keyword || undefined,
};
Object.keys(filteredParams).forEach((key) => {
if (filteredParams[key] === undefined) {
delete filteredParams[key];
}
});
const queryParams = new URLSearchParams(filteredParams).toString();
const resp = await axios.get(`https://${v1_base_url}/search?${queryParams}`, {
headers: {
Accept:
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": DEFAULT_HEADERS,
},
});
const $ = cheerio.load(resp.data);
const elements = "#main-content .film_list-wrap .flw-item";
const totalPage =
Number(
$('.pre-pagination nav .pagination > .page-item a[title="Last"]')
?.attr("href")
?.split("=")
.pop() ??
$('.pre-pagination nav .pagination > .page-item a[title="Next"]')
?.attr("href")
?.split("=")
.pop() ??
$(".pre-pagination nav .pagination > .page-item.active a")
?.text()
?.trim()
) || 1;
const result = [];
$(elements).each((_, el) => {
const id =
$(el)
.find(".film-detail .film-name .dynamic-name")
?.attr("href")
?.slice(1)
.split("?ref=search")[0] || null;
result.push({
id: id,
data_id: $(el)
.find(".film-poster .film-poster-ahref").attr("data-id"),
title: $(el)
.find(".film-detail .film-name .dynamic-name")
?.text()
?.trim(),
japanese_title:
$(el)
.find(".film-detail .film-name .dynamic-name")
?.attr("data-jname")
?.trim() || null,
poster:
$(el)
.find(".film-poster .film-poster-img")
?.attr("data-src")
?.trim() || null,
duration:
$(el)
.find(".film-detail .fd-infor .fdi-item.fdi-duration")
?.text()
?.trim(),
tvInfo: {
showType:
$(el)
.find(".film-detail .fd-infor .fdi-item:nth-of-type(1)")
.text()
.trim() || "Unknown",
rating: $(el).find(".film-poster .tick-rate")?.text()?.trim() || null,
sub:
Number(
$(el)
.find(".film-poster .tick-sub")
?.text()
?.trim()
.split(" ")
.pop()
) || null,
dub:
Number(
$(el)
.find(".film-poster .tick-dub")
?.text()
?.trim()
.split(" ")
.pop()
) || null,
eps:
Number(
$(el)
.find(".film-poster .tick-eps")
?.text()
?.trim()
.split(" ")
.pop()
) || null,
},
});
});
return [parseInt(totalPage, 10), result.length > 0 ? result : []];
} catch (e) {
console.error(e);
return e;
}
}
export default extractSearchResults;

View File

@@ -0,0 +1,30 @@
import axios from "axios";
import * as cheerio from "cheerio";
import formatTitle from "../helper/formatTitle.helper.js";
import { v1_base_url } from "../utils/base_v1.js";
async function extractSeasons(id) {
try {
const resp = await axios.get(`https://${v1_base_url}/watch/${id}`);
const $ = cheerio.load(resp.data);
const seasons = $(".anis-watch>.other-season>.inner>.os-list>a")
.map((index, element) => {
const data_number = index;
const data_id = parseInt($(element).attr("href").split("-").pop());
const season = $(element).find(".title").text().trim();
const title = $(element).attr("title").trim();
const id = href.replace(/^\/+/, "");
const season_poster = $(element)
.find(".season-poster")
.attr("style")
.match(/url\((.*?)\)/)[1];
return { id, data_number, data_id, season, title, season_poster };
})
.get();
return seasons;
} catch (e) {
console.log(e);
}
}
export default extractSeasons;

View File

@@ -0,0 +1,103 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function extractSpotlights() {
try {
const resp = await axios.get(`https://${v1_base_url}/home`);
const $ = cheerio.load(resp.data);
const slideElements = $(
"div.deslide-wrap > div.container > div#slider > div.swiper-wrapper > div.swiper-slide"
);
const promises = slideElements
.map(async (ind, ele) => {
const poster = $(ele)
.find(
"div.deslide-item > div.deslide-cover > div.deslide-cover-img > img.film-poster-img"
)
.attr("data-src");
const title = $(ele)
.find(
"div.deslide-item > div.deslide-item-content > div.desi-head-title"
)
.text()
.trim();
const japanese_title = $(ele)
.find(
"div.deslide-item > div.deslide-item-content > div.desi-head-title"
)
.attr("data-jname")
.trim();
const description = $(ele)
.find(
"div.deslide-item > div.deslide-item-content > div.desi-description"
)
.text()
.trim();
const id = $(ele)
.find(
".deslide-item > .deslide-item-content > .desi-buttons > a:eq(0)"
)
.attr("href")
.split("/")
.pop();
const data_id = $(ele)
.find(
".deslide-item > .deslide-item-content > .desi-buttons > a:eq(0)"
)
.attr("href")
.split("/")
.pop()
.split("-")
.pop();
const tvInfoMapping = {
0: "showType",
1: "duration",
2: "releaseDate",
3: "quality",
4: "episodeInfo",
};
const tvInfo = {};
await Promise.all(
$(ele)
.find("div.sc-detail > div.scd-item")
.map(async (index, element) => {
const key = tvInfoMapping[index];
let value = $(element).text().trim().replace(/\n/g, "");
const tickContainer = $(element).find(".tick");
if (tickContainer.length > 0) {
value = {
sub: tickContainer.find(".tick-sub").text().trim(),
dub: tickContainer.find(".tick-dub").text().trim(),
};
}
tvInfo[key] = value;
})
);
return {
id,
data_id,
poster,
title,
japanese_title,
description,
tvInfo,
};
})
.get();
const serverData = await Promise.all(promises);
return JSON.parse(JSON.stringify(serverData, null, 2));
} catch (error) {
console.error("Error fetching data:", error.message);
return error;
}
}
export default extractSpotlights;

View File

@@ -0,0 +1,68 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
// import decryptMegacloud from "../parsers/decryptors/megacloud.decryptor.js";
// import AniplayExtractor from "../parsers/aniplay.parser.js";
import { decryptSources_v1 } from "../parsers/decryptors/decrypt_v1.decryptor.js";
export async function extractServers(id) {
try {
const resp = await axios.get(
`https://${v1_base_url}/ajax/v2/episode/servers?episodeId=${id}`
);
const $ = cheerio.load(resp.data.html);
const serverData = [];
$(".server-item").each((index, element) => {
const data_id = $(element).attr("data-id");
const server_id = $(element).attr("data-server-id");
const type = $(element).attr("data-type");
const serverName = $(element).find("a").text().trim();
serverData.push({
type,
data_id,
server_id,
serverName,
});
});
return serverData;
} catch (error) {
console.log(error);
return [];
}
}
async function extractStreamingInfo(id, name, type, fallback) {
try {
const servers = await extractServers(id.split("?ep=").pop());
let requestedServer = servers.filter(
(server) =>
server.serverName.toLowerCase() === name.toLowerCase() &&
server.type.toLowerCase() === type.toLowerCase()
);
if (requestedServer.length === 0) {
requestedServer = servers.filter(
(server) =>
server.serverName.toLowerCase() === name.toLowerCase() &&
server.type.toLowerCase() === "raw"
);
}
if (requestedServer.length === 0) {
throw new Error(
`No matching server found for name: ${name}, type: ${type}`
);
}
const streamingLink = await decryptSources_v1(
id,
requestedServer[0].data_id,
name,
type,
fallback
);
return { streamingLink, servers };
} catch (error) {
console.error("An error occurred:", error);
return { streamingLink: [], servers: [] };
}
}
export { extractStreamingInfo };

View File

@@ -0,0 +1,19 @@
import axios from "axios";
import { v1_base_url } from "../utils/base_v1.js";
import { provider } from "../utils/provider.js";
export async function extractSubtitle(id) {
const resp = await axios.get(
`https://${v1_base_url}/ajax/v2/episode/sources/?id=${id}`
);
const source = await axios.get(
`${provider}/embed-2/ajax/e-1/getSources?id=${resp.data.link
.split("/")
.pop()
.replace(/\?k=\d?/g, "")}`
);
const subtitles = source.data.tracks;
const intro = source.data.intro;
const outro = source.data.outro;
return { subtitles, intro, outro };
}

View File

@@ -0,0 +1,50 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function getSuggestions(keyword) {
try {
const resp = await axios.get(
`https://${v1_base_url}/ajax/search/suggest?keyword=${keyword}`
);
const $ = cheerio.load(resp.data.html);
const results = [];
$(".nav-item")
.not(".nav-bottom")
.each((i, element) => {
const id = $(element).attr("href").split("?")[0].replace("/", "");
const data_id = id.split("-").pop();
const poster = $(element).find(".film-poster-img").attr("data-src");
const title = $(element).find(".film-name").text().trim();
const japanese_title = $(element).find(".film-name").attr("data-jname").trim();
const releaseDate = $(element)
.find(".film-infor span")
.first()
.text()
.trim();
const filmInforHtml = $(element).find(".film-infor").html();
const showTypeMatch = /<i class="dot"><\/i>([^<]+)<i class="dot"><\/i>/;
const showType = showTypeMatch.exec(filmInforHtml)?.[1]?.trim() || "";
const duration = $(element)
.find(".film-infor span")
.last()
.text()
.trim();
results.push({
id,
data_id,
poster,
title,
japanese_title,
releaseDate,
showType,
duration,
});
});
return results;
} catch (error) {
console.log(error.message);
}
}
export default getSuggestions;

View File

@@ -0,0 +1,21 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function extractTopSearch() {
try {
const { data } = await axios.get(`https://${v1_base_url}`);
const $ = cheerio.load(data);
const results = [];
$(".xhashtag a.item").each((_, element) => {
const title = $(element).text().trim();
const link = $(element).attr("href");
results.push({ title, link });
});
return results;
} catch (error) {
console.error("Error fetching data:", error.message);
}
}
export default extractTopSearch;

View File

@@ -0,0 +1,51 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function extractTopTen() {
try {
const resp = await axios.get(`https://${v1_base_url}/home`);
const $ = cheerio.load(resp.data);
const labels = ["today", "week", "month"];
const result = {};
labels.forEach((label, idx) => {
const data = $(
`#main-sidebar .block_area-realtime .block_area-content ul:eq(${idx})>li`
)
.map((index, element) => {
const number = $(".film-number>span", element).text().trim();
const title = $(".film-detail>.film-name>a", element).text().trim();
const poster = $(".film-poster>img", element).attr("data-src");
const japanese_title = $(".film-detail>.film-name>a", element)
.attr("data-jname")
.trim();
const data_id = $(".film-poster", element).attr("data-id");
const id = $(".film-detail>.film-name>a", element)
.attr("href")
.split("/")
.pop();
const tvInfo = ["sub", "dub", "eps"].reduce((info, property) => {
const value = $(`.tick .tick-${property}`, element).text().trim();
if (value) {
info[property] = value;
}
return info;
}, {});
return { id, data_id, number, title, japanese_title, poster, tvInfo };
})
.get();
result[label] = data;
});
return result;
} catch (error) {
console.error("Error fetching data:", error);
throw error;
}
}
export default extractTopTen;

View File

@@ -0,0 +1,35 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
async function fetchAnimeDetails(element) {
const data_id = element.attr("data-id");
const number = element.find(".number > span").text();
const poster = element.find("img").attr("data-src");
const title = element.find(".film-title").text().trim();
const japanese_title = element.find(".film-title").attr("data-jname").trim();
const id = element.find("a").attr("href").split("/").pop();
return { id, data_id, number, poster, title, japanese_title };
}
async function extractTrending() {
try {
const resp = await axios.get(`https://${v1_base_url}/home`);
const $ = cheerio.load(resp.data);
const trendingElements = $("#anime-trending #trending-home .swiper-slide");
const elementPromises = trendingElements
.map((index, element) => {
return fetchAnimeDetails($(element));
})
.get();
const trendingData = await Promise.all(elementPromises);
return JSON.parse(JSON.stringify(trendingData));
} catch (error) {
console.error("Error fetching data:", error.message);
return error;
}
}
export default extractTrending;

View File

@@ -0,0 +1,78 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export default async function extractVoiceActor(id, page) {
try {
const resp = await axios.get(
`https://${v1_base_url}/ajax/character/list/${id
.split("-")
.pop()}?page=${page}`
);
const $ = cheerio.load(resp.data.html);
let totalPages = 1;
const paginationList = $(".pre-pagination nav ul");
if (paginationList.length) {
const lastPageLink = paginationList.find("li").last().find("a");
const pageNumber =
lastPageLink.attr("data-url")?.match(/page=(\d+)/)?.[1] ||
lastPageLink.text().trim();
totalPages = parseInt(pageNumber) || totalPages;
}
const charactersVoiceActors = $(".bac-list-wrap .bac-item")
.map((index, el) => {
const character = {
id:
$(el)
.find(".per-info.ltr .pi-avatar")
.attr("href")
?.split("/")[2] || "",
poster:
$(el).find(".per-info.ltr .pi-avatar img").attr("data-src") || "",
name: $(el).find(".per-info.ltr .pi-detail a").text(),
cast: $(el).find(".per-info.ltr .pi-detail .pi-cast").text(),
};
let voiceActors = [];
const rtlVoiceActors = $(el).find(".per-info.rtl");
const xxVoiceActors = $(el).find(
".per-info.per-info-xx .pix-list .pi-avatar"
);
if (rtlVoiceActors.length > 0) {
voiceActors = rtlVoiceActors
.map((_, actorEl) => ({
id: $(actorEl).find("a").attr("href")?.split("/").pop() || "",
poster: $(actorEl).find("img").attr("data-src") || "",
name:
$(actorEl).find(".pi-detail .pi-name a").text().trim() || "",
}))
.get();
} else if (xxVoiceActors.length > 0) {
voiceActors = xxVoiceActors
.map((_, actorEl) => ({
id: $(actorEl).attr("href")?.split("/").pop() || "",
poster: $(actorEl).find("img").attr("data-src") || "",
name: $(actorEl).attr("title") || "",
}))
.get();
}
if (voiceActors.length === 0) {
voiceActors = $(el)
.find(".per-info.per-info-xx .pix-list .pi-avatar")
.map((_, actorEl) => ({
id: $(actorEl).attr("href")?.split("/")[2] || "",
poster: $(actorEl).find("img").attr("data-src") || "",
name: $(actorEl).attr("title") || "",
}))
.get();
}
return { character, voiceActors };
})
.get();
return { totalPages, charactersVoiceActors };
} catch (error) {
console.error("Error in extractVoiceActor:", error);
throw new Error("Could not extract voice actors");
}
}

View File

@@ -0,0 +1,63 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export default async function extractWatchlist(userId, page = 1) {
try {
const url = `https://${v1_base_url}/community/user/${userId}/watch-list?page=${page}`;
const { data } = await axios.get(url);
const $ = cheerio.load(data);
const watchlist = [];
const totalPages =
Number(
$('.pre-pagination nav .pagination > .page-item a[title="Last"]')
?.attr("href")
?.split("=")
.pop() ??
$('.pre-pagination nav .pagination > .page-item a[title="Next"]')
?.attr("href")
?.split("=")
.pop() ??
$(".pre-pagination nav .pagination > .page-item.active a")
?.text()
?.trim()
) || 1;
$(".flw-item").each((index, element) => {
const title = $(".film-name a", element).text().trim();
const poster = $(".film-poster img", element).attr("data-src");
const duration = $(".fdi-duration", element).text().trim();
const type = $(".fdi-item", element).first().text().trim();
const id = $(".film-poster a", element).attr("data-id");
const subCount = $(".tick-item.tick-sub", element).text().trim();
const dubCount = $(".tick-item.tick-dub", element).text().trim();
const link = $(".film-name a", element).attr("href");
const animeId = link.split("/").pop();
watchlist.push({
id: animeId,
title,
poster,
duration,
type,
subCount,
dubCount,
link: `https://${v1_base_url}${link}`,
showType: type,
tvInfo: {
showType: type,
duration: duration,
sub: subCount,
dub: dubCount,
},
});
});
return { watchlist, totalPages };
} catch (error) {
console.error("Error fetching watchlist:", error.message);
throw error;
}
}

View File

@@ -0,0 +1,34 @@
import axios from "axios";
import dotenv from "dotenv";
dotenv.config();
const CACHE_SERVER_URL = process.env.CACHE_URL || null;
export const getCachedData = async (key) => {
try {
if (!CACHE_SERVER_URL) {
console.log(CACHE_SERVER_URL);
return;
}
const response = await axios.get(`${CACHE_SERVER_URL}/${key}`);
return response.data;
} catch (error) {
if (error.response && error.response.status === 404) {
return null;
}
throw error;
}
};
export const setCachedData = async (key, value) => {
try {
if (!CACHE_SERVER_URL) {
return;
}
await axios.post(CACHE_SERVER_URL, { key, value });
} catch (error) {
console.error("Error setting cache data:", error);
throw error;
}
};

View File

@@ -0,0 +1,24 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
const axiosInstance = axios.create({ headers: DEFAULT_HEADERS });
async function countPages(url) {
try {
const { data } = await axiosInstance.get(url);
const $ = cheerio.load(data);
const lastPageHref = $(
".tab-content .pagination .page-item:last-child a"
).attr("href");
const lastPageNumber = lastPageHref
? parseInt(lastPageHref.split("=").pop())
: 1;
return lastPageNumber;
} catch (error) {
console.error("Error counting pages:", error.message);
throw error;
}
}
export default countPages;

View File

@@ -0,0 +1,92 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
import { DEFAULT_HEADERS } from "../configs/header.config.js";
const axiosInstance = axios.create({ headers: DEFAULT_HEADERS });
async function extractPage(page, params) {
try {
const resp = await axiosInstance.get(`https://${v1_base_url}/${params}?page=${page}`);
const $ = cheerio.load(resp.data);
const totalPages =
Number(
$('.pre-pagination nav .pagination > .page-item a[title="Last"]')
?.attr("href")
?.split("=")
.pop() ??
$('.pre-pagination nav .pagination > .page-item a[title="Next"]')
?.attr("href")
?.split("=")
.pop() ??
$(".pre-pagination nav .pagination > .page-item.active a")
?.text()
?.trim()
) || 1;
const contentSelector = params.includes("az-list")
? ".tab-content"
: "#main-content";
const data = await Promise.all(
$(`${contentSelector} .film_list-wrap .flw-item`).map(
async (index, element) => {
const $fdiItems = $(".film-detail .fd-infor .fdi-item", element);
const showType = $fdiItems
.filter((_, item) => {
const text = $(item).text().trim().toLowerCase();
return ["tv", "ona", "movie", "ova", "special", "music"].some((type) =>
text.includes(type)
);
})
.first();
const poster = $(".film-poster>img", element).attr("data-src");
const title = $(".film-detail .film-name", element).text();
const japanese_title = $(".film-detail>.film-name>a", element).attr(
"data-jname"
);
const description = $(".film-detail .description", element)
.text()
.trim();
const data_id = $(".film-poster>a", element).attr("data-id");
const id = $(".film-poster>a", element).attr("href").split("/").pop();
const tvInfo = {
showType: showType ? showType.text().trim() : "Unknown",
duration: $(".film-detail .fd-infor .fdi-duration", element)
.text()
.trim(),
};
let adultContent = false;
const tickRateText = $(".film-poster>.tick-rate", element)
.text()
.trim();
if (tickRateText.includes("18+")) {
adultContent = true;
}
["sub", "dub", "eps"].forEach((property) => {
const value = $(`.tick .tick-${property}`, element).text().trim();
if (value) {
tvInfo[property] = value;
}
});
return {
id,
data_id,
poster,
title,
japanese_title,
description,
tvInfo,
adultContent,
};
}
)
);
return [data, parseInt(totalPages, 10)];
} catch (error) {
console.error(`Error extracting data from page ${page}:`, error.message);
throw error;
}
}
export default extractPage;

View File

@@ -0,0 +1,8 @@
import axios from "axios";
async function fetchScript(url) {
const response = await axios.get(url);
return response.data;
}
export default fetchScript;

View File

@@ -0,0 +1,76 @@
import axios from "axios";
// import { getCachedData, setCachedData } from "./cache.helper";
async function getEnglishTitleFromAniList(userInput) {
// const cacheKey = `translation:${userInput}`;
try {
// Check cache
// const cachedValue = await getCachedData(cacheKey);
// if (cachedValue) {
// console.log(`Cache Hit ${userInput} -> ${cachedValue}`)
// }
const query = `
query ($search: String) {
Media (search: $search, type: ANIME) {
title {
romaji
english
}
}
}
`;
const response = await axios.post('https://graphql.anilist.co', {
query,
variables: { search: userInput }
}, {
headers: { 'Content-Type': 'application/json' },
timeout: 3000 // 3 seconds
});
const titles = response.data?.data?.Media?.title;
if (!titles) {
console.log(`AniList no match found for: ${userInput}`);
return userInput;
}
const result = titles.english || titles.romaji || userInput;
// await setCachedData(cacheKey, result);
return result;
} catch (error) {
console.error("AniList API Error:", error.response?.data || error.message);
throw error;
}
}
async function convertForeignLanguage(userInput) {
try {
if (!userInput) return '';
// If it's only Latin characters, return as-is
if (/^[a-zA-Z\s]+$/.test(userInput)) {
return userInput;
}
// Detect if it is Japanese, Chinese or Korean
const isForeign = /[\u3040-\u30ff\u3000-\u303f\u4e00-\u9faf\uac00-\ud7af]/.test(userInput);
if (isForeign) {
const translated = await getEnglishTitleFromAniList(userInput);
return translated;
}
return userInput;
} catch (error) {
console.error(`Error converting foreign input ${userInput}:`, error.message);
return userInput;
}
}
export default convertForeignLanguage;

View File

@@ -0,0 +1,9 @@
function formatTitle(title, data_id) {
let formattedTitle = title.replace(/[^\w\s]/g, "");
formattedTitle = formattedTitle.toLowerCase();
formattedTitle = formattedTitle.replace(/\s+/g, "-");
formattedTitle = `${formattedTitle}-${data_id}`;
return formattedTitle;
}
export default formatTitle;

View File

@@ -0,0 +1,34 @@
class ErrorLoadingException extends Error {
constructor(message) {
super(message);
this.name = "ErrorLoadingException";
}
}
function matchingKey(value, script) {
const regex = new RegExp(`,${value}=((?:0x)?([0-9a-fA-F]+))`);
const match = script.match(regex);
if (match) {
return match[2];
} else {
throw new ErrorLoadingException("Failed to match the key");
}
}
function getKeys(script) {
const regex =
/case\s*0x[0-9a-f]+:(?![^;]*=partKey)\s*\w+\s*=\s*(\w+)\s*,\s*\w+\s*=\s*(\w+);/g;
const matches = script.matchAll(regex);
return Array.from(matches, (match) => {
const matchKey1 = matchingKey(match[1], script);
const matchKey2 = matchingKey(match[2], script);
try {
return [parseInt(matchKey1, 16), parseInt(matchKey2, 16)];
} catch (e) {
return [];
}
}).filter((pair) => pair.length > 0);
}
export default getKeys;

View File

@@ -0,0 +1,71 @@
import axios from 'axios';
import * as cheerio from 'cheerio';
import { v1_base_url } from '../utils/base_v1.js';
export default async function extractToken(url) {
try {
const { data: html } = await axios.get(url, {
headers: {
Referer: `https://${v1_base_url}/`
}
});
const $ = cheerio.load(html);
const results = {};
// 1. Meta tag
const meta = $('meta[name="_gg_fb"]').attr('content');
if (meta) results.meta = meta;
// 2. Data attribute
const dpi = $('[data-dpi]').attr('data-dpi');
if (dpi) results.dataDpi = dpi;
// 3. Nonce from empty script
const nonceScript = $('script[nonce]').filter((i, el) => {
return $(el).text().includes('empty nonce script');
}).attr('nonce');
if (nonceScript) results.nonce = nonceScript;
// 4. JS string assignment: window.<key> = "value";
const stringAssignRegex = /window\.(\w+)\s*=\s*["']([\w-]+)["']/g;
const stringMatches = [...html.matchAll(stringAssignRegex)];
for (const [_, key, value] of stringMatches) {
results[`window.${key}`] = value;
}
// 5. JS object assignment: window.<key> = { ... };
const objectAssignRegex = /window\.(\w+)\s*=\s*(\{[\s\S]*?\});/g;
const matches = [...html.matchAll(objectAssignRegex)];
for (const [_, varName, rawObj] of matches) {
try {
const parsedObj = eval('(' + rawObj + ')');
if (parsedObj && typeof parsedObj === 'object') {
const stringValues = Object.values(parsedObj).filter(val => typeof val === 'string');
const concatenated = stringValues.join('');
if (concatenated.length >= 20) {
results[`window.${varName}`] = concatenated;
}
}
} catch (e) {
// Skip invalid object
}
}
// 6. HTML comment: <!-- _is_th:... -->
$('*').contents().each(function () {
if (this.type === 'comment') {
const match = this.data.trim().match(/^_is_th:([\w-]+)$/);
if (match) {
results.commentToken = match[1].trim();
}
}
});
const token = Object.values(results)[0];
return token || null;
} catch (err) {
console.error('Error:', err.message);
return null;
}
}

View File

@@ -0,0 +1,94 @@
import axios from "axios";
import { v3_base_url } from "../utils/base_v3.js";
const DEFAULT_BASE_URL = `https://${v3_base_url}`;
class AniplayExtractor {
constructor(baseUrl = DEFAULT_BASE_URL) {
this.baseUrl = baseUrl;
this.keys = null;
this.keysTs = 0;
}
isCacheValid() {
const now = Math.floor(Date.now() / 1000);
return this.keys && now - this.keysTs < 3600;
}
async fetchHtml(url) {
const { data } = await axios.get(url);
return data;
}
async fetchStaticJsUrl() {
const html = await this.fetchHtml(`${this.baseUrl}/anime/watch/1`);
const prefix = "/_next/static/chunks/app/(user)/(media)/";
const start = html.indexOf(prefix);
if (start === -1) throw new Error("Static chunk path not found in HTML.");
const slugStart = start + prefix.length;
const slugEnd = html.indexOf('"', slugStart);
const jsSlug = html.slice(slugStart, slugEnd);
return `${this.baseUrl}${prefix}${jsSlug}`;
}
async extractKeys() {
if (this.isCacheValid()) return this.keys;
const scriptUrl = await this.fetchStaticJsUrl();
const script = await this.fetchHtml(scriptUrl);
const regex =
/\(0,\w+\.createServerReference\)\("([a-f0-9]+)",\w+\.callServer,void 0,\w+\.findSourceMapURL,"(getSources|getEpisodes)"\)/g;
const matches = script.matchAll(regex);
const keysMap = { baseUrl: this.baseUrl };
for (const match of matches) {
const [, hash, fn] = match;
keysMap[fn] = hash;
}
if (!keysMap["getSources"] || !keysMap["getEpisodes"]) {
throw new Error("Could not extract all required keys.");
}
this.keys = keysMap;
this.keysTs = Math.floor(Date.now() / 1000);
return keysMap;
}
async getNextAction() {
const keys = await this.extractKeys();
return {
watch: keys["getSources"],
info: keys["getEpisodes"],
};
}
async fetchEpisode(animeId, ep, host = "hika", type = "sub") {
const nextAction = await this.getNextAction();
const url = `${this.baseUrl}/anime/watch/${animeId}?host=${host}&ep=${ep}&type=${type}`;
const payload = [
String(animeId),
host,
`${animeId}/${ep}`,
String(ep),
type,
];
try {
const res = await axios.post(url, payload, {
headers: {
"Next-Action": nextAction.watch,
},
});
const dataStr = res.data.split("1:")[1];
return JSON.parse(dataStr);
} catch (err) {
throw new Error(`Request failed: ${err.message}`);
}
}
}
export default AniplayExtractor;

View File

@@ -0,0 +1,844 @@
export const data = new Uint8ClampedArray([
246, 246, 246, 255, 226, 234, 236, 255, 113, 170, 187, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 0,
255, 255, 1, 61, 139, 163, 192, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 130, 180, 196, 254, 242, 243, 244, 254, 246, 246,
246, 254, 243, 244, 245, 254, 105, 165, 184, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 132, 181,
196, 254, 243, 245, 245, 254, 188, 212, 220, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139,
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 139,
164, 254, 63, 142, 165, 254, 217, 230, 233, 254, 132, 181, 196, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 177, 206, 216, 254, 119, 174,
190, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 164, 198,
210, 255, 119, 174, 190, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140,
164, 255, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140,
164, 254, 60, 140, 164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140,
164, 254, 60, 140, 165, 254, 60, 140, 164, 254, 60, 140, 164, 254, 60, 140,
164, 255, 163, 198, 210, 254, 119, 174, 190, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 255, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 255, 60, 139, 164, 254, 163, 198, 210, 254, 119, 174, 190, 254, 60, 139,
164, 254, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139,
164, 255, 60, 140, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 139, 164, 254, 60, 140,
164, 255, 60, 139, 164, 254, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174,
190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 131, 180,
195, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233,
239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255,
233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240,
255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239,
240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233,
239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255,
233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240,
255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239,
240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233, 239, 240, 255, 233,
239, 240, 255, 233, 239, 240, 255, 218, 230, 234, 255, 143, 187, 200, 255, 66,
143, 167, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60,
139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 223, 233, 236,
255, 136, 183, 197, 255, 69, 145, 168, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164,
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 184, 199, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 242, 243, 244, 255, 219, 231, 235, 255, 217, 229, 233, 255,
217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233,
255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229,
233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217, 229, 233, 255, 217,
229, 233, 255, 217, 229, 233, 255, 97, 160, 180, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190,
255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 139, 185, 199,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119,
174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164,
183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198,
210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164,
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164,
255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 139, 164,
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 184, 199, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119,
174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 241, 243, 244, 255, 227, 235, 238, 255, 243, 245, 245, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
230, 237, 239, 255, 239, 242, 243, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 233, 239, 241, 255, 235, 239, 241,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 239, 242, 243, 255, 230, 236, 239, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164,
183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198,
210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139,
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 216, 230, 233, 255, 94, 160, 179, 255, 66, 143, 166, 255, 99, 161, 182,
255, 221, 232, 236, 255, 246, 246, 246, 255, 245, 245, 245, 255, 127, 178,
194, 255, 68, 144, 168, 255, 79, 150, 173, 255, 187, 213, 220, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 154, 193, 205, 255, 73, 147, 170, 255, 72, 146, 169,
255, 156, 194, 206, 255, 246, 246, 246, 255, 246, 246, 246, 255, 208, 223,
229, 255, 85, 154, 176, 255, 65, 143, 166, 255, 112, 169, 187, 255, 236, 240,
242, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210,
255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164,
255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
137, 184, 197, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60, 140, 164, 255,
88, 156, 177, 255, 210, 226, 230, 255, 133, 181, 196, 255, 60, 140, 164, 255,
60, 140, 165, 255, 60, 140, 164, 255, 85, 154, 175, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 239,
242, 243, 255, 64, 143, 166, 255, 60, 140, 165, 255, 60, 140, 164, 255, 60,
140, 164, 255, 146, 189, 202, 255, 203, 221, 227, 255, 70, 145, 168, 255, 60,
140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 165, 200, 210, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164,
255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 137, 183, 198, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 67,
144, 167, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 86, 154, 175, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 237, 241, 242, 255, 64, 142, 166,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164,
255, 64, 141, 166, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 164, 200, 211, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 139, 184, 199, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 218, 230, 234, 255, 83, 153, 174, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 67, 144, 167, 255, 174, 206, 215,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 153, 193, 205, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 101, 163, 182, 255,
226, 235, 238, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102,
164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163,
198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 213, 227, 231, 255, 85, 154, 175, 255, 60, 140,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
164, 255, 64, 142, 165, 255, 178, 208, 216, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 242, 243, 244, 255, 148, 189, 203, 255, 60, 139, 164,
255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 140, 164, 255, 97, 160, 180, 255, 231, 238, 240, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119,
173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 244, 245, 245, 255, 112, 169, 187, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 67, 143,
167, 255, 229, 236, 239, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 200, 220, 226, 255, 61, 141, 165, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 140, 186, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 235, 240, 242, 255,
133, 181, 196, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
86, 155, 176, 255, 221, 232, 236, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 203,
221, 227, 255, 70, 145, 168, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 150, 191, 204, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140,
164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 165, 199, 210, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255,
115, 171, 188, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 245, 245, 245, 255, 83, 153, 174, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 61,
141, 165, 255, 192, 215, 222, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140,
164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140,
165, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 131, 181, 196, 255, 60, 140, 165, 255, 60, 140, 164, 255,
60, 140, 164, 255, 67, 143, 168, 255, 140, 185, 200, 255, 83, 153, 174, 255,
60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 79, 151, 173, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 231, 238, 240, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60,
140, 164, 255, 60, 140, 164, 255, 89, 156, 177, 255, 124, 176, 192, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 158,
195, 207, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164,
183, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198,
210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 181, 208, 217, 255, 68, 144, 167, 255, 60, 139, 164, 255, 64, 141, 166,
255, 174, 205, 214, 255, 245, 246, 246, 255, 209, 224, 229, 255, 81, 151, 173,
255, 60, 139, 164, 255, 60, 139, 164, 255, 129, 179, 194, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
245, 246, 246, 255, 104, 165, 184, 255, 60, 139, 164, 255, 60, 139, 164, 255,
96, 160, 180, 255, 231, 237, 239, 255, 242, 244, 244, 255, 140, 185, 200, 255,
60, 139, 164, 255, 60, 139, 164, 255, 68, 144, 168, 255, 205, 222, 228, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 163, 183, 255, 60, 139,
164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174,
190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185,
199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 192, 215, 222, 255, 155, 194, 206, 255, 194, 216, 223, 255, 245, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 220, 231, 235, 255, 160,
197, 208, 255, 177, 207, 215, 255, 238, 242, 242, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 227, 236, 237, 255, 169, 202, 212, 255, 169, 202, 212, 255,
226, 234, 237, 255, 246, 246, 246, 255, 246, 246, 246, 255, 242, 244, 244,
255, 183, 210, 219, 255, 155, 194, 206, 255, 205, 222, 228, 255, 245, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210,
255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164,
255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255,
163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 139, 164, 255, 139, 184, 199, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255,
60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140, 164, 255,
60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 215, 229, 233, 255, 222, 233, 236,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 223, 233, 236, 255, 213, 227, 232, 255, 245, 245, 245, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139,
164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 140,
164, 255, 60, 140, 165, 255, 60, 140, 164, 255, 139, 185, 199, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 242, 244, 244, 255, 111, 169, 186, 255, 60, 140, 165, 255, 64, 142,
166, 255, 132, 181, 196, 255, 216, 229, 233, 255, 246, 246, 246, 255, 246,
246, 246, 255, 222, 233, 236, 255, 164, 199, 210, 255, 97, 161, 180, 255, 73,
147, 170, 255, 106, 166, 184, 255, 178, 207, 216, 255, 235, 239, 242, 255,
246, 246, 246, 255, 241, 243, 244, 255, 201, 221, 226, 255, 117, 172, 189,
255, 61, 141, 165, 255, 61, 140, 164, 255, 138, 185, 198, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140, 164, 255, 60, 140, 164,
255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173, 190, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 196, 217, 224, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 74, 147, 171, 255, 132, 181, 195, 255, 150, 191, 204,
255, 77, 149, 172, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 95, 160, 180, 255, 158, 196, 207,
255, 120, 173, 191, 255, 61, 140, 165, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 63, 141, 165, 255, 230, 237, 239, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 102, 163, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 163, 198, 210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 202, 221, 226,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 67, 144, 167, 255, 236, 241, 242, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183,
255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210,
255, 119, 174, 190, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140, 164,
255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 136, 183,
198, 255, 64, 142, 166, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140,
165, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140,
165, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140,
164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 60, 140,
164, 255, 60, 140, 164, 255, 60, 140, 165, 255, 71, 145, 168, 255, 153, 192,
205, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 140,
164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 173,
190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 185,
199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 206,
224, 229, 255, 118, 172, 190, 255, 64, 141, 165, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 95,
160, 180, 255, 143, 187, 200, 255, 87, 155, 176, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 63,
141, 165, 255, 134, 181, 196, 255, 225, 235, 237, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 102, 163, 183, 255, 60, 139, 164, 255,
60, 139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255,
60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139, 184, 199, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 212, 227, 231, 255, 129, 179, 194, 255, 89, 157, 177,
255, 85, 154, 175, 255, 111, 169, 187, 255, 183, 210, 219, 255, 246, 246, 246,
255, 246, 246, 246, 255, 243, 245, 245, 255, 172, 203, 213, 255, 106, 165,
184, 255, 83, 153, 174, 255, 93, 159, 179, 255, 142, 187, 201, 255, 221, 231,
235, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 163, 198, 210, 255, 119, 174, 190, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 139, 185, 199, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 244, 245, 246,
255, 244, 245, 245, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 243, 244, 245, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60, 139, 164,
255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119, 174, 190,
255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 139, 185, 199,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 164, 183, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 163, 198, 210, 255, 119,
174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 139,
185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 102, 163,
183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 163, 198,
210, 255, 119, 174, 190, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139,
164, 255, 139, 185, 199, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246,
246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246,
246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255,
246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246, 255, 246, 246, 246,
255, 102, 164, 183, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164,
255, 163, 198, 210, 255, 129, 180, 195, 255, 60, 140, 164, 255, 60, 140, 165,
255, 60, 140, 164, 255, 93, 158, 178, 255, 158, 196, 207, 255, 159, 197, 207,
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255,
159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207,
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255,
159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207,
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159,
197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255,
159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207,
255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197,
207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 159, 197, 207, 255, 156,
194, 207, 255, 69, 144, 168, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
140, 164, 255, 169, 202, 212, 255, 176, 206, 215, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 203, 221, 227, 255, 234, 239, 240, 255, 84,
153, 175, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 107, 167, 185, 255, 241, 243, 244, 255, 246,
246, 246, 255, 203, 221, 227, 255, 85, 154, 176, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60,
140, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60,
139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
139, 164, 255, 60, 140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 140, 164, 255, 60, 139, 164, 255, 60, 139, 164, 255, 60,
140, 164, 255, 60, 139, 164, 255, 92, 157, 179, 255, 222, 232, 236, 255, 246,
246, 246, 255,
]);

View File

@@ -0,0 +1,22 @@
// this is kept as backup in case megacloud's architecture rollback to it's previous architecture
// import decryptMegacloud from "./megacloud.decryptor.js";
// export async function decryptAllServers(data) {
// const promises = data.map(async (server) => {
// try {
// if (
// server.type === "sub" ||
// server.type === "dub" ||
// server.type === "raw"
// ) {
// return await decryptMegacloud(server.id, server.name, server.type);
// }
// } catch (error) {
// console.error(`Error decrypting server ${server.id}:`, error);
// return null;
// }
// });
// return Promise.all(promises);
// }

View File

@@ -0,0 +1,152 @@
import axios from "axios";
import CryptoJS from "crypto-js";
import * as cheerio from "cheerio";
import { v1_base_url } from "../../utils/base_v1.js";
import { v4_base_url } from "../../utils/base_v4.js";
import { fallback_1, fallback_2 } from "../../utils/fallback.js";
function fetch_key(data) {
let key = null;
const xyMatch = data.match(/window\._xy_ws\s*=\s*["']([^"']+)["']/);
if (xyMatch) {
key = xyMatch[1];
}
if (!key) {
const lkMatch = data.match(/window\._lk_db\s*=\s*\{([^}]+)\}/);
if (lkMatch) {
key = [...lkMatch[1].matchAll(/:\s*["']([^"']+)["']/g)]
.map((v) => v[1])
.join("");
}
}
if (!key) {
const nonceMatch = data.match(/nonce\s*=\s*["']([^"']+)["']/);
if (nonceMatch) {
key = nonceMatch[1];
}
}
if (!key) {
const dpiMatch = data.match(/data-dpi\s*=\s*["']([^"']+)["']/);
if (dpiMatch) {
key = dpiMatch[1];
}
}
if (!key) {
const metaMatch = data.match(
/<meta[^>]*name\s*=\s*["']_gg_fb["'][^>]*content\s*=\s*["']([^"']+)["']/i,
);
if (metaMatch) {
key = metaMatch[1];
}
}
if (!key) {
const isThMatch = data.match(/_is_th\s*:\s*([A-Za-z0-9]+)/);
if (isThMatch) {
key = isThMatch[1];
}
}
return key;
}
export async function decryptSources_v1(epID, id, name, type, fallback) {
try {
let decryptedSources = null;
let iframeURL = null;
if (fallback) {
const fallback_server = ["hd-1", "hd-3"].includes(name.toLowerCase())
? fallback_1
: fallback_2;
iframeURL = `https://${fallback_server}/stream/s-2/${epID}/${type}`;
const { data } = await axios.get(
`https://${fallback_server}/stream/s-2/${epID}/${type}`,
{
headers: {
Referer: `https://${fallback_server}/`,
},
},
);
const $ = cheerio.load(data);
const dataId = $("#megaplay-player").attr("data-id");
const { data: decryptedData } = await axios.get(
`https://${fallback_server}/stream/getSources?id=${dataId}`,
{
headers: {
"X-Requested-With": "XMLHttpRequest",
},
},
);
decryptedSources = decryptedData;
} else {
const { data: sourcesData } = await axios.get(
`https://${v1_base_url}/ajax/v2/episode/sources?id=${id}`,
);
const ajaxLink = sourcesData?.link;
if (!ajaxLink) throw new Error("Missing link in sourcesData");
console.log(ajaxLink);
const sourceIdMatch = /\/([^/?]+)\?/.exec(ajaxLink);
const sourceId = sourceIdMatch?.[1];
if (!sourceId) throw new Error("Unable to extract sourceId from link");
const new_url = `https://megacloud.blog/embed-2/v3/e-1/${sourceId}?k=1`;
const { data: stream_data } = await axios.post(
"https://megacloud.zenime.site/get-sources",
{
embedUrl: new_url,
},
{
headers: {
"Content-Type": "application/json",
},
},
);
decryptedSources = stream_data;
// const baseUrlMatch = ajaxLink.match(
// /^(https?:\/\/[^\/]+(?:\/[^\/]+){3})/,
// );
// if (!baseUrlMatch) throw new Error("Could not extract base URL");
// const baseUrl = baseUrlMatch[1];
// iframeURL = `${baseUrl}/${sourceId}?k=1&autoPlay=0&oa=0&asi=1`;
// const { data: rawSourceData } = await axios.get(
// `${baseUrl}/getSources?id=${sourceId}`,
// );
// decryptedSources = rawSourceData;
}
return {
id,
type,
link: {
file: fallback
? (decryptedSources?.sources?.file ?? "")
: (decryptedSources?.sources?.[0].file ?? ""),
type: "hls",
},
tracks: decryptedSources.tracks ?? [],
intro: decryptedSources.intro ?? null,
outro: decryptedSources.outro ?? null,
iframe: iframeURL,
server: name,
};
} catch (error) {
console.error(
`Error during decryptSources_v1(${id}, epID=${epID}, server=${name}):`,
error.message,
);
return null;
}
}

View File

@@ -0,0 +1,68 @@
// this is kept as backup in case megacloud's architecture rollback to it's previous architecture
// import axios from "axios";
// import CryptoJS from "crypto-js";
// import { v2_base_url } from "../../utils/base_v2.js";
// import fetchScript from "../../helper/fetchScript.helper.js";
// import getKeys from "../../helper/getKey.helper.js";
// import { PLAYER_SCRIPT_URL } from "../../configs/player_v2.config.js";
// async function decryptSources_v2(id, name, type) {
// try {
// const [{ data: sourcesData }, decryptKey_v2] = await Promise.all([
// axios.get(`https://${v2_base_url}/ajax/episode/sources?id=${id}`),
// getKeys(await fetchScript(PLAYER_SCRIPT_URL)),
// ]);
// const ajaxResp = sourcesData.link;
// const [hostname] = /^(https?:\/\/(?:www\.)?[^\/\?]+)/.exec(ajaxResp) || [];
// const [_, sourceId] = /\/([^\/\?]+)\?/.exec(ajaxResp) || [];
// const { data: source } = await axios.get(
// `${hostname}/ajax/embed-6-v2/getSources?id=${sourceId}`
// );
// if (source.encrypted === true) {
// const sourcesArray = source.sources.split("");
// let extractedKey = "";
// let currentIndex = 0;
// for (const index of decryptKey_v2) {
// const start = index[0] + currentIndex;
// const end = start + index[1];
// for (let i = start; i < end; i++) {
// extractedKey += sourcesArray[i];
// sourcesArray[i] = "";
// }
// currentIndex += index[1];
// }
// const decrypted = CryptoJS.AES.decrypt(
// sourcesArray.join(""),
// extractedKey
// ).toString(CryptoJS.enc.Utf8);
// const decryptedSources = JSON.parse(decrypted);
// source.sources = null;
// source.sources = {
// file: decryptedSources[0].file,
// type: "hls",
// };
// }
// if (source.hasOwnProperty("server")) {
// delete source.server;
// }
// return {
// id: id,
// type: type,
// link: source.sources,
// tracks: source.tracks,
// intro: source.intro,
// outro: source.outro,
// server: name,
// };
// } catch (error) {
// console.error("Error during decryption:", error);
// }
// }
// export { decryptSources_v2 };

View File

@@ -0,0 +1,811 @@
//inspired from https://github.com/drblgn/rabbit_wasm
import util from "util";
import pixels from "image-pixels";
import cryptoJs from "crypto-js";
import axios from "axios";
const user_agent =
"Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0";
import { webcrypto } from "crypto";
const crypto = webcrypto;
import { dataURL } from "../../configs/dataUrl.js";
// import { v1_base_url } from "../../utils/base_v1.js";
import { v4_base_url } from "../../utils/base_v4.js";
let wasm;
let arr = new Array(128).fill(void 0);
const dateNow = Date.now();
let content;
let referrer = "";
function isDetached(buffer) {
if (buffer.byteLength === 0) {
const formatted = util.format(buffer);
return formatted.includes("detached");
}
return false;
}
const meta = {
content: content,
};
const image_data = {
height: 50,
width: 65,
data: new Uint8ClampedArray(),
};
const canvas = {
baseUrl: "",
width: 0,
height: 0,
style: {
style: {
display: "inline",
},
},
context2d: {},
};
const fake_window = {
localStorage: {
setItem: function (item, value) {
fake_window.localStorage[item] = value;
},
},
navigator: {
webdriver: false,
userAgent: user_agent,
},
length: 0,
document: {
cookie: "",
},
origin: "",
location: {
href: "",
origin: "",
},
performance: {
timeOrigin: dateNow,
},
xrax: "",
c: false,
G: "",
z: function (a) {
return [
(4278190080 & a) >> 24,
(16711680 & a) >> 16,
(65280 & a) >> 8,
255 & a,
];
},
crypto: crypto,
msCrypto: crypto,
browser_version: 1676800512,
};
const nodeList = {
image: {
src: "",
height: 50,
width: 65,
complete: true,
},
context2d: {},
length: 1,
};
function get(index) {
return arr[index];
}
arr.push(void 0, null, true, false);
let size = 0;
let memoryBuff;
function getMemBuff() {
return (memoryBuff =
null !== memoryBuff && 0 !== memoryBuff.byteLength
? memoryBuff
: new Uint8Array(wasm.memory.buffer));
}
const encoder = new TextEncoder();
const encode = function (text, array) {
return encoder.encodeInto(text, array);
};
function parse(text, func, func2) {
if (void 0 === func2) {
var encoded = encoder.encode(text);
const parsedIndex = func(encoded.length, 1) >>> 0;
return (
getMemBuff()
.subarray(parsedIndex, parsedIndex + encoded.length)
.set(encoded),
(size = encoded.length),
parsedIndex
);
}
let len = text.length;
let parsedLen = func(len, 1) >>> 0;
var new_arr = getMemBuff();
let i = 0;
for (; i < len; i++) {
var char = text.charCodeAt(i);
if (127 < char) {
break;
}
new_arr[parsedLen + i] = char;
}
return (
i !== len &&
(0 !== i && (text = text.slice(i)),
(parsedLen = func2(parsedLen, len, (len = i + 3 * text.length), 1) >>> 0),
(encoded = getMemBuff().subarray(parsedLen + i, parsedLen + len)),
(i += encode(text, encoded).written),
(parsedLen = func2(parsedLen, len, i, 1) >>> 0)),
(size = i),
parsedLen
);
}
let dataView;
function isNull(test) {
return null == test;
}
function getDataView() {
return (dataView =
dataView === null ||
isDetached(dataView.buffer) ||
dataView.buffer !== wasm.memory.buffer
? new DataView(wasm.memory.buffer)
: dataView);
}
let pointer = arr.length;
function shift(QP) {
QP < 132 || ((arr[QP] = pointer), (pointer = QP));
}
function shiftGet(QP) {
var Qn = get(QP);
return shift(QP), Qn;
}
const decoder = new TextDecoder("utf-8", {
fatal: true,
ignoreBOM: true,
});
function decodeSub(index, offset) {
return (
(index >>>= 0), decoder.decode(getMemBuff().subarray(index, index + offset))
);
}
function addToStack(item) {
pointer === arr.length && arr.push(arr.length + 1);
var Qn = pointer;
return (pointer = arr[Qn]), (arr[Qn] = item), Qn;
}
function args(QP, Qn, QT, func) {
const Qx = {
a: QP,
b: Qn,
cnt: 1,
dtor: QT,
};
return (
(QP = (...Qw) => {
Qx.cnt++;
try {
return func(Qx.a, Qx.b, ...Qw);
} finally {
0 == --Qx.cnt &&
(wasm.__wbindgen_export_2.get(Qx.dtor)(Qx.a, Qx.b), (Qx.a = 0));
}
}),
((QP.original = Qx), QP)
);
}
function export3(QP, Qn) {
return shiftGet(wasm.__wbindgen_export_3(QP, Qn));
}
function export4(Qy, QO, QX) {
wasm.__wbindgen_export_4(Qy, QO, addToStack(QX));
}
function export5(QP, Qn) {
wasm.__wbindgen_export_5(QP, Qn);
}
function applyToWindow(func, args) {
try {
return func.apply(fake_window, args);
} catch (error) {
wasm.__wbindgen_export_6(addToStack(error));
}
}
function Qj(QP, Qn) {
return (
(Qn = Qn(+QP.length, 1) >>> 0),
(getMemBuff().set(QP, Qn), (size = QP.length), Qn)
);
}
async function QN(QP, Qn) {
let QT, Qt;
return "function" == typeof Response && QP instanceof Response
? ((QT = await QP.arrayBuffer()),
(Qt = await WebAssembly.instantiate(QT, Qn)),
Object.assign(Qt, { bytes: QT }))
: (Qt = await WebAssembly.instantiate(QP, Qn)) instanceof
WebAssembly.Instance
? {
instance: Qt,
module: QP,
}
: Qt;
}
function initWasm() {
const wasmObj = {
wbg: {
__wbindgen_is_function: function (index) {
return typeof get(index) == "function";
},
__wbindgen_is_string: function (index) {
return typeof get(index) == "string";
},
__wbindgen_is_object: function (index) {
let object = get(index);
return typeof object == "object" && object !== null;
},
__wbindgen_number_get: function (offset, index) {
let number = get(index);
getDataView().setFloat64(offset + 8, isNull(number) ? 0 : number, true);
getDataView().setInt32(offset, isNull(number) ? 0 : 1, true);
},
__wbindgen_string_get: function (offset, index) {
let str = get(index);
let val = parse(
str,
wasm.__wbindgen_export_0,
wasm.__wbindgen_export_1
);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, val, true);
},
__wbindgen_object_drop_ref: function (index) {
shiftGet(index);
},
__wbindgen_cb_drop: function (index) {
let org = shiftGet(index).original;
return 1 == org.cnt-- && !(org.a = 0);
},
__wbindgen_string_new: function (index, offset) {
return addToStack(decodeSub(index, offset));
},
__wbindgen_is_null: function (index) {
return null === get(index);
},
__wbindgen_is_undefined: function (index) {
return void 0 === get(index);
},
__wbindgen_boolean_get: function (index) {
let bool = get(index);
return "boolean" == typeof bool ? (bool ? 1 : 0) : 2;
},
__wbg_instanceof_CanvasRenderingContext2d_4ec30ddd3f29f8f9: function () {
return true;
},
__wbg_subarray_adc418253d76e2f1: function (index, num1, num2) {
return addToStack(get(index).subarray(num1 >>> 0, num2 >>> 0));
},
__wbg_randomFillSync_5c9c955aa56b6049: function () {},
__wbg_getRandomValues_3aa56aa6edec874c: function () {
return applyToWindow(function (index1, index2) {
get(index1).getRandomValues(get(index2));
}, arguments);
},
__wbg_msCrypto_eb05e62b530a1508: function (index) {
return addToStack(get(index).msCrypto);
},
__wbg_toString_6eb7c1f755c00453: function (index) {
let fakestr = "[object Storage]";
return addToStack(fakestr);
},
__wbg_toString_139023ab33acec36: function (index) {
return addToStack(get(index).toString());
},
__wbg_require_cca90b1a94a0255b: function () {
return applyToWindow(function () {
return addToStack(module.require);
}, arguments);
},
__wbg_crypto_1d1f22824a6a080c: function (index) {
return addToStack(get(index).crypto);
},
__wbg_process_4a72847cc503995b: function (index) {
return addToStack(get(index).process);
},
__wbg_versions_f686565e586dd935: function (index) {
return addToStack(get(index).versions);
},
__wbg_node_104a2ff8d6ea03a2: function (index) {
return addToStack(get(index).node);
},
__wbg_localStorage_3d538af21ea07fcc: function () {
return applyToWindow(function (index) {
let data = fake_window.localStorage;
if (isNull(data)) {
return 0;
} else {
return addToStack(data);
}
}, arguments);
},
__wbg_setfillStyle_59f426135f52910f: function () {},
__wbg_setshadowBlur_229c56539d02f401: function () {},
__wbg_setshadowColor_340d5290cdc4ae9d: function () {},
__wbg_setfont_16d6e31e06a420a5: function () {},
__wbg_settextBaseline_c3266d3bd4a6695c: function () {},
__wbg_drawImage_cb13768a1bdc04bd: function () {},
__wbg_getImageData_66269d289f37d3c7: function () {
return applyToWindow(function () {
return addToStack(image_data);
}, arguments);
},
__wbg_rect_2fa1df87ef638738: function () {},
__wbg_fillRect_4dd28e628381d240: function () {},
__wbg_fillText_07e5da9e41652f20: function () {},
__wbg_setProperty_5144ddce66bbde41: function () {},
__wbg_createElement_03cf347ddad1c8c0: function () {
return applyToWindow(function (index, decodeIndex, decodeIndexOffset) {
return addToStack(canvas);
}, arguments);
},
__wbg_querySelector_118a0639aa1f51cd: function () {
return applyToWindow(function (index, decodeIndex, decodeOffset) {
return addToStack(meta);
}, arguments);
},
__wbg_querySelectorAll_50c79cd4f7573825: function () {
return applyToWindow(function () {
return addToStack(nodeList);
}, arguments);
},
__wbg_getAttribute_706ae88bd37410fa: function (
offset,
index,
decodeIndex,
decodeOffset
) {
let attr = meta.content;
let todo = isNull(attr)
? 0
: parse(attr, wasm.__wbindgen_export_0, wasm.__wbindgen_export_1);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, todo, true);
},
__wbg_target_6795373f170fd786: function (index) {
let target = get(index).target;
return isNull(target) ? 0 : addToStack(target);
},
__wbg_addEventListener_f984e99465a6a7f4: function () {},
__wbg_instanceof_HtmlCanvasElement_1e81f71f630e46bc: function () {
return true;
},
__wbg_setwidth_233645b297bb3318: function (index, set) {
get(index).width = set >>> 0;
},
__wbg_setheight_fcb491cf54e3527c: function (index, set) {
get(index).height = set >>> 0;
},
__wbg_getContext_dfc91ab0837db1d1: function () {
return applyToWindow(function (index) {
return addToStack(get(index).context2d);
}, arguments);
},
__wbg_toDataURL_97b108dd1a4b7454: function () {
return applyToWindow(function (offset, index) {
let _dataUrl = parse(
dataURL,
wasm.__wbindgen_export_0,
wasm.__wbindgen_export_1
);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, _dataUrl, true);
}, arguments);
},
__wbg_instanceof_HtmlDocument_1100f8a983ca79f9: function () {
return true;
},
__wbg_style_ca229e3326b3c3fb: function (index) {
addToStack(get(index).style);
},
__wbg_instanceof_HtmlImageElement_9c82d4e3651a8533: function () {
return true;
},
__wbg_src_87a0e38af6229364: function (offset, index) {
let _src = parse(
get(index).src,
wasm.__wbindgen_export_0,
wasm.__wbindgen_export_1
);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, _src, true);
},
__wbg_width_e1a38bdd483e1283: function (index) {
return get(index).width;
},
__wbg_height_e4cc2294187313c9: function (index) {
return get(index).height;
},
__wbg_complete_1162c2697406af11: function (index) {
return get(index).complete;
},
__wbg_data_d34dc554f90b8652: function (offset, index) {
var _data = Qj(get(index).data, wasm.__wbindgen_export_0);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, _data, true);
},
__wbg_origin_305402044aa148ce: function () {
return applyToWindow(function (offset, index) {
let _origin = parse(
get(index).origin,
wasm.__wbindgen_export_0,
wasm.__wbindgen_export_1
);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, _origin, true);
}, arguments);
},
__wbg_length_8a9352f7b7360c37: function (index) {
return get(index).length;
},
__wbg_get_c30ae0782d86747f: function (index) {
let _image = get(index).image;
return isNull(_image) ? 0 : addToStack(_image);
},
__wbg_timeOrigin_f462952854d802ec: function (index) {
return get(index).timeOrigin;
},
__wbg_instanceof_Window_cee7a886d55e7df5: function () {
return true;
},
__wbg_document_eb7fd66bde3ee213: function (index) {
let _document = get(index).document;
return isNull(_document) ? 0 : addToStack(_document);
},
__wbg_location_b17760ac7977a47a: function (index) {
return addToStack(get(index).location);
},
__wbg_performance_4ca1873776fdb3d2: function (index) {
let _performance = get(index).performance;
return isNull(_performance) ? 0 : addToStack(_performance);
},
__wbg_origin_e1f8acdeb3a39a2b: function (offset, index) {
let _origin = parse(
get(index).origin,
wasm.__wbindgen_export_0,
wasm.__wbindgen_export_1
);
getDataView().setInt32(offset + 4, size, true);
getDataView().setInt32(offset, _origin, true);
},
__wbg_get_8986951b1ee310e0: function (index, decode1, decode2) {
let data = get(index)[decodeSub(decode1, decode2)];
return isNull(data) ? 0 : addToStack(data);
},
__wbg_setTimeout_6ed7182ebad5d297: function () {
return applyToWindow(function () {
return 7;
}, arguments);
},
__wbg_self_05040bd9523805b9: function () {
return applyToWindow(function () {
return addToStack(fake_window);
}, arguments);
},
__wbg_window_adc720039f2cb14f: function () {
return applyToWindow(function () {
return addToStack(fake_window);
}, arguments);
},
__wbg_globalThis_622105db80c1457d: function () {
return applyToWindow(function () {
return addToStack(fake_window);
}, arguments);
},
__wbg_global_f56b013ed9bcf359: function () {
return applyToWindow(function () {
return addToStack(fake_window);
}, arguments);
},
__wbg_newnoargs_cfecb3965268594c: function (index, offset) {
return addToStack(new Function(decodeSub(index, offset)));
},
__wbindgen_object_clone_ref: function (index) {
return addToStack(get(index));
},
__wbg_eval_c824e170787ad184: function () {
return applyToWindow(function (index, offset) {
let fake_str = "fake_" + decodeSub(index, offset);
let ev = eval(fake_str);
return addToStack(ev);
}, arguments);
},
__wbg_call_3f093dd26d5569f8: function () {
return applyToWindow(function (index, index2) {
return addToStack(get(index).call(get(index2)));
}, arguments);
},
__wbg_call_67f2111acd2dfdb6: function () {
return applyToWindow(function (index, index2, index3) {
return addToStack(get(index).call(get(index2), get(index3)));
}, arguments);
},
__wbg_set_961700853a212a39: function () {
return applyToWindow(function (index, index2, index3) {
return Reflect.set(get(index), get(index2), get(index3));
}, arguments);
},
__wbg_buffer_b914fb8b50ebbc3e: function (index) {
return addToStack(get(index).buffer);
},
__wbg_newwithbyteoffsetandlength_0de9ee56e9f6ee6e: function (
index,
val,
val2
) {
return addToStack(new Uint8Array(get(index), val >>> 0, val2 >>> 0));
},
__wbg_newwithlength_0d03cef43b68a530: function (length) {
return addToStack(new Uint8Array(length >>> 0));
},
__wbg_new_b1f2d6842d615181: function (index) {
return addToStack(new Uint8Array(get(index)));
},
__wbg_buffer_67e624f5a0ab2319: function (index) {
return addToStack(get(index).buffer);
},
__wbg_length_21c4b0ae73cba59d: function (index) {
return get(index).length;
},
__wbg_set_7d988c98e6ced92d: function (index, index2, val) {
get(index).set(get(index2), val >>> 0);
},
__wbindgen_debug_string: function () {},
__wbindgen_throw: function (index, offset) {
throw new Error(decodeSub(index, offset));
},
__wbindgen_memory: function () {
return addToStack(wasm.memory);
},
__wbindgen_closure_wrapper117: function (Qn, QT) {
return addToStack(args(Qn, QT, 2, export3));
},
__wbindgen_closure_wrapper119: function (Qn, QT) {
return addToStack(args(Qn, QT, 2, export4));
},
__wbindgen_closure_wrapper121: function (Qn, QT) {
return addToStack(args(Qn, QT, 2, export5));
},
__wbindgen_closure_wrapper123: function (Qn, QT) {
let test = addToStack(args(Qn, QT, 9, export4));
return test;
},
},
};
return wasmObj;
}
function assignWasm(resp) {
wasm = resp.exports;
(dataView = null), (memoryBuff = null), wasm;
}
function QZ(QP) {
let Qn;
return (
(Qn = initWasm()),
QP instanceof WebAssembly.Module || (QP = new WebAssembly.Module(QP)),
assignWasm(new WebAssembly.Instance(QP, Qn))
);
}
async function loadWasm(url) {
let mod, buffer;
return (
(mod = initWasm()),
({
instance: url,
module: mod,
bytes: buffer,
} = ((url = fetch(url)), void 0, await QN(await url, mod))),
assignWasm(url),
buffer
);
}
const grootLoader = {
groot: function () {
wasm.groot();
},
};
let wasmLoader = Object.assign(loadWasm, { initSync: QZ }, grootLoader);
const V = async (url) => {
let Q0 = await wasmLoader(url);
fake_window.bytes = Q0;
try {
wasmLoader.groot();
} catch (error) {
console.error("error: ", error);
}
fake_window.jwt_plugin(Q0);
return fake_window.navigate();
};
const getMeta = async (url) => {
let resp = await fetch(url, {
headers: {
UserAgent: user_agent,
Referrer: referrer,
},
});
let txt = await resp.text();
let regx = /name="j_crt" content="[A-Za-z0-9]*/g;
let match = txt.match(regx)[0];
let content = match.slice(match.lastIndexOf('"') + 1);
meta.content = content + "==";
};
const i = (a, P) => {
try {
for (let Q0 = 0; Q0 < a.length; Q0++) {
a[Q0] = a[Q0] ^ P[Q0 % P.length];
}
} catch (Q1) {
return null;
}
};
const M = (a, P) => {
try {
var Q0 = cryptoJs.AES.decrypt(a, P);
return JSON.parse(Q0.toString(cryptoJs.enc.Utf8));
} catch (Q1) {
var Q0 = cryptoJs.AES.decrypt(a, P);
}
return [];
};
function z(a) {
return [
(a & 4278190080) >> 24,
(a & 16711680) >> 16,
(a & 65280) >> 8,
a & 255,
];
}
const decryptSource = async (embed_url) => {
referrer = embed_url.includes("mega")
? `https://${v4_base_url}`
: new URL(embed_url).origin;
// let regx = /([A-Z])\w+/;
let xrax = embed_url.split("/").pop().split("?").shift();
// regx = /https:\/\/[a-zA-Z0-9.]*/;
// let base_url = embed_url.match(regx)[0];
const base_url = new URL(embed_url).origin;
nodeList.image.src = base_url + "/images/image.png?v=0.0.9";
let data = new Uint8ClampedArray((await pixels(nodeList.image.src)).data);
image_data.data = data;
let test = embed_url.split("/");
let browser_version = 1676800512;
canvas.baseUrl = base_url;
fake_window.origin = base_url;
fake_window.location.origin = base_url;
fake_window.location.href = embed_url;
fake_window.xrax = xrax;
fake_window.G = xrax;
await getMeta(embed_url);
let Q5 = await V(base_url + "/images/loading.png?v=0.0.9");
let getSourcesUrl = "";
if (base_url.includes("mega")) {
getSourcesUrl =
base_url +
"/" +
test[3] +
"/ajax/" +
test[4] +
"/getSources?id=" +
fake_window.pid +
"&v=" +
fake_window.localStorage.kversion +
"&h=" +
fake_window.localStorage.kid +
"&b=" +
browser_version;
} else {
getSourcesUrl =
base_url +
"/ajax/" +
test[3] +
// "/" +
// test[4] +
"/getSources?id=" +
fake_window.pid +
"&v=" +
fake_window.localStorage.kversion +
"&h=" +
fake_window.localStorage.kid +
"&b=" +
browser_version;
}
let { data: resp } = await axios.get(getSourcesUrl, {
headers: {
"User-Agent": user_agent,
Referrer: embed_url + "&autoPlay=1&oa=0&asi=1",
"Accept-Language": "en,bn;q=0.9,en-US;q=0.8",
"sec-ch-ua":
'"Google Chrome";v="131", "Chromium";v="131", "Not_A Brand";v="24"',
"sec-ch-ua-mobile": "?1",
"sec-ch-ua-platform": '"Android"',
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Site": "same-origin",
"X-Requested-With": "XMLHttpRequest",
"Sec-Fetch-Mode": "cors",
},
});
let Q3 = fake_window.localStorage.kversion;
let Q1 = z(Q3);
Q5 = new Uint8Array(Q5);
let Q8 = resp.t != 0 ? (i(Q5, Q1), Q5) : ((Q8 = resp.k), i(Q8, Q1), Q8);
let str = btoa(String.fromCharCode.apply(null, new Uint8Array(Q8)));
var decryptedSource = M(resp.sources, str);
resp.sources = decryptedSource;
return resp;
};
export default async function decryptMegacloud(id, name, type) {
try {
const { data: sourcesData } = await axios.get(
// `https://${v1_base_url}/ajax/v2/episode/sources?id=${id}`
`https://${v4_base_url}/ajax/episode/sources?id=${id}`
);
const source = await decryptSource(sourcesData.link);
return {
id: id,
type: type,
link: source.sources[0],
tracks: source.tracks,
intro: source.intro,
outro: source.outro,
server: name,
iframe: sourcesData.link,
};
} catch (error) {
console.error("Error during decryption:", error);
}
}

View File

@@ -0,0 +1,29 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v1_base_url } from "../utils/base_v1.js";
export async function fetchServerData_v1(id) {
try {
const { data } = await axios.get(
`https://${v1_base_url}/ajax/v2/episode/servers?episodeId=${id}`
);
const $ = cheerio.load(data.html);
const serverData = $("div.ps_-block > div.ps__-list > div.server-item")
.filter((_, ele) => {
const name = $(ele).find("a.btn").text();
return name === "HD-1" || name === "HD-2";
})
.map((_, ele) => ({
name: $(ele).find("a.btn").text(),
id: $(ele).attr("data-id"),
type: $(ele).attr("data-type"),
}))
.get();
return serverData;
} catch (error) {
console.error("Error fetching server data:", error);
return [];
}
}

View File

@@ -0,0 +1,29 @@
import axios from "axios";
import * as cheerio from "cheerio";
import { v2_base_url } from "../utils/base_v2.js";
export async function fetchServerData_v2(id) {
try {
const { data } = await axios.get(
`https://${v2_base_url}/ajax/episode/servers?episodeId=${id}`
);
const $ = cheerio.load(data.html);
const serverData = $("div.ps_-block > div.ps__-list > div.server-item")
.filter((_, ele) => {
const name = $(ele).find("a.btn").text().trim();
return name === "Vidcloud";
})
.map((_, ele) => ({
name: $(ele).find("a.btn").text().trim(),
id: $(ele).attr("data-id"),
type: $(ele).attr("data-type"),
}))
.get();
return serverData;
} catch (error) {
console.error("Error fetching server data:", error);
return [];
}
}

89
src/routes/apiRoutes.js Normal file
View File

@@ -0,0 +1,89 @@
import * as homeInfoController from "../controllers/homeInfo.controller.js";
import * as categoryController from "../controllers/category.controller.js";
import * as topTenController from "../controllers/topten.controller.js";
import * as animeInfoController from "../controllers/animeInfo.controller.js";
import * as streamController from "../controllers/streamInfo.controller.js";
import * as searchController from "../controllers/search.controller.js";
import * as episodeListController from "../controllers/episodeList.controller.js";
import * as suggestionsController from "../controllers/suggestion.controller.js";
import * as scheduleController from "../controllers/schedule.controller.js";
import * as serversController from "../controllers/servers.controller.js";
import * as randomController from "../controllers/random.controller.js";
import * as qtipController from "../controllers/qtip.controller.js";
import * as randomIdController from "../controllers/randomId.controller.js";
import * as producerController from "../controllers/producer.controller.js";
import * as characterListController from "../controllers/voiceactor.controller.js";
import * as nextEpisodeScheduleController from "../controllers/nextEpisodeSchedule.controller.js";
import { routeTypes } from "./category.route.js";
import { getWatchlist } from "../controllers/watchlist.controller.js";
import getVoiceActors from "../controllers/actors.controller.js";
import getCharacter from "../controllers/characters.controller.js";
import * as filterController from "../controllers/filter.controller.js";
import getTopSearch from "../controllers/topsearch.controller.js";
export const createApiRoutes = (app, jsonResponse, jsonError) => {
const createRoute = (path, controllerMethod) => {
app.get(path, async (req, res) => {
try {
const data = await controllerMethod(req, res);
if (!res.headersSent) {
return jsonResponse(res, data);
}
} catch (err) {
console.error(`Error in route ${path}:`, err);
if (!res.headersSent) {
return jsonError(res, err.message || "Internal server error");
}
}
});
};
["/api", "/api/"].forEach((route) => {
app.get(route, async (req, res) => {
try {
const data = await homeInfoController.getHomeInfo(req, res);
if (!res.headersSent) {
return jsonResponse(res, data);
}
} catch (err) {
console.error("Error in home route:", err);
if (!res.headersSent) {
return jsonError(res, err.message || "Internal server error");
}
}
});
});
routeTypes.forEach((routeType) =>
createRoute(`/api/${routeType}`, (req, res) =>
categoryController.getCategory(req, res, routeType)
)
);
createRoute("/api/top-ten", topTenController.getTopTen);
createRoute("/api/info", animeInfoController.getAnimeInfo);
createRoute("/api/episodes/:id", episodeListController.getEpisodes);
createRoute("/api/servers/:id", serversController.getServers);
createRoute("/api/stream", (req, res) => streamController.getStreamInfo(req, res, false));
createRoute("/api/stream/fallback", (req, res) => streamController.getStreamInfo(req, res, true));
createRoute("/api/search", searchController.search);
createRoute("/api/filter", filterController.filter);
createRoute("/api/search/suggest", suggestionsController.getSuggestions);
createRoute("/api/schedule", scheduleController.getSchedule);
createRoute(
"/api/schedule/:id",
nextEpisodeScheduleController.getNextEpisodeSchedule
);
createRoute("/api/random", randomController.getRandom);
createRoute("/api/random/id", randomIdController.getRandomId);
createRoute("/api/qtip/:id", qtipController.getQtip);
createRoute("/api/producer/:id", producerController.getProducer);
createRoute(
"/api/character/list/:id",
characterListController.getVoiceActors
);
createRoute("/api/watchlist/:userId{/:page}", getWatchlist);
createRoute("/api/actors/:id", getVoiceActors);
createRoute("/api/character/:id", getCharacter);
createRoute("/api/top-search", getTopSearch);
};

View File

@@ -0,0 +1,87 @@
export const routeTypes = [
"genre/action",
"genre/adventure",
"genre/cars",
"genre/comedy",
"genre/dementia",
"genre/demons",
"genre/drama",
"genre/ecchi",
"genre/fantasy",
"genre/game",
"genre/harem",
"genre/historical",
"genre/horror",
"genre/isekai",
"genre/josei",
"genre/kids",
"genre/magic",
"genre/martial-arts",
"genre/mecha",
"genre/military",
"genre/music",
"genre/mystery",
"genre/parody",
"genre/police",
"genre/psychological",
"genre/romance",
"genre/samurai",
"genre/school",
"genre/sci-fi",
"genre/seinen",
"genre/shoujo",
"genre/shoujo-ai",
"genre/shounen",
"genre/shounen-ai",
"genre/slice-of-life",
"genre/space",
"genre/sports",
"genre/super-power",
"genre/supernatural",
"genre/thriller",
"genre/vampire",
"top-airing",
"most-popular",
"most-favorite",
"completed",
"recently-updated",
"recently-added",
"top-upcoming",
"subbed-anime",
"dubbed-anime",
"movie",
"special",
"ova",
"ona",
"tv",
"music",
"az-list",
"az-list/other",
"az-list/0-9",
"az-list/a",
"az-list/b",
"az-list/c",
"az-list/d",
"az-list/e",
"az-list/f",
"az-list/g",
"az-list/h",
"az-list/i",
"az-list/j",
"az-list/k",
"az-list/l",
"az-list/m",
"az-list/n",
"az-list/o",
"az-list/p",
"az-list/q",
"az-list/r",
"az-list/s",
"az-list/t",
"az-list/u",
"az-list/v",
"az-list/w",
"az-list/x",
"az-list/y",
"az-list/z",
];

129
src/routes/filter.maping.js Normal file
View File

@@ -0,0 +1,129 @@
const FILTER_LANGUAGE_MAP = {
ALL: '',
SUB: '1',
DUB: '2',
SUB_DUB: '3'
};
const GENRE_MAP = {
ACTION: '1',
ADVENTURE: '2',
CARS: '3',
COMEDY: '4',
DEMENTIA: '5',
DEMONS: '6',
DRAMA: '8',
ECCHI: '9',
FANTASY: '10',
GAME: '11',
HAREM: '35',
HISTORICAL: '13',
HORROR: '14',
ISEKAI: '44',
JOSEI: '43',
KIDS: '15',
MAGIC: '16',
MARTIAL_ARTS: '17',
MECHA: '18',
MILITARY: '38',
MUSIC: '19',
MYSTERY: '7',
PARODY: '20',
POLICE: '39',
PSYCHOLOGICAL: '40',
ROMANCE: '22',
SAMURAI: '21',
SCHOOL: '23',
SCI_FI: '24',
SEINEN: '42',
SHOUJO: '25',
SHOUJO_AI: '26',
SHOUNEN: '27',
SHOUNEN_AI: '28',
SLICE_OF_LIFE: '36',
SPACE: '29',
SPORTS: '30',
SUPER_POWER: '31',
SUPERNATURAL: '37',
THRILLER: '41',
VAMPIRE: '32'
};
const FILTER_TYPES = {
ALL: '',
MOVIE: '1',
TV: '2',
OVA: '3',
ONA: '4',
SPECIAL: '5',
MUSIC: '6'
};
const FILTER_STATUS = {
ALL: '',
FINISHED: '1',
CURRENTLY_AIRING: '2',
NOT_YET_AIRED: '3'
};
const FILTER_RATED = {
ALL: '',
G: '1',
PG: '2',
PG_13: '3',
R: '4',
R_PLUS: '5',
RX: '6'
};
const FILTER_SCORE = {
ALL: '',
APPALLING: '1',
HORRIBLE: '2',
VERY_BAD: '3',
BAD: '4',
AVERAGE: '5',
FINE: '6',
GOOD: '7',
VERY_GOOD: '8',
GREAT: '9',
MASTERPIECE: '10'
};
const FILTER_SEASON = {
ALL: '',
SPRING: '1',
SUMMER: '2',
FALL: '3',
WINTER: '4'
};
const FILTER_LANGUAGE = {
ALL: '',
SUB: '1',
DUB: '2',
SUB_DUB: '3'
};
const FILTER_SORT = {
DEFAULT: 'default',
RECENTLY_ADDED: 'recently_added',
RECENTLY_UPDATED: 'recently_updated',
SCORE: 'score',
NAME_AZ: 'name_az',
RELEASED_DATE: 'released_date',
MOST_WATCHED: 'most_watched'
};
export {
FILTER_LANGUAGE_MAP,
GENRE_MAP,
FILTER_TYPES,
FILTER_STATUS,
FILTER_RATED,
FILTER_SCORE,
FILTER_SEASON,
FILTER_LANGUAGE,
FILTER_SORT
};

1
src/utils/base_v1.js Normal file
View File

@@ -0,0 +1 @@
export const v1_base_url = "hianime.do";

1
src/utils/base_v2.js Normal file
View File

@@ -0,0 +1 @@
export const v2_base_url = "kaido.to";

1
src/utils/base_v3.js Normal file
View File

@@ -0,0 +1 @@
export const v3_base_url = "aniplay.lol";

1
src/utils/base_v4.js Normal file
View File

@@ -0,0 +1 @@
export const v4_base_url = "9animetv.to";

2
src/utils/fallback.js Normal file
View File

@@ -0,0 +1,2 @@
export const fallback_1="megaplay.buzz"
export const fallback_2="vidwish.live"

1
src/utils/provider.js Normal file
View File

@@ -0,0 +1 @@
export const provider = "https://megacloud.club";

18
vercel.json Normal file
View File

@@ -0,0 +1,18 @@
{
"version": 2,
"builds": [
{
"src": "server.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "server.js",
"headers": {
"Access-Control-Allow-Origin": "*"
}
}
]
}