CoolFace
Apppublic

mikmc5/bitsa

sourceHugging Faceupdated 1y agoView on Hugging Face
0likes
ruserver.js293 linesDownload Raw Back to root
1// Update your jac-server.js to use the new Jacred adapter2import express from 'express';3import { fetchRSSFeeds } from './rutor.js'; // Your updated Jacred adapter4 5const app = express();6const port = process.env.PORT || 7860;7const TMDB_API_KEY = 'f051e7366c6105ad4f9aafe4733d9dae';8const TMDB_BASE_URL = 'https://api.themoviedb.org/3';9 10app.use(express.json());11 12function standardizeEpisodeFormat(query) {13    // Handle 1x01 format14    const altMatch = query.match(/^(.*?)(\d{1,2})x(\d{1,2})/i);15    if (altMatch) {16        const [_, title, season, episode] = altMatch;17        const paddedSeason = String(season).padStart(2, '0');18        const paddedEpisode = String(episode).padStart(2, '0');19        return `${title.trim()} S${paddedSeason}E${paddedEpisode}`;20    }21    return query;22}23 24function isExactEpisodeMatch(torrentTitle, showTitle, seasonNum, episodeNum) {25    if (!torrentTitle || !showTitle) return false;26    27    // Clean up title28    torrentTitle = torrentTitle.replace(/\[.*?\]/g, '')29        .replace(/\(.*?\)/g, '')30        .replace(/\s+/g, ' ')31        .trim();32    33    // Normalize the titles34    const normalizedTorrentTitle = torrentTitle.toLowerCase();35    const normalizedShowTitle = showTitle.toLowerCase().trim();36    37    // Split show title into significant words38    const showWords = normalizedShowTitle.split(' ')39        .filter(word => word.length > 2)40        .filter(word => !['the', 'and', 'or', 'in', 'on', 'at', 'to'].includes(word));41    42    const hasAllShowWords = showWords.every(word => 43        normalizedTorrentTitle.includes(word)44    );45    46    if (!hasAllShowWords) return false;47    48    // Format season and episode numbers49    const seasonStr = String(seasonNum).padStart(2, '0');50    const episodeStr = String(episodeNum).padStart(2, '0');51    52    // Common episode patterns53    const patterns = [54        new RegExp(`s${seasonStr}e${episodeStr}`, 'i'),55        new RegExp(`${seasonNum}x${episodeStr}`, 'i'),56        new RegExp(`[^0-9]${seasonNum}${episodeStr}[^0-9]`, 'i'),57        new RegExp(`season\\s*${seasonNum}\\s*episode\\s*${episodeNum}`, 'i'),58        new RegExp(`s${seasonStr}\\.?e${episodeStr}`, 'i'),59        new RegExp(`${seasonStr}${episodeStr}`, 'i')60    ];61    62    return patterns.some(pattern => pattern.test(normalizedTorrentTitle));63}64 65function isExactMovieMatch(torrentTitle, movieTitle, year) {66    // Clean title67    torrentTitle = torrentTitle.replace(/\[.*?\]/g, '')68        .replace(/\(.*?\)/g, '')69        .replace(/\s+/g, ' ')70        .trim();71    72    const normalizedTorrentTitle = torrentTitle.toLowerCase();73    const normalizedMovieTitle = movieTitle.toLowerCase();74    75    const movieWords = normalizedMovieTitle.split(' ')76        .filter(word => word.length > 2)77        .filter(word => !['the', 'and', 'or', 'in', 'on', 'at', 'to'].includes(word));78    79    const hasAllMovieWords = movieWords.every(word => 80        normalizedTorrentTitle.includes(word)81    );82    83    if (!hasAllMovieWords) return false;84    85    const yearMatch = torrentTitle.match(/(?:19|20)\d{2}/);86    return yearMatch && yearMatch[0] === year.toString();87}88 89async function getTMDBDetails(tmdbId, type = 'movie') {90    try {91        const response = await fetch(`${TMDB_BASE_URL}/${type}/${tmdbId}?api_key=${TMDB_API_KEY}&append_to_response=external_ids`);92        if (!response.ok) throw new Error(`TMDB API error: ${response.status}`);93        return await response.json();94    } catch (error) {95        console.error('TMDB fetch error:', error);96        return null;97    }98}99 100async function getTVShowDetails(tmdbId, seasonNum, episodeNum) {101    try {102        const [showResponse, episodeResponse] = await Promise.all([103            fetch(`${TMDB_BASE_URL}/tv/${tmdbId}?api_key=${TMDB_API_KEY}&append_to_response=external_ids`),104            fetch(`${TMDB_BASE_URL}/tv/${tmdbId}/season/${seasonNum}/episode/${episodeNum}?api_key=${TMDB_API_KEY}`)105        ]);106 107        if (!showResponse.ok || !episodeResponse.ok) {108            throw new Error('TMDB API error');109        }110 111        const [showData, episodeData] = await Promise.all([112            showResponse.json(),113            episodeResponse.json()114        ]);115 116        return {117            showTitle: showData.name,118            episodeTitle: episodeData.name,119            seasonNumber: seasonNum,120            episodeNumber: episodeNum,121            airDate: episodeData.air_date,122            imdbId: showData.external_ids?.imdb_id123        };124    } catch (error) {125        console.error('TMDB fetch error:', error);126        return null;127    }128}129 130async function handleSearch({ query, type }) {131    if (!query) throw new Error('Missing required parameter: query');132    if (!['movie', 'series'].includes(type)) throw new Error('Invalid type. Must be either "movie" or "series"');133 134    let searchQuery = query;135    let exactMatchParams = {};136    let tmdbData = null;137 138    if (type === 'series') {139        // Handle TMDB ID format: "tmdb:12345:1:1"140        const tmdbMatch = query.match(/^tmdb:(\d+):(\d+):(\d+)$/);141        if (tmdbMatch) {142            const [_, tmdbId, seasonNum, episodeNum] = tmdbMatch;143            tmdbData = await getTVShowDetails(tmdbId, parseInt(seasonNum), parseInt(episodeNum));144            if (tmdbData) {145                exactMatchParams = {146                    title: tmdbData.showTitle,147                    season: parseInt(seasonNum),148                    episode: parseInt(episodeNum)149                };150                searchQuery = `${tmdbData.showTitle} S${String(seasonNum).padStart(2, '0')}E${String(episodeNum).padStart(2, '0')}`;151            }152        }153        // Handle IMDB ID format: "tt1234567:1:1"154        else if (query.startsWith('tt')) {155            const imdbMatch = query.match(/^(tt\d+):(\d+):(\d+)$/);156            if (imdbMatch) {157                const [_, imdbId, seasonNum, episodeNum] = imdbMatch;158                const findResponse = await fetch(159                    `${TMDB_BASE_URL}/find/${imdbId}?api_key=${TMDB_API_KEY}&external_source=imdb_id`160                );161                if (findResponse.ok) {162                    const findData = await findResponse.json();163                    if (findData.tv_results?.[0]) {164                        const tmdbId = findData.tv_results[0].id;165                        tmdbData = await getTVShowDetails(tmdbId, parseInt(seasonNum), parseInt(episodeNum));166                        if (tmdbData) {167                            exactMatchParams = {168                                title: tmdbData.showTitle,169                                season: parseInt(seasonNum),170                                episode: parseInt(episodeNum)171                            };172                            searchQuery = `${tmdbData.showTitle} S${String(seasonNum).padStart(2, '0')}E${String(episodeNum).padStart(2, '0')}`;173                        }174                    }175                }176            }177        }178        else {179            searchQuery = standardizeEpisodeFormat(query);180            const seasonEpisodeMatch = searchQuery.match(/^(.*?)S(\d{1,2})E(\d{1,2})/i);181            if (seasonEpisodeMatch) {182                exactMatchParams = {183                    title: seasonEpisodeMatch[1].trim(),184                    season: parseInt(seasonEpisodeMatch[2]),185                    episode: parseInt(seasonEpisodeMatch[3])186                };187            }188        }189    } else if (type === 'movie') {190        // Handle TMDB ID format191        if (query.startsWith('tmdb:')) {192            const tmdbId = query.replace('tmdb:', '');193            tmdbData = await getTMDBDetails(tmdbId, 'movie');194            if (tmdbData) {195                exactMatchParams = {196                    title: tmdbData.title,197                    year: new Date(tmdbData.release_date).getFullYear()198                };199                searchQuery = `${tmdbData.title} (${exactMatchParams.year})`;200            }201        }202        // Handle IMDB ID format203        else if (query.startsWith('tt')) {204            const findResponse = await fetch(205                `${TMDB_BASE_URL}/find/${query}?api_key=${TMDB_API_KEY}&external_source=imdb_id`206            );207            if (findResponse.ok) {208                const findData = await findResponse.json();209                if (findData.movie_results?.[0]) {210                    tmdbData = findData.movie_results[0];211                    exactMatchParams = {212                        title: tmdbData.title,213                        year: new Date(tmdbData.release_date).getFullYear()214                    };215                    searchQuery = `${tmdbData.title} (${exactMatchParams.year})`;216                }217            }218        }219        else {220            const movieYearMatch = query.match(/^(.+?)(?:\s*\(?(\d{4})\)?)?$/);221            if (movieYearMatch) {222                exactMatchParams = {223                    title: movieYearMatch[1].trim(),224                    year: movieYearMatch[2] ? parseInt(movieYearMatch[2]) : null225                };226            }227        }228    }229 230    // ✅ Fetch results using the updated Jacred adapter231    let results = await fetchRSSFeeds(searchQuery, type);232 233    // Filter results based on exact match parameters234    if (type === 'series' && exactMatchParams.season !== undefined) {235        results = results.filter(result => 236            isExactEpisodeMatch(237                result.websiteTitle,238                exactMatchParams.title,239                exactMatchParams.season,240                exactMatchParams.episode241            )242        );243    } else if (type === 'movie' && exactMatchParams.year) {244        results = results.filter(result =>245            isExactMovieMatch(246                result.websiteTitle,247                exactMatchParams.title,248                exactMatchParams.year249            )250        );251    }252 253    return {254        query: searchQuery,255        originalQuery: query,256        type,257        tmdbData,258        results: results.map(result => ({259            title: result.websiteTitle,260            quality: result.quality,261            size: result.size,262            seeders: result.seeders || 0, // ✅ Now using actual seeder data from Jacred263            leechers: result.peers || 0,  // ✅ Now using actual peer data from Jacred  264            magnetLink: result.magnetLink265        }))266    };267}268 269app.get('/api/search', async (req, res) => {270    try {271        const result = await handleSearch({ query: req.query.query, type: req.query.type });272        res.json(result);273    } catch (error) {274        res.status(500).json({ error: 'Internal server error', message: error.message });275    }276});277 278app.post('/api/search', async (req, res) => {279    try {280        const result = await handleSearch(req.body);281        res.json(result);282    } catch (error) {283        res.status(500).json({ error: 'Internal server error', message: error.message });284    }285});286 287app.listen(port, () => {288    console.log(`Server running at http://localhost:${port}`);289    console.log(`🚀 Using Jacred API for Rutor torrents`);290    if (!TMDB_API_KEY) {291        console.warn('Warning: TMDB_API_KEY not set. TMDB features will not work.');292    }293});