Streaming anime
No comments yet. Be the first to comment!
1/**
2 * ANIME LOVERS
3 * Author : Gienetic
4 * Base : https://play.google.com/store/apps/details?id=com.aniverseid.animelovers
5 * Note : Kaya nya dev apk nya orang indo (mungkin diantara kita :v), maaf ya bang , anggap aja sebagai pengujian apk nya
6 sekali lagi maaf ya puh
7 */
8
9
10const axios = require('axios');
11const crypto = require('crypto');
12const readline = require('readline');
13const fs = require('fs');
14
15const API_URL = 'https://apps.animekita.org/api/v1.2.5';
16
17// ==========================================
18// CORE ENGINE
19// ==========================================
20
21const getHeaders = (isFlutter = false, isPost = false) => {
22 const headers = {
23 'accept': 'application/json',
24 'accept-encoding': 'gzip',
25 'host': 'apps.animekita.org',
26 'access-control-allow-origin': '*',
27 'user-agent': isFlutter ? 'Flutter/2.5.3' : 'Dart/3.9 (dart:io)'
28 };
29 if (isPost) headers['content-type'] = 'text/plain; charset=utf-8';
30 return headers;
31};
32
33const loginGuest = async () => {
34 const uid = crypto.randomBytes(5).toString('hex');
35 const payload = {
36 user: `Guest_${uid}`,
37 email: `gienetic_${uid}@gmail.com`,
38 profil: "https://lh3.googleusercontent.com/a/default"
39 };
40 try {
41 const res = await axios.post(`${API_URL}/model/login.php`, JSON.stringify(payload), {
42 headers: getHeaders(false, true)
43 });
44 return res.data?.data?.[0]?.token || null;
45 } catch (err) {
46 return null;
47 }
48};
49
50const getRekomendasi = async () => {
51 try {
52 const res = await axios.get(`${API_URL}/rekomendasi.php`, {
53 headers: getHeaders()
54 });
55 return res.data || [];
56 } catch (err) {
57 return [];
58 }
59};
60
61const getOngoing = async (page = 1) => {
62 try {
63 const res = await axios.get(`${API_URL}/home/ongoing.php`, {
64 headers: getHeaders(),
65 params: {
66 page,
67 type: 'all'
68 }
69 });
70 return res.data || [];
71 } catch (err) {
72 return [];
73 }
74};
75
76const getBaruUpload = async (page = 1) => {
77 try {
78 const res = await axios.get(`${API_URL}/baruupload.php`, {
79 headers: getHeaders(),
80 params: {
81 page
82 }
83 });
84 return res.data || [];
85 } catch (err) {
86 return [];
87 }
88};
89
90const getMovie = async (page = 1) => {
91 try {
92 const res = await axios.get(`${API_URL}/movie.php`, {
93 headers: getHeaders(),
94 params: {
95 page
96 }
97 });
98 return res.data || [];
99 } catch (err) {
100 return [];
101 }
102};
103
104// Fungsi Baru: Mendapatkan Jadwal Rilis Mingguan
105const getJadwal = async () => {
106 try {
107 // Method POST, tapi payload kosong sesuai capture
108 const res = await axios.post(`${API_URL}/jadwal.php`, "", {
109 headers: getHeaders(false, true)
110 });
111 return res.data?.data || [];
112 } catch (err) {
113 return [];
114 }
115};
116
117const searchAnime = async (keyword, page = 1) => {
118 try {
119 const res = await axios.get(`${API_URL}/search.php`, {
120 headers: getHeaders(),
121 params: {
122 keyword,
123 page,
124 per_page: 20
125 }
126 });
127 return res.data?.data?.[0] || null;
128 } catch (err) {
129 return null;
130 }
131};
132
133const getSeriesDetail = async (seriesSlug, token = "") => {
134 const payload = {
135 get: "top",
136 post_type: "1",
137 post_id: seriesSlug,
138 token
139 };
140 try {
141 const res = await axios.post(`${API_URL}/series.php?url=${seriesSlug}`, JSON.stringify(payload), {
142 headers: getHeaders(false, true)
143 });
144 return res.data?.data?.[0] || null;
145 } catch (err) {
146 return null;
147 }
148};
149
150const getStreamLinks = async (postId, seriesSlug, episode, token) => {
151 const payload = {
152 post_type: "2",
153 post_id: postId,
154 series_id: seriesSlug,
155 series_url: seriesSlug,
156 episode: episode.toString(),
157 token
158 };
159 try {
160 const res = await axios.post(`${API_URL}/series/episode/data.php?url=${postId}`, JSON.stringify(payload), {
161 headers: getHeaders(true, true)
162 });
163 return res.data?.data?.[0]?.streams || null;
164 } catch (err) {
165 return null;
166 }
167};
168
169module.exports = {
170 loginGuest,
171 getRekomendasi,
172 getOngoing,
173 getBaruUpload,
174 getMovie,
175 getJadwal,
176 searchAnime,
177 getSeriesDetail,
178 getStreamLinks
179};
180
181// ==========================================
182// CLI ENGINE
183// ==========================================
184
185if (require.main === module) {
186 const rl = readline.createInterface({
187 input: process.stdin,
188 output: process.stdout
189 });
190 const ask = (q) => new Promise((resolve) => rl.question(`? ${q}`, resolve));
191
192 let sessionToken = null;
193
194 const saveAndPrintJSON = (filename, data) => {
195 const jsonString = JSON.stringify(data, null, 2);
196 console.log(`\n=== OUTPUT JSON ===\n${jsonString}\n===================`);
197 try {
198 fs.writeFileSync(filename, jsonString);
199 console.log(`[+] File tersimpan: ${filename}`);
200 } catch (e) {
201 console.log(`[-] Gagal menyimpan file: ${e.message}`);
202 }
203 };
204
205 const delay = (ms) => new Promise(res => setTimeout(res, ms));
206
207 const fetchSmart = async (fetchFn, typeName, ...args) => {
208 let allData = [];
209 let seenIds = new Set();
210 let page = 1;
211 let keepFetching = true;
212
213 console.log(`\n[*] Mengekstrak data ${typeName}...`);
214
215 while (keepFetching) {
216 process.stdout.write(`[~] Fetching page ${page}... `);
217 const data = await fetchFn(...args, page);
218
219 let items = [];
220 let hasNextPage = true;
221
222 if (data && data.result) {
223 items = data.result;
224 hasNextPage = data.pagination ? data.pagination.has_next : false;
225 } else if (Array.isArray(data)) {
226 items = data;
227 } else {
228 console.log('ERROR FORMAT');
229 break;
230 }
231
232 if (items.length === 0) {
233 console.log('KOSONG (Mencapai batas akhir)');
234 break;
235 }
236
237 let newItemsCount = 0;
238 for (const item of items) {
239 const uniqueKey = item.id || item.url;
240 if (!seenIds.has(uniqueKey)) {
241 seenIds.add(uniqueKey);
242 allData.push(item);
243 newItemsCount++;
244 }
245 }
246
247 console.log(`OK (+${newItemsCount} data baru)`);
248
249 if (newItemsCount === 0 || !hasNextPage) {
250 keepFetching = false;
251 } else {
252 page++;
253 await delay(800);
254 }
255 }
256
257 console.log(`[+] Total Data Bersih: ${allData.length} item.`);
258 if (allData.length > 0) {
259 saveAndPrintJSON(`${typeName.replace(/\s/g, '_').toLowerCase()}.json`, allData);
260 }
261 };
262
263 const handleStreamExtraction = async () => {
264 console.log('\n--- Ekstraksi Stream Video ---');
265 const slug = await ask('Slug Anime (contoh: compass-20-sub-indo): ');
266 if (!slug.trim()) return;
267
268 process.stdout.write('[~] Memuat detail series... ');
269 const detail = await getSeriesDetail(slug, sessionToken);
270
271 if (!detail || !detail.chapter || detail.chapter.length === 0) {
272 return console.log('\n[-] Gagal memuat atau anime belum memiliki episode.');
273 }
274 console.log('OK\n');
275
276 const chapters = detail.chapter.sort((a, b) => parseInt(a.ch) - parseInt(b.ch));
277 chapters.forEach(c => console.log(`Ep ${c.ch} \t| ID: ${c.url}`));
278
279 const epSelected = await ask('\nPilih nomor episode: ');
280 const targetChapter = chapters.find(c => c.ch === epSelected.trim());
281
282 if (!targetChapter) return console.log('[-] Nomor episode tidak valid.');
283
284 process.stdout.write(`[~] Mengekstrak stream Episode ${targetChapter.ch}... `);
285 const streams = await getStreamLinks(targetChapter.url, slug, targetChapter.ch, sessionToken);
286
287 if (!streams) return console.log('\n[-] Ekstraksi gagal atau link stream belum tersedia.');
288 console.log('OK');
289
290 saveAndPrintJSON(`stream_${slug}_ep${targetChapter.ch}.json`, streams);
291 };
292
293 const runCLI = async () => {
294 console.clear();
295 console.log("===================================");
296 console.log(" ANIMELOVER CLI ");
297 console.log("===================================");
298
299 process.stdout.write('\n[*] Inisiasi sesi anonim... ');
300 sessionToken = await loginGuest();
301 if (!sessionToken) {
302 console.log('GAGAL KONEKSI');
303 process.exit(1);
304 }
305 console.log('OK');
306
307 while (true) {
308 console.log('\n[ MAIN DASHBOARD ]');
309 console.log('1. Fetch Rekomendasi');
310 console.log('2. Fetch Ongoing (Ambil Semua)');
311 console.log('3. Fetch Baru Upload (Ambil Semua)');
312 console.log('4. Fetch List Movie (Ambil Semua)');
313 console.log('5. Search Anime');
314 console.log('6. Ekstrak Link Stream Video');
315 console.log('7. Fetch Jadwal Rilis (Mingguan)'); // Menu Baru
316 console.log('0. Keluar');
317
318 const menu = await ask('\nPilihan: ');
319
320 switch (menu.trim()) {
321 case '1':
322 console.log('\n[*] Memuat rekomendasi...');
323 const rekData = await getRekomendasi();
324 console.log(`[+] Total: ${rekData.length} item.`);
325 if (rekData.length > 0) saveAndPrintJSON('rekomendasi.json', rekData);
326 break;
327 case '2':
328 await fetchSmart(getOngoing, 'Ongoing');
329 break;
330 case '3':
331 await fetchSmart(getBaruUpload, 'Baru Upload');
332 break;
333 case '4':
334 await fetchSmart(getMovie, 'Movie List');
335 break;
336 case '5':
337 const kw = await ask('Masukkan Kata Kunci: ');
338 if (kw.trim()) await fetchSmart((kw, page) => searchAnime(kw, page), `Search_${kw}`, kw);
339 break;
340 case '6':
341 await handleStreamExtraction();
342 break;
343 case '7':
344 console.log('\n[*] Memuat jadwal rilis mingguan...');
345 const jadwalData = await getJadwal();
346 if (jadwalData && jadwalData.length > 0) {
347 console.log(`[+] Jadwal ditemukan untuk ${jadwalData.length} hari.`);
348 jadwalData.forEach(hari => {
349 console.log(` - ${hari.day} (${hari.date}): ${hari.animeList?.length || 0} Judul Anime`);
350 });
351 saveAndPrintJSON('jadwal_rilis.json', jadwalData);
352 } else {
353 console.log('[-] Data jadwal kosong atau gagal dimuat.');
354 }
355 break;
356 case '0':
357 console.log('\n[!] Terminated.');
358 rl.close();
359 return;
360 default:
361 console.log('[-] Pilihan tidak valid.');
362 }
363 }
364 };
365
366 runCLI();
367}