meow:3.
qrtz
javascript
26
6
https://meownime.ltd
09 Agu 2026
Code
const axios = require('axios');
const cheerio = require('cheerio');
const https = require('https');
const BASE_URL = 'https://meownime.ltd';
const USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/119.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0',
'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0',
];
function getRandomUA() {
return USER_AGENTS[Math.floor(Math.random() * USER_AGENTS.length)];
}
function getRandomIP() {
return `${Math.floor(Math.random()*256)}.${Math.floor(Math.random()*256)}.${Math.floor(Math.random()*256)}.${Math.floor(Math.random()*256)}`;
}
function buildHeaders() {
const ua = getRandomUA();
const isChrome = ua.includes('Chrome');
const headers = {
'User-Agent': ua,
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Cache-Control': 'max-age=0',
'X-Forwarded-For': getRandomIP(),
'Client-IP': getRandomIP(),
};
if (isChrome) {
headers['Sec-Ch-Ua'] = '"Google Chrome";v="120", "Not_A Brand";v="99"';
headers['Sec-Ch-Ua-Mobile'] = '?0';
headers['Sec-Ch-Ua-Platform'] = '"Windows"';
}
return headers;
}
const agent = new https.Agent({
rejectUnauthorized: false,
keepAlive: true,
});
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function fetchHTML(url, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
const response = await axios.get(url, {
headers: buildHeaders(),
httpsAgent: agent,
timeout: 30000,
maxRedirects: 5,
decompress: true,
});
await sleep(600 + Math.random() * 800);
return response.data;
} catch (e) {
if (i === retries - 1) throw e;
await sleep(1500 * (i + 1) + Math.random() * 1000);
}
}
}
function cleanText(text) {
return text?.replace(/\s+/g, ' ').trim() || '';
}
function extractFromClass(classes, prefix) {
const regex = new RegExp(`${prefix}-([a-zA-Z0-9-]+)`, 'g');
const matches = classes?.match(regex) || [];
return matches.map(m => m.replace(`${prefix}-`, ''));
}
function extractSlug(input) {
if (input?.startsWith('http')) {
const urlObj = new URL(input);
const pathParts = urlObj.pathname.split('/').filter(p => p);
return pathParts[pathParts.length - 1] || pathParts[0];
}
return input;
}
function buildListUrl(type, page, order) {
const pathMap = {
home: '/',
ongoing: '/status/ongoing/',
completed: '/status/completed/',
movie: '/type/movie/',
};
let path = pathMap[type] || `/type/${type}/`;
if (page && page > 1 && type !== 'home') {
path = path.replace(/\/$/, '') + `/page/${page}/`;
}
const params = new URLSearchParams();
if (order) params.set('orderby', order);
const query = params.toString();
return BASE_URL + path + (query ? '?' + query : '');
}
function parseListItems(html) {
const $ = cheerio.load(html);
const items = [];
$('article.meownime.post, article.post').each(function() {
const $el = $(this);
const classes = $el.attr('class') || '';
const id = $el.attr('id') || '';
const postId = id.replace('post-', '');
const $thumb = $el.find('.featured-thumb.grid-img');
const $link = $thumb.find('a');
const $img = $thumb.find('img');
const $title = $el.find('.entry-title.title-font a');
const $postedon = $el.find('.postedon');
const title = cleanText($title.text());
const url = $link.attr('href') || '';
const thumbnail = $img.attr('src') || '';
const ratingText = cleanText($postedon.text()).replace('★', '').trim();
const rating = parseFloat(ratingText) || 0;
const genres = extractFromClass(classes, 'genre');
const studios = extractFromClass(classes, 'studio');
const season = extractFromClass(classes, 'season')[0] || '';
const status = extractFromClass(classes, 'status')[0] || '';
const type = extractFromClass(classes, 'type')[0] || '';
if (title || url) {
items.push({
id: postId,
title,
url: url.startsWith('http') ? url : BASE_URL + url,
thumbnail,
rating,
genres,
studios,
season,
status,
type,
});
}
});
return items;
}
function parsePagination(html) {
const $ = cheerio.load(html);
const pageNumbers = [];
$('.paginations .nav-links .page-numbers, .paginations .page-numbers').each(function() {
const text = $(this).text().trim();
if (text && !isNaN(text) && text !== '...') {
const num = parseInt(text);
if (!pageNumbers.includes(num)) pageNumbers.push(num);
}
});
if (pageNumbers.length > 0) {
const maxPage = Math.max(...pageNumbers);
const result = [];
for (let i = 1; i <= maxPage; i++) {
result.push({ page: i, url: null });
}
return result;
}
const nextLink = $('.paginations .next.page-numbers').attr('href');
if (nextLink) {
const match = nextLink.match(/page\/(\d+)/);
if (match) {
const currentPage = parseInt(match[1]) - 1 || 1;
return [{ page: currentPage, url: null }, { page: currentPage + 1, url: nextLink }];
}
}
return [{ page: 1, url: null }];
}
async function scrapeList(type = 'home', page = 1, order = '') {
if (type === 'home' && page > 1) {
return { items: [], totalPages: 1, currentPage: page };
}
const url = buildListUrl(type, page, order);
const html = await fetchHTML(url);
const items = parseListItems(html);
const pages = parsePagination(html);
let totalPages = pages.length > 0 ? Math.max(...pages.map(p => p.page)) : 1;
if (totalPages < page) totalPages = page;
return { items, totalPages, currentPage: page };
}
async function scrapeGenre() {
const html = await fetchHTML(BASE_URL + '/genre/');
const $ = cheerio.load(html);
const genres = [];
$('.meow-genres ul li a').each(function() {
const $el = $(this);
const name = cleanText($el.text());
const url = $el.attr('href') || '';
genres.push({
name,
url: url.startsWith('http') ? url : BASE_URL + url,
});
});
return genres;
}
async function scrapeJadwal() {
const html = await fetchHTML(BASE_URL + '/jadwal/');
const $ = cheerio.load(html);
const schedule = {};
$('aside.meow-list.meow-archive').each(function() {
const $el = $(this);
const day = cleanText($el.find('h1.letter').text());
const items = [];
$el.find('.penzbar .jdlbar ul li a.nyaalist').each(function() {
const $a = $(this);
const title = cleanText($a.text());
const url = $a.attr('href') || '';
const rel = $a.attr('rel') || '';
items.push({
title,
url: url.startsWith('http') ? url : BASE_URL + url,
id: rel,
});
});
if (day && items.length > 0) {
schedule[day] = items;
}
});
return schedule;
}
async function scrapeGenreDetail(genre, page = 1) {
let url = page > 1 ? `/genre/${genre}/page/${page}/` : `/genre/${genre}/`;
let html = await fetchHTML(BASE_URL + url);
if (html.includes('Page not found') || html.includes('nothing was found')) {
url = page > 1 ? `/genre/${genre}/page/1/` : `/genre/${genre}/`;
html = await fetchHTML(BASE_URL + url);
}
const items = parseListItems(html);
const pages = parsePagination(html);
let totalPages = pages.length > 0 ? Math.max(...pages.map(p => p.page)) : 1;
if (totalPages < page) totalPages = page;
return { items, totalPages, currentPage: page };
}
async function scrapeSearch(query, page = 1) {
const url = page > 1 ? `/search/${encodeURIComponent(query)}/page/${page}/` : `/search/${encodeURIComponent(query)}/`;
const html = await fetchHTML(BASE_URL + url);
const items = parseListItems(html);
const pages = parsePagination(html);
let totalPages = pages.length > 0 ? Math.max(...pages.map(p => p.page)) : 1;
if (totalPages < page) totalPages = page;
return { items, totalPages, currentPage: page };
}
async function scrapeDetail(slugOrUrl) {
const slug = extractSlug(slugOrUrl);
const url = `/${slug}/`;
const html = await fetchHTML(BASE_URL + url);
const $ = cheerio.load(html);
const title = cleanText($('.entry-title').text());
const thumbnail = $('.single-featured').attr('src') || $('.featured-thumb img').attr('src') || '';
let sinopsis = '';
const sinopsisEl = $('.entry-content .mb-33 p, .entry-content p');
sinopsisEl.each(function() {
const text = cleanText($(this).text());
if (text.length > 50) {
sinopsis = text;
return false;
}
});
const genres = [];
$('li.Genrex, .genre-tag a, .genres a').each(function() {
const text = cleanText($(this).text());
if (text) {
const parts = text.split(',').map(s => cleanText(s));
parts.forEach(p => {
if (p && !genres.includes(p)) genres.push(p);
});
}
});
if (genres.length === 0) {
const articleClasses = $('article.post').attr('class') || '';
const genreClasses = extractFromClass(articleClasses, 'genre');
genreClasses.forEach(g => {
if (g && !genres.includes(g)) genres.push(g);
});
}
const episodes = [];
$('table.table-hover').each(function() {
const $table = $(this);
const rows = $table.find('tr');
if (rows.length >= 2) {
const titleRow = rows.eq(0);
const linkRow = rows.eq(1);
const resolution = cleanText(titleRow.text());
const links = [];
linkRow.find('a').each(function() {
const href = $(this).attr('href') || '';
const label = cleanText($(this).text());
if (href && href.startsWith('http')) {
links.push({ label, url: href });
}
});
if (resolution && links.length > 0) {
episodes.push({
number: resolution,
date: '',
url: links[0].url,
allLinks: links,
});
}
}
});
if (episodes.length === 0) {
$('.chapter-list .chapter-link, .eps-list a, .download-list a, .list-episode a').each(function() {
const $el = $(this);
const number = cleanText($el.find('.chapter-number').text()) || cleanText($el.find('.eps-number').text()) || cleanText($el.text());
const date = cleanText($el.find('.chapter-date').text()) || '';
const link = $el.attr('href') || '';
if (link && link.startsWith('http')) {
episodes.push({
number: number || 'Link',
date: date || '',
url: link,
});
}
});
}
return { title, thumbnail, sinopsis, genres, episodes };
}
function formatOutput(data, halaman = 'list', page = 1, totalPages = 1) {
let halamanDisplay = halaman;
if (halaman.startsWith('list-')) {
halamanDisplay = halaman.substring(5);
}
return {
creator: 'rynaqrtz',
halaman: halamanDisplay,
page,
totalPages,
totalItems: Array.isArray(data) ? data.length : (typeof data === 'object' ? Object.keys(data).length : 0),
data,
};
}
async function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.log(JSON.stringify({
creator: 'rynaqrtz',
error: 'Perintah diperlukan',
usage: {
list: 'node meownime.js list [--type home|ongoing|completed|movie] [--page 1] [--order a-z|newest|oldest|most_viewed]',
genre: 'node meownime.js genre',
jadwal: 'node meownime.js jadwal',
search: 'node meownime.js search <query> [--page 1]',
detail: 'node meownime.js detail <slug|url>',
},
example: {
list: 'node meownime.js list --type ongoing --page 1 --order a-z',
genre: 'node meownime.js genre',
jadwal: 'node meownime.js jadwal',
search: 'node meownime.js search haibara --page 1',
detail: 'node meownime.js detail kimi-no-na-wa-sub-indo',
detailUrl: 'node meownime.js detail https://meownime.ltd/kimi-no-na-wa-sub-indo/',
}
}, null, 2));
return;
}
const command = args[0];
try {
let result, output;
if (command === 'list') {
let type = 'home';
let page = 1;
let order = '';
for (let i = 1; i < args.length; i++) {
if (args[i] === '--type' && args[i + 1]) {
type = args[i + 1];
i++;
} else if (args[i] === '--page' && args[i + 1]) {
page = parseInt(args[i + 1]) || 1;
i++;
} else if (args[i] === '--order' && args[i + 1]) {
order = args[i + 1];
i++;
}
}
const data = await scrapeList(type, page, order);
output = formatOutput(data.items, `list-${type}`, data.currentPage, data.totalPages);
} else if (command === 'genre') {
const data = await scrapeGenre();
output = formatOutput(data, 'genre', 1, 1);
} else if (command === 'jadwal') {
const data = await scrapeJadwal();
output = formatOutput(data, 'jadwal', 1, 1);
} else if (command === 'genre-detail') {
const genre = args[1];
if (!genre) {
console.log(JSON.stringify({ creator: 'rynaqrtz', error: 'Genre slug diperlukan' }, null, 2));
return;
}
let page = 1;
for (let i = 2; i < args.length; i++) {
if (args[i] === '--page' && args[i + 1]) {
page = parseInt(args[i + 1]) || 1;
i++;
}
}
const data = await scrapeGenreDetail(genre, page);
output = formatOutput(data.items, `genre-${genre}`, data.currentPage, data.totalPages);
} else if (command === 'search') {
const query = args.slice(1).find(a => !a.startsWith('--')) || '';
if (!query) {
console.log(JSON.stringify({ creator: 'rynaqrtz', error: 'Query pencarian diperlukan' }, null, 2));
return;
}
let page = 1;
for (let i = 1; i < args.length; i++) {
if (args[i] === '--page' && args[i + 1]) {
page = parseInt(args[i + 1]) || 1;
i++;
}
}
const data = await scrapeSearch(query, page);
output = formatOutput(data.items, 'search', data.currentPage, data.totalPages);
} else if (command === 'detail') {
const input = args[1];
if (!input) {
console.log(JSON.stringify({ creator: 'rynaqrtz', error: 'Slug atau URL diperlukan' }, null, 2));
return;
}
const data = await scrapeDetail(input);
output = formatOutput(data, 'detail', 1, 1);
} else {
console.log(JSON.stringify({
creator: 'rynaqrtz',
error: `Perintah tidak dikenal: ${command}`,
available: ['list', 'genre', 'genre-detail', 'jadwal', 'search', 'detail']
}, null, 2));
return;
}
console.log(JSON.stringify(output, null, 2));
} catch (err) {
console.log(JSON.stringify({
creator: 'rynaqrtz',
error: err.message,
stack: process.env.DEBUG ? err.stack : undefined,
}, null, 2));
}
}
if (require.main === module) {
main();
}
module.exports = {
fetchHTML,
parseListItems,
parsePagination,
scrapeList,
scrapeGenre,
scrapeGenreDetail,
scrapeJadwal,
scrapeSearch,
scrapeDetail,
cleanText,
extractFromClass,
extractSlug,
buildListUrl,
formatOutput,
};Rating
Gimana snippet ini menurutmu?
Embed ke website / blog
<iframe src="https://qrtzcode.vercel.app/api/embed/anime/meownime" style="width:100%;height:400px;border:none;border-radius:12px;"></iframe>