/*** @ Base: https://play.google.com/store/apps/details?id=com.streamx.nontonanimex @ Author: Shannz @ Note: Wrapper from AnimeXNonton apk ***/ import axios from 'axios'; import qs from 'qs'; const CONFIG = { BASE_URL: "https://animeku.my.id/nontonanime-x/phalcon/api/", HEADERS: { 'User-Agent': 'okhttp/3.12.13', 'Connection': 'Keep-Alive', 'Accept-Encoding': 'gzip', 'Content-Type': 'application/x-www-form-urlencoded', 'Cache-Control': 'max-age=0', 'Data-Agent': 'AnimeXNonton 2026.1.12/12' } }; function cleanHtml(html) { if (!html) return ""; return html .replace(//gi, '\n') .replace(/<\/p>/gi, '\n\n') .replace(/ /g, ' ') .replace(/<[^>]*>?/gm, '') .replace(/\n\s*\n/g, '\n\n') .trim(); } async function request(endpoint, data = {}, method = 'POST') { try { if (method === 'POST') { data.isAPKvalid = 'true'; } const options = { method: method, url: CONFIG.BASE_URL + endpoint, headers: CONFIG.HEADERS, data: method === 'POST' ? qs.stringify(data) : undefined }; const response = await axios.request(options); return response.data; } catch (error) { console.error(`[AnimeKu Error] ${endpoint}:`, error.message); return { status: "error", message: error.message }; } } export const animeku = { latest: async (page = 1, count = 20) => { return await request('get_posts/', { 'page': page.toString(), 'count': count.toString() }); }, ongoing: async (page = 1, count = 100) => { return await request('get_category_ongoing/', { 'page': page.toString(), 'count': count.toString(), 'lang': 'All' }); }, search: async (query, page = 1) => { return await request('search_category_collection/', { 'search': query, 'page': page.toString(), 'count': '20', 'lang': 'All' }); }, detail: async (categoryId) => { try { const episodeData = await request('get_category_posts_secure/', { 'id': categoryId.toString() }); if (episodeData.status !== 'ok' || !episodeData.posts || episodeData.posts.length === 0) { return { status: 'error', message: 'Episodes not found or invalid Category ID' }; } const detailedEpisodesPromises = episodeData.posts.map(async (baseEp) => { try { const detailEp = await request('get_post_description/', { 'channel_id': baseEp.channel_id.toString() }); if (detailEp.status === 'ok') { return { ...baseEp, streams: { sd: detailEp.channel_url_ori || detailEp.channel_url, hd: detailEp.channel_url_hd_ori || detailEp.channel_url_hd, fhd: detailEp.channel_url_fhd_ori || detailEp.channel_url_fhd, external_1: detailEp.episode_url_1 || "", external_2: detailEp.episode_url_2 || "" }, _metaData: detailEp }; } return baseEp; } catch (err) { return baseEp; } }); const episodesWithStream = await Promise.all(detailedEpisodesPromises); const firstValidEp = episodesWithStream.find(e => e._metaData) || {}; const descData = firstValidEp._metaData || {}; let info = {}; if (descData.channel_id) { info = { title: descData.category_name || descData.channel_name, genres: descData.genre, rating: descData.rating, status: descData.status, synopsis: cleanHtml(descData.channel_description), image: descData.img_url, year: descData.years }; } const cleanEpisodes = episodesWithStream.map(({ _metaData, ...rest }) => rest); return { status: 'ok', series_info: info, category_id: categoryId, total_episodes: episodeData.count_total, episodes: cleanEpisodes }; } catch (error) { console.error("[Detail Error]", error); return { status: "error", message: error.message }; } }, byGenre: async (genre1, genre2 = '', sort = 'ASC', page = 1) => { return await request('get_anime_by_genre/', { 'genre1': genre1, 'genre2': genre2, 'lang': 'All', 'sort': sort, 'page': page.toString(), 'count': '20' }); }, getGenreList: async () => { return await request('get_anime_genre_list/', {}, 'GET'); } };