Add copy playlist command

This commit is contained in:
Maks Snegov 2024-02-02 14:26:51 -08:00
parent 439e160cdd
commit b71bfb0674

72
main.py
View File

@ -22,6 +22,7 @@ def _truncate_title(title: str, length: int = 30) -> str:
return title return title
return title[:length-3] + (title[length:] and '...') return title[:length-3] + (title[length:] and '...')
def get_yt_creds(): def get_yt_creds():
""" Get YouTube API credentials """ """ Get YouTube API credentials """
creds = None creds = None
@ -70,7 +71,8 @@ def get_playlist_id(playlist_name: str) -> str:
def get_playlist_name(playlist_id: str) -> str: def get_playlist_name(playlist_id: str) -> str:
if not _playlists: if not _playlists:
read_playlists_file() read_playlists_file()
return next((name for name, id in _playlists.items() if id == playlist_id), playlist_id) return next((name for name, plid in _playlists.items()
if plid == playlist_id), playlist_id)
def get_videos(yt_api, playlist_id): def get_videos(yt_api, playlist_id):
@ -100,7 +102,8 @@ def get_videos(yt_api, playlist_id):
return videos return videos
def add_video_to_playlist(yt_api, video, playlist_id, dry_run: bool = False) -> bool: def add_video_to_playlist(yt_api, video, playlist_id,
dry_run: bool = False) -> bool:
playlist_name = get_playlist_name(playlist_id) playlist_name = get_playlist_name(playlist_id)
video_id = video['snippet']['resourceId']['videoId'] video_id = video['snippet']['resourceId']['videoId']
video_title = _truncate_title(video['snippet']['title']) video_title = _truncate_title(video['snippet']['title'])
@ -127,25 +130,34 @@ def add_video_to_playlist(yt_api, video, playlist_id, dry_run: bool = False) ->
return False return False
def remove_video_from_playlist(yt_api, video, playlist_id, dry_run: bool = False) -> bool: def remove_video_from_playlist(yt_api, video, playlist_id,
dry_run: bool = False) -> bool:
playlist_name = get_playlist_name(playlist_id) playlist_name = get_playlist_name(playlist_id)
video_id = video['snippet']['resourceId']['videoId'] video_id = video['snippet']['resourceId']['videoId']
video_title = _truncate_title(video['snippet']['title']) video_title = _truncate_title(video['snippet']['title'])
try: try:
if dry_run: if dry_run:
print(f"Would remove video '{video_title}' [{video_id}] from playlist {playlist_name}") print(f"Would remove video '{video_title}' [{video_id}]"
f" from playlist {playlist_name}")
return True return True
yt_api.playlistItems().delete( yt_api.playlistItems().delete(
id=video['id'] id=video['id']
).execute() ).execute()
print(f"Removed video '{video_title}' [{video_id}] from playlist {playlist_name}") print(f"Removed video '{video_title}' [{video_id}]"
f" from playlist {playlist_name}")
return True return True
except HttpError as e: except HttpError as e:
print(f"Error removing video '{video_title}' [{video_id}] from playlist {playlist_name}: {e}") print(f"Error removing video '{video_title}' [{video_id}]"
f" from playlist {playlist_name}: {e}")
return False return False
def move_all_videos(yt_api, src_playlist: str, dst_playlist: str, limit: int = -1, dry_run: bool = False): def copy_playlist_items(yt_api,
src_playlist: str,
dst_playlist: str,
delete_from_src: bool = False,
limit: int = -1,
dry_run: bool = False):
if limit < 0: if limit < 0:
limit = len(src_playlist) limit = len(src_playlist)
@ -159,36 +171,52 @@ def move_all_videos(yt_api, src_playlist: str, dst_playlist: str, limit: int = -
video_id = src_video['snippet']['resourceId']['videoId'] video_id = src_video['snippet']['resourceId']['videoId']
if video_id not in dst_video_map: if video_id not in dst_video_map:
add_video_to_playlist(yt_api, src_video, dst_playlist, dry_run) add_video_to_playlist(yt_api, src_video, dst_playlist, dry_run)
if delete_from_src:
remove_video_from_playlist(yt_api, src_video, src_playlist, dry_run) remove_video_from_playlist(yt_api, src_video, src_playlist, dry_run)
def main(): def main():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('src_playlist', help='Source playlist ID') parser.add_argument('-n', '--dry-run', action='store_true',
parser.add_argument('dst_playlist', help='Destination playlist ID') help='Dry run, do not send changes to YoutubeAPI')
parser.add_argument( subparsers = parser.add_subparsers(title='commands', dest='command')
'-l', '--limit', help='Limit number of videos to move', type=int, default=-1
) parser_move = subparsers.add_parser('move', help='Move videos from one playlist to another')
parser.add_argument( parser_move.add_argument('src_playlist', help='Source playlist ID')
'-n', '--dry-run', action='store_true', parser_move.add_argument('dst_playlist', help='Destination playlist ID')
help='Dry run, do not send changes to YoutubeAPI' parser_move.add_argument('-l', '--limit', type=int, default=-1,
) help='Limit number of videos to process')
parser_copy = subparsers.add_parser('copy', help='Copy videos from one playlist to another')
parser_copy.add_argument('src_playlist', help='Source playlist ID')
parser_copy.add_argument('dst_playlist', help='Destination playlist ID')
parser_copy.add_argument('-l', '--limit', type=int, default=-1,
help='Limit number of videos to process')
args = parser.parse_args() args = parser.parse_args()
# Disable OAuthlib's HTTPS verification when running locally. # Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production. # *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
src_playlist = get_playlist_id(args.src_playlist)
dst_playlist = get_playlist_id(args.dst_playlist)
api_service_name = "youtube" api_service_name = "youtube"
api_version = "v3" api_version = "v3"
creds = get_yt_creds() creds = get_yt_creds()
youtube = build(api_service_name, api_version, credentials=creds) youtube = build(api_service_name, api_version, credentials=creds)
move_all_videos(youtube, src_playlist, dst_playlist, if args.command is None:
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) limit=args.limit, dry_run=args.dry_run)