Files
vega-providers/test-providers.js

640 lines
21 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const axios = require("axios");
const cheerio = require("cheerio");
const fs = require("fs");
const path = require("path");
// Load utilities
let providerContext;
try {
const { getBaseUrl } = require("./dist/getBaseUrl.js");
providerContext = {
axios,
cheerio,
getBaseUrl,
commonHeaders: {
"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",
},
Aes: {},
};
} catch (error) {
console.log(
"⚠️ Could not load provider context. Run 'npm run build' first.",
);
providerContext = null;
}
/**
* Helper to pick random items from array
*/
function pickRandom(arr, count = 1) {
const shuffled = [...arr].sort(() => Math.random() - 0.5);
return count === 1 ? shuffled[0] : shuffled.slice(0, count);
}
/**
* Sleep helper
*/
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Provider testing utility - Full integration test
*/
class ProviderTester {
constructor(options = {}) {
this.timeout = options.timeout || 30000;
this.postsToTest = options.postsToTest || 2;
this.linksToTest = options.linksToTest || 2;
this.signal = new AbortController().signal;
this.results = {};
}
/**
* Load provider module
*/
loadModule(providerName, moduleName) {
try {
const modulePath = `./dist/${providerName}/${moduleName}.js`;
// Clear cache to get fresh module
delete require.cache[require.resolve(modulePath)];
return require(modulePath);
} catch (error) {
return null;
}
}
/**
* Load manifest to get enabled providers
*/
loadManifest() {
try {
const manifestPath = path.join(__dirname, "manifest.json");
if (!fs.existsSync(manifestPath)) {
console.log("⚠️ manifest.json not found");
return [];
}
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
return manifest;
} catch (error) {
console.log("⚠️ Failed to load manifest:", error.message);
return [];
}
}
/**
* Get available providers from dist folder (excluding disabled ones)
*/
getAvailableProviders() {
const distPath = path.join(__dirname, "dist");
if (!fs.existsSync(distPath)) {
console.log("❌ dist folder not found. Run 'npm run build' first.");
return [];
}
// Load manifest to check for disabled providers
const manifest = this.loadManifest();
const disabledProviders = manifest
.filter((p) => p.disabled === true)
.map((p) => p.value);
if (disabledProviders.length > 0) {
console.log(
`\n⏭️ Skipping disabled providers: ${disabledProviders.join(", ")}`,
);
}
const providers = fs
.readdirSync(distPath, { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name)
.filter((name) => {
// Skip disabled providers
if (disabledProviders.includes(name)) {
return false;
}
// Check if it has required modules
const hasRequired = [
"catalog.js",
"posts.js",
"meta.js",
"stream.js",
].every((file) => fs.existsSync(path.join(distPath, name, file)));
return hasRequired;
});
return providers;
}
/**
* Test a single provider with full flow
*/
async testProvider(providerName) {
console.log(`\n${"=".repeat(60)}`);
console.log(`🧪 Testing Provider: ${providerName}`);
console.log("=".repeat(60));
const result = {
provider: providerName,
catalog: { success: false, data: null, error: null },
posts: { success: false, data: null, error: null },
meta: { success: false, data: null, error: null },
episodes: { success: false, data: null, error: null, skipped: false },
stream: { success: false, data: null, error: null, skipped: false },
summary: { passed: 0, failed: 0, skipped: 0 },
};
try {
// Step 1: Load and test catalog
console.log("\n📂 Step 1: Loading Catalog...");
const catalogModule = this.loadModule(providerName, "catalog");
if (!catalogModule) {
throw new Error("Catalog module not found");
}
const catalog = catalogModule.catalog || [];
// const genres = catalogModule.genres || [];
const allFilters = [...catalog];
if (allFilters.length === 0) {
throw new Error("No filters found in catalog");
}
result.catalog.success = true;
result.catalog.data = {
catalogCount: catalog.length,
};
console.log(` ✅ Found ${catalog.length} catalog items`);
// Pick a random filter
const randomFilter = pickRandom(allFilters);
console.log(
` 🎲 Selected random filter: "${randomFilter.title}" (${randomFilter.filter})`,
);
// Step 2: Test getPosts with random filter
console.log("\n📝 Step 2: Testing getPosts...");
const postsModule = this.loadModule(providerName, "posts");
if (!postsModule || !postsModule.getPosts) {
throw new Error("getPosts function not found");
}
const posts = await postsModule.getPosts({
filter: randomFilter.filter,
page: 1,
providerValue: providerName,
signal: this.signal,
providerContext,
});
if (!Array.isArray(posts) || posts.length === 0) {
throw new Error("getPosts returned empty or invalid result");
}
result.posts.success = true;
result.posts.data = { count: posts.length };
console.log(` ✅ Got ${posts.length} posts`);
// Pick random posts to test
const postsToTest = pickRandom(
posts,
Math.min(this.postsToTest, posts.length),
);
console.log(
` 🎲 Selected ${postsToTest.length} random posts for meta testing`,
);
// Step 3: Test getMeta with random posts
console.log("\n📋 Step 3: Testing getMeta...");
const metaModule = this.loadModule(providerName, "meta");
if (!metaModule || !metaModule.getMeta) {
throw new Error("getMeta function not found");
}
const metaResults = [];
for (const post of postsToTest) {
console.log(`\n 📌 Testing: "${post.title.substring(0, 50)}..."`);
console.log(` Link: ${post.link}`);
try {
await sleep(500); // Small delay between requests
const meta = await metaModule.getMeta({
link: post.link,
providerContext,
});
if (!meta || !meta.linkList) {
console.log(
` ⚠️ Meta returned but linkList is empty/missing`,
);
continue;
}
metaResults.push({ post, meta });
console.log(
` ✅ Got meta: type=${meta.type}, links=${meta.linkList.length}`,
);
// Show link structure
meta.linkList.forEach((link, i) => {
const hasEpisodes = !!link.episodesLink;
const hasDirectLinks =
link.directLinks && link.directLinks.length > 0;
console.log(
` [${i + 1}] ${link.title.substring(0, 30)} - ${
hasEpisodes ? "📺 Episodes" : ""
}${hasDirectLinks ? "🎬 Direct" : ""}`,
);
});
} catch (err) {
console.log(` ❌ Error: ${err.message}`);
}
}
if (metaResults.length === 0) {
throw new Error("No valid meta data retrieved");
}
result.meta.success = true;
result.meta.data = { testedCount: metaResults.length };
// Step 4: Test episodes OR stream based on meta content
console.log("\n🔗 Step 4: Testing Episodes/Stream...");
// Find links with episodes
const episodeLinks = [];
const directLinks = [];
for (const { meta } of metaResults) {
for (const link of meta.linkList) {
if (link.episodesLink) {
episodeLinks.push({ meta, link });
}
if (link.directLinks && link.directLinks.length > 0) {
directLinks.push({ meta, link });
}
}
}
// Test episodes if available
if (episodeLinks.length > 0) {
console.log(`\n 📺 Found ${episodeLinks.length} episode links`);
const episodesModule = this.loadModule(providerName, "episodes");
if (episodesModule && episodesModule.getEpisodes) {
const testEpisodeLink = pickRandom(episodeLinks);
console.log(
` 🎲 Testing episodes from: ${testEpisodeLink.link.title}`,
);
console.log(` URL: ${testEpisodeLink.link.episodesLink}`);
try {
const episodes = await episodesModule.getEpisodes({
url: testEpisodeLink.link.episodesLink,
providerContext,
});
if (Array.isArray(episodes) && episodes.length > 0) {
result.episodes.success = true;
result.episodes.data = { count: episodes.length };
console.log(` ✅ Got ${episodes.length} episodes`);
// Show first few episodes
episodes.slice(0, 3).forEach((ep, i) => {
console.log(` [${i + 1}] ${ep.title}`);
});
// Test stream with random episode
console.log(`\n 🎬 Testing stream with random episode...`);
const randomEpisode = pickRandom(episodes);
console.log(` Episode: ${randomEpisode.title}`);
try {
const streamModule = this.loadModule(providerName, "stream");
if (streamModule && streamModule.getStream) {
const streams = await streamModule.getStream({
link: randomEpisode.link,
type: "series",
signal: this.signal,
providerContext,
});
if (Array.isArray(streams) && streams.length > 0) {
result.stream.success = true;
result.stream.data = {
count: streams.length,
type: "series",
};
console.log(` ✅ Got ${streams.length} stream(s)`);
streams.forEach((s, i) => {
console.log(
` [${i + 1}] ${s.server} - ${
s.quality || "unknown"
} quality`,
);
});
} else {
console.log(` ⚠️ No streams returned`);
result.stream.error = "No streams returned";
}
}
} catch (err) {
console.log(` ❌ Stream error: ${err.message}`);
result.stream.error = err.message;
}
} else {
console.log(` ⚠️ No episodes returned`);
result.episodes.error = "No episodes returned";
}
} catch (err) {
console.log(` ❌ Episodes error: ${err.message}`);
result.episodes.error = err.message;
}
} else {
console.log(` ⚠️ getEpisodes function not found`);
result.episodes.skipped = true;
result.episodes.error = "Function not available";
}
} else {
result.episodes.skipped = true;
console.log(` No episode links found, skipping episodes test`);
}
// Test direct links/stream if episodes not tested or no episode links
if (directLinks.length > 0 && !result.stream.success) {
console.log(`\n 🎬 Found ${directLinks.length} direct link entries`);
const testDirectLink = pickRandom(directLinks);
const linksToTest = pickRandom(
testDirectLink.link.directLinks,
Math.min(this.linksToTest, testDirectLink.link.directLinks.length),
);
console.log(
` 🎲 Testing ${linksToTest.length} random direct link(s)`,
);
const streamModule = this.loadModule(providerName, "stream");
if (streamModule && streamModule.getStream) {
for (const directLink of Array.isArray(linksToTest)
? linksToTest
: [linksToTest]) {
console.log(`\n Testing: ${directLink.title}`);
console.log(` Link: ${directLink.link}`);
try {
await sleep(500);
const streams = await streamModule.getStream({
link: directLink.link,
type: directLink.type || "movie",
signal: this.signal,
providerContext,
});
if (Array.isArray(streams) && streams.length > 0) {
result.stream.success = true;
result.stream.data = {
count: streams.length,
type: directLink.type || "movie",
};
console.log(` ✅ Got ${streams.length} stream(s)`);
streams.forEach((s, i) => {
console.log(
` [${i + 1}] ${s.server} - ${
s.quality || "unknown"
} quality`,
);
});
break; // One success is enough
} else {
console.log(` ⚠️ No streams returned`);
}
} catch (err) {
console.log(` ❌ Stream error: ${err.message}`);
result.stream.error = err.message;
}
}
} else {
console.log(` ❌ getStream function not found`);
result.stream.error = "Function not available";
}
} else if (
!result.stream.success &&
directLinks.length === 0 &&
episodeLinks.length === 0
) {
result.stream.skipped = true;
console.log(` No links to test stream with`);
}
} catch (error) {
console.log(`\n❌ Test failed: ${error.message}`);
// Determine which step failed
if (!result.catalog.success) {
result.catalog.error = error.message;
} else if (!result.posts.success) {
result.posts.error = error.message;
} else if (!result.meta.success) {
result.meta.error = error.message;
}
}
// Calculate summary
const steps = ["catalog", "posts", "meta", "episodes", "stream"];
for (const step of steps) {
if (result[step].success) {
result.summary.passed++;
} else if (result[step].skipped) {
result.summary.skipped++;
} else if (result[step].error) {
result.summary.failed++;
}
}
// Print summary
console.log(`\n${"─".repeat(60)}`);
console.log(`📊 Provider Summary: ${providerName}`);
console.log("─".repeat(60));
console.log(` ✅ Passed: ${result.summary.passed}`);
console.log(` ❌ Failed: ${result.summary.failed}`);
console.log(` ⏭️ Skipped: ${result.summary.skipped}`);
// List which steps passed/failed/skipped
console.log("\n Step Results:");
for (const step of steps) {
if (result[step].success) {
console.log(`${step}`);
} else if (result[step].skipped) {
console.log(` ⏭️ ${step} (skipped)`);
} else if (result[step].error) {
console.log(`${step}: ${result[step].error}`);
} else {
console.log(`${step} (not tested)`);
}
}
const statusIcon = result.summary.failed === 0 ? "✅" : "❌";
console.log(
`\n ${statusIcon} Overall: ${
result.summary.failed === 0 ? "PASSED" : "FAILED"
}`,
);
return result;
}
/**
* Test all providers
*/
async testAllProviders() {
console.log("🚀 Starting comprehensive provider tests...\n");
if (!providerContext) {
console.log("❌ Provider context not loaded. Run 'npm run build' first.");
return null;
}
const providers = this.getAvailableProviders();
if (providers.length === 0) {
console.log("❌ No providers found.");
return null;
}
console.log(`📦 Found ${providers.length} providers to test:`);
providers.forEach((p) => console.log(`${p}`));
const results = {};
let passed = 0;
let failed = 0;
for (const provider of providers) {
try {
results[provider] = await this.testProvider(provider);
if (results[provider].summary.failed === 0) {
passed++;
} else {
failed++;
}
} catch (error) {
console.log(
`\n❌ Critical error testing ${provider}: ${error.message}`,
);
failed++;
results[provider] = { error: error.message };
}
// Small delay between providers
await sleep(1000);
}
// Final summary
console.log(`\n${"═".repeat(60)}`);
console.log("📊 FINAL TEST SUMMARY");
console.log("═".repeat(60));
console.log(` Total Providers: ${providers.length}`);
console.log(` ✅ Passed: ${passed}`);
console.log(` ❌ Failed: ${failed}`);
// List failed providers with details
if (failed > 0) {
console.log(`\n${"─".repeat(60)}`);
console.log("❌ FAILED PROVIDERS:");
console.log("─".repeat(60));
for (const [name, result] of Object.entries(results)) {
if (result.error) {
// Critical error
console.log(`\n${name}`);
console.log(` Error: ${result.error}`);
} else if (result.summary?.failed > 0) {
// Step failures
console.log(`\n${name}`);
const steps = ["catalog", "posts", "meta", "episodes", "stream"];
for (const step of steps) {
if (result[step]?.error && !result[step]?.skipped) {
console.log(`${step}: ${result[step].error}`);
}
}
}
}
}
// List passed providers
if (passed > 0) {
console.log(`\n${"─".repeat(60)}`);
console.log("✅ PASSED PROVIDERS:");
console.log("─".repeat(60));
const passedProviders = Object.entries(results)
.filter(([_, result]) => result.summary?.failed === 0 && !result.error)
.map(([name]) => name);
console.log(` ${passedProviders.join(", ")}`);
}
console.log(`\n${"═".repeat(60)}`);
return results;
}
}
/**
* CLI interface
*/
async function main() {
const args = process.argv.slice(2);
const providerName = args[0];
// Check for options
const postsToTest =
parseInt(args.find((a) => a.startsWith("--posts="))?.split("=")[1]) || 2;
const linksToTest =
parseInt(args.find((a) => a.startsWith("--links="))?.split("=")[1]) || 2;
const tester = new ProviderTester({ postsToTest, linksToTest });
if (args.includes("--help") || args.includes("-h")) {
console.log(`
🎯 Vega Providers Integration Tester
=====================================
Usage: node test-providers.js [provider] [options]
Arguments:
provider Name of specific provider to test (optional)
If not provided, tests all providers
Options:
--posts=N Number of random posts to test (default: 2)
--links=N Number of random direct links to test (default: 2)
--help, -h Show this help message
Test Flow:
1. Load catalog → pick random filter
2. Call getPosts with filter
3. Pick random posts → call getMeta
4. If episodesLink → call getEpisodes → getStream
5. If directLinks → call getStream
Examples:
node test-providers.js # Test all providers
node test-providers.js vega # Test only vega provider
node test-providers.js mod --posts=3 # Test mod with 3 random posts
node test-providers.js --posts=1 --links=1 # Quick test all providers
`);
return;
}
if (providerName && !providerName.startsWith("--")) {
await tester.testProvider(providerName);
} else {
await tester.testAllProviders();
}
}
if (require.main === module) {
main().catch(console.error);
}
module.exports = ProviderTester;