calculus / src /yourbittorrent.js
no1b4me's picture
Upload 12 files
8740315 verified
import readTorrent from 'read-torrent';
import { promisify } from 'util';
const readTorrentPromise = promisify(readTorrent);
const SITE_CONFIG = {
baseUrl: 'https://yourbittorrent.com',
fallbackUrls: [
'https://yourbittorrent2.com'
]
};
async function getCinemetaMetadata(imdbId) {
try {
if (!imdbId.startsWith('tt')) {
return null;
}
console.log(`\n🎬 Fetching Cinemeta data for ${imdbId}`);
const response = await fetch(`https://v3-cinemeta.strem.io/meta/movie/${imdbId}.json`);
if (!response.ok) throw new Error('Failed to fetch from Cinemeta');
const data = await response.json();
console.log('✅ Found:', data.meta.name);
return data;
} catch (error) {
console.error('❌ Cinemeta error:', error);
return null;
}
}
async function fetchWithTimeout(url, options = {}, timeout = 30000) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
return response;
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
async function downloadAndParseTorrent(url) {
try {
console.log('Downloading torrent from:', url);
const options = {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': '*/*',
}
};
const torrentInfo = await readTorrentPromise(url, options);
// Check if any of the files are video files
const hasVideoFiles = torrentInfo.files?.some(file => {
const filePath = Array.isArray(file.path) ? file.path.join('/') : file.path;
return /\.(mp4|mkv|avi|mov|wmv)$/i.test(filePath);
});
if (!hasVideoFiles) {
console.log('No video files found');
return null;
}
// Find main video file
const videoFiles = torrentInfo.files.filter(file => {
const filePath = Array.isArray(file.path) ? file.path.join('/') : file.path;
return /\.(mp4|mkv|avi|mov|wmv)$/i.test(filePath);
}).sort((a, b) => b.length - a.length);
const mainFile = videoFiles[0];
const mainFilePath = Array.isArray(mainFile.path) ? mainFile.path.join('/') : mainFile.path;
const magnetUri = `magnet:?xt=urn:btih:${torrentInfo.infoHash}` +
`&dn=${encodeURIComponent(torrentInfo.name)}` +
(torrentInfo.announce ? torrentInfo.announce.map(tr => `&tr=${encodeURIComponent(tr)}`).join('') : '');
return {
magnetLink: magnetUri,
filename: mainFilePath,
torrentName: torrentInfo.name,
infoHash: torrentInfo.infoHash,
mainFileSize: mainFile.length
};
} catch (error) {
console.error('Error downloading/parsing torrent:', error);
return null;
}
}
function isCorrectEpisode(title, searchQuery) {
if (!title || !searchQuery) return false;
// Extract season and episode numbers from search query
const queryMatch = searchQuery.match(/S(\d{2})E(\d{2})/i);
if (!queryMatch) return false;
const querySeasonNum = parseInt(queryMatch[1]);
const queryEpisodeNum = parseInt(queryMatch[2]);
// Common episode patterns
const patterns = [
/S(\d{2})E(\d{2})/i, // S01E01
/(\d{1,2})x(\d{2})/i, // 1x01
/[. ](\d{1,2})(\d{2})[. ]/, // .101. or ' 101 '
/Season (\d{1,2}) Episode (\d{1,2})/i, // Season 1 Episode 1
/Season.?(\d{1,2}).?Episode.?(\d{1,2})/i // Season1Episode1, Season.1.Episode.1
];
for (const pattern of patterns) {
const match = title.match(pattern);
if (match) {
const seasonNum = parseInt(match[1]);
const episodeNum = parseInt(match[2]);
// Check if it matches the requested season and episode
if (seasonNum === querySeasonNum && episodeNum === queryEpisodeNum) {
return true;
}
}
}
// Handle combined season+episode format (101, 102, etc)
const combinedMatch = title.match(/[^0-9](\d)(\d{2})[^0-9]/);
if (combinedMatch) {
const seasonNum = parseInt(combinedMatch[1]);
const episodeNum = parseInt(combinedMatch[2]);
if (seasonNum === querySeasonNum && episodeNum === queryEpisodeNum) {
return true;
}
}
return false;
}
function parseSearchResults(html) {
const results = [];
const rows = html.match(/<tr class="table-default">[\s\S]*?<\/tr>/g) || [];
for (const row of rows) {
try {
const titleMatch = row.match(/href="\/torrent\/.*?">(.*?)<\/a>/);
const sizeMatch = row.match(/<td.*?>\s*([\d.]+\s*[KMGT]B)\s*<\/td>/);
const seedersMatch = row.match(/<td.*?>\s*(\d+)\s*<\/td>/);
const leechersMatch = row.match(/<td.*?>\s*(\d+)\s*<\/td>/);
const downloadIdMatch = row.match(/\/torrent\/(\d+)\//);
if (titleMatch && downloadIdMatch) {
const title = titleMatch[1].trim();
// Basic video file check
if (!title.match(/\.(mp4|mkv|avi|mov|wmv)$/i) &&
!title.match(/(1080p|720p|2160p|4k|uhd|web-?dl|bluray)/i)) {
continue;
}
results.push({
title: title,
size: sizeMatch?.[1] || 'Unknown',
seeders: seedersMatch?.[1] || '0',
leechers: leechersMatch?.[1] || '0',
downloadId: downloadIdMatch[1],
magnetLink: downloadIdMatch[1] ?
`${SITE_CONFIG.baseUrl}/down/${downloadIdMatch[1]}.torrent` :
null
});
}
} catch (error) {
console.error('Error parsing result row:', error);
}
}
return results;
}
async function searchTorrents(searchQuery, type = 'movie') {
console.log('\n🔄 Searching YourBittorrent for:', searchQuery);
try {
let title = searchQuery;
let year = '';
let searchTitle;
if (type === 'movie' && searchQuery.startsWith('tt')) {
const metadata = await getCinemetaMetadata(searchQuery);
if (!metadata?.meta) {
throw new Error('Failed to get movie metadata');
}
title = metadata.meta.name;
year = new Date(metadata.meta.released).getFullYear().toString();
searchTitle = title;
} else if (type === 'series') {
// For series, extract show name without episode info for initial search
const showMatch = searchQuery.match(/(.+?)S\d{2}E\d{2}/i);
searchTitle = showMatch ? showMatch[1].trim() : searchQuery;
}
const formattedQuery = searchTitle
.replace(/[\\s]+/g, '-')
.toLowerCase();
const url = `${SITE_CONFIG.baseUrl}/?v=&c=${type === 'movie' ? 'movies' : 'tv'}&q=${encodeURIComponent(formattedQuery)}`;
console.log('Request URL:', url);
const response = await fetchWithTimeout(url, {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9',
'Accept-Language': 'en-US,en;q=0.9'
}
});
if (!response.ok) {
throw new Error(`Search request failed: ${response.status}`);
}
const html = await response.text();
const results = parseSearchResults(html);
console.log(`Found ${results.length} raw results`);
// Process each result through read-torrent
const streams = await Promise.all(results.map(async result => {
try {
if (!result.magnetLink) return null;
const torrentInfo = await downloadAndParseTorrent(result.magnetLink);
if (!torrentInfo) return null;
return {
magnetLink: torrentInfo.magnetLink,
filename: torrentInfo.filename,
websiteTitle: result.title,
quality: extractQuality(result.title),
size: result.size,
source: 'YourBittorrent',
seeders: parseInt(result.seeders) || 0,
leechers: parseInt(result.leechers) || 0,
mainFileSize: torrentInfo.mainFileSize
};
} catch (error) {
console.error('Error processing result:', error);
return null;
}
}));
const validStreams = streams.filter(Boolean);
let filteredStreams;
if (type === 'movie' && year) {
const searchTerms = title.toLowerCase().split(' ');
filteredStreams = validStreams.filter(stream => {
const streamTitle = stream.websiteTitle.toLowerCase();
const hasYear = streamTitle.includes(year);
const hasAllTerms = searchTerms.every(term =>
streamTitle.includes(term.toLowerCase())
);
return hasYear && hasAllTerms;
});
} else if (type === 'series') {
// Use strict episode matching
filteredStreams = validStreams.filter(stream =>
isCorrectEpisode(stream.websiteTitle, searchQuery)
);
} else {
filteredStreams = validStreams;
}
console.log(`Found ${filteredStreams.length} relevant streams after filtering`);
if (filteredStreams.length > 0) {
console.log('Sample matched stream:', {
title: filteredStreams[0].websiteTitle,
quality: filteredStreams[0].quality,
size: filteredStreams[0].size
});
}
filteredStreams.sort((a, b) => {
const qualityOrder = { '2160p': 4, '4k': 4, '1080p': 3, '720p': 2 };
const qualityDiff = (qualityOrder[b.quality] || 0) - (qualityOrder[a.quality] || 0);
if (qualityDiff === 0) {
return b.seeders - a.seeders;
}
return qualityDiff;
});
return filteredStreams;
} catch (error) {
console.error('❌ Error searching YourBittorrent:', error);
for (const fallbackUrl of SITE_CONFIG.fallbackUrls) {
try {
SITE_CONFIG.baseUrl = fallbackUrl;
return await searchTorrents(searchQuery, type);
} catch (fallbackError) {
console.error(`Fallback ${fallbackUrl} also failed:`, fallbackError);
}
}
return [];
}
}
function extractQuality(title) {
const qualityMatch = title.match(/\b(2160p|1080p|720p|4k|uhd)\b/i);
return qualityMatch ? qualityMatch[1].toLowerCase() : '';
}
export { searchTorrents };