Skip to content

Commit 0e329b3

Browse files
committed
Add privacy setting option for playlist creation and copy functions
This commit introduces the ability to specify the privacy setting for playlists when creating or copying them in the `spotify2ytmusic` tool. It allows users to choose between PRIVATE, PUBLIC, or UNLISTED for the visibility of their newly created playlists on YouTube Music. PRIVATE remains the default.
1 parent 7d95b52 commit 0e329b3

File tree

2 files changed

+57
-15
lines changed

2 files changed

+57
-15
lines changed

spotify2ytmusic/backend.py

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,27 @@ def get_ytmusic() -> YTMusic:
3232
sys.exit(1)
3333

3434

35-
def _ytmusic_create_playlist(yt: YTMusic, title: str, description: str) -> str:
35+
def _ytmusic_create_playlist(
36+
yt: YTMusic, title: str, description: str, privacy_status: str = "PRIVATE"
37+
) -> str:
3638
"""Wrapper on ytmusic.create_playlist
3739
3840
This wrapper does retries with back-off because sometimes YouTube Music will
3941
rate limit requests or otherwise fail.
42+
43+
privacy_status can be: PRIVATE, PUBLIC, or UNLISTED
4044
"""
4145

42-
def _create(yt: YTMusic, title: str, description: str) -> Union[str, dict]:
46+
def _create(
47+
yt: YTMusic, title: str, description: str, privacy_status: str
48+
) -> Union[str, dict]:
4349
exception_sleep = 5
4450
for _ in range(10):
4551
try:
4652
"""Create a playlist on YTMusic, retrying if it fails."""
47-
id = yt.create_playlist(title=title, description=description)
53+
id = yt.create_playlist(
54+
title=title, description=description, privacy_status=privacy_status
55+
)
4856
return id
4957
except Exception as e:
5058
print(
@@ -57,7 +65,7 @@ def _create(yt: YTMusic, title: str, description: str) -> Union[str, dict]:
5765
"s2yt error": 'ERROR: Could not create playlist "{title}" after multiple retries'
5866
}
5967

60-
id = _create(yt, title, description)
68+
id = _create(yt, title, description, privacy_status)
6169
# create_playlist returns a dict if there was an error
6270
if isinstance(id, dict):
6371
print(f"ERROR: Failed to create playlist (name: {title}): {id}")
@@ -73,16 +81,20 @@ def load_playlists_json(filename: str = "playlists.json", encoding: str = "utf-8
7381
return json.load(open(filename, "r", encoding=encoding))
7482

7583

76-
def create_playlist(pl_name: str) -> None:
84+
def create_playlist(pl_name: str, privacy_status: str = "PRIVATE") -> None:
7785
"""Create a YTMusic playlist
7886
7987
8088
Args:
8189
`pl_name` (str): The name of the playlist to create. It should be different to "".
90+
91+
`privacy_status` (str: PRIVATE, PUBLIC, UNLISTED) The privacy setting of created playlist.
8292
"""
8393
yt = get_ytmusic()
8494

85-
id = _ytmusic_create_playlist(yt, title=pl_name, description=pl_name)
95+
id = _ytmusic_create_playlist(
96+
yt, title=pl_name, description=pl_name, privacy_status=privacy_status
97+
)
8698
print(f"Playlist ID: {id}")
8799

88100

@@ -405,6 +417,7 @@ def copy_playlist(
405417
track_sleep: float = 0.1,
406418
yt_search_algo: int = 0,
407419
reverse_playlist: bool = True,
420+
privacy_status: str = "PRIVATE",
408421
):
409422
"""
410423
Copy a Spotify playlist to a YTMusic playlist
@@ -429,9 +442,11 @@ def copy_playlist(
429442
pl_name = pl["name"]
430443

431444
ytmusic_playlist_id = _ytmusic_create_playlist(
432-
yt, title=pl_name, description=pl_name
445+
yt,
446+
title=pl_name,
447+
description=pl_name,
448+
privacy_status=privacy_status,
433449
)
434-
time.sleep(1) # seems to be needed to avoid missing playlist ID error
435450

436451
# create_playlist returns a dict if there was an error
437452
if isinstance(ytmusic_playlist_id, dict):
@@ -459,6 +474,7 @@ def copy_all_playlists(
459474
spotify_playlists_encoding: str = "utf-8",
460475
yt_search_algo: int = 0,
461476
reverse_playlist: bool = True,
477+
privacy_status: str = "PRIVATE",
462478
):
463479
"""
464480
Copy all Spotify playlists (except Liked Songs) to YTMusic playlists
@@ -477,8 +493,9 @@ def copy_all_playlists(
477493
dst_pl_id = get_playlist_id_by_name(yt, pl_name)
478494
print(f"Looking up playlist '{pl_name}': id={dst_pl_id}")
479495
if dst_pl_id is None:
480-
dst_pl_id = _ytmusic_create_playlist(yt, title=pl_name, description=pl_name)
481-
time.sleep(1) # seems to be needed to avoid missing playlist ID error
496+
dst_pl_id = _ytmusic_create_playlist(
497+
yt, title=pl_name, description=pl_name, privacy_status=privacy_status
498+
)
482499

483500
# create_playlist returns a dict if there was an error
484501
if isinstance(dst_pl_id, dict):

spotify2ytmusic/cli.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,25 @@ def create_playlist():
4040
"""
4141
Create a YTMusic playlist
4242
"""
43-
if len(sys.argv) != 2:
44-
print(f"usage: {os.path.basename(sys.argv[0])} <YT PLAYLIST NAME>")
45-
sys.exit(1)
4643

47-
pl_name = sys.argv[1]
48-
backend.create_playlist(pl_name)
44+
def parse_arguments():
45+
parser = ArgumentParser()
46+
parser.add_argument(
47+
"--privacy",
48+
default="PRIVATE",
49+
help="The privacy seting of created playlists (PRIVATE, PUBLIC, UNLISTED, default PRIVATE)",
50+
)
51+
parser.add_argument(
52+
"playlist_name",
53+
type=str,
54+
help="Name of playlist to create.",
55+
)
56+
57+
return parser.parse_args()
58+
59+
args = parse_arguments()
60+
61+
backend.create_playlist(args.playlist_name, privacy_status=args.privacy)
4962

5063

5164
def search():
@@ -229,6 +242,11 @@ def parse_arguments():
229242
help="Do not reverse playlist on load, regular playlists are reversed normally "
230243
"so they end up in the same order as on Spotify.",
231244
)
245+
parser.add_argument(
246+
"--privacy",
247+
default="PRIVATE",
248+
help="The privacy seting of created playlists (PRIVATE, PUBLIC, UNLISTED, default PRIVATE)",
249+
)
232250

233251
return parser.parse_args()
234252

@@ -240,6 +258,7 @@ def parse_arguments():
240258
dry_run=args.dry_run,
241259
spotify_playlists_encoding=args.spotify_playlists_encoding,
242260
reverse_playlist=not args.no_reverse_playlist,
261+
privacy_status=args.privacy,
243262
)
244263

245264

@@ -278,6 +297,11 @@ def parse_arguments():
278297
help="Do not reverse playlist on load, regular playlists are reversed normally "
279298
"so they end up in the same order as on Spotify.",
280299
)
300+
parser.add_argument(
301+
"--privacy",
302+
default="PRIVATE",
303+
help="The privacy seting of created playlists (PRIVATE, PUBLIC, UNLISTED, default PRIVATE)",
304+
)
281305

282306
return parser.parse_args()
283307

@@ -287,6 +311,7 @@ def parse_arguments():
287311
dry_run=args.dry_run,
288312
spotify_playlists_encoding=args.spotify_playlists_encoding,
289313
reverse_playlist=not args.no_reverse_playlist,
314+
privacy_status=args.privacy,
290315
)
291316

292317

0 commit comments

Comments
 (0)