import Knex from "knex"; import { TrackWithRefsWithId, AlbumWithRefsWithId, ArtistWithRefsWithId, TagWithRefsWithId, TrackWithRefs, AlbumBaseWithRefs, DBImportResponse, IDMappings } from "../../client/src/api/api"; import * as api from '../../client/src/api/api'; import asJson from "../lib/asJson"; import { createArtist } from "./Artist"; import { createTag } from "./Tag"; import { createAlbum } from "./Album"; import { createTrack } from "./Track"; let _ = require('lodash'); export async function exportDB(userId: number, knex: Knex): Promise { // First, retrieve all the objects without taking linking tables into account. // Fetch the links separately. let tracksPromise: Promise = knex.select('id', 'name', 'storeLinks', 'album') .from('tracks') .where({ 'user': userId }) .then((ts: any[]) => ts.map((t: any) => { return { mbApi_typename: 'track', name: t.name, id: t.id, storeLinks: asJson(t.storeLinks), albumId: t.album, artistIds: [], tagIds: [], } })); let albumsPromise: Promise = knex.select('name', 'storeLinks', 'id') .from('albums') .where({ 'user': userId }) .then((ts: any[]) => ts.map((t: any) => { return { mbApi_typename: 'album', id: t.id, name: t.name, storeLinks: asJson(t.storeLinks), artistIds: [], tagIds: [], trackIds: [], } })); let artistsPromise: Promise = knex.select('name', 'storeLinks', 'id') .from('artists') .where({ 'user': userId }) .then((ts: any[]) => ts.map((t: any) => { return { mbApi_typename: 'artist', id: t.id, name: t.name, storeLinks: asJson(t.storeLinks), albumIds: [], tagIds: [], trackIds: [], } })); let tagsPromise: Promise = knex.select('name', 'parentId', 'id') .from('tags') .where({ 'user': userId }) .then((ts: any[]) => ts.map((t: any) => { return { mbApi_typename: 'tag', id: t.id, name: t.name, parentId: t.parentId, } })); let tracksArtistsPromise: Promise<[number, number][]> = knex.select('trackId', 'artistId') .from('tracks_artists') .then((rs: any) => rs.map((r: any) => [r.trackId, r.artistId])); let tracksTagsPromise: Promise<[number, number][]> = knex.select('trackId', 'tagId') .from('tracks_tags') .then((rs: any) => rs.map((r: any) => [r.trackId, r.tagId])); let artistsTagsPromise: Promise<[number, number][]> = knex.select('artistId', 'tagId') .from('artists_tags') .then((rs: any) => rs.map((r: any) => [r.artistId, r.tagId])); let albumsTagsPromise: Promise<[number, number][]> = knex.select('albumId', 'tagId') .from('albums_tags') .then((rs: any) => rs.map((r: any) => [r.albumId, r.tagId])); let artistsAlbumsPromise: Promise<[number, number][]> = knex.select('albumId', 'artistId') .from('artists_albums') .then((rs: any) => rs.map((r: any) => [r.albumId, r.artistId])); let [ tracks, albums, artists, tags, tracksArtists, tracksTags, artistsTags, albumsTags, artistsAlbums, ] = await Promise.all([ tracksPromise, albumsPromise, artistsPromise, tagsPromise, tracksArtistsPromise, tracksTagsPromise, artistsTagsPromise, albumsTagsPromise, artistsAlbumsPromise, ]); // Now store the links inside the resource objects. tracksArtists.forEach((v: [number, number]) => { let [trackId, artistId] = v; tracks.find((t: TrackWithRefsWithId) => t.id === trackId)?.artistIds.push(artistId); artists.find((a: ArtistWithRefsWithId) => a.id === artistId)?.trackIds.push(trackId); }) tracks.forEach((t: api.TrackWithRefsWithId) => { albums.find((a: AlbumWithRefsWithId) => t.albumId && a.id === t.albumId)?.trackIds.push(t.id); }) tracksTags.forEach((v: [number, number]) => { let [trackId, tagId] = v; tracks.find((t: TrackWithRefsWithId) => t.id === trackId)?.tagIds.push(tagId); }) artistsTags.forEach((v: [number, number]) => { let [artistId, tagId] = v; artists.find((t: ArtistWithRefsWithId) => t.id === artistId)?.tagIds.push(tagId); }) albumsTags.forEach((v: [number, number]) => { let [albumId, tagId] = v; albums.find((t: AlbumWithRefsWithId) => t.id === albumId)?.tagIds.push(tagId); }) artistsAlbums.forEach((v: [number, number]) => { let [albumId, artistId] = v; artists.find((t: ArtistWithRefsWithId) => t.id === artistId)?.albumIds.push(albumId); albums.find((t: AlbumWithRefsWithId) => t.id === albumId)?.artistIds.push(artistId); }) return { tracks: tracks, albums: albums, artists: artists, tags: tags, } } export async function importDB(userId: number, db: api.DBDataFormat, knex: Knex): Promise { // Store the ID mappings in this record. let maps: IDMappings = { tracks: {}, artists: {}, albums: {}, tags: {}, } // Insert items one by one, remapping the IDs as we go. for(const tag of db.tags) { let _tag = { ..._.omit(tag, 'id'), parentId: tag.parentId ? maps.tags[tag.parentId] : null, } maps.tags[tag.id] = await createTag(userId, _tag, knex); } for(const artist of db.artists) { maps.artists[artist.id] = await createArtist(userId, { ..._.omit(artist, 'id'), tagIds: artist.tagIds.map((id: number) => maps.tags[id]), trackIds: [], albumIds: [], }, knex); } for(const album of db.albums) { maps.albums[album.id] = await createAlbum(userId, { ..._.omit(album, 'id'), tagIds: album.tagIds.map((id: number) => maps.tags[id]), artistIds: album.artistIds.map((id: number) => maps.artists[id]), trackIds: [], }, knex); } for(const track of db.tracks) { maps.tracks[track.id] = await createTrack(userId, { ..._.omit(track, 'id'), tagIds: track.tagIds.map((id: number) => maps.tags[id]), artistIds: track.artistIds.map((id: number) => maps.artists[id]), albumId: track.albumId ? maps.albums[track.albumId] : null, }, knex); } return maps; } export async function wipeDB(userId: number, knex: Knex) { return await knex.transaction(async (trx) => { await Promise.all([ trx('tracks').where({ 'user': userId }).del(), trx('artists').where({ 'user': userId }).del(), trx('albums').where({ 'user': userId }).del(), trx('tags').where({ 'user': userId }).del(), ]) }) }