|
import xml2js from 'xml2js'; |
|
import readTorrent from 'read-torrent'; |
|
import { promisify } from 'util'; |
|
|
|
const readTorrentPromise = promisify(readTorrent); |
|
|
|
const RSS_FEEDS = [ |
|
{ |
|
name: 'IPT', |
|
url: 'https://ipt.beelyrics.net/t.rss?u=2181360;tp=aa51b88f9fa0d5abdbb925b10c0d9d59;48;20;7;100;101;90;89;68;6;62;54;22;99;4;5;65;23;26;79;25;24;download' |
|
} |
|
]; |
|
|
|
const parser = new xml2js.Parser({ |
|
explicitArray: false, |
|
ignoreAttrs: true |
|
}); |
|
|
|
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': '*/*', |
|
'Cookie': 'uid=2105434; pass=82acec029bda7c69257905656252be36; login=1', |
|
'Referer': 'https://ipt.beelyrics.net/', |
|
'Origin': 'https://ipt.beelyrics.net' |
|
} |
|
}; |
|
|
|
const torrentInfo = await readTorrentPromise(url, options); |
|
console.log('Parsed torrent info:', { |
|
name: torrentInfo.name, |
|
length: torrentInfo.length, |
|
files: torrentInfo.files?.length || 0, |
|
announce: torrentInfo.announce |
|
}); |
|
|
|
if (!torrentInfo?.infoHash) { |
|
console.error('No info hash found'); |
|
return null; |
|
} |
|
|
|
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); |
|
}) || []; |
|
|
|
if (videoFiles.length === 0) { |
|
console.log('No video files found'); |
|
return null; |
|
} |
|
|
|
videoFiles.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, |
|
files: videoFiles, |
|
infoHash: torrentInfo.infoHash, |
|
mainFile: { |
|
path: mainFilePath, |
|
length: mainFile.length |
|
} |
|
}; |
|
|
|
} catch (error) { |
|
console.error('Error:', error); |
|
return null; |
|
} |
|
} |
|
|
|
function formatFileSize(bytes) { |
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; |
|
if (bytes === 0) return '0 Bytes'; |
|
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); |
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]; |
|
} |
|
|
|
function parseTorrentInfo(item) { |
|
let size = 'Unknown'; |
|
let category = 'Unknown'; |
|
let sizeInMB = 0; |
|
|
|
const parts = (item.description || '').split(/[;|]/); |
|
if (parts.length >= 1) { |
|
const sizeInfo = formatSize(parts[0].trim()); |
|
size = sizeInfo.size; |
|
sizeInMB = sizeInfo.sizeInMB; |
|
} |
|
if (parts.length >= 2) { |
|
category = parts[1].trim(); |
|
} |
|
|
|
return { size, category, sizeInMB }; |
|
} |
|
|
|
function formatSize(sizeStr) { |
|
if (!sizeStr) return { size: 'Unknown', sizeInMB: 0 }; |
|
|
|
const match = sizeStr.match(/([\d.]+)\s*(GB|MB|KB|B)/i); |
|
if (!match) return { size: sizeStr, sizeInMB: 0 }; |
|
|
|
const [, value, unit] = match; |
|
const numValue = parseFloat(value); |
|
let sizeInMB = numValue; |
|
|
|
switch (unit.toUpperCase()) { |
|
case 'GB': |
|
sizeInMB *= 1024; |
|
break; |
|
case 'KB': |
|
sizeInMB /= 1024; |
|
break; |
|
case 'B': |
|
sizeInMB /= (1024 * 1024); |
|
break; |
|
} |
|
|
|
return { |
|
size: sizeStr.trim(), |
|
sizeInMB: Math.round(sizeInMB * 100) / 100 |
|
}; |
|
} |
|
|
|
function extractQuality(title) { |
|
const qualityMatch = title.match(/\b(2160p|1080p|720p|4k|uhd)\b/i); |
|
return qualityMatch ? qualityMatch[1].toLowerCase() : ''; |
|
} |
|
|
|
async function fetchRSSFeeds(imdbId) { |
|
console.log('\n🔄 Fetching RSS feeds for:', imdbId); |
|
let allStreams = []; |
|
|
|
for (const feed of RSS_FEEDS) { |
|
try { |
|
console.log(`\nFetching from ${feed.name}...`); |
|
|
|
const rssUrl = `${feed.url};download;q=${imdbId}`; |
|
|
|
const response = await fetch(rssUrl, { |
|
headers: { |
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', |
|
'Accept': 'application/rss+xml,application/xml,text/xml', |
|
'Cookie': 'uid=2105434; pass=82acec029bda7c69257905656252be36; login=1', |
|
'Referer': 'https://ipt.beelyrics.net/', |
|
'Origin': 'https://ipt.beelyrics.net' |
|
} |
|
}); |
|
|
|
if (!response.ok) { |
|
console.error(`❌ Failed to fetch from ${feed.name}:`, response.status); |
|
continue; |
|
} |
|
|
|
const rssData = await response.text(); |
|
const result = await parser.parseStringPromise(rssData); |
|
|
|
if (!result?.rss?.channel?.item) { |
|
console.log(`No items found in ${feed.name}`); |
|
continue; |
|
} |
|
|
|
const items = Array.isArray(result.rss.channel.item) ? |
|
result.rss.channel.item : [result.rss.channel.item]; |
|
|
|
console.log(`Found ${items.length} items in ${feed.name}`); |
|
|
|
const streams = await Promise.all(items.map(async (item, index) => { |
|
try { |
|
console.log(`\nProcessing item ${index + 1}/${items.length}:`, item.title); |
|
|
|
const torrentInfo = await downloadAndParseTorrent(item.link); |
|
if (!torrentInfo) return null; |
|
|
|
const { size, category } = parseTorrentInfo(item); |
|
const quality = extractQuality(item.title); |
|
|
|
return { |
|
magnetLink: torrentInfo.magnetLink, |
|
filename: torrentInfo.mainFile.path, |
|
websiteTitle: item.title, |
|
quality, |
|
size, |
|
source: feed.name, |
|
infoHash: torrentInfo.infoHash, |
|
mainFileSize: torrentInfo.mainFile.length |
|
}; |
|
} catch (error) { |
|
console.error(`Error processing item ${index + 1}:`, error); |
|
return null; |
|
} |
|
})); |
|
|
|
const validStreams = streams.filter(Boolean); |
|
console.log(`✅ Processed ${validStreams.length} valid streams from ${feed.name}`); |
|
allStreams = [...allStreams, ...validStreams]; |
|
|
|
} catch (error) { |
|
console.error(`❌ Error fetching ${feed.name}:`, error); |
|
} |
|
} |
|
|
|
allStreams.sort((a, b) => { |
|
const qualityOrder = { '2160p': 4, '4k': 4, '1080p': 3, '720p': 2 }; |
|
return (qualityOrder[b.quality] || 0) - (qualityOrder[a.quality] || 0); |
|
}); |
|
|
|
return allStreams; |
|
} |
|
|
|
export { fetchRSSFeeds }; |
|
|