Back to Home
Gistify Snippet
kyiov
Sep 4·1 file(s)
Public

Scraper animeindo

Scraper animeindo

#github-gist#javascript#gist:a6480100e68ba991e6b7d021dc6831aa#gistsync:2026-09-02T12:40:33Z
1
Raw
animeindo.jsjavascript
240 lines
1
2
3import * as __ns_513 from 'cheerio'
4const cheerio = __ns_513
5import * as __ns_514 from 'node:url'
6const pathToFileURL = __ns_514.pathToFileURL
7
8const BASE = 'https://anime-indo.lol'
9const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
10
11async function get(url, referer = BASE + '/') {
12 const r = await fetch(url, {
13 headers: {
14 'User-Agent': UA,
15 Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
16 'Accept-Language': 'id-ID,id;q=0.9,en;q=0.8',
17 Referer: referer,
18 },
19 redirect: 'follow',
20 signal: AbortSignal.timeout(25000),
21 })
22 const body = await r.text()
23 if (r.status !== 200) throw new Error(`HTTP ${r.status} untuk ${url}`)
24 return body
25}
26
27function toAbs(u) {
28 if (!u) return ''
29 if (u.startsWith('http')) return u
30 return BASE + (u.startsWith('/') ? u : '/' + u)
31}
32
33async function searchAnime(query) {
34 const q = String(query || '').trim()
35 if (!q) return []
36 const seen = new Set()
37 const results = []
38
39 try {
40 const html = await get(`${BASE}/search.php?q=${encodeURIComponent(q)}`)
41 const $ = cheerio.load(html)
42 $('a[href*="/anime/"]').each((i, el) => {
43 const href = $(el).attr('href') || ''
44 const title = $(el).text().trim().replace(/\s+/g, ' ')
45 if (!href || seen.has(href)) return
46 seen.add(href)
47 results.push({
48 source: 'animeindo',
49 title: title || href.replace(/^\/anime\//, '').replace(/\/$/, '').replace(/-/g, ' '),
50 url: toAbs(href),
51 image: '',
52 type: '',
53 })
54 })
55 } catch {}
56
57 if (!results.length) {
58 try {
59 const html = await get(`${BASE}/anime-list/`)
60 const $ = cheerio.load(html)
61 const ql = q.toLowerCase()
62 const tokens = ql.split(/[^a-z0-9]+/).filter(t => t.length > 1)
63 const scored = []
64 $('a[href*="/anime/"]').each((i, el) => {
65 const href = $(el).attr('href') || ''
66 const title = $(el).text().trim().replace(/\s+/g, ' ')
67 if (!href || seen.has(href)) return
68 const hay = (title + ' ' + href).toLowerCase()
69 const hits = tokens.filter(t => hay.includes(t)).length
70 if (!hits) return
71 seen.add(href)
72 scored.push({
73 score: hits,
74 item: {
75 source: 'animeindo',
76 title: title || href.replace(/^\/anime\//, '').replace(/\/$/, '').replace(/-/g, ' '),
77 url: toAbs(href),
78 image: '',
79 type: '',
80 },
81 })
82 })
83 scored.sort((a, b) => b.score - a.score)
84 scored.forEach(s => results.push(s.item))
85 } catch {}
86 } else {
87 const ql = q.toLowerCase()
88 const tokens = ql.split(/[^a-z0-9]+/).filter(t => t.length > 1)
89 results.sort((a, b) => {
90 const sa = tokens.filter(t => a.title.toLowerCase().includes(t)).length
91 const sb = tokens.filter(t => b.title.toLowerCase().includes(t)).length
92 return sb - sa
93 })
94 }
95
96 return results
97}
98
99async function getAnimeDetail(url) {
100 const html = await get(url)
101 const $ = cheerio.load(html)
102
103 const title = $('h1.entry-title, h1').first().text().trim() || $('title').text().replace(/ - Animeindo.*$/i, '').trim()
104 const img = $('.animeinfo img, .poster img, .thumb img').first().attr('src') || ''
105 const genres = []
106 $('a[href*="/genre/"]').each((i, el) => {
107 const t = $(el).text().trim()
108 if (t && !genres.includes(t)) genres.push(t)
109 })
110
111 const episodes = []
112 $('div.ep a[href*="episode-"]').each((i, el) => {
113 const href = $(el).attr('href') || ''
114 const m = href.match(/episode-(\d+)/)
115 if (!m) return
116 episodes.push({
117 number: parseInt(m[1], 10),
118 title: `Episode ${m[1]}`,
119 url: toAbs(href),
120 date: '',
121 })
122 })
123 episodes.sort((a, b) => a.number - b.number)
124
125 return { source: 'animeindo', title, image: img, genres, synopsis: '', episodes }
126}
127
128async function getEpisode(url) {
129 const html = await get(url, url)
130 const $ = cheerio.load(html)
131
132 const title = $('h1.entry-title, h1').first().text().trim() || $('title').text().replace(/ - Animeindo.*$/i, '').trim()
133
134 let iframe = ''
135 $('iframe').each((i, el) => {
136 const src = $(el).attr('src') || $(el).attr('data-src') || ''
137 if (src) { iframe = src; return false }
138 })
139
140 const downloadLinks = []
141 const seenDl = new Set()
142 $('a').each((i, el) => {
143 const href = $(el).attr('href') || ''
144 const txt = $(el).text().trim().replace(/\s+/g, ' ').slice(0, 60)
145 if (!href || href === '#' || href.startsWith(BASE) || /^(javascript|mailto):/i.test(href)) return
146 if (seenDl.has(href)) return
147 seenDl.add(href)
148 downloadLinks.push({ label: txt || 'link', url: href })
149 })
150
151 return { source: 'animeindo', title, iframe: toAbs(iframe), downloadLinks }
152}
153
154async function resolveVideo(iframeUrl) {
155 if (!iframeUrl) return null
156 if (/\/(?:mp4|webm|mkv)(?:\?|$)/i.test(iframeUrl)) return iframeUrl
157 if (/m3u8/i.test(iframeUrl)) return iframeUrl
158
159 if (iframeUrl.includes('btube3.php')) {
160 try {
161 const body = await get(iframeUrl, BASE + '/')
162 const m = body.match(/https?:\\?\/\\?\/[^"'\s<>\\]+googlevideo\.com\/videoplayback[^"'\s<>\\]*/i)
163 if (m) {
164 return m[0]
165 .replace(/\\u002f/gi, '/')
166 .replace(/\\u0026/gi, '&')
167 .replace(/\\\//g, '/')
168 .replace(/\\/g, '')
169 }
170 const alt = body.match(/https?:\\?\/\\?\/[^"'\s<>\\]+(?:\.m3u8|\.mp4)[^"'\s<>\\]*/i)
171 if (alt) return alt[0].replace(/\\u002f/gi, '/').replace(/\\u0026/gi, '&').replace(/\\\//g, '/')
172 return null
173 } catch {
174 return null
175 }
176 }
177
178 if (/hls\.php|xtwap/i.test(iframeUrl)) {
179 try {
180 const body = await get(iframeUrl, BASE + '/')
181 const m = body.match(/https?:\\?\/\\?\/[^"'\s<>\\]+\.m3u8[^"'\s<>\\]*/i)
182 if (m) {
183 return m[0]
184 .replace(/\\u002f/gi, '/')
185 .replace(/\\u0026/gi, '&')
186 .replace(/\\\//g, '/')
187 .replace(/\\/g, '')
188 }
189 return null
190 } catch {
191 return null
192 }
193 }
194
195 return iframeUrl
196}
197
198const __dflt_7 = { searchAnime, getAnimeDetail, getEpisode, resolveVideo }
199
200if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
201 const __exports = { searchAnime: searchAnime, getAnimeDetail: getAnimeDetail, getEpisode: getEpisode, resolveVideo: resolveVideo, default: __dflt_7 }
202 const __fns = Object.fromEntries(Object.entries(__exports).filter(([, v]) => typeof v === 'function'))
203 const __names = Object.keys(__fns)
204 if (__names.length === 0) {
205 console.log('Tidak ada fungsi yang bisa di-demo. Import modul ini dari kode lain.')
206 } else {
207 const __search = __names.find((n) => /search|cari/i.test(n))
208 const __name = __search || __names[0]
209 const __fn = __fns[__name]
210 const __isClass = /^class\s/.test(Function.prototype.toString.call(__fn))
211 const __defaultArgs = (() => {
212 const n = __name.toLowerCase()
213 if (/emoji/.test(n)) return [{ emoji: '😀😃' }]
214 if (/textpro|generate.*text|effect/.test(n)) return ['neon', 'Hello']
215 if (/image|wallpaper|search|query|anime|movie|manga|news|lyrics|wiki|quote|stalk|detail/.test(n)) return ['naruto']
216 if (/download|youtube|video|audio|music|spotify|tiktok|instagram|facebook|twitter|reels|terabox|media/.test(n)) return ['https://www.youtube.com/watch?v=dQw4w9WgXcQ']
217 if (/dork/.test(n)) return ['example.com', 'documents']
218 if (/tts|speech|prompt|chat|ask|translate/.test(n)) return ['Hello world']
219 if (/parse|format|normalize|list|features/.test(n)) return []
220 return ['test']
221 })()
222 let __args = __defaultArgs
223 if (process.argv.length > 2) __args = process.argv.slice(2).map((a) => { try { return JSON.parse(a) } catch { return a } })
224 if (__args.length === 0 && !__search && !__isClass && __fn.length > 0) {
225 console.log('Fungsi: ' + __names.join(', '))
226 console.log('Contoh: node ' + "animeindo.js" + ' ' + __name + ' <arg>')
227 } else {
228 ;(async () => {
229 try {
230 const r = __isClass ? new __fn(...__args) : await __fn(...__args)
231 console.log(JSON.stringify(r, null, 2))
232 } catch (e) {
233 console.error('Error: ' + (e && e.message ? e.message : e))
234 process.exitCode = 1
235 }
236 })()
237 }
238 }
239}
240
Discussion & Comments0

No comments yet — be the first to leave feedback or start a code discussion.

Sign in with your GitHub account to write comments and join the discussion.