#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ MKV Remux Worker - کامل و عملیاتی با REST API """ import os import re import time import json import logging import threading import tempfile import subprocess import hashlib from urllib.parse import urlsplit, unquote from ftplib import FTP, FTP_TLS, error_perm import requests from flask import Flask, request, jsonify # ========== تنظیمات ========== API_KEY = os.environ.get("REMUX_API_KEY", "TALAFILM_REMUX_2026_SECURE_KEY") FTP_HOST = "89.235.79.130" FTP_USER = "dl6.tinyzmoviez.ir" FTP_PASS = "NVMyYPNbbCAXw1cm" BASE_URL = "https://dl6.tinyzmoviez.ir" # آدرس سرور وردپرس WP_API_BASE = os.environ.get("WP_API_BASE", "https://your-wordpress-site.com/wp-json/mkv/v1") WP_API_KEY = os.environ.get("WP_API_KEY", "your_strong_key") TMP_DIR = "/tmp/remux" os.makedirs(TMP_DIR, exist_ok=True) HOST = "0.0.0.0" PORT = int(os.environ.get("PORT", 5001)) logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") app = Flask(__name__) # ========== توابع کمکی ========== def clean_name(name: str) -> str: name = name.replace("_", " ").replace("-", " ") name = re.sub(r"\.+", ".", name) name = re.sub(r"\s+", " ", name).strip() return name.replace(" ", ".") def detect_quality_folder(filename: str) -> str: fn = filename.lower() if "1080" in fn: return "1080p" if "720" in fn: return "720p" if "480" in fn: return "480p" return "Other" def build_ftp_path(filename: str) -> str: m = re.search(r"(.+?)\.S(\d{2})E\d{2}", filename, re.I) if m: series = clean_name(m.group(1)) season = "S" + m.group(2) q = detect_quality_folder(filename) return f"/Serial/{series}/{season}/{q}" base = filename.rsplit(".", 1)[0] m2 = re.search(r"(19|20)\d{2}", base) year = m2.group() if m2 else "Unknown" name = base[:m2.start()] if m2 else base name = clean_name(name) return f"/New.Film/{year}/{name}" def safe_filename_from_url(url: str) -> str: path = urlsplit(url).path name = unquote(path.split("/")[-1] or "") if not name: raise ValueError("Invalid filename") return name def ftp_upload_resume(ftp, local_file, remote_file, blocksize=1024*1024): local_size = os.path.getsize(local_file) try: remote_size = ftp.size(remote_file) or 0 except: remote_size = 0 if remote_size >= local_size: return with open(local_file, "rb") as f: if remote_size > 0: f.seek(remote_size) ftp.sendcmd("TYPE I") conn = ftp.transfercmd(f"STOR {remote_file}", remote_size) while True: data = f.read(blocksize) if not data: break conn.sendall(data) conn.close() ftp.voidresp() else: ftp.storbinary(f"STOR {remote_file}", f, blocksize=blocksize) # ========== ارتباط با API وردپرس ========== def wp_api_request(method, endpoint, data=None, params=None): url = f"{WP_API_BASE}/{endpoint.lstrip('/')}" headers = { "X-API-Key": WP_API_KEY, "Content-Type": "application/json" } if method == "GET": resp = requests.get(url, params=params, headers=headers, timeout=30) elif method == "POST": resp = requests.post(url, json=data, headers=headers, timeout=30) else: raise ValueError("Unsupported method") resp.raise_for_status() return resp.json() # ========== پردازش یک Job ========== def process_job(job_id): logging.info(f"Starting job {job_id}") try: # ۱. دریافت اطلاعات job از API (شامل post_id) job_info = wp_api_request("GET", f"job/{job_id}") post_id = job_info.get('post_id') if not post_id: raise Exception("post_id not found in job") # ۲. دریافت لیست لینک‌های MKV برای این پست links = wp_api_request("GET", f"post/{post_id}/links") if not links: update_job(job_id, { 'status': 'completed', 'log': '✅ هیچ لینک جدیدی برای پردازش وجود ندارد.', 'progress': 100 }) return total = len(links) processed = 0 log_lines = [f"📦 تعداد فایل‌ها: {total}"] for item in links: key = item['meta_key'] url = item['url'] # بررسی درخواست توقف job_check = wp_api_request("GET", f"job/{job_id}") if job_check.get('stop_requested'): log_lines.append("⛔ توقف توسط کاربر") update_job(job_id, { 'status': 'failed', 'log': "\n".join(log_lines), 'progress': int((processed/total)*100) }) return log_lines.append(f"🚀 شروع: {key} -> {url}") try: filename = safe_filename_from_url(url) # دانلود log_lines.append("⬇️ دانلود...") session = requests.Session() with tempfile.TemporaryDirectory(prefix="remux-", dir=TMP_DIR) as workdir: mkv_path = os.path.join(workdir, filename + ".part") mp4_tmp = os.path.join(workdir, filename.rsplit(".",1)[0] + ".tmp.mp4") mp4_final = filename.rsplit(".",1)[0] + ".mp4" resp = session.get(url, stream=True, timeout=120) resp.raise_for_status() total_size = int(resp.headers.get("Content-Length", 0)) down = 0 last_t = time.time() with open(mkv_path, "wb") as f: for chunk in resp.iter_content(1024*1024): if not chunk: continue f.write(chunk) down += len(chunk) if time.time() - last_t > 2: speed = down / (1024*1024) / (time.time() - last_t + 0.001) pct = int((down/total_size)*100) if total_size else 0 log_lines[-1] = f"⬇️ دانلود: {pct}% ({down//(1024**2)}MB) سرعت: {speed:.1f} MB/s" update_job(job_id, { 'log': "\n".join(log_lines[-10:]), 'progress': int((processed/total)*100 + (pct/total)), 'speed_download': f"{speed:.1f} MB/s", 'eta': f"{((total_size-down)/speed)/(1024**2):.1f} دقیقه" if speed > 0 else "--" }) # Remux log_lines.append("🎬 Remux...") cmd = ["ffmpeg", "-y", "-i", mkv_path, "-c", "copy", mp4_tmp] subprocess.run(cmd, check=True, capture_output=True) if not os.path.exists(mp4_tmp): raise Exception("Remux failed") # آپلود FTP log_lines.append("⬆️ آپلود FTP...") ftp = FTP(FTP_HOST, timeout=120) ftp.login(FTP_USER, FTP_PASS) ftp.set_pasv(True) remote_dir = build_ftp_path(mp4_final) cur = "" for p in [p for p in remote_dir.strip("/").split("/") if p]: cur += "/" + p try: ftp.mkd(cur) except: pass ftp.cwd(remote_dir) tmp_remote = mp4_final + ".uploading" ftp_upload_resume(ftp, mp4_tmp, tmp_remote) ftp.rename(tmp_remote, mp4_final) ftp.quit() final_url = f"{BASE_URL}{remote_dir}/{mp4_final}" # به‌روزرسانی متا در وردپرس از طریق API wp_api_request("POST", f"post/{post_id}/meta", data={ 'meta_key': key, 'new_url': final_url, 'old_url': url }) log_lines.append(f"✅ آپلود شد: {final_url}") os.remove(mkv_path) os.remove(mp4_tmp) except Exception as e: logging.exception(f"Error processing file {key}") log_lines.append(f"❌ خطا: {str(e)}") processed += 1 update_job(job_id, { 'log': "\n".join(log_lines[-20:]), 'progress': int((processed/total)*100) }) # پایان update_job(job_id, { 'status': 'completed' if processed == total else 'failed', 'log': "\n".join(log_lines), 'progress': 100, 'time_end': time.strftime('%Y-%m-%d %H:%M:%S') }) except Exception as e: logging.exception("Job failed") update_job(job_id, { 'status': 'failed', 'log': f"❌ خطای کلی: {str(e)}", 'progress': 0 }) def update_job(job_id, data): try: wp_api_request("POST", f"job/{job_id}", data=data) except Exception as e: logging.error(f"Failed to update job {job_id}: {e}") # ========== Worker Loop (دائم) ========== def worker_loop(): while True: try: # دریافت jobهای pending jobs = wp_api_request("GET", "jobs/pending") for job in jobs: # قفل کردن job (تغییر وضعیت به processing) update_job(job['id'], {'status': 'processing'}) # پردازش در یک ترد جدا threading.Thread(target=process_job, args=(job['id'],)).start() except Exception as e: logging.error(f"Worker loop error: {e}") time.sleep(5) # ========== Routes Flask ========== @app.route("/status", methods=["GET"]) def server_status(): return jsonify({ 'status': 'online', 'version': '6.1', 'last_seen': int(time.time()) }) @app.route("/start", methods=["POST"]) def start_job(): data = request.get_json() job_id = data.get('job_id') if not job_id: return jsonify({'error': 'job_id required'}), 400 # تغییر وضعیت به processing و شروع پردازش update_job(job_id, {'status': 'processing'}) threading.Thread(target=process_job, args=(job_id,)).start() return jsonify({'status': 'started'}) if __name__ == "__main__": # اجرای Worker Loop در پس‌زمینه threading.Thread(target=worker_loop, daemon=True).start() logging.info(f"MKV Worker (No DB) started on port {PORT}") app.run(host=HOST, port=PORT, threaded=True) بایگانی‌های ایالات متحده - صفحه 116 از 365 - طلا فیلم - دانلود فیلم بدون سانسور
جستجو در سایت
جستجوهای اخیر

جستجو و فیلتر فیلم‌ها

پاک‌سازی همه
همه نسخه‌ها
همه ژانرها
1 انتخاب شده
همه سال‌ها
همه امتیازها
جدیدترین