mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 13:03:02 +00:00
rewrite migrations
+ delete older migrations ... oops + change migratrions from "migrations" to "dbmigrations" + restructure migrations, order them based on release version + add a utils/decorators.py file with a coroutine decorator
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
import json
|
||||
import time
|
||||
from collections import OrderedDict
|
||||
from typing import Generator
|
||||
|
||||
from app.db.sqlite.utils import SQLiteManager
|
||||
from app.utils.decorators import coroutine
|
||||
from app.utils.hashing import create_hash
|
||||
|
||||
# playlists table
|
||||
# ---------------
|
||||
# 0: id
|
||||
# 1: banner_pos
|
||||
# 2: has_gif
|
||||
# 3: image
|
||||
# 4: last_updated
|
||||
# 5: name
|
||||
# 6: trackhashes
|
||||
|
||||
|
||||
class RemovePlaylistArtistHashes:
|
||||
"""
|
||||
This migration removes the artisthashes column from the playlists table.
|
||||
"""
|
||||
|
||||
name = "RemovePlaylistArtistHashes"
|
||||
|
||||
@staticmethod
|
||||
def migrate():
|
||||
# remove artisthashes column
|
||||
sql = "ALTER TABLE playlists DROP COLUMN artisthashes"
|
||||
|
||||
with SQLiteManager(userdata_db=True) as cur:
|
||||
cur.execute(sql)
|
||||
cur.close()
|
||||
|
||||
|
||||
class AddSettingsToPlaylistTable:
|
||||
"""
|
||||
This migration adds the settings column and removes the banner_pos and has_gif columns
|
||||
to the playlists table.
|
||||
"""
|
||||
|
||||
name = "AddSettingsToPlaylistTable"
|
||||
|
||||
@staticmethod
|
||||
def migrate():
|
||||
# existing_playlists = []
|
||||
|
||||
select_playlists_sql = "SELECT * FROM playlists"
|
||||
|
||||
with SQLiteManager(userdata_db=True) as cur:
|
||||
create_playlist_table_sql = """CREATE TABLE IF NOT EXISTS playlists (
|
||||
id integer PRIMARY KEY,
|
||||
image text,
|
||||
last_updated text not null,
|
||||
name text not null,
|
||||
settings text,
|
||||
trackhashes text
|
||||
);"""
|
||||
|
||||
insert_playlist_sql = """INSERT INTO playlists(
|
||||
image,
|
||||
last_updated,
|
||||
name,
|
||||
settings,
|
||||
trackhashes
|
||||
) VALUES(:image, :last_updated, :name, :settings, :trackhashes)
|
||||
"""
|
||||
|
||||
cur.execute(select_playlists_sql)
|
||||
|
||||
# load all playlists
|
||||
playlists = cur.fetchall()
|
||||
|
||||
# drop old playlists table
|
||||
cur.execute("DROP TABLE playlists")
|
||||
|
||||
# create new playlists table
|
||||
cur.execute(create_playlist_table_sql)
|
||||
|
||||
def transform_playlists(pipeline: Generator, playlists: tuple):
|
||||
for playlist in playlists:
|
||||
# create dict that matches the new schema
|
||||
p = {
|
||||
"id": playlist[0],
|
||||
"name": playlist[5],
|
||||
"image": playlist[3],
|
||||
"trackhashes": playlist[6],
|
||||
"last_updated": playlist[4],
|
||||
"settings": json.dumps(
|
||||
{
|
||||
"has_gif": False,
|
||||
"banner_pos": playlist[1],
|
||||
"square_img": False,
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
pipeline.send(p)
|
||||
|
||||
@coroutine
|
||||
def insert_playlist():
|
||||
while True:
|
||||
playlist = yield
|
||||
p = OrderedDict(sorted(playlist.items()))
|
||||
cur.execute(insert_playlist_sql, p)
|
||||
|
||||
# insert playlists using a coroutine
|
||||
# (my first coroutine)
|
||||
pipeline = insert_playlist()
|
||||
transform_playlists(pipeline, playlists)
|
||||
|
||||
cur.close()
|
||||
|
||||
|
||||
class AddLastUpdatedToTrackTable:
|
||||
"""
|
||||
This migration adds the last modified column to the tracks table.
|
||||
"""
|
||||
|
||||
name = "AddLastUpdatedToTrackTable"
|
||||
|
||||
@staticmethod
|
||||
def migrate():
|
||||
# add last_mod column and default to current timestamp
|
||||
timestamp = time.time()
|
||||
sql = f"ALTER TABLE tracks ADD COLUMN last_mod text not null DEFAULT '{timestamp}'"
|
||||
|
||||
with SQLiteManager() as cur:
|
||||
cur.execute(sql)
|
||||
cur.close()
|
||||
|
||||
|
||||
class MovePlaylistsAndFavoritesTo10BitHashes:
|
||||
"""
|
||||
This migration moves the playlists and favorites to 10 bit hashes.
|
||||
"""
|
||||
|
||||
name = "MovePlaylistsAndFavoritesTo10BitHashes"
|
||||
|
||||
@staticmethod
|
||||
def migrate():
|
||||
def get_track_data_by_hash(trackhash: str, tracks: list[tuple]) -> tuple:
|
||||
for track in tracks:
|
||||
# trackhash is the 15th bit hash
|
||||
if track[15] == trackhash:
|
||||
# return artist, album, title
|
||||
return track[4], track[1], track[13]
|
||||
|
||||
def get_track_by_albumhash(albumhash: str, tracks: list[tuple]) -> tuple:
|
||||
for track in tracks:
|
||||
# albumhash is the 3rd bit hash
|
||||
if track[3] == albumhash:
|
||||
# return album, albumartist
|
||||
return track[1], track[2]
|
||||
|
||||
_base = "SELECT * FROM"
|
||||
fetch_playlists_sql = f"{_base} playlists"
|
||||
fetch_tracks_sql = f"{_base} tracks"
|
||||
|
||||
update_playlist_hashes_sql = (
|
||||
"UPDATE playlists SET trackhashes = :trackhashes WHERE id = :id"
|
||||
)
|
||||
fetch_favorites_sql = f"{_base} favorites"
|
||||
update_fav_sql = "UPDATE favorites SET hash = :hash WHERE id = :id"
|
||||
remove_fav_sql = "DELETE FROM favorites WHERE id = :id"
|
||||
|
||||
db_tracks = []
|
||||
|
||||
# read tracks from db
|
||||
with SQLiteManager() as cur:
|
||||
cur.execute(fetch_tracks_sql)
|
||||
db_tracks.extend(cur.fetchall())
|
||||
cur.close()
|
||||
|
||||
# update playlists
|
||||
with SQLiteManager(userdata_db=True) as cur:
|
||||
cur.execute(fetch_playlists_sql)
|
||||
playlists = cur.fetchall()
|
||||
|
||||
# for each playlist
|
||||
for p in playlists:
|
||||
pid = p[0]
|
||||
|
||||
# load trackhashes
|
||||
trackhashes: list[str] = json.loads(p[5])
|
||||
|
||||
for index, t in enumerate(trackhashes):
|
||||
(artist, album, title) = get_track_data_by_hash(t, db_tracks)
|
||||
|
||||
# create new hash
|
||||
new_hash = create_hash(artist, album, title, decode=True, limit=10)
|
||||
trackhashes[index] = new_hash
|
||||
|
||||
# convert to string
|
||||
trackhashes = json.dumps(trackhashes)
|
||||
|
||||
# save to db
|
||||
cur.execute(
|
||||
update_playlist_hashes_sql, {"trackhashes": trackhashes, "id": pid}
|
||||
)
|
||||
|
||||
cur.close()
|
||||
|
||||
# update favorites
|
||||
with SQLiteManager(userdata_db=True) as cur:
|
||||
cur.execute(fetch_favorites_sql)
|
||||
favorites = cur.fetchall()
|
||||
|
||||
# for each favorite
|
||||
for f in favorites:
|
||||
fid = f[0]
|
||||
|
||||
fhash: str = f[1]
|
||||
ftype: str = f[2] # "track" || "album"
|
||||
|
||||
if ftype == "album":
|
||||
(album, albumartist) = get_track_by_albumhash(fhash, db_tracks)
|
||||
|
||||
# create new hash
|
||||
new_hash = create_hash(album, albumartist, decode=True, limit=10)
|
||||
|
||||
# save to db
|
||||
cur.execute(update_fav_sql, {"hash": new_hash, "id": fid})
|
||||
continue
|
||||
|
||||
if ftype == "track":
|
||||
(artist, album, title) = get_track_data_by_hash(fhash, db_tracks)
|
||||
|
||||
# create new hash
|
||||
new_hash = create_hash(artist, album, title, decode=True, limit=10)
|
||||
|
||||
# save to db
|
||||
cur.execute(update_fav_sql, {"hash": new_hash, "id": fid})
|
||||
continue
|
||||
|
||||
# remove favorites that are not track or album. ie. artists
|
||||
cur.execute(remove_fav_sql, {"id": fid})
|
||||
|
||||
cur.close()
|
||||
|
||||
|
||||
class RemoveAllTracks:
|
||||
"""
|
||||
This migration removes all tracks from the tracks table.
|
||||
"""
|
||||
|
||||
name = "RemoveAllTracks"
|
||||
|
||||
@staticmethod
|
||||
def migrate():
|
||||
sql = "DELETE FROM tracks"
|
||||
|
||||
with SQLiteManager() as cur:
|
||||
cur.execute(sql)
|
||||
cur.close()
|
||||
Reference in New Issue
Block a user