from flask import Flask, request, render_template_string, jsonify, redirect, send_file from seedrcc import Seedr import time import os import threading import requests app = Flask(__name__) # Default credentials (can be overridden by user) DEFAULT_EMAIL = "" DEFAULT_PASSWORD = "" # Download folder (for local development only) DOWNLOAD_FOLDER = os.path.join(os.path.expanduser("~"), "Downloads", "Magnet2Direct") if os.path.expanduser("~") != "~" else "/tmp" # Global variables download_status = {"progress": 0, "status": "idle", "filename": "", "download_url": "", "file_size": "", "error": ""} # Create download folder (only if not in serverless environment) try: if not os.path.exists(DOWNLOAD_FOLDER) and os.path.expanduser("~") != "~": os.makedirs(DOWNLOAD_FOLDER) except: pass # Skip folder creation in serverless environments HTML_TEMPLATE = """ Magnet 2 Direct

🧲 Magnet2Direct

No more Seedr login marathons - Just paste, wait & enjoy! 🍿

""" def get_account_info(client): """Get Seedr account information including storage limits.""" try: # Get account settings/info settings = client.get_settings() if settings and hasattr(settings, 'account'): account = settings.account space_max = getattr(account, 'space_max', 0) if space_max > 0: # Since we clear Seedr each time, max file size = total space return { 'max_file_size': format_file_size(space_max) } # Fallback for free accounts return { 'max_file_size': '2.0 GB (Free Account Limit)' } except Exception as e: print(f"Error getting account info: {e}") # Return default values for free account return { 'max_file_size': '2.0 GB (Free Account Limit)' } def connect_to_seedr(email, password, retries=3): """Connect to Seedr with retry logic.""" for attempt in range(retries): try: print(f"Connecting to Seedr (attempt {attempt + 1}/{retries})...") client = Seedr.from_password(email, password) print("Connected to Seedr successfully") return client except Exception as e: print(f"Connection attempt {attempt + 1} failed: {e}") if attempt < retries - 1: time.sleep(2) # Wait before retry else: raise Exception(f"Could not connect to Seedr after {retries} attempts: {e}") def clear_seedr_account(client): """Delete all files and folders from Seedr account.""" try: contents = client.list_contents() deleted_count = 0 # Delete all files for file in contents.files: try: client.delete_file(file.folder_file_id) print(f"Deleted file: {file.name}") deleted_count += 1 except Exception as e: print(f"Error deleting file {file.name}: {e}") # Delete all folders for folder in contents.folders: try: client.delete_folder(folder.id) print(f"Deleted folder: {folder.name}") deleted_count += 1 except Exception as e: print(f"Error deleting folder {folder.name}: {e}") print(f"Cleared Seedr account: {deleted_count} items deleted") return True except Exception as e: print(f"Error clearing Seedr account: {e}") return False def process_magnet(magnet_link, email, password): """Process magnet link and find video.""" global download_status try: download_status = {"progress": 10, "status": "processing", "filename": "", "download_url": "", "file_size": "", "error": ""} client = connect_to_seedr(email, password) with client: print("Connected to Seedr successfully") # First clear the Seedr account download_status["progress"] = 15 download_status["filename"] = "Clearing Seedr account..." clear_success = clear_seedr_account(client) if not clear_success: print("Warning: Could not fully clear Seedr account, continuing anyway...") # Add the new magnet download_status["progress"] = 30 download_status["filename"] = "Adding magnet to Seedr..." try: result = client.add_torrent(magnet_link) print(f"Magnet added successfully: {type(result)}") except Exception as e: raise Exception(f"Failed to add magnet: {e}") # Wait for files to appear download_status["progress"] = 40 download_status["filename"] = "Waiting for download..." max_wait = 300 # 5 minutes wait_time = 0 while wait_time < max_wait: try: video_file = find_biggest_video(client) if video_file: print(f"Found video file: {video_file.name}") break except Exception as e: print(f"Error checking for files: {e}") progress = 40 + int((wait_time / max_wait) * 50) # 40-90% download_status["progress"] = progress download_status["filename"] = f"Downloading... ({wait_time}s)" time.sleep(10) wait_time += 10 if not video_file: raise Exception("No video file found after 5 minutes. The torrent might be slow or invalid.") # Get download URL download_status["progress"] = 95 download_status["filename"] = video_file.name try: fetch_result = client.fetch_file(video_file.folder_file_id) download_url = fetch_result.url if hasattr(fetch_result, 'url') else str(fetch_result) except Exception as e: raise Exception(f"Could not get download URL: {e}") # Success download_status = { "progress": 100, "status": "completed", "filename": video_file.name, "download_url": download_url, "file_size": format_file_size(video_file.size), "error": "" } print(f"Success! Video: {video_file.name} ({download_status['file_size']})") except Exception as e: print(f"Process error: {e}") import traceback traceback.print_exc() download_status = { "progress": 0, "status": "error", "filename": "", "download_url": "", "file_size": "", "error": f"Error: {str(e)}. Try again with a different magnet link." } def find_biggest_video(client, folder_id=None): """Find the biggest video file.""" biggest_video = None biggest_size = 0 try: contents = client.list_contents(folder_id) folder_name = "root" if folder_id is None else f"folder_{folder_id}" print(f"Searching {folder_name}: {len(contents.files)} files, {len(contents.folders)} folders") # Check files in current folder for file in contents.files: print(f"Found file: {file.name} ({format_file_size(file.size)})") if file.name.lower().endswith(('.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.m4v', '.webm', '.mpg', '.mpeg')): print(f"Video file: {file.name}") if file.size > biggest_size: biggest_video = file biggest_size = file.size print(f"New biggest video: {file.name} ({format_file_size(file.size)})") # Check subfolders for subfolder in contents.folders: print(f"Checking subfolder: {subfolder.name}") result = find_biggest_video(client, subfolder.id) if result and result.size > biggest_size: biggest_video = result biggest_size = result.size print(f"New biggest from subfolder: {result.name} ({format_file_size(result.size)})") except Exception as e: print(f"Error searching folder {folder_id}: {e}") import traceback traceback.print_exc() if biggest_video: print(f"Returning biggest video: {biggest_video.name} ({format_file_size(biggest_video.size)})") else: print("No video files found in this search") return biggest_video def format_file_size(size_bytes): """Convert bytes to human readable format.""" if size_bytes == 0: return "0 B" for unit in ['B', 'KB', 'MB', 'GB']: if size_bytes < 1024.0: return f"{size_bytes:.1f} {unit}" size_bytes /= 1024.0 @app.route('/') def index(): return render_template_string(HTML_TEMPLATE) @app.route('/add-magnet', methods=['POST']) def add_magnet(): magnet = request.form['magnet'] email = request.form.get('email', DEFAULT_EMAIL) password = request.form.get('password', DEFAULT_PASSWORD) if not email or not password: return jsonify({"status": "error", "error": "Seedr credentials required"}) # Start processing in background thread = threading.Thread(target=process_magnet, args=(magnet, email, password)) thread.daemon = True thread.start() return jsonify({"status": "started"}) @app.route('/test-credentials', methods=['POST']) def test_credentials(): """Test if Seedr credentials are valid.""" data = request.get_json() email = data.get('email', '') password = data.get('password', '') try: client = connect_to_seedr(email, password, retries=1) with client: # Try to get account info to verify credentials settings = client.get_settings() return jsonify({"valid": True}) except Exception as e: print(f"Credential test failed: {e}") return jsonify({"valid": False}) @app.route('/account-info', methods=['POST']) def get_account_info_route(): """Get Seedr account information.""" try: data = request.get_json() email = data.get('email', DEFAULT_EMAIL) password = data.get('password', DEFAULT_PASSWORD) if not email or not password: return jsonify({'max_file_size': 'No credentials provided'}) client = connect_to_seedr(email, password) with client: account_info = get_account_info(client) return jsonify(account_info) except Exception as e: return jsonify({ 'max_file_size': 'Connection Error' }) @app.route('/status') def get_status(): return jsonify(download_status) @app.route('/download-file', methods=['GET']) def download_file(): """Redirect to direct download URL for browser download.""" global download_status if download_status["status"] != "completed" or not download_status["download_url"]: return "No file ready for download", 400 # Redirect browser to the direct download URL return redirect(download_status["download_url"]) # Favicon and manifest routes @app.route('/favicon.ico') def favicon(): return send_file('favicon.ico') @app.route('/apple-touch-icon.png') def apple_touch_icon(): return send_file('apple-touch-icon.png') @app.route('/favicon-32x32.png') def favicon_32(): return send_file('favicon-32x32.png') @app.route('/favicon-16x16.png') def favicon_16(): return send_file('favicon-16x16.png') @app.route('/site.webmanifest') def site_manifest(): return send_file('site.webmanifest') @app.route('/android-chrome-192x192.png') def android_chrome_192(): return send_file('android-chrome-192x192.png') @app.route('/android-chrome-512x512.png') def android_chrome_512(): return send_file('android-chrome-512x512.png') if __name__ == '__main__': print("🧲 Starting Simple Magnet2Direct...") print(f"📁 Downloads folder: {DOWNLOAD_FOLDER}") print("🌐 Open http://localhost:5000") app.run(debug=True, host='0.0.0.0', port=5000)