Skip to content

Commit ae966bb

Browse files
apastelsigma67
andauthored
Gracefully retrieve uploaded songs with a blank duration (sigma67#578) (sigma67#580)
* add unit test that attempts to retrieve a song after its been uploaded * prevent parsing errors when an uploaded song lacks a duration * update unit test to delete/re-upload song if it already is uploaded * format * fix test * add another sleep to end2end playlist --------- Co-authored-by: sigma67 <[email protected]>
1 parent 614999c commit ae966bb

File tree

4 files changed

+41
-2
lines changed

4 files changed

+41
-2
lines changed

tests/mixins/test_playlists.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def test_end2end(self, yt_brand, sample_video):
107107
assert len(response["playlistEditResults"]) > 0, "Adding playlist item failed"
108108
time.sleep(3)
109109
yt_brand.edit_playlist(playlist_id, addToTop=False)
110+
time.sleep(3)
110111
playlist = yt_brand.get_playlist(playlist_id, related=True)
111112
assert len(playlist["tracks"]) == 46, "Getting playlist items failed"
112113
response = yt_brand.remove_playlist_items(playlist_id, playlist["tracks"])

tests/mixins/test_uploads.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import tempfile
2+
import time
23

34
import pytest
45

56
from tests.conftest import get_resource
7+
from ytmusicapi.ytmusic import YTMusic
68

79

810
class TestUploads:
@@ -47,6 +49,39 @@ def test_upload_song(self, config, yt_auth):
4749
response = yt_auth.upload_song(get_resource(config["uploads"]["file"]))
4850
assert response.status_code == 409
4951

52+
def test_upload_song_and_verify(self, config, yt_auth: YTMusic):
53+
"""Upload a song and verify it can be retrieved after it finishes processing."""
54+
upload_response = yt_auth.upload_song(get_resource(config["uploads"]["file"]))
55+
if not isinstance(upload_response, str) and upload_response.status_code == 409:
56+
# Song is already in uploads. Delete it and re-upload
57+
songs = yt_auth.get_library_upload_songs(limit=None, order="recently_added")
58+
delete_response = None
59+
for song in songs:
60+
if song.get("title") in config["uploads"]["file"]:
61+
delete_response = yt_auth.delete_upload_entity(song["entityId"])
62+
assert delete_response == "STATUS_SUCCEEDED"
63+
# Need to wait for song to be fully deleted
64+
time.sleep(10)
65+
# Now re-upload
66+
upload_response = yt_auth.upload_song(get_resource(config["uploads"]["file"]))
67+
68+
assert (
69+
upload_response == "STATUS_SUCCEEDED" or upload_response.status_code == 200
70+
), f"Song failed to upload {upload_response}"
71+
72+
# Wait for upload to finish processing and verify it can be retrieved
73+
retries_remaining = 5
74+
while retries_remaining:
75+
time.sleep(5)
76+
songs = yt_auth.get_library_upload_songs(limit=None, order="recently_added")
77+
for song in songs:
78+
if song.get("title") in config["uploads"]["file"]:
79+
# Uploaded song found
80+
return
81+
retries_remaining -= 1
82+
83+
raise AssertionError("Uploaded song was not found in library")
84+
5085
@pytest.mark.skip(reason="Do not delete uploads")
5186
def test_delete_upload_entity(self, yt_oauth):
5287
results = yt_oauth.get_library_upload_songs()

ytmusicapi/parsers/_utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ def get_dot_separator_index(runs):
6161

6262

6363
def parse_duration(duration):
64-
if duration is None:
64+
# duration may be falsy or a single space: ' '
65+
if not duration or not duration.strip():
6566
return duration
6667
mapped_increments = zip([1, 60, 3600], reversed(duration.split(":")))
6768
seconds = sum(multiplier * int(time) for multiplier, time in mapped_increments)

ytmusicapi/parsers/uploads.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ def parse_uploaded_items(results):
3030
title = get_item_text(data, 0)
3131
like = nav(data, MENU_LIKE_STATUS)
3232
thumbnails = nav(data, THUMBNAILS) if "thumbnail" in data else None
33-
duration = get_fixed_column_item(data, 0)["text"]["runs"][0]["text"]
33+
duration = None
34+
if "fixedColumns" in data:
35+
duration = get_fixed_column_item(data, 0)["text"]["runs"][0]["text"]
3436
song = {
3537
"entityId": entityId,
3638
"videoId": videoId,

0 commit comments

Comments
 (0)