Loading snippets...
website nonton film dari sinhala - from request
/**
* sinhalasub
* base: https://sinhalasub.lk
* Creator: ShanMolvyr
* Jangan Hapus Kreator hargai rakyat kecil
* Note: cek https://snippet.vyr.my.id/shanmolvyr/sinhalasub/README.md
* Sumber: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q
*/
import axios from 'axios';
import * as cheerio from 'cheerio';
const BASE = 'https://sinhalasub.lk';
const http = axios.create({
baseURL: BASE,
timeout: 20000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
Referer: BASE,
},
});
let _nonce = { value: null, expiry: 0 };
async function getNonce() {
if (_nonce.value && Date.now() < _nonce.expiry) return _nonce.value;
const res = await http.get('/');
const m = res.data.match(/"nonce"\s*:\s*"([a-f0-9]+)"/);
if (!m) throw new Error('Nonce not found');
_nonce = { value: m[1], expiry: Date.now() + 10 * 60 * 1000 };
return _nonce.value;
}
function parseCard($, el) {
const $el = $(el);
const $a = $el.find('a[href*="sinhalasub.lk"]').first();
const $img = $el.find('img.thumb, img.mli-thumb, img.poster-img').first();
return {
id: $el.attr('id')?.replace('item-', '') || null,
title: $el.find('.item-desc-title h3, h3').first().text().trim() || $a.attr('title') || null,
url: $a.attr('href') || null,
type: $a.attr('data-ptype') || null,
poster: $img.attr('src') || $img.attr('data-original') || null,
language: $el.find('.language').text().trim() || null,
quality: $el.find('.quality').text().trim() || null,
resolution: $el.find('.qty').text().trim() || null,
year: $el.find('.item-date').text().trim() || null,
};
}
function parseDownloadLinks($) {
const links = [];
$('table.links-table').each((_, table) => {
const tab = $(table).attr('id') || 'unknown';
$(table).find('tbody tr').each((_, row) => {
const $row = $(row);
const $a = $row.find('td a').first();
links.push({
host: $a.text().trim() || tab,
tab,
quality: $row.find('.quality').text().trim() || null,
size: $row.find('td:nth-child(3) span').text().trim() || null,
clicks: $row.find('td:nth-child(4) span').text().trim() || null,
href: $a.attr('href') || null,
});
});
});
return links;
}
async function resolveTarget(target) {
if (target.startsWith('http')) return target;
if (/^\d+$/.test(target)) {
for (const pt of ['movies', 'tvshows', 'posts']) {
const r = await http.get(`/wp-json/wp/v2/${pt}/${target}`, { validateStatus: () => true });
if (r.data?.link) return r.data.link;
}
throw new Error(`ID ${target} not found`);
}
throw new Error(`Unknown target format: ${target}`);
}
const out = (data) => console.log(JSON.stringify(data, null, 2));
async function home() {
const res = await http.get('/');
const $ = cheerio.load(res.data);
const items = [];
$('.module-item').each((_, el) => items.push(parseCard($, el)));
out({ total: items.length, items });
}
async function search(query) {
if (!query) throw new Error('query required');
let items = [];
try {
const nonce = await getNonce();
const res = await http.get('/wp-json/zetaflix/search/', {
params: { s: query },
headers: { 'X-WP-Nonce': nonce },
});
if (Array.isArray(res.data) && res.data.length > 0) items = res.data;
} catch (_) {}
if (items.length === 0) {
const res = await http.get('/', { params: { s: query } });
const $ = cheerio.load(res.data);
$('.display-item').each((_, el) => items.push(parseCard($, el)));
}
out({ query, total: items.length, items });
}
async function detail(target) {
if (!target) throw new Error('url or id required');
const url = await resolveTarget(target);
const res = await http.get(url);
const $ = cheerio.load(res.data);
const genres = [];
$('.details-genre a').each((_, el) => genres.push($(el).text().trim()));
const cast = [];
$('.cast-card').each((_, el) => {
cast.push({
name: $(el).find('.cast-card-name').text().trim(),
image: $(el).find('img').attr('src') || null,
url: $(el).attr('href') || null,
});
});
const players = [];
$('.zetaflix_player_option').each((_, el) => {
players.push({ nume: $(el).attr('data-nume'), label: $(el).find('.opt-name').text().trim() });
});
const episodes = [];
$('.episodes-list li, .episodios li').each((_, el) => {
const $ep = $(el);
episodes.push({
num: $ep.find('.num-epi, .numerando').text().trim() || null,
title: $ep.find('.episodiotitle, .epi-name').text().trim() || null,
url: $ep.find('a').attr('href') || null,
});
});
const similar = [];
$('.similar-module .module-item, .similar-wrapper .module-item').each((_, el) => {
similar.push(parseCard($, el));
});
out({
title: $('.details-title h3').text().trim(),
url,
postId: $('.zetaflix_player_option').first().attr('data-post') || null,
poster: $('img.poster-img').attr('src') || null,
backdrop: $('.player-splash .splash-bg img').attr('src') || null,
meta: {
imdb: $('.data-imdb').text().replace('IMDb:', '').trim(),
quality: $('.data-quality').text().trim(),
runtime: $('[itemprop="duration"]').text().trim(),
views: $('.data-views').last().text().trim(),
year: $('.details-info a[href*="/release/"]').first().text().trim(),
},
genres,
description: $('.details-desc p').not(':empty').first().text().trim(),
info: {
language: $('.details-info a[href*="/language/"]').first().text().trim(),
director: $('.details-info a[href*="/director/"]').first().text().trim(),
subtitleAuthor: $('.details-info a[href*="/subtitle/"]').first().text().trim(),
subtitleSite: $('.details-info a[href*="/site/"]').first().text().trim(),
},
players,
downloadLinks: parseDownloadLinks($),
cast,
episodes,
similar,
});
}
async function listing(path, page = 1) {
if (!path) throw new Error('path required');
const url = page > 1 ? `/${path}/page/${page}/` : `/${path}/`;
const res = await http.get(url);
const $ = cheerio.load(res.data);
const items = [];
$('.display-item, .module-item').each((_, el) => items.push(parseCard($, el)));
out({
path, page,
total: items.length,
nextPage: $('.pagination a[rel="next"]').attr('href') ? page + 1 : null,
items,
});
}
async function genres() {
const res = await http.get('/genre/');
const $ = cheerio.load(res.data);
const items = [];
$('.genre-list li a, .terms .item-box a, .genre-item a').each((_, el) => {
items.push({
name: $(el).text().trim(),
slug: $(el).attr('href')?.replace(/.*\/genre\//, '').replace(/\//g, '') || null,
url: $(el).attr('href') || null,
count: $(el).find('.g-total, .count').text().trim() || null,
});
});
out({ total: items.length, items });
}
const [,, cmd, ...args] = process.argv;
const commands = {
home: () => home(),
search: () => search(args.join(' ')),
detail: () => detail(args[0]),
listing: () => listing(args[0], parseInt(args[1]) || 1),
trending: () => listing('trending', parseInt(args[0]) || 1),
imdb: () => listing('imdb', parseInt(args[0]) || 1),
genres: () => genres(),
};
if (!cmd || !commands[cmd]) {
console.log('Usage:\n node sinhala.js home\n node sinhala.js search <query>\n node sinhala.js detail <url|id>\n node sinhala.js listing <path> [page]\n node sinhala.js trending [page]\n node sinhala.js imdb [page]\n node sinhala.js genres');
} else {
commands[cmd]().catch(err => console.error(JSON.stringify({ error: err.message })));
}
bash* Usage: * node sinhala.js home * node sinhala.js search <query> * node sinhala.js detail <url|id> * node sinhala.js trending * node sinhala.js imdb * node sinhala.js listing movies [page] * node sinhala.js listing tvshows [page] * node sinhala.js listing genre/action [page] * node sinhala.js listing language/korean [page] * node sinhala.js genres * * "detail" mencakup info, players, download links, cast, similar — semua dalam 1 command. * <url> → https://sinhalasub.lk/movies/meteor-final-impact-2025-sinhala-subtitles/ * <id> → 8349 (dari hasil search)
bashby ShanMolvyr