mirror of
https://github.com/Inside4ndroid/TMDB-Embed-API.git
synced 2026-10-22 09:35:11 +00:00
- 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.
128 lines
4.9 KiB
JavaScript
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 }; |