Created
December 12, 2025 01:47
-
-
Save alip-jmbd/46f682de822b01d90fc6f3c0cf4cc69f to your computer and use it in GitHub Desktop.
Scraper Anichin.cafe, search, detail, stream (embed), ongoing, completed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // install dulu | |
| // npm i @elainaa/tls cheerio commander | |
| import { ElainaTLS } from '@elainaa/tls' | |
| import * as cheerio from 'cheerio' | |
| import { program } from 'commander' | |
| const client = new ElainaTLS() | |
| const BASE_URL = 'https://anichin.cafe' | |
| const fetchHTML = async (url) => { | |
| try { | |
| const response = await client.fetch(url, { | |
| headers: { | |
| 'Referer': BASE_URL, | |
| 'Upgrade-Insecure-Requests': '1', | |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' | |
| } | |
| }) | |
| if (response.status !== 200) { | |
| throw new Error(`Status Code: ${response.status}`) | |
| } | |
| return cheerio.load(response.body) | |
| } catch (error) { | |
| console.log(JSON.stringify({ status: false, message: error.message }, null, 2)) | |
| process.exit(1) | |
| } | |
| } | |
| const parseList = ($) => { | |
| const results = [] | |
| $('.listupd .bs').each((i, el) => { | |
| const title = $(el).find('.tt h2').text().trim() | |
| const url = $(el).find('a').attr('href') | |
| const image = $(el).find('img').attr('src') | |
| const type = $(el).find('.typez').text().trim() | |
| const status = $(el).find('.status').text().trim() | |
| const episode = $(el).find('.epx').text().trim() | |
| results.push({ title, url, image, type, status, episode }) | |
| }) | |
| return results | |
| } | |
| const search = async (query) => { | |
| const url = `${BASE_URL}/?s=${encodeURIComponent(query)}` | |
| const $ = await fetchHTML(url) | |
| const data = parseList($) | |
| console.log(JSON.stringify({ status: true, data }, null, 2)) | |
| } | |
| const ongoing = async (page = 1) => { | |
| const url = page == 1 ? `${BASE_URL}/ongoing/` : `${BASE_URL}/ongoing/page/${page}/` | |
| const $ = await fetchHTML(url) | |
| const data = parseList($) | |
| console.log(JSON.stringify({ status: true, page: parseInt(page), data }, null, 2)) | |
| } | |
| const completed = async (page = 1) => { | |
| const url = page == 1 ? `${BASE_URL}/completed/` : `${BASE_URL}/completed/page/${page}/` | |
| const $ = await fetchHTML(url) | |
| const data = parseList($) | |
| console.log(JSON.stringify({ status: true, page: parseInt(page), data }, null, 2)) | |
| } | |
| const detail = async (url) => { | |
| const $ = await fetchHTML(url) | |
| const title = $('.entry-title').text().trim() | |
| const alt_title = $('.alter').text().trim() | |
| const image = $('.thumb img').attr('src') | |
| const rating = $('.rating strong').text().replace('Rating', '').trim() | |
| const synopsis = $('.entry-content[itemprop="description"]').text().trim() | |
| const metadata = {} | |
| $('.infox .spe span').each((i, el) => { | |
| const key = $(el).find('b').text().replace(':', '').trim().toLowerCase().replace(/\s/g, '_') | |
| const val = $(el).text().replace($(el).find('b').text(), '').trim() | |
| if(key) metadata[key] = val | |
| }) | |
| const genres = [] | |
| $('.genxed a').each((i, el) => genres.push($(el).text())) | |
| const episodes = [] | |
| const episodeList = $('.eplister ul li') | |
| episodeList.each((i, el) => { | |
| const epNum = $(el).find('.epl-num').text().trim() | |
| const epTitle = $(el).find('.epl-title').text().trim() | |
| const epUrl = $(el).find('a').attr('href') | |
| const date = $(el).find('.epl-date').text().trim() | |
| episodes.push({ | |
| episode: epNum, | |
| title: epTitle, | |
| url: epUrl, | |
| release_date: date | |
| }) | |
| }) | |
| console.log(JSON.stringify({ | |
| status: true, | |
| data: { | |
| title, | |
| alternative_title: alt_title, | |
| image, | |
| rating, | |
| synopsis, | |
| metadata, | |
| genres, | |
| episodes | |
| } | |
| }, null, 2)) | |
| } | |
| const stream = async (url) => { | |
| const $ = await fetchHTML(url) | |
| const title = $('.entry-title').text().trim() | |
| const seriesUrl = $('.ts-breadcrumb ol li:nth-child(2) a').attr('href') | |
| const prevUrl = $('.naveps .nvs a[rel="prev"]').attr('href') || null | |
| const nextUrl = $('.naveps .nvs a[rel="next"]').attr('href') || null | |
| const streamServers = [] | |
| const defaultEmbed = $('#pembed iframe').attr('src') | |
| if(defaultEmbed) { | |
| streamServers.push({ name: 'Default', url: defaultEmbed }) | |
| } | |
| $('select.mirror option').each((i, el) => { | |
| const name = $(el).text().trim() | |
| const val = $(el).val() | |
| if (val) { | |
| try { | |
| const decoded = Buffer.from(val, 'base64').toString('utf-8') | |
| const srcMatch = decoded.match(/src="([^"]+)"/) | |
| if (srcMatch && srcMatch[1]) { | |
| streamServers.push({ name, url: srcMatch[1] }) | |
| } | |
| } catch (e) {} | |
| } | |
| }) | |
| const downloadLinks = [] | |
| $('.soraurlx').each((i, el) => { | |
| const resolution = $(el).find('strong').text().trim() | |
| const links = [] | |
| $(el).find('a').each((j, link) => { | |
| links.push({ | |
| host: $(link).text().trim(), | |
| url: $(link).attr('href') | |
| }) | |
| }) | |
| downloadLinks.push({ resolution, links }) | |
| }) | |
| console.log(JSON.stringify({ | |
| status: true, | |
| data: { | |
| title, | |
| series_url: seriesUrl, | |
| navigation: { prev: prevUrl, next: nextUrl }, | |
| streams: streamServers, | |
| downloads: downloadLinks | |
| } | |
| }, null, 2)) | |
| } | |
| program | |
| .name('anichin') | |
| .description('CLI Data Scraper for Anichin.cafe') | |
| .version('1.0.0') | |
| program.command('search') | |
| .argument('<keyword>', 'Search query') | |
| .action(search) | |
| program.command('ongoing') | |
| .argument('[page]', 'Page number', 1) | |
| .action(ongoing) | |
| program.command('completed') | |
| .argument('[page]', 'Page number', 1) | |
| .action(completed) | |
| program.command('detail') | |
| .argument('<url>', 'Anime URL') | |
| .action(detail) | |
| program.command('stream') | |
| .argument('<url>', 'Episode URL') | |
| .action(stream) | |
| program.parse() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Filter nya malas imtu amja omkeh