-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutil.py
168 lines (126 loc) · 5.46 KB
/
util.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import math
import sys
from time import time
from constants import USER_ID, TEST_MIN_DISTANCE
def get_tracks(sp, playlist_id):
"""
Returns a dict with id, URI, name and is_local for all the
tracks in the playlist with the given ID
"""
result = []
page_size = 100
offset = 0
while True:
track_batch = sp.playlist_items(playlist_id, limit=page_size, offset=offset)
result.extend([{
'id': item['track']['id'],
'uri': item['track']['uri'],
'name': item['track']['name'],
'artist': item['track']['artists'][0]['name'],
'is_local': item['track']['is_local'],
} for item in track_batch['items']])
if not track_batch['next']:
break
offset += page_size
return result
def get_nr_of_tracks(sp, playlist_id):
""" Returns the number of tracks in the playlist with the given ID """
return sp.playlist_items(playlist_id)['total']
def get_playlist_id(sp, playlist_name):
""" Given the playlist's name, returns its ID """
playlists = sp.user_playlists(USER_ID)['items']
for playlist in playlists:
if playlist['name'].lower() == playlist_name.lower():
return playlist['id']
sys.exit(f'No playlist with name "{playlist_name}" found.')
def get_total_time(start_time):
""" Returns the time elapsed since the start_time in a human-readable way """
end_time = time()
total_time = end_time - start_time
unit = 'seconds'
if total_time >= 3600:
total_time /= 3600
unit = 'hours'
elif total_time >= 60:
total_time /= 60
unit = 'minutes'
total_time = round(total_time, 2)
return f'{total_time} {unit}'
def divide_in_chunks(list_to_divide, chunk_size):
""" Returns a list of lists - the given list divided into chunks of the given size """
return [list_to_divide[i:i + chunk_size] for i in range(0, len(list_to_divide), chunk_size)]
def test(sp, main_playlist_id, good_playlist_id, best_playlist_id):
""" Checks playlists are OK """
print('\nStarted test...')
check_well_ordered(sp, main_playlist_id)
check_clones_ok(sp, main_playlist_id, good_playlist_id, best_playlist_id)
print('\nDone')
def check_well_ordered(sp, playlist_id):
"""
Checks whether the playlist is well-ordered, i.e. the tracks occurring
more than once (clones) are all far enough from each other
"""
tracks = get_tracks(sp, playlist_id)
playlist_size = len(tracks)
min_distance = math.floor(playlist_size * TEST_MIN_DISTANCE)
clones_too_close = 0
# For each track in playlist check if it is too close to any of its clones. Only looks forward.
for track_index, track in enumerate(tracks):
for current_track_index in range(track_index + 1, len(tracks)):
if current_track_index - track_index >= min_distance:
# Min distance already reached, clones can not be too close
break
if tracks[current_track_index] == track:
track_name = track['name']
print(f'(!) Clones of track "{track_name}" too close! Indices: {track_index}, {current_track_index}')
clones_too_close += 1
if clones_too_close == 0:
print(f'\nMain playlist is well ordered!\n')
def check_clones_ok(sp, main_playlist_id, good_playlist_id, best_playlist_id):
""" Checks whether all double clones in the playlist are present
in the good playlist and the same for the triple ones """
all_tracks = get_tracks(sp, main_playlist_id)
good_tracks = get_tracks(sp, good_playlist_id)
best_tracks = get_tracks(sp, best_playlist_id)
checked_tracks = []
for track in all_tracks:
if track in checked_tracks:
continue
# Checking by URI because local tracks don't have ID
track_indices = [index for index, t in enumerate(all_tracks) if t['uri'] == track['uri']]
if len(track_indices) == 2:
checked_tracks.append(track)
if track not in good_tracks:
track_name = track['name']
print(
f'(!) "{track_name}" is present twice in the main playlist, but not present in the good playlist.')
elif len(track_indices) == 3:
checked_tracks.append(track)
if track not in best_tracks:
track_name = track['name']
print(
f'(!) "{track_name}" is present three times in the main playlist, but not present in the best playlist.')
def get_youtube_search_url(artist_name: str, track_name: str) -> str:
""" Returns the URL to a YouTube search for
the given song by the given artist """
query = f'{artist_name} {track_name}'.replace(' ', '+')
return f'https://www.youtube.com/results?search_query={query}'
def get_last_occurrence_index(text, char):
""" Returns the index of the last occurrence of the given char in the given text """
return len(text) - 1 - text[::-1].index(char)
def get_track_name_core(track_name):
""" Returns the most significant part of a track name.
I.e. strips away things like featured artists """
stripped = track_name.split('(')[0]
stripped = stripped.split('-')[0]
stripped = stripped.split('feat')[0]
stripped = stripped.split('ft')[0]
return stripped.strip()
class CustomLogger:
""" Used to suppress YoutubeDL output """
def error(self):
pass
def warning(self):
pass
def debug(self):
pass