-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgpm2spotify.py
180 lines (137 loc) · 5.99 KB
/
gpm2spotify.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
169
170
171
172
173
174
175
176
177
178
179
180
import functools
import gpm_file_parser
import json
import logging
import os
import queue
import song_finder
import spotify_adder
import threading
class Gpm2Spotify:
def __init__(self,
filepath="",
spotify_app=None,
spotify_user=None,
thumbs_up_is_library=True,
browser_messenger = None):
self._filepath = "/Users/aman23091998/Downloads/Takeout/Google Play Music"
self._song_finder = song_finder.SongFinder(spotify_app, browser_messenger)
self._spotify_adder = spotify_adder.SpotifyAdder(spotify_user)
self._thumbs_up_is_library = thumbs_up_is_library
self._browser_messenger = browser_messenger
self._parser_lock = threading.Lock()
self._gpm_file_parser = gpm_file_parser.GpmFileParser()
self._logger = logging.getLogger("gpm2spotify")
def _add_songs_to_spotify_thread(self, read_queue, add_to_spotify):
"""Thread to batch add songs to spotify
:param read_queue: Queue, Contians songs that need to be added to spotify
:param add_to_spotify: Function(ids), Add upto 50 ids to spotify. Function handles
library or playlist.
"""
songs = []
while True:
if len(songs) == 50:
add_to_spotify(songs)
songs = []
song = read_queue.get()
if not song:
break
search_result = self._song_finder.get_song(song)
if search_result and "id" in search_result:
songs.append(search_result)
add_to_spotify(songs)
def _parse_song_files(self, tracks_filepath, add_to_spotify):
"""Reads songs from Google Takeout and adds them to spotify
:param tracks_filepath: Path to songs that need to be added
:param add_to_spotify: Function(ids), Add upto 50 ids to spotify. Function handles
library or playlist.
"""
files = os.listdir(tracks_filepath)
read_queue = queue.Queue() # Files read from tracks_filepath
thread_count = 10
threads = []
for x in range(thread_count):
threads.append(
threading.Thread(target=self._add_songs_to_spotify_thread, args=(read_queue, add_to_spotify))
)
for x in range(thread_count):
threads[x].start()
for file in files:
song = self._gpm_file_parser.parse_file(os.path.join(tracks_filepath, file))
if not song.title:
continue
read_queue.put(song)
for x in range(thread_count):
read_queue.put(None)
for x in range(thread_count):
threads[x].join()
def _post_library(self, songs):
"""Adds songs to Spotify Library
:param songs: Array of JSON Objects, (Max 50) songs that need to be added to the library
"""
song_count = len(songs)
if song_count == 0:
return
if song_count > 50:
self._logger.info(f"ID list should be less than 50, found:{song_count}, handling gracefully...")
while song_count > 0:
selected = 50 if song_count >= 50 else song_count
if self._spotify_adder.add_to_library(
[song["id"]
for song in songs[song_count - selected: song_count -1]]
):
self._logger.info(f"Added {selected} songs to library")
self._browser_messenger.songs_added(selected)
else:
self._logger.error(f"Failed to add {song_count} to library")
self._browser_messenger.songs_add_failed(selected)
song_count -= selected
def parse_library(self):
"""Adds songs from GPM to spotify library
"""
if self._thumbs_up_is_library:
tracks_filepath = self._filepath + "/Playlists/Thumbs Up/"
self._parser_lock.acquire()
self._parse_song_files(tracks_filepath, self._post_library)
self._parser_lock.release()
def _post_playlist(self, playlist, songs):
"""Adds songs to a Spotify Playlist
:param playlist: JSON Object, playlist songs need to be added to
:param songs: Array of JSON Objects, (Max 50) songs that need to be added to the playlist
"""
if len(songs) < 1:
return
song_uris = [ song["uri"] for song in songs ]
id = playlist["id"]
name = playlist["name"]
if self._spotify_adder.add_songs_to_playlist(id, song_uris):
self._logger.info(f"{len(song_uris)} songs added to Playlist {name}")
self._browser_messenger.songs_added(songs, False, playlist)
else:
self._logger.error(f"Failed to add {len(song_uris)} songs to Playlist {name}")
self._browser_messenger.songs_add_failed(songs, False, playlist)
def _parse_playlist(self, name, playlist_file_path):
resp = self._spotify_adder.create_playlist(name)
if resp:
self._logger.info(f"Created playlist {name}")
self._browser_messenger.playlist_created(resp)
else:
self._logger.error(f"Failed to created playlist {name}")
self._browser_messenger.playlist_create_failed(name)
return
tracks_filepath = playlist_file_path + ("/Tracks" if name is not "Thumbs Up" else "")
self._parse_song_files(
tracks_filepath,
functools.partial(self._post_playlist, resp),
)
def parse_playlists(self):
"""Adds playlists from GPM to Spotify
"""
self._parser_lock.acquire()
playlists_filepath = self._filepath + "/Playlists"
playlists = os.listdir(playlists_filepath)
if self._thumbs_up_is_library and "Thumbs Up" in playlists:
playlists.remove("Thumbs Up")
for playlist in playlists:
self._parse_playlist(playlist, playlists_filepath + f"/{playlist}")
self._parser_lock.release()