mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 10:55:10 +00:00
fix: update extractors to match actual anikototv.to structure
- Analyzed actual HTML structure of anikototv.to - Updated all extractors with correct CSS selectors - Changed ID-based to slug-based lookups - Updated URL patterns to match actual website - Fixed stream info to use AJAX server list - Improved error handling
This commit is contained in:
+17
-3
@@ -11,11 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Initial project setup
|
- Initial project setup
|
||||||
- Express.js server with Vercel deployment support
|
- Express.js server with Vercel deployment support
|
||||||
- Home page API endpoint (`/api/`)
|
- Home page API endpoint (`/api/`)
|
||||||
- Anime info endpoint (`/api/info?id={anime-id}`)
|
- Anime info endpoint (`/api/info?id={anime-slug}`)
|
||||||
- Search endpoint (`/api/search?keyword={keyword}`)
|
- Search endpoint (`/api/search?keyword={keyword}`)
|
||||||
- Search suggestions endpoint (`/api/search/suggest?keyword={keyword}`)
|
- Search suggestions endpoint (`/api/search/suggest?keyword={keyword}`)
|
||||||
- Episode list endpoint (`/api/episodes/{anime-id}`)
|
- Episode list endpoint (`/api/episodes/{anime-slug}`)
|
||||||
- Stream info endpoint (`/api/stream?id={episode-id}&server={server}&type={type}`)
|
- Stream info endpoint (`/api/stream?id={episode-id}`)
|
||||||
- Schedule endpoint (`/api/schedule?date={YYYY-MM-DD}`)
|
- Schedule endpoint (`/api/schedule?date={YYYY-MM-DD}`)
|
||||||
- Spotlight endpoint (`/api/spotlight`)
|
- Spotlight endpoint (`/api/spotlight`)
|
||||||
- Trending endpoint (`/api/trending`)
|
- Trending endpoint (`/api/trending`)
|
||||||
@@ -33,3 +33,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Render.yaml for Render deployment
|
- Render.yaml for Render deployment
|
||||||
- MIT License
|
- MIT License
|
||||||
- README with API documentation
|
- README with API documentation
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Updated all extractors to match actual anikototv.to HTML structure
|
||||||
|
- Changed ID-based lookups to slug-based lookups
|
||||||
|
- Updated URL patterns to match actual website structure
|
||||||
|
- Improved CSS selectors for better accuracy
|
||||||
|
|
||||||
|
## [1.1.0] - 2024-12-19
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Corrected HTML selectors based on actual website analysis
|
||||||
|
- Updated episode list extraction
|
||||||
|
- Fixed stream info endpoint to use AJAX server list
|
||||||
|
- Improved error handling
|
||||||
+36
-8
@@ -114,12 +114,12 @@
|
|||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/</span>
|
<span class="path">/api/</span>
|
||||||
<p class="description">Get home page data including spotlight, trending, top airing, etc.</p>
|
<p class="description">Get home page data including spotlight, trending, top anime, etc.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/info?id={anime-id}</span>
|
<span class="path">/api/info?id={anime-slug}</span>
|
||||||
<p class="description">Get detailed information about a specific anime</p>
|
<p class="description">Get detailed information about a specific anime</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -137,14 +137,14 @@
|
|||||||
|
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/episodes/{anime-id}</span>
|
<span class="path">/api/episodes/{anime-slug}</span>
|
||||||
<p class="description">Get episode list for an anime</p>
|
<p class="description">Get episode list for an anime</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/stream?id={episode-id}&server={server}&type={sub/dub}</span>
|
<span class="path">/api/stream?id={episode-id}</span>
|
||||||
<p class="description">Get streaming information for an episode</p>
|
<p class="description">Get streaming servers for an episode</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
@@ -192,20 +192,48 @@
|
|||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/filter?{params}</span>
|
<span class="path">/api/filter?{params}</span>
|
||||||
<p class="description">Filter anime by type, status, genre, etc.</p>
|
<p class="description">Filter anime by genre, type, status, etc.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="method">GET</span>
|
<span class="method">GET</span>
|
||||||
<span class="path">/api/{category}?page={page}</span>
|
<span class="path">/api/{category}?page={page}</span>
|
||||||
<p class="description">Get anime by category (movie, tv, ova, ona, etc.)</p>
|
<p class="description">Get anime by category (genre/action, type/tv, etc.)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="api-section">
|
||||||
|
<h2>URL Structure</h2>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Anime: /watch/{anime-slug}</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Episode: /watch/{anime-slug}/ep-{number}</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Search: /filter?keyword={query}</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Genre: /genre/{genre-slug}</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Type: /type/{type-slug}</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Status: /status/{status-slug}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="api-section">
|
<div class="api-section">
|
||||||
<h2>Categories</h2>
|
<h2>Categories</h2>
|
||||||
<div class="endpoint">
|
<div class="endpoint">
|
||||||
<span class="path">top-airing, most-popular, most-favorite, completed, recently-updated, recently-added, subbed-anime, dubbed-anime, movie, tv, ova, ona, special, music</span>
|
<span class="path">Genres: action, adventure, cars, comedy, dementia, demons, drama, ecchi, fantasy, game, harem, historical, horror, isekai, josei, kids, magic, mahou-shoujo, martial-arts, mecha, military, music, mystery, parody, police, psychological, romance, samurai, school, sci-fi, seinen, shoujo, shoujo-ai, shounen, shounen-ai, slice-of-life, space, sports, super-power, supernatural, thriller, unknown, vampire</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Types: movie, music, ona, ova, special, tv</span>
|
||||||
|
</div>
|
||||||
|
<div class="endpoint">
|
||||||
|
<span class="path">Status: currently-airing, finished-airing, not-yet-aired</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
+10
-18
@@ -9,26 +9,18 @@ const ALT_DOMAINS = [
|
|||||||
|
|
||||||
const URLS = {
|
const URLS = {
|
||||||
home: `${BASE_URL}/home`,
|
home: `${BASE_URL}/home`,
|
||||||
search: `${BASE_URL}/search`,
|
search: `${BASE_URL}/filter`,
|
||||||
genre: (genre) => `${BASE_URL}/genre/${genre}`,
|
genre: (genre) => `${BASE_URL}/genre/${genre}`,
|
||||||
|
type: (type) => `${BASE_URL}/type/${type}`,
|
||||||
|
status: (status) => `${BASE_URL}/status/${status}`,
|
||||||
azList: (letter) => `${BASE_URL}/az-list/${letter}`,
|
azList: (letter) => `${BASE_URL}/az-list/${letter}`,
|
||||||
category: (cat) => `${BASE_URL}/${cat}`,
|
watch: (slug) => `${BASE_URL}/watch/${slug}`,
|
||||||
anime: (id) => `${BASE_URL}/anime/${id}`,
|
episode: (slug, ep) => `${BASE_URL}/watch/${slug}/ep-${ep}`,
|
||||||
episode: (id) => `${BASE_URL}/episode/${id}`,
|
latestUpdated: `${BASE_URL}/latest-updated`,
|
||||||
schedule: (date) => `${BASE_URL}/schedule/${date}`,
|
newRelease: `${BASE_URL}/new-release`,
|
||||||
topTen: `${BASE_URL}/top-ten`,
|
mostViewed: `${BASE_URL}/most-viewed`,
|
||||||
mostPopular: `${BASE_URL}/most-popular`,
|
random: `${BASE_URL}/random`,
|
||||||
mostFavorite: `${BASE_URL}/most-favorite`,
|
serverList: `${BASE_URL}/ajax/server/list`
|
||||||
recentlyUpdated: `${BASE_URL}/recently-updated`,
|
|
||||||
recentlyAdded: `${BASE_URL}/recently-added`,
|
|
||||||
subbedAnime: `${BASE_URL}/subbed-anime`,
|
|
||||||
dubbedAnime: `${BASE_URL}/dubbed-anime`,
|
|
||||||
movie: `${BASE_URL}/movie`,
|
|
||||||
ova: `${BASE_URL}/ova`,
|
|
||||||
ona: `${BASE_URL}/ona`,
|
|
||||||
special: `${BASE_URL}/special`,
|
|
||||||
tv: `${BASE_URL}/tv`,
|
|
||||||
music: `${BASE_URL}/music`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export { BASE_URL, ALT_DOMAINS, URLS };
|
export { BASE_URL, ALT_DOMAINS, URLS };
|
||||||
@@ -5,7 +5,7 @@ const getAnimeInfo = async (req, res, next) => {
|
|||||||
try {
|
try {
|
||||||
const { id } = req.query;
|
const { id } = req.query;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return res.status(400).json({ success: false, message: "Anime ID is required" });
|
return res.status(400).json({ success: false, message: "Anime slug is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `anime_${id}`;
|
const cacheKey = `anime_${id}`;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const getEpisodeList = async (req, res, next) => {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return res.status(400).json({ success: false, message: "Anime ID is required" });
|
return res.status(400).json({ success: false, message: "Anime slug is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `episodes_${id}`;
|
const cacheKey = `episodes_${id}`;
|
||||||
|
|||||||
@@ -3,19 +3,19 @@ import { getCache, setCache } from "../helper/cache.helper.js";
|
|||||||
|
|
||||||
const getStreamInfo = async (req, res, next) => {
|
const getStreamInfo = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const { id, server = "hd-1", type = "sub" } = req.query;
|
const { id } = req.query;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return res.status(400).json({ success: false, message: "Episode ID is required" });
|
return res.status(400).json({ success: false, message: "Episode ID is required" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `stream_${id}_${server}_${type}`;
|
const cacheKey = `stream_${id}`;
|
||||||
const cached = getCache(cacheKey);
|
const cached = getCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await extractStreamInfo(id, server, type);
|
const data = await extractStreamInfo(id);
|
||||||
setCache(cacheKey, data);
|
setCache(cacheKey, data);
|
||||||
res.json({ success: true, results: data });
|
res.json({ success: true, results: data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { BASE_URL } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
|
|
||||||
const extractAnimeInfo = async (id) => {
|
const extractAnimeInfo = async (slug) => {
|
||||||
try {
|
try {
|
||||||
const url = `${BASE_URL}/anime/${id}`;
|
const url = URLS.watch(slug);
|
||||||
const { data } = await axios.get(url, { headers });
|
const { data } = await axios.get(url, { headers });
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const title = $(".film-name h1, .anisc-detail .film-name").text().trim() || "";
|
const title = $(".anisc-detail .film-name, .film-name h1").text().trim() || "";
|
||||||
const japaneseTitle = $(".film-name .alternate-title, .anisc-detail .film-name small").text().trim() || "";
|
const japaneseTitle = $(".anisc-detail .film-name .alternate-title, .film-name small").text().trim() || "";
|
||||||
const poster = $(".film-poster img").attr("src") || "";
|
const poster = $(".film-poster img, .anisc-poster img").attr("src") || "";
|
||||||
const showType = $(".film-info .row .col1 .item:first-child .name").text().trim() || "";
|
const type = $(".film-info .row .col1 .item:first-child .name").text().trim() || "";
|
||||||
const quality = $(".film-info .row .col1 .item:nth-child(2) .name").text().trim() || "";
|
const rating = $(".film-info .row .col1 .item:nth-child(2) .name").text().trim() || "";
|
||||||
|
const quality = $(".film-info .row .col1 .item:nth-child(3) .name").text().trim() || "";
|
||||||
|
|
||||||
const animeInfo = {};
|
const animeInfo = {};
|
||||||
$(".anisc-info .item").each((i, el) => {
|
$(".anisc-info .item").each((i, el) => {
|
||||||
@@ -32,66 +33,63 @@ const extractAnimeInfo = async (id) => {
|
|||||||
if (genre) genres.push(genre);
|
if (genre) genres.push(genre);
|
||||||
});
|
});
|
||||||
|
|
||||||
const seasons = [];
|
const episodes = [];
|
||||||
$(".seasons .season-item, .ss-list a").each((i, el) => {
|
$(".episodes-list .ep-item, .ss-list a").each((i, el) => {
|
||||||
const seasonId = $(el).attr("href")?.split("/").pop() || "";
|
const epId = $(el).attr("data-id") || $(el).attr("href")?.split("/").pop() || "";
|
||||||
const seasonTitle = $(el).text().trim() || "";
|
const epNumber = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
|
||||||
const seasonPoster = $(el).find("img").attr("src") || "";
|
const title = $(el).find(".ep-title, .ep-name").text().trim() || "";
|
||||||
|
|
||||||
if (seasonId) {
|
if (epId) {
|
||||||
seasons.push({
|
episodes.push({
|
||||||
id: seasonId,
|
id: epId,
|
||||||
title: seasonTitle,
|
episode_no: epNumber,
|
||||||
poster: seasonPoster
|
title
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const relatedData = [];
|
const relatedData = [];
|
||||||
$(".related-content .film-detail, .film-list-block .film-detail").each((i, el) => {
|
$(".related-content .film-detail, .film-list-block .film-detail").each((i, el) => {
|
||||||
const relId = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const relSlug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const relPoster = $(el).find("img").attr("src") || "";
|
const relPoster = $(el).find("img").attr("src") || "";
|
||||||
const relTitle = $(el).find(".film-name a").text().trim() || "";
|
const relTitle = $(el).find(".film-name a").text().trim() || "";
|
||||||
const relType = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
|
||||||
|
|
||||||
if (relId) {
|
if (relSlug) {
|
||||||
relatedData.push({
|
relatedData.push({
|
||||||
id: relId,
|
slug: relSlug,
|
||||||
poster: relPoster,
|
poster: relPoster,
|
||||||
title: relTitle,
|
title: relTitle
|
||||||
type: relType
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const recommendedData = [];
|
const recommendedData = [];
|
||||||
$(".recommend-content .film-detail, .film-list-block .film-detail").each((i, el) => {
|
$(".recommend-content .film-detail, .film-list-block .film-detail").each((i, el) => {
|
||||||
const recId = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const recSlug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const recPoster = $(el).find("img").attr("src") || "";
|
const recPoster = $(el).find("img").attr("src") || "";
|
||||||
const recTitle = $(el).find(".film-name a").text().trim() || "";
|
const recTitle = $(el).find(".film-name a").text().trim() || "";
|
||||||
const recType = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
|
||||||
|
|
||||||
if (recId) {
|
if (recSlug) {
|
||||||
recommendedData.push({
|
recommendedData.push({
|
||||||
id: recId,
|
slug: recSlug,
|
||||||
poster: recPoster,
|
poster: recPoster,
|
||||||
title: recTitle,
|
title: recTitle
|
||||||
type: recType
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
slug,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
poster,
|
poster,
|
||||||
showType,
|
type,
|
||||||
|
rating,
|
||||||
quality,
|
quality,
|
||||||
overview,
|
overview,
|
||||||
animeInfo,
|
animeInfo,
|
||||||
genres,
|
genres,
|
||||||
seasons,
|
episodes,
|
||||||
relatedData,
|
relatedData,
|
||||||
recommendedData
|
recommendedData
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,30 +7,43 @@ import { extractPages } from "../helper/extractPages.helper.js";
|
|||||||
|
|
||||||
const extractCategory = async (category, page = 1) => {
|
const extractCategory = async (category, page = 1) => {
|
||||||
try {
|
try {
|
||||||
const url = URLS.category(category);
|
let url;
|
||||||
|
if (category.startsWith("genre/")) {
|
||||||
|
url = URLS.genre(category.replace("genre/", ""));
|
||||||
|
} else if (category.startsWith("type/")) {
|
||||||
|
url = URLS.type(category.replace("type/", ""));
|
||||||
|
} else if (category.startsWith("status/")) {
|
||||||
|
url = URLS.status(category.replace("status/", ""));
|
||||||
|
} else {
|
||||||
|
url = `${URLS.home}/${category}`;
|
||||||
|
}
|
||||||
|
|
||||||
const $ = await extractPages(url, page);
|
const $ = await extractPages(url, page);
|
||||||
const totalPages = countPages($);
|
const totalPages = countPages($);
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name a, .name.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || "";
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:nth-child(2)").text().trim() || "";
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const sub = parseInt($(el).find(".tick-sub").text().trim()) || 0;
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
const dub = parseInt($(el).find(".tick-dub").text().trim()) || 0;
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
const eps = parseInt($(el).find(".tick-eps").text().trim()) || 0;
|
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || "";
|
||||||
const adultContent = $(el).find(".tick-adult, .adult-badge").length > 0;
|
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
results.push({
|
results.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
tvInfo: { showType, sub, dub, eps },
|
sub,
|
||||||
adultContent
|
dub,
|
||||||
|
total,
|
||||||
|
type,
|
||||||
|
rating
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { BASE_URL } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
|
|
||||||
const extractEpisodeList = async (id) => {
|
const extractEpisodeList = async (slug) => {
|
||||||
try {
|
try {
|
||||||
const url = `${BASE_URL}/anime/${id}`;
|
const url = URLS.watch(slug);
|
||||||
const { data } = await axios.get(url, { headers });
|
const { data } = await axios.get(url, { headers });
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const episodes = [];
|
const episodes = [];
|
||||||
$(".episodes-list .ep-item, .ss-list a").each((i, el) => {
|
$(".episodes-list .ep-item, .ss-list a").each((i, el) => {
|
||||||
const epId = $(el).attr("data-id") || $(el).attr("href")?.split("/").pop() || "";
|
const epId = $(el).attr("data-id") || $(el).attr("href")?.split("/ep-").pop() || "";
|
||||||
const epNumber = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
|
const epNumber = parseInt($(el).find(".ep-number, .ep-num").text().trim()) || i + 1;
|
||||||
const title = $(el).find(".ep-title, .ep-name").text().trim() || "";
|
const title = $(el).find(".ep-title, .ep-name").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".ep-jp").text().trim() || "";
|
|
||||||
const filler = $(el).hasClass("filler") || false;
|
const filler = $(el).hasClass("filler") || false;
|
||||||
|
|
||||||
if (epId) {
|
if (epId) {
|
||||||
@@ -22,7 +21,6 @@ const extractEpisodeList = async (id) => {
|
|||||||
id: epId,
|
id: epId,
|
||||||
episode_no: epNumber,
|
episode_no: epNumber,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
|
||||||
filler
|
filler
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { BASE_URL } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
import { countPages } from "../helper/countPages.helper.js";
|
import { countPages } from "../helper/countPages.helper.js";
|
||||||
import { extractPages } from "../helper/extractPages.helper.js";
|
import { extractPages } from "../helper/extractPages.helper.js";
|
||||||
|
|
||||||
@@ -9,44 +9,43 @@ const extractFilter = async (params) => {
|
|||||||
try {
|
try {
|
||||||
const queryParams = new URLSearchParams();
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
if (params.keyword) queryParams.set("keyword", params.keyword);
|
||||||
|
if (params.genre) queryParams.set("genre", params.genre);
|
||||||
|
if (params.season) queryParams.set("season", params.season);
|
||||||
|
if (params.year) queryParams.set("year", params.year);
|
||||||
if (params.type) queryParams.set("type", params.type);
|
if (params.type) queryParams.set("type", params.type);
|
||||||
if (params.status) queryParams.set("status", params.status);
|
if (params.status) queryParams.set("status", params.status);
|
||||||
if (params.rated) queryParams.set("rated", params.rated);
|
|
||||||
if (params.score) queryParams.set("score", params.score);
|
|
||||||
if (params.season) queryParams.set("season", params.season);
|
|
||||||
if (params.language) queryParams.set("language", params.language);
|
if (params.language) queryParams.set("language", params.language);
|
||||||
if (params.genres) queryParams.set("genres", params.genres);
|
if (params.rating) queryParams.set("rating", params.rating);
|
||||||
if (params.sort) queryParams.set("sort", params.sort);
|
if (params.sort) queryParams.set("sort", params.sort);
|
||||||
if (params.sy) queryParams.set("sy", params.sy);
|
|
||||||
if (params.sm) queryParams.set("sm", params.sm);
|
|
||||||
if (params.sd) queryParams.set("sd", params.sd);
|
|
||||||
if (params.ey) queryParams.set("ey", params.ey);
|
|
||||||
if (params.em) queryParams.set("em", params.em);
|
|
||||||
if (params.ed) queryParams.set("ed", params.ed);
|
|
||||||
if (params.keyword) queryParams.set("keyword", params.keyword);
|
|
||||||
|
|
||||||
const url = `${BASE_URL}/filter?${queryParams.toString()}`;
|
const url = `${URLS.search}?${queryParams.toString()}`;
|
||||||
const $ = await extractPages(url, params.page || 1);
|
const $ = await extractPages(url, params.page || 1);
|
||||||
const totalPages = countPages($);
|
const totalPages = countPages($);
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name a, .name.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || "";
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:nth-child(2)").text().trim() || "";
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const sub = parseInt($(el).find(".tick-sub").text().trim()) || 0;
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
const dub = parseInt($(el).find(".tick-dub").text().trim()) || 0;
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
const eps = parseInt($(el).find(".tick-eps").text().trim()) || 0;
|
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || "";
|
||||||
|
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
results.push({
|
results.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
tvInfo: { showType, sub, dub, eps }
|
sub,
|
||||||
|
dub,
|
||||||
|
total,
|
||||||
|
type,
|
||||||
|
rating
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,111 +9,86 @@ const extractHomeInfo = async () => {
|
|||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const spotlights = [];
|
const spotlights = [];
|
||||||
$(".swiper-slide, .spotlight-item").each((i, el) => {
|
$("#hotest .swiper-slide.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a.play").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".image div").attr("style")?.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find("h2.title.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fdi-item:nth-child(1)").text().trim() || "";
|
const japaneseTitle = $(el).find("h2.title.d-title").attr("data-jp") || "";
|
||||||
const description = $(el).find(".fd-infor .desc, .film-description").text().trim() || "";
|
const description = $(el).find(".desc").text().trim() || "";
|
||||||
const showType = $(el).find(".fdi-item:nth-child(2)").text().trim() || "";
|
const rating = $(el).find("i.rating").text().trim() || "";
|
||||||
const duration = $(el).find(".fdi-item:nth-child(3)").text().trim() || "";
|
const quality = $(el).find("i.quality").text().trim() || "";
|
||||||
const releaseDate = $(el).find(".fdi-item:nth-child(4)").text().trim() || "";
|
const sub = parseInt($(el).find("i.sub").text().trim()) || 0;
|
||||||
|
const dub = parseInt($(el).find("i.dub").text().trim()) || 0;
|
||||||
|
const date = $(el).find("i.date").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
spotlights.push({
|
spotlights.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
description,
|
description,
|
||||||
tvInfo: { showType, duration, releaseDate }
|
rating,
|
||||||
|
quality,
|
||||||
|
sub,
|
||||||
|
dub,
|
||||||
|
date
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const trending = [];
|
const trending = [];
|
||||||
$(".trending-list .film-detail, .trending-item").each((i, el) => {
|
$(".section-updated .item, #recent-update .item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a.name.d-title").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find("a.name.d-title").text().trim() || "";
|
||||||
const number = parseInt($(el).find(".number").text().trim()) || i + 1;
|
const japaneseTitle = $(el).find("a.name.d-title").attr("data-jp") || "";
|
||||||
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".meta .inner .right").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
trending.push({ id, number, poster, title });
|
trending.push({
|
||||||
|
slug,
|
||||||
|
poster,
|
||||||
|
title,
|
||||||
|
japaneseTitle,
|
||||||
|
sub,
|
||||||
|
dub,
|
||||||
|
total,
|
||||||
|
type
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const topAiring = [];
|
const topAiring = [];
|
||||||
$(".section-id-02 .film-detail, .top-airing .film-item").each((i, el) => {
|
$("#top-anime .tab-content[data-name='day'] a.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".name").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".type").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
topAiring.push({ id, poster, title, japaneseTitle });
|
topAiring.push({ slug, poster, title, sub, dub, type });
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const mostPopular = [];
|
|
||||||
$(".section-id-03 .film-detail, .popular .film-item").each((i, el) => {
|
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
mostPopular.push({ id, poster, title });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const mostFavorite = [];
|
|
||||||
$(".section-id-04 .film-detail, .favorite .film-item").each((i, el) => {
|
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
mostFavorite.push({ id, poster, title });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const latestCompleted = [];
|
|
||||||
$(".section-id-05 .film-detail, .completed .film-item").each((i, el) => {
|
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
latestCompleted.push({ id, poster, title });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const latestEpisode = [];
|
|
||||||
$(".section-id-06 .film-detail, .latest .film-item").each((i, el) => {
|
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
|
||||||
const episode = $(el).find(".tick-eps, .tick-sub").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
latestEpisode.push({ id, poster, title, episode });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const genres = [];
|
const genres = [];
|
||||||
$(".genre-item, .genres a").each((i, el) => {
|
$("#menu ul.c4 li a").each((i, el) => {
|
||||||
const genre = $(el).text().trim();
|
const genre = $(el).text().trim();
|
||||||
if (genre) genres.push(genre);
|
const href = $(el).attr("href") || "";
|
||||||
|
if (genre && href.includes("/genre/")) {
|
||||||
|
genres.push(genre);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
spotlights,
|
spotlights,
|
||||||
trending,
|
trending,
|
||||||
topAiring,
|
topAiring,
|
||||||
mostPopular,
|
|
||||||
mostFavorite,
|
|
||||||
latestCompleted,
|
|
||||||
latestEpisode,
|
|
||||||
genres
|
genres
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -7,27 +7,32 @@ import { extractPages } from "../helper/extractPages.helper.js";
|
|||||||
|
|
||||||
const extractPopular = async (page = 1) => {
|
const extractPopular = async (page = 1) => {
|
||||||
try {
|
try {
|
||||||
const $ = await extractPages(URLS.mostPopular, page);
|
const $ = await extractPages(URLS.mostViewed, page);
|
||||||
const totalPages = countPages($);
|
const totalPages = countPages($);
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name a, .name.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || "";
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:nth-child(2)").text().trim() || "";
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const sub = parseInt($(el).find(".tick-sub").text().trim()) || 0;
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
const dub = parseInt($(el).find(".tick-dub").text().trim()) || 0;
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
const eps = parseInt($(el).find(".tick-eps").text().trim()) || 0;
|
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || "";
|
||||||
|
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
results.push({
|
results.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
tvInfo: { showType, sub, dub, eps }
|
sub,
|
||||||
|
dub,
|
||||||
|
total,
|
||||||
|
type,
|
||||||
|
rating
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { BASE_URL } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
|
|
||||||
const extractRandom = async () => {
|
const extractRandom = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(`${BASE_URL}/random`, {
|
const { data } = await axios.get(URLS.random, {
|
||||||
headers,
|
headers,
|
||||||
maxRedirects: 5
|
maxRedirects: 5
|
||||||
});
|
});
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
|
const slug = window.location.pathname.split("/watch/").pop() || "";
|
||||||
const title = $(".film-name h1, .anisc-detail .film-name").text().trim() || "";
|
const title = $(".film-name h1, .anisc-detail .film-name").text().trim() || "";
|
||||||
const japaneseTitle = $(".film-name .alternate-title, .anisc-detail .film-name small").text().trim() || "";
|
const japaneseTitle = $(".film-name .alternate-title, .anisc-detail .film-name small").text().trim() || "";
|
||||||
const poster = $(".film-poster img").attr("src") || "";
|
const poster = $(".film-poster img").attr("src") || "";
|
||||||
const showType = $(".film-info .row .col1 .item:first-child .name").text().trim() || "";
|
const type = $(".film-info .row .col1 .item:first-child .name").text().trim() || "";
|
||||||
|
|
||||||
const animeInfo = {};
|
const animeInfo = {};
|
||||||
$(".anisc-info .item").each((i, el) => {
|
$(".anisc-info .item").each((i, el) => {
|
||||||
@@ -26,10 +27,11 @@ const extractRandom = async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
slug,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
poster,
|
poster,
|
||||||
showType,
|
type,
|
||||||
animeInfo
|
animeInfo
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -5,27 +5,23 @@ import { URLS } from "../configs/dataUrl.js";
|
|||||||
|
|
||||||
const extractSchedule = async (date) => {
|
const extractSchedule = async (date) => {
|
||||||
try {
|
try {
|
||||||
const url = URLS.schedule(date);
|
const url = `${URLS.home}?date=${date}`;
|
||||||
const { data } = await axios.get(url, { headers });
|
const { data } = await axios.get(url, { headers });
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const schedule = [];
|
const schedule = [];
|
||||||
$(".schedule-item, .anime-schedule .item").each((i, el) => {
|
$(".schedule-item, .anime-schedule .item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name a, .name").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
|
||||||
const time = $(el).find(".time, .schedule-time").text().trim() || "";
|
const time = $(el).find(".time, .schedule-time").text().trim() || "";
|
||||||
const episodeNo = parseInt($(el).find(".episode-no, .ep-num").text().trim()) || 0;
|
const episodeNo = parseInt($(el).find(".episode-no, .ep-num").text().trim()) || 0;
|
||||||
const releaseDate = $(el).find(".date, .release-date").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
schedule.push({
|
schedule.push({
|
||||||
id,
|
slug,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
|
||||||
time,
|
time,
|
||||||
episode_no: episodeNo,
|
episode_no: episodeNo
|
||||||
releaseDate
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import * as cheerio from "cheerio";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { URLS } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
import { countPages } from "../helper/countPages.helper.js";
|
|
||||||
|
|
||||||
import { extractPages } from "../helper/extractPages.helper.js";
|
import { extractPages } from "../helper/extractPages.helper.js";
|
||||||
|
import { countPages } from "../helper/countPages.helper.js";
|
||||||
|
|
||||||
const extractSearchResults = async (keyword, page = 1) => {
|
const extractSearchResults = async (keyword, page = 1) => {
|
||||||
try {
|
try {
|
||||||
@@ -13,23 +12,28 @@ const extractSearchResults = async (keyword, page = 1) => {
|
|||||||
const totalPages = countPages($);
|
const totalPages = countPages($);
|
||||||
|
|
||||||
const results = [];
|
const results = [];
|
||||||
$(".film_list-wrap .flw-item, .search-list .film-detail").each((i, el) => {
|
$(".film_list-wrap .flw-item, .film-detail").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name a, .name.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const japaneseTitle = $(el).find(".name.d-title").attr("data-jp") || "";
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:nth-child(2)").text().trim() || "";
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const sub = parseInt($(el).find(".tick-sub").text().trim()) || 0;
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
const dub = parseInt($(el).find(".tick-dub").text().trim()) || 0;
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
const eps = parseInt($(el).find(".tick-eps").text().trim()) || 0;
|
const type = $(el).find(".meta .inner .right, .fdi-item:nth-child(2)").text().trim() || "";
|
||||||
|
const rating = $(el).find(".rating, .fdi-item:nth-child(3)").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
results.push({
|
results.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
tvInfo: { showType, sub, dub, eps }
|
sub,
|
||||||
|
dub,
|
||||||
|
total,
|
||||||
|
type,
|
||||||
|
rating
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -48,13 +52,12 @@ const extractSearchSuggestions = async (keyword) => {
|
|||||||
|
|
||||||
const suggestions = [];
|
const suggestions = [];
|
||||||
$(".search-suggest .film-detail, .nav-item a").each((i, el) => {
|
$(".search-suggest .film-detail, .nav-item a").each((i, el) => {
|
||||||
const id = $(el).attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name, .name").text().trim() || "";
|
||||||
const showType = $(el).find(".fdi-item:first-child").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
suggestions.push({ id, poster, title, showType });
|
suggestions.push({ slug, poster, title });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,24 +9,30 @@ const extractSpotlight = async () => {
|
|||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const spotlights = [];
|
const spotlights = [];
|
||||||
$(".swiper-slide, .spotlight-item").each((i, el) => {
|
$("#hotest .swiper-slide.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a.play").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".image div").attr("style")?.match(/url\(['"]?(.+?)['"]?\)/)?.[1] || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find("h2.title.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fdi-item:nth-child(1)").text().trim() || "";
|
const japaneseTitle = $(el).find("h2.title.d-title").attr("data-jp") || "";
|
||||||
const description = $(el).find(".fd-infor .desc, .film-description").text().trim() || "";
|
const description = $(el).find(".desc").text().trim() || "";
|
||||||
const showType = $(el).find(".fdi-item:nth-child(2)").text().trim() || "";
|
const rating = $(el).find("i.rating").text().trim() || "";
|
||||||
const duration = $(el).find(".fdi-item:nth-child(3)").text().trim() || "";
|
const quality = $(el).find("i.quality").text().trim() || "";
|
||||||
const releaseDate = $(el).find(".fdi-item:nth-child(4)").text().trim() || "";
|
const sub = parseInt($(el).find("i.sub").text().trim()) || 0;
|
||||||
|
const dub = parseInt($(el).find("i.dub").text().trim()) || 0;
|
||||||
|
const date = $(el).find("i.date").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
spotlights.push({
|
spotlights.push({
|
||||||
id,
|
slug,
|
||||||
poster,
|
poster,
|
||||||
title,
|
title,
|
||||||
japaneseTitle,
|
japaneseTitle,
|
||||||
description,
|
description,
|
||||||
tvInfo: { showType, duration, releaseDate }
|
rating,
|
||||||
|
quality,
|
||||||
|
sub,
|
||||||
|
dub,
|
||||||
|
date
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { headers } from "../configs/header.config.js";
|
import { headers } from "../configs/header.config.js";
|
||||||
import { BASE_URL } from "../configs/dataUrl.js";
|
import { URLS } from "../configs/dataUrl.js";
|
||||||
|
|
||||||
const extractStreamInfo = async (episodeId, server = "hd-1", type = "sub") => {
|
const extractStreamInfo = async (episodeId) => {
|
||||||
try {
|
try {
|
||||||
const url = `${BASE_URL}/watch/${episodeId}`;
|
const url = `${URLS.serverList}?episodeId=${episodeId}`;
|
||||||
const { data } = await axios.get(url, { headers });
|
const { data } = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
...headers,
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const servers = [];
|
const servers = [];
|
||||||
@@ -20,35 +26,20 @@ const extractStreamInfo = async (episodeId, server = "hd-1", type = "sub") => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const sources = [];
|
const links = [];
|
||||||
$(".player-source .source-item, .embed-source").each((i, el) => {
|
$("li[data-link-id]").each((i, el) => {
|
||||||
const sourceId = $(el).attr("data-id") || "";
|
const linkId = $(el).attr("data-link-id") || "";
|
||||||
const sourceUrl = $(el).attr("data-src") || $(el).find("iframe").attr("src") || "";
|
const linkType = $(el).attr("data-type") || "hls";
|
||||||
const sourceType = $(el).attr("data-type") || "hls";
|
|
||||||
|
|
||||||
if (sourceId || sourceUrl) {
|
if (linkId) {
|
||||||
sources.push({ id: sourceId, url: sourceUrl, type: sourceType });
|
links.push({ id: linkId, type: linkType });
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const subtitles = [];
|
|
||||||
$(".player-sub .sub-item, .subtitle-item").each((i, el) => {
|
|
||||||
const subUrl = $(el).attr("data-url") || $(el).attr("src") || "";
|
|
||||||
const subLabel = $(el).text().trim() || "";
|
|
||||||
const subLang = $(el).attr("data-lang") || "en";
|
|
||||||
|
|
||||||
if (subUrl) {
|
|
||||||
subtitles.push({ url: subUrl, label: subLabel, language: subLang });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
episodeId,
|
episodeId,
|
||||||
server,
|
|
||||||
type,
|
|
||||||
servers,
|
servers,
|
||||||
sources,
|
links
|
||||||
subtitles
|
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|||||||
@@ -11,15 +11,13 @@ const extractSuggestions = async (keyword) => {
|
|||||||
|
|
||||||
const suggestions = [];
|
const suggestions = [];
|
||||||
$(".search-suggest .film-detail, .nav-item a").each((i, el) => {
|
$(".search-suggest .film-detail, .nav-item a").each((i, el) => {
|
||||||
const id = $(el).attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find("img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name, .dynamic-name").text().trim() || "";
|
const title = $(el).find(".film-name, .name").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const type = $(el).find(".fdi-item:first-child").text().trim() || "";
|
||||||
const showType = $(el).find(".fdi-item:first-child").text().trim() || "";
|
|
||||||
const releaseDate = $(el).find(".fdi-item:nth-child(2)").text().trim() || "";
|
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
suggestions.push({ id, poster, title, japaneseTitle, showType, releaseDate });
|
suggestions.push({ slug, poster, title, type });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,46 +5,52 @@ import { URLS } from "../configs/dataUrl.js";
|
|||||||
|
|
||||||
const extractTopTen = async () => {
|
const extractTopTen = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await axios.get(URLS.topTen, { headers });
|
const { data } = await axios.get(URLS.home, { headers });
|
||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const today = [];
|
const today = [];
|
||||||
const week = [];
|
const week = [];
|
||||||
const month = [];
|
const month = [];
|
||||||
|
|
||||||
$(".top-ten-list.today .top-ten-item, #top-viewed-day .top-list-item").each((i, el) => {
|
$("#top-anime .tab-content[data-name='day'] a.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const name = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const name = $(el).find(".name").text().trim() || "";
|
||||||
const number = parseInt($(el).find(".number").text().trim()) || i + 1;
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".type").text().trim() || "";
|
||||||
|
const rank = i + 1;
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
today.push({ id, number, name, poster, tvInfo: { showType } });
|
today.push({ slug, rank, name, poster, sub, dub, type });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".top-ten-list.week .top-ten-item, #top-viewed-week .top-list-item").each((i, el) => {
|
$("#top-anime .tab-content[data-name='week'] a.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const name = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const name = $(el).find(".name").text().trim() || "";
|
||||||
const number = parseInt($(el).find(".number").text().trim()) || i + 1;
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".type").text().trim() || "";
|
||||||
|
const rank = i + 1;
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
week.push({ id, number, name, poster, tvInfo: { showType } });
|
week.push({ slug, rank, name, poster, sub, dub, type });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".top-ten-list.month .top-ten-item, #top-viewed-month .top-list-item").each((i, el) => {
|
$("#top-anime .tab-content[data-name='month'] a.item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const name = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const name = $(el).find(".name").text().trim() || "";
|
||||||
const number = parseInt($(el).find(".number").text().trim()) || i + 1;
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
const showType = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".type").text().trim() || "";
|
||||||
|
const rank = i + 1;
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
month.push({ id, number, name, poster, tvInfo: { showType } });
|
month.push({ slug, rank, name, poster, sub, dub, type });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,27 @@ const extractTrending = async () => {
|
|||||||
const $ = cheerio.load(data);
|
const $ = cheerio.load(data);
|
||||||
|
|
||||||
const trending = [];
|
const trending = [];
|
||||||
$(".trending-list .film-detail, .trending-item").each((i, el) => {
|
$(".section-updated .item, #recent-update .item").each((i, el) => {
|
||||||
const id = $(el).find("a").attr("href")?.split("/").pop() || "";
|
const slug = $(el).find("a.name.d-title").attr("href")?.split("/watch/").pop() || "";
|
||||||
const poster = $(el).find("img").attr("src") || "";
|
const poster = $(el).find(".poster img").attr("src") || "";
|
||||||
const title = $(el).find(".film-name a, .dynamic-name").text().trim() || "";
|
const title = $(el).find("a.name.d-title").text().trim() || "";
|
||||||
const japaneseTitle = $(el).find(".fd-infor .fdi-item:first-child").text().trim() || "";
|
const japaneseTitle = $(el).find("a.name.d-title").attr("data-jp") || "";
|
||||||
const number = parseInt($(el).find(".number").text().trim()) || i + 1;
|
const sub = parseInt($(el).find(".ep-status.sub span").text().trim()) || 0;
|
||||||
|
const dub = parseInt($(el).find(".ep-status.dub span").text().trim()) || 0;
|
||||||
|
const total = parseInt($(el).find(".ep-status.total span").text().trim()) || 0;
|
||||||
|
const type = $(el).find(".meta .inner .right").text().trim() || "";
|
||||||
|
|
||||||
if (id) {
|
if (slug) {
|
||||||
trending.push({ id, number, poster, title, japaneseTitle });
|
trending.push({
|
||||||
|
slug,
|
||||||
|
poster,
|
||||||
|
title,
|
||||||
|
japaneseTitle,
|
||||||
|
sub,
|
||||||
|
dub,
|
||||||
|
total,
|
||||||
|
type
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user