Added database support

- Database keeps a record of already processed songs.
- Successfully downloaded songs are not processed again, this behavior allows
  the download to be interrupted and resumed several times.
- Failure to download songs is recorded as well, these cases should be inspected
  manually for now.
master
nicolas 5 years ago
parent 4488096a63
commit f155d1d162

2
.gitignore vendored

@ -0,0 +1,2 @@
songs.db

@ -0,0 +1,37 @@
Google Play Music Library Download
=
This script will download your whole google play music library. Each downloaded song is saved into a database, so if the script fails while running, you won't be downloading songs twice. If a song fails downloading, it will be saved to database with downloaded equals to 0: you should try downloading this again manually.
The generated directory structure is of the format:
`[album_artist | artist]/album_name`
And inside each album, you will get:
`track_id - track_name`
Usage
-
- In a terminal run:
`python download_library.py`
- You will get a link to login with the google account where all the music is, google will give you a string that you have to copy and then paste it back into the terminal and hit enter.
- Let it r(b)u(r)n.
Dependencies
-
- Python >= v3.6
### Packages
For this program to work you will need to (virtual environment recommended) install:
- (pip install) sqlite3
- (pip install) gmusicapi

@ -6,80 +6,108 @@ from gmusicapi import Musicmanager
from os import path from os import path
logger = logging.getLogger(__name__) def start_db():
def save_log(text):
log_file = open("log", "a")
log_file.write(text)
log_file.close()
def create_db():
try: try:
sqliteConnection = sqlite3.connect('songs.db') sqliteConnection = sqlite3.connect('songs.db')
sqlite_create_table_query = '''CREATE TABLE success_ids ( sqlite_create_table_query = '''CREATE TABLE IF NOT EXISTS song (
id INTEGER PRIMARY KEY, id INTEGER PRIMARY KEY,
google_id TEXT NOT NULL;''' google_id TEXT NOT NULL,
downloaded INTEGER);'''
cursor = sqliteConnection.cursor() cursor = sqliteConnection.cursor()
cursor.execute(sqlite_create_table_query) cursor.execute(sqlite_create_table_query)
sqlite_create_table_query = '''CREATE TABLE failure_ids (
id INTEGER PRIMARY KEY,
google_id TEXT NOT NULL;'''
cursor.execute(sqlite_create_table_query)
sqliteConnection.commit() sqliteConnection.commit()
cursor.close() cursor.close()
except sqlite3.Error as exc: except sqlite3.Error as exc:
logger.error(f'Error creating the database: {exc}') return
return sqliteConnection
mm = Musicmanager() def insert_song(connection, song_id, failure=False):
mm.perform_oauth() sql = ''' INSERT INTO song (google_id, downloaded)
mm.login() VALUES(?,?) '''
songs = mm.get_uploaded_songs(incremental=False)
cursor = connection.cursor()
cursor.execute(sql, (song_id, int(not failure)))
connection.commit()
lastrowid = cursor.lastrowid
cursor.close()
logger.info(f'There\'s {len(songs)} songs to download . . .') return lastrowid
for song in songs: def get_all_song_ids(connection):
if song['album_artist']: sql = ''' SELECT google_id from song WHERE downloaded = 1;'''
level_one = song['album_artist']
elif song['artist']: cursor = connection.cursor()
level_one = song['artist'] cursor.execute(sql)
else: results = cursor.fetchall()
level_one = 'Unknown Artist' return {r[0] for r in results}
if song['album']:
level_two = song['album']
else: def main():
level_two = 'Unknown Album' mm = Musicmanager()
full_path = f'{level_one}/{level_two}' mm.perform_oauth()
try: mm.login()
os.mkdir(level_one) songs = mm.get_uploaded_songs(incremental=False)
except FileExistsError:
pass db_connection = start_db()
except Exception as exc: already_downloaded = get_all_song_ids(db_connection)
save_log(f"\n\n{song} \n {exc} \n\n")
continue
try: for song in songs:
os.mkdir(full_path) song_id = song['id']
except FileExistsError:
pass if song_id in already_downloaded:
except Exception as exc: continue
save_log(f"\n\n{song} \n {exc} \n\n")
continue if song['album_artist']:
try: level_one = song['album_artist']
downloaded_song = mm.download_song(song['id']) elif song['artist']:
except Exception as exc: level_one = song['artist']
save_log(f"\n\n{song} \n {exc} \n\n") else:
full_path += f"/{downloaded_song[0]}" level_one = 'Unknown Artist'
file_path = path.relpath(full_path)
file_to_write = open(file_path, "wb") if song['album']:
file_to_write.write(downloaded_song[1]) level_two = song['album']
file_to_write.close() else:
level_two = 'Unknown Album'
full_path = f'{level_one}/{level_two}'
try:
os.mkdir(level_one)
except FileExistsError:
pass
except Exception as exc:
insert_song(db_connection, song_id, True)
continue
try:
os.mkdir(full_path)
except FileExistsError:
pass
except Exception as exc:
insert_song(db_connection, song_id, True)
continue
try:
downloaded_song = mm.download_song(song_id)
except Exception as exc:
insert_song(db_connection, song_id, True)
full_path += f"/{downloaded_song[0]}"
file_path = path.relpath(full_path)
file_to_write = open(file_path, "wb")
file_to_write.write(downloaded_song[1])
file_to_write.close()
insert_song(db_connection, song_id)
if __name__ == "__main__":
main()

Loading…
Cancel
Save