mirror of
https://gitlab.com/vyla-entertainment/sdk.git
synced 2026-11-14 22:27:27 -05:00
142 lines
4.5 KiB
JavaScript
142 lines
4.5 KiB
JavaScript
import { fetchJson, USER_AGENT } from '../utils/helpers.js';
|
|
|
|
const API_BASE = 'https://api.meowtv.ru';
|
|
const REFERER = 'https://meowtv.ru/';
|
|
const ENC_DEC_API = 'https://enc-dec.app/api/dec-meowtv';
|
|
|
|
const BASE_HEADERS = {
|
|
'User-Agent': USER_AGENT,
|
|
'Accept': 'application/json',
|
|
'Referer': REFERER,
|
|
'Origin': 'https://meowtv.ru',
|
|
'Accept-Language': 'en-US,en;q=0.9'
|
|
};
|
|
|
|
const SERVERS = [
|
|
'dcloud', 'acme', 'tcloud', 'ipcloud', 'turkce', 'hindiv3'
|
|
];
|
|
|
|
const TIMEOUT_MS = 15000;
|
|
|
|
export const VERIFY_HEADERS = { 'User-Agent': USER_AGENT, 'Referer': REFERER, 'Origin': 'https://meowtv.ru' };
|
|
|
|
let cachedTicket = null;
|
|
let ticketExpiry = 0;
|
|
|
|
/**
|
|
* Fetches a stream ticket or returns a cached valid ticket.
|
|
*/
|
|
async function getTicket(forceRefresh = false) {
|
|
const now = Date.now();
|
|
if (!forceRefresh && cachedTicket && now < ticketExpiry - 10000) {
|
|
return cachedTicket;
|
|
}
|
|
|
|
try {
|
|
const res = await fetchJson(`${API_BASE}/streams/ticket`, {
|
|
method: 'POST',
|
|
headers: {
|
|
...BASE_HEADERS,
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({})
|
|
});
|
|
|
|
if (res?.ticket) {
|
|
cachedTicket = res.ticket;
|
|
ticketExpiry = typeof res.exp === 'number' ? res.exp : now + 60000;
|
|
return cachedTicket;
|
|
}
|
|
} catch {
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
async function decryptPayload(payload) {
|
|
try {
|
|
const response = await fetchJson(ENC_DEC_API, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ data: payload })
|
|
});
|
|
|
|
if (response?.status !== 200 || !response?.result) {
|
|
return null;
|
|
}
|
|
|
|
return typeof response.result === 'string' ? JSON.parse(response.result) : response.result;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function getStream({ id, s, e, server }) {
|
|
try {
|
|
const isTv = !!s;
|
|
let targets = SERVERS;
|
|
if (server && server !== 'all') {
|
|
const cleanName = server.replace('MeowTV - ', '');
|
|
targets = SERVERS.includes(cleanName) ? [cleanName] : [targets[0]];
|
|
}
|
|
|
|
const ticket = await getTicket();
|
|
const requestHeaders = {
|
|
...BASE_HEADERS,
|
|
...(ticket ? { 'x-stream-ticket': ticket } : {})
|
|
};
|
|
|
|
const results = await Promise.all(targets.map(async srv => {
|
|
try {
|
|
return await Promise.race([
|
|
(async () => {
|
|
const path = isTv
|
|
? `/streams/tv/${id}/${s}/${e}?s=${encodeURIComponent(srv)}`
|
|
: `/streams/movie/${id}?s=${encodeURIComponent(srv)}`;
|
|
|
|
const payload = await fetchJson(`${API_BASE}${path}`, {
|
|
headers: requestHeaders
|
|
});
|
|
|
|
if (!payload) return null;
|
|
|
|
const data = await decryptPayload(payload);
|
|
if (!data) return null;
|
|
|
|
const urls = [];
|
|
if (data?.url?.startsWith('http')) {
|
|
urls.push({
|
|
url: data.url,
|
|
server: `MeowTV - ${srv}`,
|
|
});
|
|
}
|
|
|
|
if (Array.isArray(data?.streams)) {
|
|
for (const stream of data.streams) {
|
|
if (stream?.url?.startsWith('http')) {
|
|
urls.push({
|
|
url: stream.url,
|
|
server: `MeowTV - ${srv} (${stream.language || 'Unknown'})`,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return urls;
|
|
})(),
|
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout exceeded')), TIMEOUT_MS))
|
|
]);
|
|
} catch (err) {
|
|
return null;
|
|
}
|
|
}));
|
|
|
|
const allUrls = results.filter(Boolean).flat();
|
|
return allUrls.length ? { allUrls } : null;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function getSources() {
|
|
return SERVERS.map(s => `MeowTV - ${s}`);
|
|
} |