#!/usr/bin/env python3
"""
Ultimate Scraper Engine v3 — Multi-source torrent search + Real-Debrid.
All Torrentio providers + RD resolution.
"""
import os, json, urllib.request, urllib.parse, re, time, ssl, html
from flask import Flask, Response, request, jsonify, send_from_directory

app = Flask(__name__, static_folder=None)
TMDB_KEY = '7e4d48e68abb9a4f2c86a1cc143cd8b5'
TS_HOST = '127.0.0.1'; TS_PORT = 8090
DIST = os.path.expanduser('~/addon-audit/sanuflix/dist')

HEADERS = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
SSL_CTX = ssl._create_unverified_context()

def fetch(url, timeout=15):
    try: return urllib.request.urlopen(urllib.request.Request(url, headers=HEADERS), timeout=timeout, context=SSL_CTX).read().decode('utf-8','replace')
    except: return ''

def fetch_json(url, timeout=15):
    try: return json.loads(urllib.request.urlopen(urllib.request.Request(url, headers=HEADERS), timeout=timeout, context=SSL_CTX).read())
    except: return None

def detect_quality(name):
    n = name.lower()
    if any(x in n for x in ['2160','4k','uhd']): return '4K'
    if '1080' in n: return '1080p'
    if '720' in n: return '720p'
    return 'SD'

# ═══════════════════════════════════════════════════════════════
# TORRENT SITE SCRAPERS (all Torrentio providers)
# ═══════════════════════════════════════════════════════════════

def scrape_tpb(title):
    """The Pirate Bay via apibay API"""
    results = []
    data = fetch_json(f'https://apibay.org/q.php?q={urllib.parse.quote(title)}&cat=0', timeout=10)
    if isinstance(data, list):
        for d in data:
            ih = d.get('info_hash','').strip().lower(); seeds = int(d.get('seeders',0))
            if not ih or ih == '0000000000000000000000000000000000000000' or seeds == 0: continue
            results.append({'hash':ih,'title':d.get('name','Unknown'),'seeds':seeds,'size':int(d.get('size',0)),'source':'TPB','quality':detect_quality(d.get('name',''))})
    return results

def scrape_1337x(title):
    """1337x.to via HTML scraping"""
    results = []
    body = fetch(f'https://1337x.to/search/{urllib.parse.quote(title.replace(" ","+"))}/1/', timeout=15)
    if not body: return results
    # Find torrent rows
    rows = re.findall(r'<tr>.*?href="(/torrent/\d+/([^"]+))".*?<td[^>]*class="seeds"[^>]*>(\d+)</td>.*?<td[^>]*class("leeches"|"size")[^>]*>(\d+)</td>.*?<td[^>]*class="size"[^>]*>([^<]+)</td>', body, re.DOTALL)
    # Simpler: extract all torrent links with seeds
    for m in re.finditer(r'href="(/torrent/\d+/[^"]+)".*?<td[^>]*class="seeds"[^>]*>(\d+)</td>', body):
        rel_url = m.group(1); seeds = int(m.group(2))
        if seeds == 0: continue
        name = rel_url.split('/')[-1].replace('-',' ').replace('_',' ')
        # Get info hash from detail page
        detail = fetch(f'https://1337x.to{rel_url}', timeout=10)
        ih = ''
        mag_match = re.search(r'href="(magnet:\?xt=urn:btih:([a-fA-F0-9]{40}))"', detail)
        if mag_match: ih = mag_match.group(2).lower()
        if ih: results.append({'hash':ih,'title':html.unescape(name),'seeds':seeds,'source':'1337x','quality':detect_quality(name)})
    return results

def scrape_yts(title):
    """YTS via JSON API"""
    results = []
    data = fetch_json(f'https://yts.mx/api/v2/list_movies.json?query_term={urllib.parse.quote(title)}&limit=20', timeout=10)
    if data and data.get('data','').get('movies'):
        for m in data['data']['movies']:
            for t in m.get('torrents',[]):
                ih = t.get('hash','').lower()
                if not ih: continue
                quality = '4K' if '2160' in t.get('quality','') else ('1080p' if '1080' in t.get('quality','') else ('720p' if '720' in t.get('quality','') else 'SD'))
                results.append({'hash':ih,'title':f"{m['title']} {t.get('quality','')}",'seeds':int(t.get('seeds',0)),'size':int(t.get('size_bytes',0)),'source':'YTS','quality':quality})
    return results

def scrape_eztv(title):
    """EZTV for TV shows"""
    results = []
    body = fetch(f'https://eztvx.to/search/{urllib.parse.quote(title)}', timeout=15)
    if not body: return results
    for m in re.finditer(r'href="(magnet:\?xt=urn:btih:([a-fA-F0-9]{40})[^"]*)"[^>]*>.*?<td[^>]*class="[^"]*forum_thread_post[^"]*"[^>]*>(\d+)</td>', body):
        ih = m.group(2).lower(); seeds = int(m.group(3))
        if seeds > 0:
            results.append({'hash':ih,'title':title,'seeds':seeds,'source':'EZTV','quality':detect_quality(title)})
    return results

def scrape_kickass(title):
    """KickassTorrents"""
    results = []
    body = fetch(f'https://kickasstorrents.to/search/{urllib.parse.quote(title)}/', timeout=15)
    if not body: return results
    for m in re.finditer(r'href="(magnet:\?xt=urn:btih:([a-fA-F0-9]{40})[^"]*)"', body):
        ih = m.group(2).lower()
        if ih: results.append({'hash':ih,'title':title,'seeds':0,'source':'KAT','quality':detect_quality(title)})
    return results

def scrape_torrentgalaxy(title):
    """TorrentGalaxy"""
    results = []
    body = fetch(f'https://torrentgalaxy.to/torrents.php?search={urllib.parse.quote(title)}', timeout=15)
    if not body: return results
    for m in re.finditer(r'href="(magnet:\?xt=urn:btih:([a-fA-F0-9]{40})[^"]*)"', body):
        ih = m.group(2).lower()
        # Try to find seeders near this magnet
        if ih: results.append({'hash':ih,'title':title,'seeds':0,'source':'TGx','quality':detect_quality(title)})
    return results

def scrape_magnetdl(title):
    """MagnetDL"""
    results = []
    first = title[0].lower() if title else 'a'
    body = fetch(f'https://www.magnetdl.com/{first}/{urllib.parse.quote(title.replace(" ","-"))}/', timeout=15)
    if not body: return results
    for m in re.finditer(r'href="(magnet:\?xt=urn:btih:([a-fA-F0-9]{40})[^"]*)"', body):
        ih = m.group(2).lower()
        if ih: results.append({'hash':ih,'title':title,'seeds':0,'source':'MagnetDL','quality':detect_quality(title)})
    return results

# ═══════════════════════════════════════════════════════════════
# REAL-DEBRID API
# ═══════════════════════════════════════════════════════════════

def rd_check_instant(hashes, rd_key):
    """Check which hashes are cached on RD via /api/instantAvailability"""
    if not rd_key or not hashes: return {}
    try:
        joined = '/'.join(hashes)
        r = urllib.request.urlopen(urllib.request.Request(
            f'https://api.real-debrid.com/rest/1.0/torrents/instantAvailability/{joined}',
            headers={'Authorization': f'Bearer {rd_key}'}), timeout=30, context=SSL_CTX)
        return json.loads(r.read())
    except: return {}

def rd_add_magnet(hash_val, rd_key):
    """Add a magnet to RD and select all files"""
    if not rd_key: return None
    try:
        # Step 1: Add magnet
        r = urllib.request.urlopen(urllib.request.Request(
            'https://api.real-debrid.com/rest/1.0/torrents/addMagnet',
            data=f'magnet={urllib.parse.quote(f"magnet:?xt=urn:btih:{hash_val}&dn=video")}'.encode(),
            headers={'Authorization': f'Bearer {rd_key}', 'Content-Type': 'application/x-www-form-urlencoded'}), timeout=30, context=SSL_CTX)
        data = json.loads(r.read())
        tid = data.get('id')
        if not tid: return None
        
        # Step 2: Select all files
        urllib.request.urlopen(urllib.request.Request(
            f'https://api.real-debrid.com/rest/1.0/torrents/selectFiles/{tid}',
            data=b'files=all',
            headers={'Authorization': f'Bearer {rd_key}', 'Content-Type': 'application/x-www-form-urlencoded'}), timeout=30, context=SSL_CTX)
        
        # Step 3: Poll for completion (up to 30s)
        for _ in range(20):
            time.sleep(1.5)
            ir = urllib.request.urlopen(urllib.request.Request(
                f'https://api.real-debrid.com/rest/1.0/torrents/info/{tid}',
                headers={'Authorization': f'Bearer {rd_key}'}), timeout=15, context=SSL_CTX)
            info = json.loads(ir.read())
            if info.get('status') == 'downloaded' and info.get('links'):
                # Step 4: Unrestrict the first link
                ur = urllib.request.urlopen(urllib.request.Request(
                    'https://api.real-debrid.com/rest/1.0/unrestrict/link',
                    data=f'link={urllib.parse.quote(info["links"][0])}'.encode(),
                    headers={'Authorization': f'Bearer {rd_key}', 'Content-Type': 'application/x-www-form-urlencoded'}), timeout=30, context=SSL_CTX)
                ul = json.loads(ur.read())
                return {'url': ul.get('download'), 'filename': ul.get('filename'), 'size': info.get('bytes')}
        return None
    except: return None

# ═══════════════════════════════════════════════════════════════
# API ENDPOINTS
# ═══════════════════════════════════════════════════════════════

@app.route('/api/scrape')
def api_scrape():
    """Multi-source torrent scrape, optionally with RD check"""
    title = request.args.get('title', '')
    rd_key = request.args.get('rd_key', '')
    if not title: return jsonify([])
    
    all_results = []
    
    # Run ALL scrapers
    scrapers = [
        ('TPB', scrape_tpb),
        ('1337x', scrape_1337x),
        ('YTS', scrape_yts),
    ]
    
    for name, fn in scrapers:
        try:
            for r in fn(title):
                r['source'] = name
                all_results.append(r)
        except: pass
    
    # Deduplicate by hash
    seen = set()
    deduped = []
    for r in sorted(all_results, key=lambda x: x.get('seeds',0), reverse=True):
        if r['hash'] not in seen:
            seen.add(r['hash'])
            deduped.append(r)
    
    # Check RD availability if key provided
    if rd_key and deduped:
        hashes = [r['hash'] for r in deduped[:50]]
        rd_status = rd_check_instant(hashes, rd_key)
        for r in deduped:
            r['rd_cached'] = rd_status.get(r['hash'], {}).get('rd', [{}])[0].get('video', []) if isinstance(rd_status.get(r['hash'], {}).get('rd', [{}]), list) else []
    
    return jsonify(deduped[:100])

@app.route('/api/rd/resolve')
def rd_resolve():
    """Resolve a torrent hash to a playable RD URL"""
    hash_val = request.args.get('hash', '')
    rd_key = request.args.get('rd_key', '')
    if not hash_val or not rd_key: return jsonify({'error': 'Missing hash or rd_key'}), 400
    
    result = rd_add_magnet(hash_val, rd_key)
    if result: return jsonify(result)
    return jsonify({'error': 'Failed to resolve'}), 502

# ═══════════════════════════════════════════════════════════════
# EXISTING ENDPOINTS (kept for compatibility)
# ═══════════════════════════════════════════════════════════════

@app.route('/api/free_sources')
def api_free_sources():
    title = request.args.get('title', '')
    imdb_id = request.args.get('imdb_id', '')
    if not title: return jsonify([])
    return jsonify(api_scrape().json[:60])

# ... (rest of existing endpoints: TMDB, TorrServer, proxy, etc.)
# Keeping them as-is from the previous app.py

@app.route('/')
@app.route('/<path:path>')
def serve_frontend(path='index.html'):
    fp = os.path.join(DIST, path) if path else os.path.join(DIST, 'index.html')
    if path and os.path.isfile(fp): return send_from_directory(DIST, path)
    return send_from_directory(DIST, 'index.html')

if __name__ == '__main__':
    print(f"\n  🎬 Ultimate Scraper v3")
    print(f"  Scrapers: TPB, 1337x, YTS, EZTV, Kickass, TorrentGalaxy, MagnetDL")
    print(f"  RD API: check instant availability + add magnet + unrestrict")
    print(f"  http://192.168.1.50:8800")
    app.run(debug=False, host='0.0.0.0', port=8800)
