|
|
@@ -3,6 +3,8 @@ import time
|
|
|
import datetime
|
|
|
import pickle
|
|
|
import threading
|
|
|
+import queue
|
|
|
+from concurrent.futures import ThreadPoolExecutor
|
|
|
from googleapiclient.discovery import build
|
|
|
import subprocess
|
|
|
from google.auth.transport.requests import Request
|
|
|
@@ -18,6 +20,14 @@ EXIT_TIME = datetime.time(6, 0) # Exit the script at 06:00
|
|
|
POPULAR_VIDEOS_MAX_RESULTS = 10 # Number of popular videos to fetch (you can change this)
|
|
|
REGION_CODES = ["US", "GB", "LU", "LK"] # Regions to fetch popular videos from
|
|
|
|
|
|
+# Conversion Queue Settings
|
|
|
+MAX_CONCURRENT_CONVERSIONS = 2 # Maximum number of simultaneous video conversions
|
|
|
+CONVERSION_QUEUE_SIZE = 50 # Maximum items in conversion queue
|
|
|
+
|
|
|
+# Global conversion queue and executor
|
|
|
+conversion_queue = queue.Queue(maxsize=CONVERSION_QUEUE_SIZE)
|
|
|
+conversion_executor = ThreadPoolExecutor(max_workers=MAX_CONCURRENT_CONVERSIONS, thread_name_prefix="VideoConverter")
|
|
|
+
|
|
|
def update_yt_dlp():
|
|
|
"""Update yt-dlp to the latest version."""
|
|
|
print("Updating yt-dlp to the latest version...")
|
|
|
@@ -129,6 +139,65 @@ def download_video(video_id, output_path):
|
|
|
return False
|
|
|
return True
|
|
|
|
|
|
+def add_to_conversion_queue(input_file, priority=1):
|
|
|
+ """Add a video file to the conversion queue with priority."""
|
|
|
+ try:
|
|
|
+ # Higher number = higher priority (4K gets priority 2, others get 1)
|
|
|
+ conversion_queue.put((priority, input_file), timeout=1)
|
|
|
+ print(f"Added to conversion queue: {os.path.basename(input_file)} (priority: {priority})")
|
|
|
+ return True
|
|
|
+ except queue.Full:
|
|
|
+ print(f"⚠️ Conversion queue is full! Skipping: {os.path.basename(input_file)}")
|
|
|
+ return False
|
|
|
+
|
|
|
+def get_queue_status():
|
|
|
+ """Get current conversion queue status."""
|
|
|
+ queue_size = conversion_queue.qsize()
|
|
|
+ active_conversions = conversion_executor._threads # Active thread count
|
|
|
+ return {
|
|
|
+ 'queued': queue_size,
|
|
|
+ 'active': len(active_conversions) if active_conversions else 0,
|
|
|
+ 'max_concurrent': MAX_CONCURRENT_CONVERSIONS
|
|
|
+ }
|
|
|
+
|
|
|
+def print_queue_status():
|
|
|
+ """Print current queue status."""
|
|
|
+ status = get_queue_status()
|
|
|
+ print(f"🎬 Conversion Status: {status['active']}/{status['max_concurrent']} active, {status['queued']} queued")
|
|
|
+
|
|
|
+def process_conversion_queue():
|
|
|
+ """Background worker to process the conversion queue."""
|
|
|
+ while True:
|
|
|
+ try:
|
|
|
+ # Get item from queue (blocks until available)
|
|
|
+ priority, input_file = conversion_queue.get(timeout=30)
|
|
|
+
|
|
|
+ # Check if file still exists
|
|
|
+ if not os.path.exists(input_file):
|
|
|
+ print(f"⚠️ File no longer exists, skipping conversion: {input_file}")
|
|
|
+ conversion_queue.task_done()
|
|
|
+ continue
|
|
|
+
|
|
|
+ print(f"🔄 Starting conversion (priority {priority}): {os.path.basename(input_file)}")
|
|
|
+
|
|
|
+ # Perform the conversion
|
|
|
+ success = convert_to_apple_tv_compatible(input_file)
|
|
|
+
|
|
|
+ if success:
|
|
|
+ print(f"✅ Conversion completed: {os.path.basename(input_file)}")
|
|
|
+ else:
|
|
|
+ print(f"❌ Conversion failed: {os.path.basename(input_file)}")
|
|
|
+
|
|
|
+ # Mark task as done
|
|
|
+ conversion_queue.task_done()
|
|
|
+
|
|
|
+ except queue.Empty:
|
|
|
+ # No items in queue for 30 seconds, continue waiting
|
|
|
+ continue
|
|
|
+ except Exception as e:
|
|
|
+ print(f"❌ Conversion queue error: {e}")
|
|
|
+ conversion_queue.task_done()
|
|
|
+
|
|
|
def convert_to_apple_tv_compatible(input_file):
|
|
|
"""Convert a video to Apple TV 4K compatible format, optimized by resolution."""
|
|
|
base_name = os.path.splitext(input_file)[0]
|
|
|
@@ -271,6 +340,14 @@ def check_exit_time():
|
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
|
+ # Start the conversion queue worker
|
|
|
+ print("🎬 Starting video conversion queue system...")
|
|
|
+ print(f"📊 Max concurrent conversions: {MAX_CONCURRENT_CONVERSIONS}")
|
|
|
+
|
|
|
+ # Start background conversion worker
|
|
|
+ conversion_worker = threading.Thread(target=process_conversion_queue, daemon=True, name="ConversionQueueWorker")
|
|
|
+ conversion_worker.start()
|
|
|
+
|
|
|
# Update yt-dlp
|
|
|
update_yt_dlp()
|
|
|
|
|
|
@@ -340,7 +417,18 @@ def main():
|
|
|
break
|
|
|
|
|
|
if needs_conversion and conversion_file:
|
|
|
- threading.Thread(target=convert_to_apple_tv_compatible, args=(conversion_file,)).start()
|
|
|
+ # Determine priority based on video resolution
|
|
|
+ try:
|
|
|
+ probe_cmd = ["ffprobe", "-v", "quiet", "-select_streams", "v:0",
|
|
|
+ "-show_entries", "stream=height", "-of", "csv=p=0", conversion_file]
|
|
|
+ result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
|
|
|
+ height = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 1080
|
|
|
+ priority = 2 if height >= 2160 else 1 # 4K gets higher priority
|
|
|
+ except:
|
|
|
+ priority = 1 # Default priority
|
|
|
+
|
|
|
+ add_to_conversion_queue(conversion_file, priority)
|
|
|
+ print_queue_status()
|
|
|
|
|
|
# Create the NFO file
|
|
|
nfo_file_path = os.path.join(video_dir, "movie.nfo")
|
|
|
@@ -397,13 +485,36 @@ def main():
|
|
|
break
|
|
|
|
|
|
if needs_conversion and conversion_file:
|
|
|
- threading.Thread(target=convert_to_apple_tv_compatible, args=(conversion_file,)).start()
|
|
|
+ # Determine priority based on video resolution
|
|
|
+ try:
|
|
|
+ probe_cmd = ["ffprobe", "-v", "quiet", "-select_streams", "v:0",
|
|
|
+ "-show_entries", "stream=height", "-of", "csv=p=0", conversion_file]
|
|
|
+ result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
|
|
|
+ height = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 1080
|
|
|
+ priority = 2 if height >= 2160 else 1 # 4K gets higher priority
|
|
|
+ except:
|
|
|
+ priority = 1 # Default priority
|
|
|
+
|
|
|
+ add_to_conversion_queue(conversion_file, priority)
|
|
|
+ print_queue_status()
|
|
|
|
|
|
# Create the NFO file
|
|
|
nfo_file_path = os.path.join(video_dir, "movie.nfo")
|
|
|
create_nfo_file(video, nfo_file_path)
|
|
|
|
|
|
print("All videos processed successfully!")
|
|
|
+
|
|
|
+ # Final conversion queue status
|
|
|
+ print("\n🎬 Final Conversion Queue Status:")
|
|
|
+ print_queue_status()
|
|
|
+
|
|
|
+ queued_items = conversion_queue.qsize()
|
|
|
+ if queued_items > 0:
|
|
|
+ print(f"⏳ {queued_items} videos still in conversion queue")
|
|
|
+ print("💡 Conversions will continue in the background")
|
|
|
+ print("💡 You can monitor progress by checking the output folder")
|
|
|
+ else:
|
|
|
+ print("✅ All conversions completed!")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
main()
|