| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- import os
- import time
- import datetime
- import pickle
- import threading
- from googleapiclient.discovery import build
- import subprocess
- from google.auth.transport.requests import Request
- import sys
- # Constants
- OUTPUT_DIR = "/media/media/YouTube" # Change this to your Emby folder
- DAYS_BACK = 5 # Videos published in the last 5 days
- DAYS_TO_KEEP = 10 # Delete videos older than 10 days
- TOKEN_FILE = "youtube_token.pickle" # Token file from the OAuth script
- 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"] # Regions to fetch popular videos from
- def update_yt_dlp():
- """Update yt-dlp to the latest version."""
- print("Updating yt-dlp to the latest version...")
- subprocess.run(["pip", "install", "--upgrade", "yt-dlp"], check=True)
- print("yt-dlp updated successfully!")
- def authenticate_with_token():
- """Authenticate using the saved token."""
- if not os.path.exists(TOKEN_FILE):
- raise FileNotFoundError(f"Token file '{TOKEN_FILE}' not found. Please run the token retrieval script first.")
- with open(TOKEN_FILE, "rb") as token:
- creds = pickle.load(token)
- # Refresh the token if needed
- if creds and creds.expired and creds.refresh_token:
- creds.refresh(Request())
- youtube = build("youtube", "v3", credentials=creds)
- return youtube
- def get_subscribed_channels(youtube):
- """Fetch the list of subscribed channels."""
- channels = []
- request = youtube.subscriptions().list(
- part="snippet",
- mine=True,
- maxResults=50
- )
- while request:
- response = request.execute()
- for item in response["items"]:
- channels.append(item["snippet"]["resourceId"]["channelId"])
- request = youtube.subscriptions().list_next(request, response)
- return channels
- def get_recent_videos(youtube, channel_id):
- """Fetch videos from a channel published in the last `DAYS_BACK` days."""
- recent_videos = []
- published_after = (datetime.datetime.now() - datetime.timedelta(days=DAYS_BACK)).isoformat("T") + "Z"
-
- request = youtube.search().list(
- part="snippet",
- channelId=channel_id,
- publishedAfter=published_after,
- maxResults=50,
- type="video",
- order="date"
- )
- response = request.execute()
- for item in response["items"]:
- recent_videos.append({
- "video_id": item["id"]["videoId"],
- "title": item["snippet"]["title"],
- "description": item["snippet"]["description"],
- "published_at": item["snippet"]["publishedAt"],
- "channel_title": item["snippet"]["channelTitle"]
- })
- return recent_videos
- def get_popular_videos(youtube, region_code):
- """Fetch the most popular videos for a given region."""
- popular_videos = []
-
- request = youtube.videos().list(
- part="snippet,statistics",
- chart="mostPopular",
- regionCode=region_code, # Region code passed here
- maxResults=POPULAR_VIDEOS_MAX_RESULTS
- )
- response = request.execute()
- for item in response["items"]:
- popular_videos.append({
- "video_id": item["id"],
- "title": item["snippet"]["title"],
- "description": item["snippet"]["description"],
- "published_at": item["snippet"]["publishedAt"],
- "channel_title": item["snippet"]["channelTitle"]
- })
- return popular_videos
- def download_video(video_id, output_path):
- """Download a YouTube video in the best quality, handling errors gracefully."""
- try:
- command = [
- "yt-dlp",
- "-f", "bestvideo+bestaudio/best", # Best available format
- f"https://www.youtube.com/watch?v={video_id}",
- "-o", os.path.join(output_path, "%(title)s.%(ext)s")
- ]
- subprocess.run(command, check=True)
- except subprocess.CalledProcessError as e:
- print(f"Error downloading video {video_id}: {e}")
- return False # Return False to indicate failure
- return True # Return True if download is successful
- # Temporarily commented out video conversion functionality
- # def convert_to_mp4(input_file):
- # """Convert a video to mp4 and delete the original file."""
- # mp4_file = os.path.splitext(input_file)[0] + ".mp4"
- # print(f"Converting {input_file} to {mp4_file}...")
- # command = ["ffmpeg", "-i", input_file, "-c:v", "libx264", "-preset", "fast", "-c:a", "aac", mp4_file]
- # subprocess.run(command, check=True)
- # print(f"Conversion completed: {mp4_file}")
- # if os.path.exists(mp4_file):
- # os.remove(input_file)
- # print(f"Original file deleted: {input_file}")
- def create_nfo_file(video_data, file_path):
- """Create an Emby-compatible NFO file."""
- nfo_content = f"""<?xml version="1.0" encoding="utf-8" standalone="yes"?>
- <movie>
- <title>{video_data['title']}</title>
- <plot>{video_data['description']}</plot>
- <year>{video_data['published_at'][:4]}</year>
- <premiered>{video_data['published_at']}</premiered>
- <studio>{video_data['channel_title']}</studio>
- </movie>
- """
- with open(file_path, "w", encoding="utf-8") as nfo_file:
- nfo_file.write(nfo_content)
- def delete_old_videos(folder, days=10):
- """Delete video files and corresponding NFO files older than `days`."""
- now = time.time()
- cutoff = now - (days * 86400) # Convert days to seconds
- for root, dirs, files in os.walk(folder):
- for file in files:
- file_path = os.path.join(root, file)
- if os.path.isfile(file_path):
- # Check the file's last modification time
- if os.path.getmtime(file_path) < cutoff:
- print(f"Deleting: {file_path}")
- os.remove(file_path)
- def video_exists(output_path, video_id):
- """Check if a video already exists in the directory."""
- return os.path.exists(os.path.join(output_path, video_id))
- def check_exit_time():
- """Check if the current time is past the exit time."""
- now = datetime.datetime.now().time()
- if now >= EXIT_TIME:
- print("Current time exceeds exit time. Terminating script.")
- sys.exit(0)
- def main():
- # Update yt-dlp
- update_yt_dlp()
- # Authenticate YouTube API
- print("Authenticating with token...")
- youtube = authenticate_with_token()
- if not os.path.exists(OUTPUT_DIR):
- os.makedirs(OUTPUT_DIR)
- # Clean up old videos
- print(f"Deleting videos older than {DAYS_TO_KEEP} days in {OUTPUT_DIR}...")
- delete_old_videos(OUTPUT_DIR, days=DAYS_TO_KEEP)
-
- # Fetch subscribed channels
- print("Fetching subscribed channels...")
- channels = get_subscribed_channels(youtube)
-
- # Fetch and process recent videos from subscribed channels
- for channel_id in channels:
- print(f"Fetching recent videos for channel: {channel_id}")
- videos = get_recent_videos(youtube, channel_id)
-
- for video in videos:
- # Check the exit time before processing each video
- check_exit_time()
-
- print(f"Processing video: {video['title']}")
- video_dir = os.path.join(OUTPUT_DIR, video["video_id"])
-
- # Check if the video already exists
- if os.path.exists(video_dir):
- print(f"Skipping download, already exists: {video['title']} ({video['video_id']})")
- continue
-
- os.makedirs(video_dir, exist_ok=True)
-
- # Download the video
- if not download_video(video["video_id"], video_dir):
- print(f"Skipping video {video['video_id']} due to download error.")
- continue # Skip this video if download failed
-
- # Temporarily comment out conversion, but keep it in the code for future use
- # video_file = next(
- # (f for f in os.listdir(video_dir) if not f.endswith(".mp4")),
- # None
- # )
- # if video_file:
- # input_path = os.path.join(video_dir, video_file)
- # threading.Thread(target=convert_to_mp4, args=(input_path,)).start()
-
- # Create the NFO file
- nfo_file_path = os.path.join(video_dir, "movie.nfo")
- create_nfo_file(video, nfo_file_path)
- # Fetch and process most popular videos worldwide (from multiple regions)
- for region_code in REGION_CODES:
- print(f"Fetching the most popular videos from {region_code}...")
- popular_videos = get_popular_videos(youtube, region_code)
- for video in popular_videos:
- # Check the exit time before processing each video
- check_exit_time()
-
- print(f"Processing popular video: {video['title']} ({region_code})")
- video_dir = os.path.join(OUTPUT_DIR, video["video_id"])
-
- # Check if the video already exists
- if os.path.exists(video_dir):
- print(f"Skipping download, already exists: {video['title']} ({video['video_id']})")
- continue
-
- os.makedirs(video_dir, exist_ok=True)
-
- # Download the popular video
- if not download_video(video["video_id"], video_dir):
- print(f"Skipping popular video {video['video_id']} due to download error.")
- continue # Skip this video if download failed
-
- # Temporarily comment out conversion, but keep it in the code for future use
- # video_file = next(
- # (f for f in os.listdir(video_dir) if not f.endswith(".mp4")),
- # None
- # )
- # if video_file:
- # input_path = os.path.join(video_dir, video_file)
- # threading.Thread(target=convert_to_mp4, args=(input_path,)).start()
-
- # 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!")
- if __name__ == "__main__":
- main()
|