Add copy playlist command

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

76
main.py
View File

@ -22,6 +22,7 @@ def _truncate_title(title: str, length: int = 30) -> str:
return title
return title[:length-3] + (title[length:] and '...')
def get_yt_creds():
""" Get YouTube API credentials """
creds = None
@ -70,7 +71,8 @@ def get_playlist_id(playlist_name: str) -> str:
def get_playlist_name(playlist_id: str) -> str:
if not _playlists:
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):
@ -100,7 +102,8 @@ def get_videos(yt_api, playlist_id):
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)
video_id = video['snippet']['resourceId']['videoId']
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
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)
video_id = video['snippet']['resourceId']['videoId']
video_title = _truncate_title(video['snippet']['title'])
try:
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
yt_api.playlistItems().delete(
id=video['id']
).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
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
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:
limit = len(src_playlist)
@ -159,37 +171,53 @@ def move_all_videos(yt_api, src_playlist: str, dst_playlist: str, limit: int = -
video_id = src_video['snippet']['resourceId']['videoId']
if video_id not in dst_video_map:
add_video_to_playlist(yt_api, src_video, dst_playlist, dry_run)
remove_video_from_playlist(yt_api, src_video, src_playlist, dry_run)
if delete_from_src:
remove_video_from_playlist(yt_api, src_video, src_playlist, dry_run)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('src_playlist', help='Source playlist ID')
parser.add_argument('dst_playlist', help='Destination playlist ID')
parser.add_argument(
'-l', '--limit', help='Limit number of videos to move', type=int, default=-1
)
parser.add_argument(
'-n', '--dry-run', action='store_true',
help='Dry run, do not send changes to YoutubeAPI'
)
parser.add_argument('-n', '--dry-run', action='store_true',
help='Dry run, do not send changes to YoutubeAPI')
subparsers = parser.add_subparsers(title='commands', dest='command')
parser_move = subparsers.add_parser('move', help='Move videos from one playlist to another')
parser_move.add_argument('src_playlist', help='Source playlist ID')
parser_move.add_argument('dst_playlist', help='Destination playlist ID')
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()
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
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_version = "v3"
creds = get_yt_creds()
youtube = build(api_service_name, api_version, credentials=creds)
move_all_videos(youtube, src_playlist, dst_playlist,
limit=args.limit, dry_run=args.dry_run)
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)
if __name__ == '__main__':