Add download option
This commit is contained in:
parent
080842758d
commit
a352f3cac0
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,3 +4,4 @@ __pycache__/
|
||||
client_secrets_file.json
|
||||
token.json
|
||||
playlists.csv
|
||||
db.json
|
||||
|
||||
96
main.py
96
main.py
@ -9,8 +9,14 @@ from google.oauth2.credentials import Credentials
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from googleapiclient.discovery import build
|
||||
from googleapiclient.errors import HttpError
|
||||
from tinydb import TinyDB, Query
|
||||
from yt_dlp import YoutubeDL
|
||||
|
||||
_playlists = {}
|
||||
# output template: CHANNEL/DATE_TITLE_[ID].ext
|
||||
DEFAULT_OUTPUT_TMPL = "%(channel)s/%(upload_date)s_%(title)s_[%(id)s].%(ext)s"
|
||||
# download video 1080p or lower with audio
|
||||
DEFAULT_FORMAT = "bestvideo[height<=1080]+bestaudio/best[height<=1080]"
|
||||
|
||||
|
||||
def _truncate_title(title: str, length: int = 30) -> str:
|
||||
@ -170,13 +176,13 @@ def remove_video_from_playlist(yt_api, plitem_id: str, playlist_id: str,
|
||||
|
||||
|
||||
def copy_playlist_items(yt_api,
|
||||
src_playlist: str,
|
||||
dst_playlist: str,
|
||||
src_playlist_id: str,
|
||||
dst_playlist_id: str,
|
||||
delete_from_src: bool = False,
|
||||
limit: int = -1,
|
||||
dry_run: bool = False):
|
||||
src_playlist_items = list_playlist(yt_api, src_playlist)
|
||||
dst_playlist_items = list_playlist(yt_api, dst_playlist)
|
||||
src_playlist_items = list_playlist(yt_api, src_playlist_id)
|
||||
dst_playlist_items = list_playlist(yt_api, dst_playlist_id)
|
||||
|
||||
dst_videos = {pl_item['snippet']['resourceId']['videoId']
|
||||
for pl_item in dst_playlist_items}
|
||||
@ -188,10 +194,10 @@ def copy_playlist_items(yt_api,
|
||||
was_processed = False
|
||||
video_id = src_pl_item['snippet']['resourceId']['videoId']
|
||||
if video_id not in dst_videos:
|
||||
add_video_to_playlist(yt_api, video_id, dst_playlist, dry_run)
|
||||
add_video_to_playlist(yt_api, video_id, dst_playlist_id, dry_run)
|
||||
was_processed = True
|
||||
if delete_from_src:
|
||||
remove_video_from_playlist(yt_api, src_pl_item["id"], src_playlist, dry_run)
|
||||
remove_video_from_playlist(yt_api, src_pl_item["id"], src_playlist_id, dry_run)
|
||||
was_processed = True
|
||||
if was_processed:
|
||||
processed_amt += 1
|
||||
@ -199,8 +205,9 @@ def copy_playlist_items(yt_api,
|
||||
|
||||
def get_video_info(youtube, video_id: str):
|
||||
try:
|
||||
# TODO maybe remove 'status'
|
||||
response = youtube.videos().list(
|
||||
part="snippet",
|
||||
part="localizations,snippet,contentDetails,statistics,status,topicDetails",
|
||||
id=video_id
|
||||
).execute()
|
||||
except HttpError as e:
|
||||
@ -216,7 +223,7 @@ def get_video_info(youtube, video_id: str):
|
||||
def get_playlistitem_info(youtube, playlistitem_id: str):
|
||||
try:
|
||||
response = youtube.playlistItems().list(
|
||||
part="snippet",
|
||||
part="snippet,contentDetails",
|
||||
id=playlistitem_id
|
||||
).execute()
|
||||
except HttpError as e:
|
||||
@ -262,6 +269,14 @@ def main():
|
||||
parser_dups.add_argument('-l', '--limit', type=int, default=-1,
|
||||
help='Limit number of videos to process')
|
||||
|
||||
parser_download = subparsers.add_parser('download', help='Download videos from a playlist')
|
||||
parser_download.add_argument('playlist', help='Playlist name/ID')
|
||||
parser_download.add_argument('dst_folder', help='Destination folder')
|
||||
parser_download.add_argument('-l', '--limit', type=int, default=-1,
|
||||
help='Limit number of videos to process')
|
||||
parser_download.add_argument('-r', '--remove-from-playlist', action='store_true',
|
||||
help='Remove downloaded videos from the playlist')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Disable OAuthlib's HTTPS verification when running locally.
|
||||
@ -276,19 +291,16 @@ def main():
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
elif args.command == 'copy':
|
||||
src_playlist = get_playlist_id(args.src_playlist)
|
||||
dst_playlist = get_playlist_id(args.dst_playlist)
|
||||
copy_playlist_items(youtube, src_playlist, dst_playlist,
|
||||
delete_from_src=False,
|
||||
limit=args.limit, dry_run=args.dry_run)
|
||||
|
||||
elif args.command == 'move':
|
||||
src_playlist = get_playlist_id(args.src_playlist)
|
||||
dst_playlist = get_playlist_id(args.dst_playlist)
|
||||
copy_playlist_items(youtube, src_playlist, dst_playlist,
|
||||
delete_from_src=True,
|
||||
limit=args.limit, dry_run=args.dry_run)
|
||||
elif args.command in ('copy', 'move'):
|
||||
delete_from_src = args.command == 'move'
|
||||
copy_playlist_items(
|
||||
youtube,
|
||||
get_playlist_id(args.src_playlist),
|
||||
get_playlist_id(args.dst_playlist),
|
||||
delete_from_src=delete_from_src,
|
||||
limit=args.limit,
|
||||
dry_run=args.dry_run
|
||||
)
|
||||
|
||||
elif args.command == 'add':
|
||||
playlist_id = get_playlist_id(args.playlist)
|
||||
@ -339,6 +351,48 @@ def main():
|
||||
else:
|
||||
plitems_processed.add(video_id)
|
||||
|
||||
elif args.command == "download":
|
||||
db = TinyDB('db.json')
|
||||
query = Query()
|
||||
ydl_opts = {
|
||||
'outtmpl': os.path.join(args.dst_folder, DEFAULT_OUTPUT_TMPL),
|
||||
'format': DEFAULT_FORMAT,
|
||||
}
|
||||
|
||||
# load playlist items
|
||||
playlist_id = get_playlist_id(args.playlist)
|
||||
plitems = list_playlist(youtube, playlist_id)
|
||||
|
||||
# limit number of videos to process
|
||||
if args.limit > 0:
|
||||
plitems = plitems[:args.limit]
|
||||
|
||||
for plitem in plitems:
|
||||
video_id = plitem["snippet"]["resourceId"]["videoId"]
|
||||
# skip if video is already in the database
|
||||
if db.search(query.id == video_id):
|
||||
continue
|
||||
|
||||
video_info = get_video_info(youtube, video_id)
|
||||
# skip if video is not found on YouTube
|
||||
if not video_info:
|
||||
continue
|
||||
|
||||
video_title = _truncate_title(video_info['snippet']['title'])
|
||||
if args.dry_run:
|
||||
print(f"Would download video '{video_title}' [{video_id}]"
|
||||
f" from playlist {args.playlist} to folder {args.dst_folder}")
|
||||
|
||||
else:
|
||||
# download video
|
||||
with YoutubeDL(ydl_opts) as ydl:
|
||||
ydl.download(['https://www.youtube.com/watch?v=' + video_id])
|
||||
db.insert(video_info)
|
||||
|
||||
# remove video from playlist
|
||||
if args.remove_from_playlist:
|
||||
remove_video_from_playlist(youtube, plitem["id"], playlist_id, args.dry_run)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
@ -2,3 +2,5 @@ google-api-python-client
|
||||
google-auth
|
||||
google-auth-httplib2
|
||||
google-auth-oauthlib
|
||||
tinydb
|
||||
yt_dlp
|
||||
|
||||
Loading…
Reference in New Issue
Block a user