mirror of
https://github.com/Inside4ndroid/TMDB-Embed-API.git
synced 2026-10-22 09:35:11 +00:00
chore: provider updates (4khdhub permanent r2.dev block + zip strip, moviesclub vidora headers)
This commit is contained in:
+19
-10
@@ -2,22 +2,31 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## [1.0.4] - 2025-09-18
|
## [1.0.5] - 2025-09-19
|
||||||
|
|
||||||
### Added
|
### Improved
|
||||||
- New provider: `moviesclub` with registry mapping (`moviesclub.js` → `getMoviesClubStreams`).
|
- 4khdhub provider: Permanently block `r2.dev` FSL links (previous optional flag removed).
|
||||||
|
- Added Referer/Origin headers automatically for FSL Server links during validation (prior to block enforcement ensured proper behavior).
|
||||||
|
- Tightened URL validation: removed unconditional trust for `r2.dev`; validation logic now consistent across hosts.
|
||||||
|
- Host distribution instrumentation logs final hostname counts for easier diagnostics.
|
||||||
|
- Preserved HubCloud worker `.zip` links by stripping the `.zip` extension instead of discarding them (enables direct playback attempts).
|
||||||
|
- MoviesClub provider: Added automatic `Origin`/`Referer` headers for `vidora.stream` sources to prevent 403 responses.
|
||||||
|
|
||||||
### Changed
|
### Notes
|
||||||
- Unified provider stream schema across all providers:
|
- `r2.dev` links are always removed from final output; no env flag required.
|
||||||
```json
|
|
||||||
{ "title": "…", "url": "…", "quality": "…", "provider": "…", "headers": { } }
|
|
||||||
```
|
|
||||||
- Provider integration docs: When adding a provider, also add its exported function to `providerFunctionMap` in `providers/registry.js`.
|
|
||||||
- README Docker quick run snippet updated to: `docker run -it -p 8787:8787 inside4ndroid/tmdb-embed-api:latest`.
|
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
- README: Updated Providers section, unified schema example, MoviesClub listed, registry mapping instructions, and badge version.
|
- README: Updated Providers section, unified schema example, MoviesClub listed, registry mapping instructions, and badge version.
|
||||||
|
|
||||||
|
## [1.0.4] - 2025-09-18
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
```json
|
||||||
|
{ "title": "…", "url": "…", "quality": "…", "provider": "…", "headers": { } }
|
||||||
|
```
|
||||||
|
|
||||||
## [1.0.3] - 2025-09-17
|
## [1.0.3] - 2025-09-17
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<img src="https://img.shields.io/badge/Node.js-18%2B-brightgreen?style=flat" />
|
<img src="https://img.shields.io/badge/Node.js-18%2B-brightgreen?style=flat" />
|
||||||
<img src="https://img.shields.io/badge/Status-Active-success?style=flat" />
|
<img src="https://img.shields.io/badge/Status-Active-success?style=flat" />
|
||||||
<img src="https://img.shields.io/badge/License-MIT-blue?style=flat" />
|
<img src="https://img.shields.io/badge/License-MIT-blue?style=flat" />
|
||||||
<img src="https://img.shields.io/badge/Version-1.0.4-informational?style=flat" />
|
<img src="https://img.shields.io/badge/Version-1.0.5-informational?style=flat" />
|
||||||
<img src="https://img.shields.io/docker/pulls/inside4ndroid/tmdb-embed-api?label=Docker%20Pulls&style=flat" />
|
<img src="https://img.shields.io/docker/pulls/inside4ndroid/tmdb-embed-api?label=Docker%20Pulls&style=flat" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "tmdb-embed-api",
|
"name": "tmdb-embed-api",
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"description": "Standalone streaming embed source aggregation API (TMDB ID in, streams out)",
|
"description": "Standalone streaming embed source aggregation API (TMDB ID in, streams out)",
|
||||||
"main": "apiServer.js",
|
"main": "apiServer.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+71
-11
@@ -9,6 +9,9 @@ const path = require('path');
|
|||||||
const DOMAINS_URL = 'https://raw.githubusercontent.com/phisher98/TVVVV/refs/heads/main/domains.json';
|
const DOMAINS_URL = 'https://raw.githubusercontent.com/phisher98/TVVVV/refs/heads/main/domains.json';
|
||||||
let cachedDomains = null;
|
let cachedDomains = null;
|
||||||
|
|
||||||
|
// Permanent policy: block r2.dev links (previously optional via env flag)
|
||||||
|
console.log('[4KHDHub] r2.dev links will be removed (permanent policy)');
|
||||||
|
|
||||||
// --- Caching Configuration ---
|
// --- Caching Configuration ---
|
||||||
const CACHE_ENABLED = process.env.DISABLE_CACHE !== 'true';
|
const CACHE_ENABLED = process.env.DISABLE_CACHE !== 'true';
|
||||||
console.log(`[4KHDHub] Internal cache is ${CACHE_ENABLED ? 'enabled' : 'disabled'}.`);
|
console.log(`[4KHDHub] Internal cache is ${CACHE_ENABLED ? 'enabled' : 'disabled'}.`);
|
||||||
@@ -61,8 +64,7 @@ function validateUrl(url) {
|
|||||||
|
|
||||||
// Skip validation for known reliable hosting services
|
// Skip validation for known reliable hosting services
|
||||||
const trustedHosts = [
|
const trustedHosts = [
|
||||||
'pixeldrain.dev',
|
'pixeldrain.dev'
|
||||||
'r2.dev'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const isTrustedHost = trustedHosts.some(host => urlObj.hostname.includes(host));
|
const isTrustedHost = trustedHosts.some(host => urlObj.hostname.includes(host));
|
||||||
@@ -74,11 +76,21 @@ function validateUrl(url) {
|
|||||||
|
|
||||||
const protocol = urlObj.protocol === 'https:' ? https : http;
|
const protocol = urlObj.protocol === 'https:' ? https : http;
|
||||||
|
|
||||||
|
// Add referer/origin for r2.dev validation attempts
|
||||||
|
const extraHeaders = {};
|
||||||
|
if (/r2\.dev$/i.test(urlObj.hostname)) {
|
||||||
|
const origin = `${urlObj.protocol}//${urlObj.hostname}`;
|
||||||
|
extraHeaders['Referer'] = origin + '/';
|
||||||
|
extraHeaders['Origin'] = origin;
|
||||||
|
console.log(`[4KHDHub] Adding Referer/Origin headers for validation of r2.dev URL: ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
timeout: 15000,
|
timeout: 15000,
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||||
|
...extraHeaders
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -751,6 +763,14 @@ function extractHubCloudLinks(url, referer) {
|
|||||||
if (displayFilename) titleParts.push(displayFilename);
|
if (displayFilename) titleParts.push(displayFilename);
|
||||||
if (size) titleParts.push(size);
|
if (size) titleParts.push(size);
|
||||||
const finalTitle = titleParts.join('\n');
|
const finalTitle = titleParts.join('\n');
|
||||||
|
let headers = {};
|
||||||
|
try {
|
||||||
|
const host = new URL(link).hostname;
|
||||||
|
if (/r2\.dev$/i.test(host)) {
|
||||||
|
headers = { Referer: baseUrl + '/', Origin: baseUrl };
|
||||||
|
console.log('[4KHDHub] Added Referer/Origin headers for r2.dev FSL link');
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
name: `4KHDHub - FSL Server${qualityLabel}`,
|
name: `4KHDHub - FSL Server${qualityLabel}`,
|
||||||
@@ -758,7 +778,7 @@ function extractHubCloudLinks(url, referer) {
|
|||||||
url: link,
|
url: link,
|
||||||
quality: quality,
|
quality: quality,
|
||||||
provider: '4khdhub',
|
provider: '4khdhub',
|
||||||
headers: {}
|
headers
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -767,6 +787,14 @@ function extractHubCloudLinks(url, referer) {
|
|||||||
if (displayFilename) titleParts.push(displayFilename);
|
if (displayFilename) titleParts.push(displayFilename);
|
||||||
if (size) titleParts.push(size);
|
if (size) titleParts.push(size);
|
||||||
const finalTitle = titleParts.join('\n');
|
const finalTitle = titleParts.join('\n');
|
||||||
|
let headers = {};
|
||||||
|
try {
|
||||||
|
const host = new URL(link).hostname;
|
||||||
|
if (/r2\.dev$/i.test(host)) {
|
||||||
|
headers = { Referer: baseUrl + '/', Origin: baseUrl };
|
||||||
|
console.log('[4KHDHub] Added Referer/Origin headers for r2.dev FSL link (fallback)');
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
name: `4KHDHub - FSL Server${qualityLabel}`,
|
name: `4KHDHub - FSL Server${qualityLabel}`,
|
||||||
@@ -774,7 +802,7 @@ function extractHubCloudLinks(url, referer) {
|
|||||||
url: link,
|
url: link,
|
||||||
quality: quality,
|
quality: quality,
|
||||||
provider: '4khdhub',
|
provider: '4khdhub',
|
||||||
headers: {}
|
headers
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else if (text.includes('Download File')) {
|
} else if (text.includes('Download File')) {
|
||||||
@@ -1306,12 +1334,22 @@ function extractStreamingLinks(downloadLinks) {
|
|||||||
.then(results => {
|
.then(results => {
|
||||||
const validResults = results.filter(result => result !== null);
|
const validResults = results.filter(result => result !== null);
|
||||||
const flatResults = validResults.flat();
|
const flatResults = validResults.flat();
|
||||||
// Filter out .zip files
|
// Keep .zip links but strip the trailing .zip extension (attempt direct file URL)
|
||||||
const filteredResults = flatResults.filter(link => {
|
const transformedResults = flatResults
|
||||||
return link && link.url && !link.url.toLowerCase().endsWith('.zip');
|
.filter(link => link && link.url)
|
||||||
});
|
.map(link => {
|
||||||
|
try {
|
||||||
|
const lower = link.url.toLowerCase();
|
||||||
|
if (lower.endsWith('.zip')) {
|
||||||
|
const newUrl = link.url.slice(0, -4); // remove .zip
|
||||||
|
console.log(`[4KHDHub] Converted zip URL to non-zip: ${link.url} -> ${newUrl}`);
|
||||||
|
return { ...link, url: newUrl };
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return link;
|
||||||
|
});
|
||||||
// Note: Link count will be logged after validation completes
|
// Note: Link count will be logged after validation completes
|
||||||
return filteredResults;
|
return transformedResults;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1778,7 +1816,7 @@ async function get4KHDHubStreams(tmdbId, type, season = null, episode = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Normalize to generic stream object format
|
// Normalize to generic stream object format
|
||||||
const streams = validatedLinks.map(link => ({
|
let streams = validatedLinks.map(link => ({
|
||||||
name: link.name, // Don't add prefix since it's already included
|
name: link.name, // Don't add prefix since it's already included
|
||||||
title: link.title || link.name,
|
title: link.title || link.name,
|
||||||
url: link.url,
|
url: link.url,
|
||||||
@@ -1787,6 +1825,28 @@ async function get4KHDHubStreams(tmdbId, type, season = null, episode = null) {
|
|||||||
headers: {}
|
headers: {}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// Always remove r2.dev links
|
||||||
|
{
|
||||||
|
const before = streams.length;
|
||||||
|
streams = streams.filter(s => {
|
||||||
|
try { return !new URL(s.url).hostname.endsWith('r2.dev'); } catch { return true; }
|
||||||
|
});
|
||||||
|
const removed = before - streams.length;
|
||||||
|
if (removed > 0) console.log(`[4KHDHub] Removed ${removed} r2.dev streams (permanent policy)`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Host distribution instrumentation
|
||||||
|
try {
|
||||||
|
const hostCounts = streams.reduce((acc, s) => {
|
||||||
|
try {
|
||||||
|
const h = new URL(s.url).hostname;
|
||||||
|
acc[h] = (acc[h] || 0) + 1;
|
||||||
|
} catch {}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
console.log('[4KHDHub] Final host distribution:', hostCounts);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
console.log(`[4KHDHub] Returning ${streams.length} streams`);
|
console.log(`[4KHDHub] Returning ${streams.length} streams`);
|
||||||
return streams;
|
return streams;
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -309,7 +309,15 @@ async function handleSingleServer(embedUrl, mediaType, season, episode, tmdbId)
|
|||||||
title: playerConfig.title || `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - MoviesClub`,
|
title: playerConfig.title || `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - MoviesClub`,
|
||||||
url: playerConfig.file,
|
url: playerConfig.file,
|
||||||
quality: extractQualityFromUrl(playerConfig.file) || '720p',
|
quality: extractQualityFromUrl(playerConfig.file) || '720p',
|
||||||
headers: {}
|
headers: (() => {
|
||||||
|
if (playerConfig.file.includes('vidora.stream')) {
|
||||||
|
return {
|
||||||
|
Origin: 'https://vidora.stream',
|
||||||
|
Referer: 'https://vidora.stream/'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
})()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add poster if available
|
// Add poster if available
|
||||||
@@ -384,7 +392,7 @@ function extractStreamsFromServer(serverData, serverName, mediaType, season, epi
|
|||||||
title: `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - ${serverName}`,
|
title: `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - ${serverName}`,
|
||||||
url: serverData,
|
url: serverData,
|
||||||
quality: extractQualityFromUrl(serverData) || '720p',
|
quality: extractQualityFromUrl(serverData) || '720p',
|
||||||
headers: {}
|
headers: serverData.includes('vidora.stream') ? { Origin: 'https://vidora.stream', Referer: 'https://vidora.stream/' } : {}
|
||||||
};
|
};
|
||||||
streams.push(stream);
|
streams.push(stream);
|
||||||
}
|
}
|
||||||
@@ -406,7 +414,7 @@ function extractStreamsFromServer(serverData, serverName, mediaType, season, epi
|
|||||||
title: `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - ${serverName}`,
|
title: `${mediaType === 'movie' ? 'Movie' : `S${season.toString().padStart(2, '0')}E${episode.toString().padStart(2, '0')}`} ${tmdbId} - ${serverName}`,
|
||||||
url: serverData[key],
|
url: serverData[key],
|
||||||
quality: extractQualityFromUrl(serverData[key]) || '720p',
|
quality: extractQualityFromUrl(serverData[key]) || '720p',
|
||||||
headers: {}
|
headers: serverData[key].includes('vidora.stream') ? { Origin: 'https://vidora.stream', Referer: 'https://vidora.stream/' } : {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add poster if available
|
// Add poster if available
|
||||||
|
|||||||
Reference in New Issue
Block a user