Files
Inside4ndroid 2c9a2cabbf Add new providers for streaming services and utility functions
- Implement StreamFlix provider for fetching movie and episode streams.
- Add VaPlayer provider to retrieve streaming URLs for movies and TV shows.
- Introduce ZXCStreams provider for accessing various streaming links.
- Create utility functions for fetching episode counts from Cinemeta.
- Develop a title matching system for improved search results.
- Implement TMDB title to IMDB ID resolution for better integration with TMDB API.
2026-08-19 23:35:13 +01:00

128 lines
4.9 KiB
JavaScript

const { getDetails, resolveImdbId } = require('../utils/tmdb');
const { findBestMatchWithRetry } = require('../utils/titleMatch');
const { tmdbTitleToImdbId } = require('../utils/tmdbTitleToImdb');
const HDGHARTV_API = 'https://hdghartv.cc/api';
const HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'application/json, */*',
'Referer': 'https://hdghartv.cc/'
};
async function fetchJson(url, timeoutMs = 10000) {
try {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
const res = await fetch(url, { headers: HEADERS, signal: controller.signal });
clearTimeout(timer);
if (!res.ok) return null;
return await res.json();
} catch (err) {
console.warn(`[HDGharTV] fetch failed for ${url}: ${err.message}`);
return null;
}
}
async function searchHdghartv(title, kind, variants) {
const searchByVariant = async (variantTitle) => {
const encoded = encodeURIComponent(variantTitle);
const data = await fetchJson(`${HDGHARTV_API}/search?q=${encoded}`);
if (!data) return [];
const results = kind === 'movie' ? data.movies : data.series;
return (results || []).map((r) => ({ title: r.title, type: kind, raw: r }));
};
const { best } = await findBestMatchWithRetry(
{ title, type: kind },
variants,
searchByVariant,
{ provider: 'HDGharTV' }
);
if (!best) return null;
return { id: best.raw._id, title: best.raw.title };
}
const QUALITY_RANK = { '4K': 0, '1080p': 1, '720p': 2, '480p': 3, '360p': 4 };
function sortLinks(links) {
return [...links].sort((a, b) => (QUALITY_RANK[a.quality] || 99) - (QUALITY_RANK[b.quality] || 99));
}
function linksToStreams(links, labelSuffix = '') {
return sortLinks(links.filter((l) => l.isActive && l.url)).map((l) => ({
name: 'HDGharTV',
title: `HDGharTV${labelSuffix ? ` ${labelSuffix}` : ''} · ${l.quality}`,
url: l.url,
quality: l.quality,
provider: 'HDGharTV',
headers: HEADERS
}));
}
async function getHdghartvStreams(tmdbId, mediaType = 'movie', seasonNum = null, episodeNum = null) {
console.log(`[HDGharTV] Fetching streams for TMDB ID: ${tmdbId}, Type: ${mediaType}`);
try {
const kind = mediaType === 'tv' ? 'series' : 'movie';
const tmdbType = mediaType === 'tv' ? 'tv' : 'movie';
const details = await getDetails(tmdbType, tmdbId);
const title = (details && (details.title || details.name)) || '';
if (!title) {
console.log(`[HDGharTV] No TMDB title resolved for ${tmdbId}`);
return [];
}
const variants = [title];
const imdbId = await resolveImdbId(tmdbType, tmdbId);
const match = await searchHdghartv(title, kind, variants);
if (!match) {
console.log(`[HDGharTV] "${title}" not found`);
return [];
}
if (imdbId && imdbId.startsWith('tt')) {
const resolvedId = await tmdbTitleToImdbId(match.title, undefined, kind).catch(() => null);
if (resolvedId && resolvedId !== imdbId) {
console.log(`[HDGharTV] IMDB ID mismatch — rejecting match: expected ${imdbId}, resolved ${resolvedId}`);
return [];
}
}
if (kind === 'movie') {
const movie = await fetchJson(`${HDGHARTV_API}/movies/public/${match.id}`);
if (!movie || !movie.streamingLinks || !movie.streamingLinks.length) {
console.log(`[HDGharTV] No streaming links for movie "${match.title}"`);
return [];
}
return linksToStreams(movie.streamingLinks);
}
const season = seasonNum || 1;
const episode = episodeNum || 1;
const series = await fetchJson(`${HDGHARTV_API}/series/public/${match.id}`);
if (!series || !series.seasons || !series.seasons.length) {
console.log(`[HDGharTV] No seasons for series "${match.title}"`);
return [];
}
const seasonData = series.seasons.find((s) => s.seasonNumber === season);
if (!seasonData) {
console.log(`[HDGharTV] Season ${season} not found`);
return [];
}
const episodeData = seasonData.episodes && seasonData.episodes.find((e) => e.episodeNumber === episode);
if (!episodeData || !episodeData.streamingLinks || !episodeData.streamingLinks.length) {
console.log(`[HDGharTV] Episode ${episode} or links not found`);
return [];
}
return linksToStreams(episodeData.streamingLinks, `S${season}E${episode}`);
} catch (err) {
console.error(`[HDGharTV] Error: ${err.message}`);
return [];
}
}
module.exports = { getHdghartvStreams };