|
@@ -0,0 +1,119 @@
|
|
|
|
|
+import os
|
|
|
|
|
+import datetime
|
|
|
|
|
+import json
|
|
|
|
|
+from google_auth_oauthlib.flow import InstalledAppFlow
|
|
|
|
|
+from googleapiclient.discovery import build
|
|
|
|
|
+import subprocess
|
|
|
|
|
+
|
|
|
|
|
+# OAuth 2.0 scopes
|
|
|
|
|
+SCOPES = ["https://www.googleapis.com/auth/youtube.readonly"]
|
|
|
|
|
+
|
|
|
|
|
+# Output directory for Emby
|
|
|
|
|
+OUTPUT_DIR = "/media/media/YouTube"
|
|
|
|
|
+
|
|
|
|
|
+# Days to look back for videos
|
|
|
|
|
+DAYS_BACK = 5
|
|
|
|
|
+
|
|
|
|
|
+def authenticate_youtube():
|
|
|
|
|
+ """Authenticate the user and return a YouTube client."""
|
|
|
|
|
+ flow = InstalledAppFlow.from_client_secrets_file("client_secret.json", SCOPES)
|
|
|
|
|
+ creds = flow.run_local_server(port=0)
|
|
|
|
|
+ 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 download_video(video_id, output_path):
|
|
|
|
|
+ """Download a YouTube video using yt-dlp."""
|
|
|
|
|
+ command = [
|
|
|
|
|
+ "yt-dlp",
|
|
|
|
|
+ f"https://www.youtube.com/watch?v={video_id}",
|
|
|
|
|
+ "-o", os.path.join(output_path, "%(title)s.%(ext)s")
|
|
|
|
|
+ ]
|
|
|
|
|
+ subprocess.run(command, check=True)
|
|
|
|
|
+
|
|
|
|
|
+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 main():
|
|
|
|
|
+ # Authenticate YouTube API
|
|
|
|
|
+ print("Authenticating...")
|
|
|
|
|
+ youtube = authenticate_youtube()
|
|
|
|
|
+
|
|
|
|
|
+ if not os.path.exists(OUTPUT_DIR):
|
|
|
|
|
+ os.makedirs(OUTPUT_DIR)
|
|
|
|
|
+
|
|
|
|
|
+ # Fetch subscribed channels
|
|
|
|
|
+ print("Fetching subscribed channels...")
|
|
|
|
|
+ channels = get_subscribed_channels(youtube)
|
|
|
|
|
+
|
|
|
|
|
+ # Fetch and process recent videos
|
|
|
|
|
+ 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:
|
|
|
|
|
+ print(f"Processing video: {video['title']}")
|
|
|
|
|
+ # Create a folder for the video
|
|
|
|
|
+ video_dir = os.path.join(OUTPUT_DIR, video["title"].replace("/", "_"))
|
|
|
|
|
+ os.makedirs(video_dir, exist_ok=True)
|
|
|
|
|
+
|
|
|
|
|
+ # Download the video
|
|
|
|
|
+ download_video(video["video_id"], video_dir)
|
|
|
|
|
+
|
|
|
|
|
+ # 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()
|