Loading snippets...
get home, search, detail, episode anichin lengkap, by Shan
/**
* Anichin Complete
* base: https://anichin.moe
* Creator: ShanMolvyr
* Jangan Hapus Kreator woi ^_^
* Note: cek https://snippet.vyr.my.id/shanmolvyr/anichin/README.md
* Sumber: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q
*/
const axios = require("axios");
const cheerio = require("cheerio");
const { URL } = require("url");
const BASE_URL = process.env.ANICHIN_URL || "https://anichin.moe";
const TIMEOUT = parseInt(process.env.API_TIMEOUT || "10") * 1000;
const USER_AGENT = process.env.USER_AGENT || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
const CF_CLEARANCE = process.env.CF_CLEARANCE || "";
function buildHeaders(extra = {}) {
const h = {
"User-Agent": USER_AGENT,
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
Connection: "keep-alive",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "none",
...extra,
};
if (CF_CLEARANCE) h["Cookie"] = `cf_clearance=${CF_CLEARANCE}`;
return h;
}
function resolveUrl(slug) {
if (!slug || slug === "") return BASE_URL;
if (slug.startsWith("http")) return slug;
if (slug.startsWith("/")) return `${BASE_URL}${slug}`;
return `${BASE_URL}/${slug}`;
}
async function fetchHtml(slug) {
const url = resolveUrl(slug);
try {
const res = await axios.get(url, { headers: buildHeaders(), timeout: TIMEOUT, decompress: true });
return { html: res.data, sourceUrl: url };
} catch (err) {
throw new Error(`Fetch failed [${url}]: ${err.message}`);
}
}
function extractSlug(href) {
try {
const parts = new URL(href, BASE_URL).pathname.split("/").filter(Boolean);
return parts[parts.length - 1] || parts[parts.length - 2] || "";
} catch {
return href.split("/").filter(Boolean).pop() || "";
}
}
function parseCards($, wrapper) {
const cards = [];
$(wrapper).find("article").each((_, article) => {
const ttDiv = $(article).find("div.tt");
if (!ttDiv.length) return;
const h2 = ttDiv.find("h2");
const headline = h2.text().trim();
h2.remove();
const title = ttDiv.text().trim() || headline;
const type = $(article).find("div.typez").text().trim() || "Unknown";
const status = $(article).find("span.epx").text().trim() || "Unknown";
const img = $(article).find("img[src]").first();
const thumbnail = img.attr("data-lazy-src") || img.attr("src") || "";
const a = $(article).find("a[title]").first();
if (!a.length) return;
const slug = extractSlug(a.attr("href") || "");
if (!slug) return;
cards.push({ title, type, headline, status, thumbnail, slug });
});
return cards;
}
function parseInfoDetails($) {
const info = {};
$("div.info-content div.spe span").each((_, span) => {
const text = $(span).text().trim();
const idx = text.indexOf(":");
if (idx !== -1) {
const key = text.slice(0, idx).trim().toLowerCase().replace(/\s+/g, "_");
const val = text.slice(idx + 1).trim();
if (key && val) info[key] = val;
}
});
return info;
}
async function getHome(page = 1) {
const { html, sourceUrl } = await fetchHtml(page > 1 ? `/page/${page}/` : "");
const $ = cheerio.load(html);
const sections = [];
$("div.bixbox.bbnofrm").each((_, section) => {
const sectionEl = $(section).find("div.releases").children().first();
const sectionName = sectionEl.length
? sectionEl.text().trim().toLowerCase().replace(/\s+/g, "_")
: "unknown";
const cards = [];
$(section).find("article").each((_, article) => {
const a = $(article).find("a[href]").first();
if (!a.length) return;
const href = a.attr("href") || "";
let title = a.attr("title") || "";
if (!title) {
const ttDiv = $(article).find("div.tt");
title = ttDiv.find("h2").text().trim() || ttDiv.text().trim();
}
if (!title || title.length < 2) return;
const headline = $(article).find("h2").text().trim() || title;
const type = $(article).find("[class*='typez']").text().trim() || "Unknown";
const epsText = $(article).find("span.epx").text().replace(/\D/g, "");
const eps = epsText ? parseInt(epsText) : null;
const thumbnail = $(article).find("img[src]").first().attr("src") || "";
const slug = extractSlug(href);
if (!slug) return;
cards.push({ title, type, headline, eps, thumbnail, slug });
});
if (cards.length) sections.push({ section: sectionName, cards });
});
return { results: sections, page, total: sections.length, source: sourceUrl };
}
async function search(query) {
const { html, sourceUrl } = await fetchHtml(`/?s=${encodeURIComponent(query)}`);
const $ = cheerio.load(html);
const cards = parseCards($, "div.bixbox div.listupd");
return { results: cards, query, total: cards.length, source: sourceUrl };
}
async function getAnimeList() {
const { html, sourceUrl } = await fetchHtml("/anime");
const $ = cheerio.load(html);
const cards = parseCards($, "div.bixbox div.listupd");
return { results: cards, total: cards.length, source: sourceUrl };
}
async function getInfo(slug) {
const { html, sourceUrl } = await fetchHtml(slug);
const $ = cheerio.load(html);
const name = $("div.infox h1.entry-title[itemprop='name']").text().trim() || "Unknown Title";
const thumbImg = $("div.thumb img").first();
const thumbnail = thumbImg.attr("data-lazy-src") || thumbImg.attr("src") || "";
const genres = [];
$("div.genxed a").each((_, a) => { const g = $(a).text().trim(); if (g) genres.push(g); });
const infoDetails = parseInfoDetails($);
let rating = null;
const ratingStrong = $("div.rating strong").first();
if (ratingStrong.length) {
const parts = ratingStrong.text().trim().split(/\s+/);
rating = parts[1] || parts[0] || null;
} else {
const ns = $("div.rating div.numscore").first();
if (ns.length) rating = ns.text().trim();
}
const synDiv = $("div.entry-content[itemprop='description']");
const synTitle = (synDiv.find("h1").text().trim() || "") + (synDiv.find("h1").length ? " - " : "");
const synParagraphs = [];
synDiv.find("p").each((_, p) => { const t = $(p).text().trim(); if (t) synParagraphs.push(t); });
const episodes = [];
$("div.eplister ul li").each((_, li) => {
const a = $(li).find("a").first();
if (!a.length || !a.attr("href")) return;
const epSlug = new URL(a.attr("href"), BASE_URL).pathname.replace(/^\/|\/$/g, "");
const subtitle = $(li).find("div.epl-title").text().trim() || "Unknown";
const date = $(li).find("div.epl-date").text().trim() || "Unknown Date";
const episode = $(li).find("div.epl-num").text().trim() || null;
episodes.push({ slug: epSlug, subtitle, date, episode, thumbnail });
});
return {
result: { ...infoDetails, name, thumbnail, genre: genres, rating, sinopsis: { title: synTitle.trim(), paragraphs: synParagraphs }, episode: episodes },
source: sourceUrl,
};
}
async function getEpisode(slug) {
const { html, sourceUrl } = await fetchHtml(slug);
const $ = cheerio.load(html);
const name = $("h2[itemprop='partOfSeries']").text().trim() || "Unknown Episode";
let rootSlug = "unknown";
const bc = $("div.ts-breadcrumb li");
if (bc.length > 1) {
const a = bc.eq(1).find("a").first();
if (a.length) rootSlug = extractSlug(a.attr("href") || "");
}
const thumbEl = $("div.thumbnail img").first().length ? $("div.thumbnail img").first() : $("div.thumb img").first();
const thumbnail = thumbEl.attr("data-lazy-src") || thumbEl.attr("src") || null;
const genres = [];
$("div.genxed a").each((_, a) => { const g = $(a).text().trim(); if (g) genres.push(g); });
const infoDetails = parseInfoDetails($);
const servers = [];
$("select.mirror option").each((_, opt) => {
const label = $(opt).text().trim();
const rawValue = $(opt).attr("value") || "";
if (!rawValue) return;
let embedUrl = null;
try {
const decoded = Buffer.from(rawValue, "base64").toString("utf-8");
embedUrl = cheerio.load(decoded)("iframe").attr("src") || null;
} catch { }
servers.push({ label, embedUrl });
});
const downloads = [];
$("div.soraurlx").each((_, row) => {
const quality = $(row).find("strong").text().trim() || "Unknown";
const links = [];
$(row).find("a").each((_, a) => { links.push({ host: $(a).text().trim(), url: $(a).attr("href") || "" }); });
if (links.length) downloads.push({ quality, links });
});
const prevA = $("div.naveps a.prev, a[rel='prev']").first();
const nextA = $("div.naveps a.next, a[rel='next']").first();
return {
result: {
...infoDetails, name, rootSlug, thumbnail, genre: genres, servers, downloads,
navigation: {
prev: prevA.length ? extractSlug(prevA.attr("href") || "") : null,
next: nextA.length ? extractSlug(nextA.attr("href") || "") : null,
},
},
source: sourceUrl,
};
}
async function listGenres() {
const { html, sourceUrl } = await fetchHtml("/anime");
const $ = cheerio.load(html);
const genres = [];
$("input[name='genre[]'][value]").each((_, input) => {
const value = $(input).attr("value") || "";
if (!value) return;
const name = value.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
genres.push({ name, slug: value });
});
return { genres, total: genres.length, source: sourceUrl };
}
async function getGenre(slug, page = 1) {
let url = `/anime?genre[]=${slug}`;
if (page > 1) url += `&page=${page}`;
const { html, sourceUrl } = await fetchHtml(url);
const $ = cheerio.load(html);
const cards = parseCards($, "div.bixbox div.listupd");
return { results: cards, slug, page, total: cards.length, source: sourceUrl };
}
async function main() {
const args = process.argv.slice(2);
const cmd = args[0];
if (!cmd) {
console.log(`node anichin.js <command> [args]
home [page]
search <query>
anime
info <slug>
episode <slug>
genre list
genre get <slug> [page]`);
process.exit(0);
}
try {
let result;
switch (cmd) {
case "home": result = await getHome(parseInt(args[1]) || 1); break;
case "search": if (!args[1]) throw new Error("search <query>"); result = await search(args.slice(1).join(" ")); break;
case "anime": result = await getAnimeList(); break;
case "info": if (!args[1]) throw new Error("info <slug>"); result = await getInfo(args[1]); break;
case "episode": if (!args[1]) throw new Error("episode <slug>"); result = await getEpisode(args[1]); break;
case "genre":
if (args[1] === "list") result = await listGenres();
else if (args[1] === "get") { if (!args[2]) throw new Error("genre get <slug> [page]"); result = await getGenre(args[2], parseInt(args[3]) || 1); }
else throw new Error("genre list | genre get <slug> [page]");
break;
default: throw new Error(`Unknown command: ${cmd}`);
}
console.log(JSON.stringify(result, null, 2));
} catch (err) {
console.error("Error:", err.message);
process.exit(1);
}
}
main();
bashnpm install axios cheerio
bashnode anichin.js home # halaman 1 node anichin.js home 2 # halaman 2 node anichin.js search "solo leveling" node anichin.js anime # daftar semua anime node anichin.js info <slug-anime> node anichin.js episode <slug-episode> node anichin.js genre list node anichin.js genre get action node anichin.js genre get action 2
bashby ShanMolvyr