Sinhalasub
website nonton film dari sinhala - from request
3 Juni 2026362 file
sinhala.jsjavascript · 224 baris
/** * 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 })));}README.mdmarkdown · 23 baris
Komentar
Masuk untuk ikut berkomentar.
Belum ada komentar.