1858 lines
80 KiB
C++
1858 lines
80 KiB
C++
/*
|
|
* Copyright (C) 2020 Emeric Poupon
|
|
*
|
|
* This file is part of LMS.
|
|
*
|
|
* LMS is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* LMS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with LMS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Migration.hpp"
|
|
|
|
#include <Wt/Dbo/WtSqlTraits.h>
|
|
|
|
#include "core/Exception.hpp"
|
|
#include "core/ILogger.hpp"
|
|
#include "core/ITraceLogger.hpp"
|
|
|
|
#include "database/Session.hpp"
|
|
#include "database/objects/ScanSettings.hpp"
|
|
|
|
#include "Db.hpp"
|
|
#include "Utils.hpp"
|
|
|
|
namespace lms::db
|
|
{
|
|
namespace
|
|
{
|
|
static constexpr Version LMS_DATABASE_VERSION{ 105 };
|
|
}
|
|
|
|
VersionInfo::VersionInfo()
|
|
: _version{ LMS_DATABASE_VERSION }
|
|
{
|
|
}
|
|
|
|
VersionInfo::pointer VersionInfo::getOrCreate(Session& session)
|
|
{
|
|
session.checkWriteTransaction();
|
|
|
|
pointer versionInfo{ utils::fetchQuerySingleResult(session.getDboSession()->find<VersionInfo>()) };
|
|
if (!versionInfo)
|
|
return session.getDboSession()->add(std::make_unique<VersionInfo>());
|
|
|
|
return versionInfo;
|
|
}
|
|
|
|
VersionInfo::pointer VersionInfo::get(Session& session)
|
|
{
|
|
session.checkReadTransaction();
|
|
|
|
return utils::fetchQuerySingleResult(session.getDboSession()->find<VersionInfo>());
|
|
}
|
|
} // namespace lms::db
|
|
|
|
namespace lms::db::Migration
|
|
{
|
|
class ScopedNoForeignKeys
|
|
{
|
|
public:
|
|
ScopedNoForeignKeys(Db& db)
|
|
: _db{ db }
|
|
{
|
|
_db.executeSql("PRAGMA foreign_keys=OFF");
|
|
}
|
|
~ScopedNoForeignKeys()
|
|
{
|
|
_db.executeSql("PRAGMA foreign_keys=ON");
|
|
}
|
|
|
|
private:
|
|
ScopedNoForeignKeys(const ScopedNoForeignKeys&) = delete;
|
|
ScopedNoForeignKeys(ScopedNoForeignKeys&&) = delete;
|
|
ScopedNoForeignKeys& operator=(const ScopedNoForeignKeys&) = delete;
|
|
ScopedNoForeignKeys& operator=(ScopedNoForeignKeys&&) = delete;
|
|
|
|
Db& _db;
|
|
};
|
|
|
|
namespace
|
|
{
|
|
void dropIndexes(Session& session)
|
|
{
|
|
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
|
std::vector<std::string> indexeNames{ utils::fetchQueryResults(session.getDboSession()->query<std::string>(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
|
|
for (const auto& indexName : indexeNames)
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX " + indexName);
|
|
}
|
|
|
|
void migrateFromV33(Session& session)
|
|
{
|
|
// remove name from track_artist_link
|
|
// Drop Auth mode
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "track_artist_link_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"type" integer not null,
|
|
"track_id" bigint,
|
|
"artist_id" bigint,
|
|
constraint "fk_track_artist_link_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_artist_link_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
|
|
);
|
|
))");
|
|
utils::executeCommand(*session.getDboSession(), "INSERT INTO track_artist_link_backup SELECT id, version, type, track_id, artist_id FROM track_artist_link");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track_artist_link");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link_backup RENAME TO track_artist_link");
|
|
}
|
|
|
|
void migrateFromV34(Session& session)
|
|
{
|
|
// Add scrobbling state
|
|
// By default, everything needs to be sent
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_artist ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/ 0)) + ")");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_release ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/ 0)) + ")");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_track ADD scrobbling_state INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*ScrobblingState::PendingAdd*/ 0)) + ")");
|
|
}
|
|
|
|
void migrateFromV35(Session& session)
|
|
{
|
|
// Add creattion/last modif date time for tracklists
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE tracklist ADD creation_date_time TEXT");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE tracklist ADD last_modified_date_time TEXT");
|
|
}
|
|
|
|
void migrateFromV36(Session& session)
|
|
{
|
|
// Increased precision for track durations (now in milliseconds instead of secodns)
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV37(Session& session)
|
|
{
|
|
// Support Performer tags (via subtypes)
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link ADD subtype TEXT");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV38(Session& session)
|
|
{
|
|
// migrate release-specific tags from Track to Release
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD total_disc INTEGER");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"track_number" integer,
|
|
"disc_number" integer,
|
|
"total_track" integer,
|
|
"disc_subtitle" text not null,
|
|
"name" text not null,
|
|
"duration" integer,
|
|
"date" text,
|
|
"original_date" text,
|
|
"file_path" text not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"has_cover" boolean not null,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"track_replay_gain" real,
|
|
"release_replay_gain" real,
|
|
"release_id" bigint,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
|
);
|
|
))");
|
|
utils::executeCommand(*session.getDboSession(), "INSERT INTO track_backup SELECT id, version, scan_version, track_number, disc_number, total_track, disc_subtitle, name, duration, date, original_date, file_path, file_last_write, file_added, has_cover, mbid, recording_mbid, copyright, copyright_url, track_replay_gain, release_replay_gain, release_id FROM track");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV39(Session& session)
|
|
{
|
|
// add release type
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD primary_type INTEGER");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD secondary_types INTEGER");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV40(Session& session)
|
|
{
|
|
// add artist_display_name in Release and Track
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD artist_display_name TEXT NOT NULL DEFAULT ''");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD artist_display_name TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV41(Session& session)
|
|
{
|
|
// add artist_display_name in Release and Track
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user RENAME COLUMN subsonic_transcode_format TO subsonic_default_transcode_format");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user RENAME COLUMN subsonic_transcode_bitrate TO subsonic_default_transcode_bitrate");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user DROP COLUMN subsonic_transcode_enable");
|
|
}
|
|
|
|
void migrateFromV42(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS listen_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS listen_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS listen_user_track_scrobbler_date_time_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_artist_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_artist_artist_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_release_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_release_release_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_track_user_scrobbler_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS starred_track_track_user_scrobbler_idx");
|
|
|
|
// New feedback service that now handles the star/unstar stuff (that was previously handled by the scrobbling service)
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user RENAME COLUMN scrobbler TO scrobbling_backend");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD feedback_backend INTEGER");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE listen RENAME COLUMN scrobbler TO backend");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE listen RENAME COLUMN scrobbling_state TO sync_state");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_artist RENAME COLUMN scrobbler TO backend");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_artist RENAME COLUMN scrobbling_state TO sync_state");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_release RENAME COLUMN scrobbler TO backend");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_release RENAME COLUMN scrobbling_state TO sync_state");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_track RENAME COLUMN scrobbler TO backend");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE starred_track RENAME COLUMN scrobbling_state TO sync_state");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE user SET feedback_backend = scrobbling_backend");
|
|
}
|
|
|
|
void migrateFromV43(Session& session)
|
|
{
|
|
// add counts in genre table
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE cluster ADD track_count INTEGER");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE cluster ADD release_count INTEGER");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV44(Session& session)
|
|
{
|
|
// add bitrate
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD bitrate INTEGER NOT NULL DEFAULT 0");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV45(Session& session)
|
|
{
|
|
// add subsonic_enable_transcoding_by_default, default is disabled
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD subsonic_enable_transcoding_by_default INTEGER NOT NULL DEFAULT(" + std::to_string(static_cast<int>(/*User::defaultSubsonicEnableTranscodingByDefault*/ 0)) + ")");
|
|
}
|
|
|
|
void migrateFromV46(Session& session)
|
|
{
|
|
// add extra tags to parse
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "cluster_type_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null
|
|
);)");
|
|
utils::executeCommand(*session.getDboSession(), "INSERT INTO cluster_type_backup SELECT id, version, name FROM cluster_type");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE cluster_type");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE cluster_type_backup RENAME TO cluster_type");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN extra_tags_to_scan TEXT");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV47(Session& session)
|
|
{
|
|
// release type, new way
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release DROP primary_type");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release DROP secondary_types");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_type" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_release_type" (
|
|
"release_type_id" bigint,
|
|
"release_id" bigint,
|
|
primary key ("release_type_id", "release_id"),
|
|
constraint "fk_release_release_type_key1" foreign key ("release_type_id") references "release_type" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_release_release_type_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_release_type_release_type" on "release_release_type" ("release_type_id"))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_release_type_release" on "release_release_type" ("release_id"))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV48(Session& session)
|
|
{
|
|
// Regression for the extra tags not being parsed
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV49(Session& session)
|
|
{
|
|
// Add year / originalYear fields, as date / originalDate are not enough (we don't want a wrong date but year or nothing)
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD year INTEGER");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD original_year INTEGER");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV50(Session& session)
|
|
{
|
|
// MediaLibrary support
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "media_library" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"path" text not null,
|
|
"name" text not null
|
|
))");
|
|
|
|
const int scanSettingsId{ session.getDboSession()->query<int>("SELECT id FROM scan_settings") };
|
|
|
|
// Convert the existing media_directory in the scan_settings table to a media_library with id '1'
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO "media_library" ("id", "version", "path", "name")
|
|
SELECT 1, 0, s_s.media_directory, "Main"
|
|
FROM scan_settings s_s
|
|
WHERE id = ?)",
|
|
scanSettingsId);
|
|
|
|
// Remove the outdated column in scan_settings
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings DROP media_directory");
|
|
|
|
// Add the media_library column in tracks, with id '1'
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"track_number" integer,
|
|
"disc_number" integer,
|
|
"total_track" integer,
|
|
"disc_subtitle" text not null,
|
|
"name" text not null,
|
|
"duration" integer,
|
|
"bitrate" integer not null,
|
|
"date" text,
|
|
"year" integer,
|
|
"original_date" text,
|
|
"original_year" integer,
|
|
"file_path" text not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"has_cover" boolean not null,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"track_replay_gain" real,
|
|
"release_replay_gain" real,
|
|
"artist_display_name" text not null,
|
|
"release_id" bigint,
|
|
"media_library_id" bigint,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data, with the new media_library_id field set to 1
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO track_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
scan_version,
|
|
track_number,
|
|
disc_number,
|
|
total_track,
|
|
disc_subtitle,
|
|
name,
|
|
duration,
|
|
COALESCE(bitrate, 0),
|
|
date,
|
|
year,
|
|
original_date,
|
|
original_year,
|
|
file_path,
|
|
file_last_write,
|
|
file_added,
|
|
has_cover,
|
|
mbid,
|
|
recording_mbid,
|
|
copyright,
|
|
copyright_url,
|
|
track_replay_gain,
|
|
release_replay_gain,
|
|
artist_display_name,
|
|
release_id,
|
|
1
|
|
FROM track)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
}
|
|
|
|
void migrateFromV51(Session& session)
|
|
{
|
|
// Add custom artist tag delimiters, no need to rescan since it has no effect when empty
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD artist_tag_delimiters TEXT NOT NULL DEFAULT ''");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD default_tag_delimiters TEXT NOT NULL DEFAULT ''");
|
|
}
|
|
|
|
void migrateFromV52(Session& session)
|
|
{
|
|
// Add sort name for releases
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD sort_name TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV53(Session& session)
|
|
{
|
|
// Add release group mbid
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD group_mbid TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV54(Session& session)
|
|
{
|
|
// Add file size + relative file path
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track RENAME COLUMN file_path TO absolute_file_path");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD file_size BIGINT NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD relative_file_path TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV55(Session& session)
|
|
{
|
|
// Add bitsPerSample, channelCount and sampleRate
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD bits_per_sample INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD channel_count INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD sample_rate INTEGER NOT NULL DEFAULT(0)");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV56(Session& session)
|
|
{
|
|
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
|
dropIndexes(session);
|
|
}
|
|
|
|
void migrateFromV57(Session& session)
|
|
{
|
|
// useless index, may have been already removed in the previous step
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS cluster_name_idx");
|
|
}
|
|
|
|
void migrateFromV58(Session& session)
|
|
{
|
|
// DSF support
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_file_extensions = audio_file_extensions || ' .dsf'");
|
|
}
|
|
|
|
void migrateFromV59(Session& session)
|
|
{
|
|
// Dedicated image table
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "image" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"path" text not null,
|
|
"stem" text not null,
|
|
"file_last_write" text,
|
|
"file_size" integer not null,
|
|
"width" integer not null,
|
|
"height" integer not null,
|
|
"artist_id" bigint,
|
|
constraint "fk_image_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV60(Session& session)
|
|
{
|
|
// Dedicated directory table
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "directory" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_path" text not null,
|
|
"name" text not null,
|
|
"parent_directory_id" bigint,
|
|
constraint "fk_directory_directory" foreign key ("parent_directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Add a ref in track, need to recreate a new table
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"track_number" integer,
|
|
"disc_number" integer,
|
|
"total_track" integer,
|
|
"disc_subtitle" text not null,
|
|
"name" text not null,
|
|
"duration" integer,
|
|
"bitrate" integer not null,
|
|
"bits_per_sample" integer not null,
|
|
"channel_count" integer not null,
|
|
"sample_rate" integer not null,
|
|
"date" text,
|
|
"year" integer,
|
|
"original_date" text,
|
|
"original_year" integer,
|
|
"absolute_file_path" text not null,
|
|
"relative_file_path" text not null,
|
|
"file_size" bigint not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"has_cover" boolean not null,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"track_replay_gain" real,
|
|
"release_replay_gain" real,
|
|
"artist_display_name" text not null,
|
|
"release_id" bigint,
|
|
"media_library_id" bigint,
|
|
"directory_id" bigint,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
// Migrate data, with the new directory_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO track_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
scan_version,
|
|
track_number,
|
|
disc_number,
|
|
total_track,
|
|
disc_subtitle,
|
|
name,
|
|
duration,
|
|
bitrate,
|
|
bits_per_sample,
|
|
channel_count,
|
|
sample_rate,
|
|
date,
|
|
year,
|
|
original_date,
|
|
original_year,
|
|
absolute_file_path,
|
|
relative_file_path,
|
|
file_size,
|
|
file_last_write,
|
|
file_added,
|
|
has_cover,
|
|
mbid,
|
|
recording_mbid,
|
|
copyright,
|
|
copyright_url,
|
|
track_replay_gain,
|
|
release_replay_gain,
|
|
artist_display_name,
|
|
release_id,
|
|
media_library_id,
|
|
NULL
|
|
FROM track)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
|
|
// Add a ref in image + rename path to absolute_file_path, need to recreate a new table
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "image_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"stem" text not null,
|
|
"file_last_write" text,
|
|
"file_size" integer not null,
|
|
"width" integer not null,
|
|
"height" integer not null,
|
|
"artist_id" bigint,
|
|
"directory_id" bigint,
|
|
constraint "fk_image_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_image_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data, with the new directory_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO image_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
path,
|
|
stem,
|
|
file_last_write,
|
|
file_size,
|
|
width,
|
|
height,
|
|
artist_id,
|
|
NULL
|
|
FROM image
|
|
)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE image");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE image_backup RENAME TO image");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void migrateFromV61(Session& session)
|
|
{
|
|
// Added a media_library_id in Directory
|
|
utils::executeCommand(*session.getDboSession(), R"(
|
|
CREATE TABLE IF NOT EXISTS "directory_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_path" text not null,
|
|
"name" text not null,
|
|
"parent_directory_id" bigint,
|
|
"media_library_id" bigint,
|
|
constraint "fk_directory_parent_directory" foreign key ("parent_directory_id") references "directory" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_directory_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data, with the new directory_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO directory_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
absolute_path,
|
|
name,
|
|
parent_directory_id,
|
|
NULL
|
|
FROM directory)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE directory");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE directory_backup RENAME TO directory");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV62(Session& session)
|
|
{
|
|
// Add a new column comment
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD comment TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV63(Session& session)
|
|
{
|
|
// Add a rated entities
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "rated_artist" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"rating" integer not null,
|
|
"last_updated" text,
|
|
"artist_id" bigint,
|
|
"user_id" bigint,
|
|
constraint "fk_rated_artist_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_rated_artist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "rated_release" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"rating" integer not null,
|
|
"last_updated" text,
|
|
"release_id" bigint,
|
|
"user_id" bigint,
|
|
constraint "fk_rated_release_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_rated_release_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "rated_track" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"rating" bigint not null,
|
|
"last_updated" text,
|
|
"track_id" bigint,
|
|
"user_id" bigint,
|
|
constraint "fk_rated_track_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_rated_track_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Drop badly named index, will be recreated
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS listen_user_backend_date_time");
|
|
}
|
|
|
|
void migrateFromV64(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "label" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_label" (
|
|
"label_id" bigint,
|
|
"release_id" bigint,
|
|
primary key ("label_id", "release_id"),
|
|
constraint "fk_release_label_key1" foreign key ("label_id") references "label" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_release_label_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_label_label" on "release_label" ("label_id"))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_label_release" on "release_label" ("release_id"))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV65(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD is_compilation BOOLEAN NOT NULL DEFAULT(false)");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV66(Session& session)
|
|
{
|
|
// New way of handling UI settings
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "ui_state" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"item" text not null,
|
|
"value" text not null,
|
|
"user_id" bigint,
|
|
constraint "fk_ui_state_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user DROP COLUMN repeat_all");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user DROP COLUMN radio");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user DROP COLUMN cur_playing_track_pos");
|
|
}
|
|
|
|
void migrateFromV67(Session& session)
|
|
{
|
|
// Add a ref to release in image
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE "image_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"stem" text not null,
|
|
"file_last_write" text,
|
|
"file_size" integer not null,
|
|
"width" integer not null,
|
|
"height" integer not null,
|
|
"artist_id" bigint,
|
|
"release_id" bigint,
|
|
"directory_id" bigint,
|
|
constraint "fk_image_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_image_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_image_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data, with the new release_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO image_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
absolute_file_path,
|
|
stem,
|
|
file_last_write,
|
|
file_size,
|
|
width,
|
|
height,
|
|
artist_id,
|
|
NULL,
|
|
directory_id
|
|
FROM image
|
|
)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE image");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE image_backup RENAME TO image");
|
|
|
|
// Changed some indexes for the image table -> remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
|
std::vector<std::string> indexeNames{ utils::fetchQueryResults(session.getDboSession()->query<std::string>(R"(SELECT name FROM sqlite_master WHERE type = 'index' AND name LIKE '%_idx')")) };
|
|
for (const auto& indexName : indexeNames)
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX " + indexName);
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV68(Session& session)
|
|
{
|
|
// Changed the way we ref images from release and artists (several releases and artist can now share the same image)
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE "release_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"sort_name" text not null,
|
|
"mbid" text not null,
|
|
"group_mbid" text not null,
|
|
"total_disc" integer,
|
|
"artist_display_name" text not null,
|
|
"is_compilation" boolean not null,
|
|
"image_id" bigint,
|
|
constraint "fk_release_image" foreign key ("image_id") references "image" ("id") on delete set null deferrable initially deferred))");
|
|
|
|
// Migrate data, with the new image_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO release_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
name,
|
|
sort_name,
|
|
mbid,
|
|
group_mbid,
|
|
total_disc,
|
|
COALESCE(artist_display_name, ""),
|
|
is_compilation,
|
|
NULL
|
|
FROM release
|
|
)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE release");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release_backup RENAME TO release");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "artist_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"sort_name" text not null,
|
|
"mbid" text not null,
|
|
"image_id" bigint,
|
|
constraint "fk_artist_image" foreign key ("image_id") references "image" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data, with the new image_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artist_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
name,
|
|
sort_name,
|
|
mbid,
|
|
NULL
|
|
FROM artist
|
|
)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE artist");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_backup RENAME TO artist");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV69(Session& session)
|
|
{
|
|
// Add a field in UI settings
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD COLUMN ui_artist_release_sort_method NOT NULL DEFAULT 7"); // 7 = ReleaseSortMethod::OriginalDateDesc
|
|
}
|
|
|
|
void migrateFromV70(Session& session)
|
|
{
|
|
// Add a file name/stem in tracks
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD COLUMN file_stem TEXT NOT NULL DEFAULT ''");
|
|
|
|
// New table TrackLyrics
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_lyrics" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"stem" text not null,
|
|
"file_last_write" text,
|
|
"file_size" integer not null,
|
|
"lines" text not null,
|
|
"language" text not null,
|
|
"offset" integer,
|
|
"display_artist" text not null,
|
|
"display_title" text not null,
|
|
"synchronized" boolean not null,
|
|
"track_id" bigint,
|
|
"directory_id" bigint,
|
|
constraint "fk_track_lyrics_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_lyrics_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV71(Session& session)
|
|
{
|
|
// Add a file name/stem in tracks
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playqueue" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"current_index" integer not null,
|
|
"current_position_in_track" integer,
|
|
"last_modified_date_time" text,
|
|
"user_id" bigint,
|
|
constraint "fk_playqueue_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playqueue_track" (
|
|
"playqueue_id" bigint,
|
|
"track_id" bigint not null,
|
|
primary key ("playqueue_id", "track_id"),
|
|
constraint "fk_playqueue_track_key1" foreign key ("playqueue_id") references "playqueue" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_playqueue_track_key2" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "playqueue_track_playqueue" on "playqueue_track" ("playqueue_id"))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "playqueue_track_track" on "playqueue_track" ("track_id"))");
|
|
}
|
|
|
|
void migrateFromV72(Session& session)
|
|
{
|
|
// Add catalog number
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD barcode TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV73(Session& session)
|
|
{
|
|
// Remove any trailing '/' in library paths
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE media_library SET path = rtrim(path, '/') WHERE path LIKE '%/'");
|
|
}
|
|
|
|
void migrateFromV74(Session& session)
|
|
{
|
|
// New auth token authentication for Subsonic API
|
|
// Previous tokens are not usable any more, no problem since they are just used for the ui's "remember me" feature
|
|
utils::executeCommand(*session.getDboSession(), "DELETE FROM auth_token");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE auth_token ADD domain TEXT NOT NULL");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE auth_token ADD use_count INTEGER NOT NULL");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE auth_token ADD last_used TEXT");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE auth_token ADD max_use_count INTEGER");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS auth_token_user_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS auth_token_expiry_idx");
|
|
utils::executeCommand(*session.getDboSession(), "DROP INDEX IF EXISTS auth_token_value_idx");
|
|
}
|
|
|
|
void migrateFromV75(Session& session)
|
|
{
|
|
// Added a new option to set the bcrypt count to be use to hash user's passwords
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD bcrypt_round_count INTEGER NOT NULL DEFAULT(7)");
|
|
}
|
|
|
|
void migrateFromV76(Session& session)
|
|
{
|
|
// Rename public -> visibility (false is Private(0) and true is Public(1))
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE tracklist RENAME COLUMN public TO visibility");
|
|
|
|
// Supported extensions are now runtime
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings DROP COLUMN audio_file_extensions");
|
|
|
|
// Add PlayListFile
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "playlist_file" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"file_stem" text not null,
|
|
"file_size" bigint not null,
|
|
"file_last_write" text,
|
|
"name" text not null,
|
|
"entries" text not null,
|
|
"media_library_id" bigint,
|
|
"directory_id" bigint,
|
|
constraint "fk_playlist_file_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_playlist_file_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Add a link into tracklist to ease cleanup
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "tracklist_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"type" integer not null,
|
|
"visibility" integer not null,
|
|
"creation_date_time" text,
|
|
"last_modified_date_time" text,
|
|
"user_id" bigint,
|
|
"playlist_file_id" bigint,
|
|
constraint "fk_tracklist_user" foreign key ("user_id") references "user" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_tracklist_playlist_file" foreign key ("playlist_file_id") references "playlist_file" ("id") on delete cascade deferrable initially deferred))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO tracklist_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
name,
|
|
type,
|
|
visibility,
|
|
creation_date_time,
|
|
last_modified_date_time,
|
|
user_id,
|
|
NULL AS playlist_file_id
|
|
FROM tracklist)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(DROP TABLE tracklist)");
|
|
utils::executeCommand(*session.getDboSession(), R"(ALTER TABLE tracklist_backup RENAME TO tracklist)");
|
|
|
|
// Add a file name in tracks
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD COLUMN file_name TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV77(Session& session)
|
|
{
|
|
// added new scan settings: skip single release playlists (default value is conservative, no need to rescan)
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN skip_single_release_playlists BOOLEAN NOT NULL DEFAULT(FALSE)");
|
|
}
|
|
|
|
void migrateFromV78(Session& session)
|
|
{
|
|
// added advisory tag support
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track ADD COLUMN advisory INTEGER NOT NULL DEFAULT(0)"); // 0 means unset
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV79(Session& session)
|
|
{
|
|
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
|
dropIndexes(session);
|
|
|
|
// New partial date/time support
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN year");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN original_year");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV80(Session& session)
|
|
{
|
|
// Add release comment support
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release ADD COLUMN comment TEXT NOT NULL DEFAULT ''");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV81(Session& session)
|
|
{
|
|
// Make sure we remove all the previoulsy created index, the createIndexesIfNeeded will recreate them all
|
|
dropIndexes(session);
|
|
|
|
// Add country + release country
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "country" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_country" (
|
|
"country_id" bigint,
|
|
"release_id" bigint,
|
|
primary key ("country_id", "release_id"),
|
|
constraint "fk_release_country_key1" foreign key ("country_id") references "country" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_release_country_key2" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_country" on "release_country" ("country_id"))");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE INDEX "release_country_release" on "release_country" ("release_id"))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV82(Session& session)
|
|
{
|
|
// New setting to display inline artist relationships in the release view
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD COLUMN ui_enable_inline_artist_relationships BOOLEAN NOT NULL DEFAULT(false)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE user ADD COLUMN ui_inline_artist_relationships BIGINT NOT NULL DEFAULT(68)"); // Composer + Performer
|
|
}
|
|
|
|
void migrateFromV83(Session& session)
|
|
{
|
|
// New embedded track image handling
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN has_cover");
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_embedded_image" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"hash" text not null,
|
|
"size" integer not null,
|
|
"width" integer not null,
|
|
"height" integer not null,
|
|
"mime_type" text not null
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_embedded_image_link" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"index" integer not null,
|
|
"is_preferred" boolean not null,
|
|
"type" integer not null,
|
|
"description" text not null,
|
|
"track_id" bigint,
|
|
"track_embedded_image_id" bigint,
|
|
constraint "fk_track_embedded_image_link_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_embedded_image_link_track_embedded_image" foreign key ("track_embedded_image_id") references "track_embedded_image" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV84(Session& session)
|
|
{
|
|
// New artist info feature
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "artist_info" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"file_last_write" text,
|
|
"type" text not null,
|
|
"gender" text not null,
|
|
"disambiguation" text not null,
|
|
"biography" text not null,
|
|
"directory_id" bigint,
|
|
"artist_id" bigint,
|
|
constraint "fk_artist_info_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_artist_info_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV85(Session& session)
|
|
{
|
|
dropIndexes(session);
|
|
|
|
// Artist merging feature
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN allow_mbid_artist_merge BOOLEAN DEFAULT(false)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link ADD COLUMN artist_name TEXT NULL DEFAULT('')");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link ADD COLUMN artist_sort_name TEXT NULL DEFAULT('')");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_artist_link ADD COLUMN artist_mbid_matched BOOLEAN NOT NULL DEFAULT(false)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_info ADD COLUMN name TEXT NULL DEFAULT('')");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_info ADD COLUMN sort_name TEXT NULL DEFAULT('')");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_info ADD COLUMN mbid_matched BOOLEAN NOT NULL DEFAULT(false)");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET scan_version = scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV86(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN name TEXT NOT NULL DEFAULT('')");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings RENAME COLUMN scan_version TO audio_scan_version");
|
|
}
|
|
|
|
void migrateFromV87(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN artists_to_not_split TEXT NOT NULL DEFAULT('')");
|
|
}
|
|
|
|
void migrateFromV88(Session& session)
|
|
{
|
|
// Badly populated mbid_matched fields for tracks and artist info: need to rescan everything
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_scan_version = audio_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV89(Session& session)
|
|
{
|
|
// ArtistInfo need to be force rescanned: introduced a field for this
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN artist_info_scan_version INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_info ADD COLUMN scan_version INTEGER NOT NULL DEFAULT(0)");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET artist_info_scan_version = artist_info_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV90(Session& session)
|
|
{
|
|
// Added a fallback for missing name, using parent directory name
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET artist_info_scan_version = artist_info_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV91(Session& session)
|
|
{
|
|
dropIndexes(session);
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_embedded_image_link DROP COLUMN is_preferred");
|
|
}
|
|
|
|
void migrateFromV92(Session& session)
|
|
{
|
|
// Create the new artwork table
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "artwork" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"track_embedded_image_id" bigint,
|
|
"image_id" bigint,
|
|
constraint "fk_artwork_track_embedded_image" foreign key ("track_embedded_image_id") references "track_embedded_image" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_artwork_image" foreign key ("image_id") references "image" ("id") on delete cascade deferrable initially deferred))");
|
|
|
|
// Replaced image by artwork for release
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"sort_name" text not null,
|
|
"mbid" text not null,
|
|
"group_mbid" text not null,
|
|
"total_disc" integer,
|
|
"artist_display_name" text not null,
|
|
"is_compilation" boolean not null,
|
|
"barcode" text not null,
|
|
"comment" text not null,
|
|
"preferred_artwork_id" bigint,
|
|
constraint "fk_release_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred))");
|
|
// Migrate data, with the new preferred_artwork_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO release_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
name,
|
|
sort_name,
|
|
mbid,
|
|
group_mbid,
|
|
total_disc,
|
|
COALESCE(artist_display_name, ''),
|
|
is_compilation,
|
|
barcode,
|
|
comment,
|
|
NULL as preferred_artwork_id
|
|
FROM release)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE release");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE release_backup RENAME TO release");
|
|
|
|
// Replaced image by artwork for track
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"track_number" integer,
|
|
"disc_number" integer,
|
|
"total_track" integer,
|
|
"disc_subtitle" text not null,
|
|
"name" text not null,
|
|
"duration" integer,
|
|
"bitrate" integer not null,
|
|
"bits_per_sample" integer not null,
|
|
"channel_count" integer not null,
|
|
"sample_rate" integer not null,
|
|
"date" text,
|
|
"original_date" text,
|
|
"absolute_file_path" text not null,
|
|
"relative_file_path" text not null,
|
|
"file_stem" text not null,
|
|
"file_name" text not null,
|
|
"file_size" bigint not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"advisory" integer not null,
|
|
"track_replay_gain" real,
|
|
"release_replay_gain" real,
|
|
"artist_display_name" text not null,
|
|
"comment" text not null,
|
|
"release_id" bigint,
|
|
"media_library_id" bigint,
|
|
"directory_id" bigint,
|
|
"preferred_artwork_id" bigint,
|
|
"preferred_media_artwork_id" bigint,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_preferred_media_artwork" foreign key ("preferred_media_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
// Migrate data, with the new preferred_artwork_id and preferred_media_artwork_id fields set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO track_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
scan_version,
|
|
track_number,
|
|
disc_number,
|
|
total_track,
|
|
disc_subtitle,
|
|
name,
|
|
duration,
|
|
bitrate,
|
|
bits_per_sample,
|
|
channel_count,
|
|
sample_rate,
|
|
date,
|
|
original_date,
|
|
absolute_file_path,
|
|
relative_file_path,
|
|
file_stem,
|
|
file_name,
|
|
file_size,
|
|
file_last_write,
|
|
file_added,
|
|
mbid,
|
|
recording_mbid,
|
|
copyright,
|
|
copyright_url,
|
|
advisory,
|
|
track_replay_gain,
|
|
release_replay_gain,
|
|
artist_display_name,
|
|
comment,
|
|
release_id,
|
|
media_library_id,
|
|
directory_id,
|
|
NULL as preferred_artwork_id,
|
|
NULL as preferred_media_artwork_id
|
|
FROM track)");
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
|
|
// Replaced image by artwork for artist
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "artist_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"sort_name" text not null,
|
|
"mbid" text not null,
|
|
"preferred_artwork_id" bigint,
|
|
constraint "fk_artist_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
// Migrate data, with the new preferred_artwork_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artist_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
name,
|
|
sort_name,
|
|
mbid,
|
|
NULL as preferred_artwork_id
|
|
FROM artist)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE artist");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE artist_backup RENAME TO artist");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan everything
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_scan_version = audio_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV93(Session& session)
|
|
{
|
|
// add artist_image_fallback_to_release in ScanSettings
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN artist_image_fallback_to_release BOOLEAN NOT NULL DEFAULT(false)");
|
|
}
|
|
|
|
void migrateFromV94(Session& session)
|
|
{
|
|
// Removed not that useful columns in track
|
|
dropIndexes(session);
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN relative_file_path");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN file_stem");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track DROP COLUMN file_name");
|
|
}
|
|
|
|
void migrateFromV95(Session& session)
|
|
{
|
|
// Make sure each image and each embessed image has an artwork object
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artwork (version, track_embedded_image_id)
|
|
SELECT 1 AS version, tei.id
|
|
FROM track_embedded_image tei
|
|
LEFT JOIN artwork art ON tei.id = art.track_embedded_image_id
|
|
WHERE art.track_embedded_image_id IS NULL)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO artwork (version, image_id)
|
|
SELECT 1 AS version, img.id
|
|
FROM image img
|
|
LEFT JOIN artwork art ON img.id = art.image_id
|
|
WHERE art.image_id IS NULL)");
|
|
}
|
|
|
|
void migrateFromV96(Session& session)
|
|
{
|
|
// Removed not that useful indexes
|
|
dropIndexes(session);
|
|
}
|
|
|
|
void migrateFromV97(Session& session)
|
|
{
|
|
// Removed not that useful indexes
|
|
dropIndexes(session);
|
|
}
|
|
|
|
void migrateFromV98(Session& session)
|
|
{
|
|
// Medium support
|
|
dropIndexes(session);
|
|
|
|
// New table for mediums
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "medium" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"name" text not null,
|
|
"position" integer,
|
|
"track_count" integer,
|
|
"media" text not null,
|
|
"replay_gain" real,
|
|
"release_id" bigint,
|
|
"preferred_artwork_id" bigint,
|
|
constraint "fk_medium_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_medium_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
|
|
// New primary key in track -> need to recreate the table
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"track_number" integer,
|
|
"name" text not null,
|
|
"duration" integer,
|
|
"bitrate" integer not null,
|
|
"bits_per_sample" integer not null,
|
|
"channel_count" integer not null,
|
|
"sample_rate" integer not null,
|
|
"date" text,
|
|
"original_date" text,
|
|
"absolute_file_path" text not null,
|
|
"file_size" bigint not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"advisory" integer not null,
|
|
"replay_gain" real,
|
|
"artist_display_name" text not null,
|
|
"comment" text not null,
|
|
"medium_id" bigint,
|
|
"release_id" bigint,
|
|
"media_library_id" bigint,
|
|
"directory_id" bigint,
|
|
"preferred_artwork_id" bigint,
|
|
"preferred_media_artwork_id" bigint,
|
|
constraint "fk_track_medium" foreign key ("medium_id") references "medium" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_preferred_media_artwork" foreign key ("preferred_media_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred
|
|
))");
|
|
|
|
// Migrate data:
|
|
// - removed disc_number, total_track, disc_subtitle, release_replay_gain
|
|
// - renamed track_replay_gain to replay_gain
|
|
// - with the new medium_id field set to null
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO track_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
scan_version,
|
|
track_number,
|
|
name,
|
|
duration,
|
|
bitrate,
|
|
bits_per_sample,
|
|
channel_count,
|
|
sample_rate,
|
|
date,
|
|
original_date,
|
|
absolute_file_path,
|
|
file_size,
|
|
file_last_write,
|
|
file_added,
|
|
mbid,
|
|
recording_mbid,
|
|
copyright,
|
|
copyright_url,
|
|
advisory,
|
|
track_replay_gain AS replay_gain,
|
|
artist_display_name,
|
|
comment,
|
|
NULL as medium_id,
|
|
release_id,
|
|
media_library_id,
|
|
directory_id,
|
|
preferred_artwork_id,
|
|
preferred_media_artwork_id
|
|
FROM track)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan all audio files
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_scan_version = audio_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV99(Session& session)
|
|
{
|
|
// Podcast support
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE image ADD COLUMN mime_type TEXT NOT NULL DEFAULT ''");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "podcast" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"url" text not null,
|
|
"delete_requested" boolean not null,
|
|
"title" text not null,
|
|
"link" text not null,
|
|
"description" text not null,
|
|
"language" text not null,
|
|
"copyright" text not null,
|
|
"last_build_date" text,
|
|
"author" text not null,
|
|
"category" text not null,
|
|
"explicit" boolean not null,
|
|
"image_url" text not null,
|
|
"owner_email" text not null,
|
|
"owner_name" text not null,
|
|
"subtitle" text not null,
|
|
"summary" text not null,
|
|
"artwork_id" bigint,
|
|
constraint "fk_podcast_artwork" foreign key ("artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "podcast_episode" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"manual_download_state" integer not null,
|
|
"audio_relative_file_path" text not null,
|
|
"title" text not null,
|
|
"link" text not null,
|
|
"description" text not null,
|
|
"author" text not null,
|
|
"category" text not null,
|
|
"enclosure_url" text not null,
|
|
"enclosure_content_type" text not null,
|
|
"enclosure_size" integer not null,
|
|
"pub_date" text,
|
|
"image_url" text not null,
|
|
"subtitle" text not null,
|
|
"summary" text not null,
|
|
"explicit" boolean not null,
|
|
"duration" integer,
|
|
"artwork_id" bigint,
|
|
"podcast_id" bigint,
|
|
constraint "fk_podcast_episode_artwork" foreign key ("artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_podcast_episode_podcast" foreign key ("podcast_id") references "podcast" ("id") on delete cascade deferrable initially deferred))");
|
|
}
|
|
|
|
void migrateFromV100(Session& session)
|
|
{
|
|
// Add container and codec to track table + made bits_per_sample optional
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_backup" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"scan_version" integer not null,
|
|
"absolute_file_path" text not null,
|
|
"file_size" bigint not null,
|
|
"file_last_write" text,
|
|
"file_added" text,
|
|
"duration" integer,
|
|
"container" integer not null,
|
|
"codec" integer not null,
|
|
"bitrate" integer not null,
|
|
"channel_count" integer not null,
|
|
"sample_rate" integer not null,
|
|
"bits_per_sample" integer,
|
|
"replay_gain" real,
|
|
"track_number" integer,
|
|
"name" text not null,
|
|
"date" text,
|
|
"original_date" text,
|
|
"mbid" text not null,
|
|
"recording_mbid" text not null,
|
|
"copyright" text not null,
|
|
"copyright_url" text not null,
|
|
"artist_display_name" text not null,
|
|
"comment" text not null,
|
|
"advisory" integer not null,
|
|
"medium_id" bigint,
|
|
"release_id" bigint,
|
|
"media_library_id" bigint,
|
|
"directory_id" bigint,
|
|
"preferred_artwork_id" bigint,
|
|
"preferred_media_artwork_id" bigint,
|
|
constraint "fk_track_medium" foreign key ("medium_id") references "medium" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_media_library" foreign key ("media_library_id") references "media_library" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_directory" foreign key ("directory_id") references "directory" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_track_preferred_artwork" foreign key ("preferred_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred,
|
|
constraint "fk_track_preferred_media_artwork" foreign key ("preferred_media_artwork_id") references "artwork" ("id") on delete set null deferrable initially deferred))");
|
|
|
|
// Copy data from track to track_backup
|
|
utils::executeCommand(*session.getDboSession(), R"(INSERT INTO track_backup
|
|
SELECT
|
|
id,
|
|
version,
|
|
scan_version,
|
|
absolute_file_path,
|
|
file_size,
|
|
file_last_write,
|
|
file_added,
|
|
duration,
|
|
0 as container,
|
|
0 as codec,
|
|
bitrate,
|
|
channel_count,
|
|
sample_rate,
|
|
CASE WHEN bits_per_sample = 0 THEN NULL ELSE bits_per_sample END as bits_per_sample,
|
|
replay_gain,
|
|
track_number,
|
|
name,
|
|
date,
|
|
original_date,
|
|
mbid,
|
|
recording_mbid,
|
|
copyright,
|
|
copyright_url,
|
|
artist_display_name,
|
|
comment,
|
|
advisory,
|
|
medium_id,
|
|
release_id,
|
|
media_library_id,
|
|
directory_id,
|
|
preferred_artwork_id,
|
|
preferred_media_artwork_id
|
|
FROM track)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "DROP TABLE track");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE track_backup RENAME TO track");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan all audio files
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_scan_version = audio_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV101(Session& session)
|
|
{
|
|
// Add audio properties for podcast episodes
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD container INTEGER NOT NULL DEFAULT(0)"); // 0 means unknown
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD codec INTEGER NOT NULL DEFAULT(0)"); // 0 means unknown
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD bitrate INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD channel_count INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD sample_rate INTEGER NOT NULL DEFAULT(0)");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE podcast_episode ADD bits_per_sample INTEGER");
|
|
}
|
|
|
|
void migrateFromV102(Session& session)
|
|
{
|
|
// New link to artists from releases
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "release_artist_link" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"artist_name" text not null,
|
|
"artist_sort_name" text not null,
|
|
"artist_mbid_matched" boolean not null,
|
|
"release_id" bigint,
|
|
"artist_id" bigint,
|
|
constraint "fk_release_artist_link_release" foreign key ("release_id") references "release" ("id") on delete cascade deferrable initially deferred,
|
|
constraint "fk_release_artist_link_artist" foreign key ("artist_id") references "artist" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
// Remove all artist links where the link type is TrackArtistLinkType::ReleaseArtist = 8
|
|
utils::executeCommand(*session.getDboSession(), R"(DELETE FROM track_artist_link WHERE type = 8)");
|
|
|
|
// Remove outdated UI state entries
|
|
utils::executeCommand(*session.getDboSession(), R"(DELETE FROM ui_state WHERE item = 'artists_link_type')");
|
|
|
|
// Just increment the scan version of the settings to make the next scan rescan all audio files
|
|
utils::executeCommand(*session.getDboSession(), "UPDATE scan_settings SET audio_scan_version = audio_scan_version + 1");
|
|
}
|
|
|
|
void migrateFromV103(Session& session)
|
|
{
|
|
// Drop previous track_audio_features with a brand new table dedicated to embeddings
|
|
utils::executeCommand(*session.getDboSession(), R"(DROP TABLE track_features)");
|
|
|
|
utils::executeCommand(*session.getDboSession(), R"(CREATE TABLE IF NOT EXISTS "track_musicnn_embeddings" (
|
|
"id" integer primary key autoincrement,
|
|
"version" integer not null,
|
|
"data" blob not null,
|
|
"track_id" bigint,
|
|
constraint "fk_track_musicnn_embeddings_track" foreign key ("track_id") references "track" ("id") on delete cascade deferrable initially deferred
|
|
))");
|
|
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings RENAME COLUMN similarity_engine_type TO recommendation_engine_type");
|
|
utils::executeCommand(*session.getDboSession(), "ALTER TABLE scan_settings ADD COLUMN musicnn_model_identifier TEXT NOT NULL DEFAULT ''");
|
|
}
|
|
|
|
void migrateFromV104(Session& session)
|
|
{
|
|
utils::executeCommand(*session.getDboSession(), R"(ALTER TABLE "playlist_file" ADD COLUMN "preferred_artwork_id" bigint)");
|
|
utils::executeCommand(*session.getDboSession(), R"(ALTER TABLE "playlist_file" ADD COLUMN "cover_image_file" text NOT NULL DEFAULT '')");
|
|
}
|
|
|
|
bool doDbMigration(Session& session)
|
|
{
|
|
constexpr std::string_view outdatedMsg{ "Outdated database, please rebuild it (delete the .db file and restart)" };
|
|
|
|
ScopedNoForeignKeys noPragmaKeys{ static_cast<Db&>(session.getDb()) };
|
|
|
|
using MigrationFunction = std::function<void(Session&)>;
|
|
|
|
const std::map<unsigned, MigrationFunction> migrationFunctions{
|
|
{ 33, migrateFromV33 },
|
|
{ 34, migrateFromV34 },
|
|
{ 35, migrateFromV35 },
|
|
{ 36, migrateFromV36 },
|
|
{ 37, migrateFromV37 },
|
|
{ 38, migrateFromV38 },
|
|
{ 39, migrateFromV39 },
|
|
{ 40, migrateFromV40 },
|
|
{ 41, migrateFromV41 },
|
|
{ 42, migrateFromV42 },
|
|
{ 43, migrateFromV43 },
|
|
{ 44, migrateFromV44 },
|
|
{ 45, migrateFromV45 },
|
|
{ 46, migrateFromV46 },
|
|
{ 47, migrateFromV47 },
|
|
{ 48, migrateFromV48 },
|
|
{ 49, migrateFromV49 },
|
|
{ 50, migrateFromV50 },
|
|
{ 51, migrateFromV51 },
|
|
{ 52, migrateFromV52 },
|
|
{ 53, migrateFromV53 },
|
|
{ 54, migrateFromV54 },
|
|
{ 55, migrateFromV55 },
|
|
{ 56, migrateFromV56 },
|
|
{ 57, migrateFromV57 },
|
|
{ 58, migrateFromV58 },
|
|
{ 59, migrateFromV59 },
|
|
{ 60, migrateFromV60 },
|
|
{ 61, migrateFromV61 },
|
|
{ 62, migrateFromV62 },
|
|
{ 63, migrateFromV63 },
|
|
{ 64, migrateFromV64 },
|
|
{ 65, migrateFromV65 },
|
|
{ 66, migrateFromV66 },
|
|
{ 67, migrateFromV67 },
|
|
{ 68, migrateFromV68 },
|
|
{ 69, migrateFromV69 },
|
|
{ 70, migrateFromV70 },
|
|
{ 71, migrateFromV71 },
|
|
{ 72, migrateFromV72 },
|
|
{ 73, migrateFromV73 },
|
|
{ 74, migrateFromV74 },
|
|
{ 75, migrateFromV75 },
|
|
{ 76, migrateFromV76 },
|
|
{ 77, migrateFromV77 },
|
|
{ 78, migrateFromV78 },
|
|
{ 79, migrateFromV79 },
|
|
{ 80, migrateFromV80 },
|
|
{ 81, migrateFromV81 },
|
|
{ 82, migrateFromV82 },
|
|
{ 83, migrateFromV83 },
|
|
{ 84, migrateFromV84 },
|
|
{ 85, migrateFromV85 },
|
|
{ 86, migrateFromV86 },
|
|
{ 87, migrateFromV87 },
|
|
{ 88, migrateFromV88 },
|
|
{ 89, migrateFromV89 },
|
|
{ 90, migrateFromV90 },
|
|
{ 91, migrateFromV91 },
|
|
{ 92, migrateFromV92 },
|
|
{ 93, migrateFromV93 },
|
|
{ 94, migrateFromV94 },
|
|
{ 95, migrateFromV95 },
|
|
{ 96, migrateFromV96 },
|
|
{ 97, migrateFromV97 },
|
|
{ 98, migrateFromV98 },
|
|
{ 99, migrateFromV99 },
|
|
{ 100, migrateFromV100 },
|
|
{ 101, migrateFromV101 },
|
|
{ 102, migrateFromV102 },
|
|
{ 103, migrateFromV103 },
|
|
{ 104, migrateFromV104 },
|
|
};
|
|
|
|
bool migrationPerformed{};
|
|
{
|
|
LMS_SCOPED_TRACE_OVERVIEW("Database", "Migration");
|
|
auto transaction{ session.createWriteTransaction() };
|
|
|
|
Version version;
|
|
try
|
|
{
|
|
version = VersionInfo::getOrCreate(session)->getVersion();
|
|
LMS_LOG(DB, INFO, "Database version = " << version << ", LMS binary version = " << LMS_DATABASE_VERSION);
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
LMS_LOG(DB, ERROR, "Cannot get database version info: " << e.what());
|
|
throw core::LmsException{ outdatedMsg };
|
|
}
|
|
|
|
if (version > LMS_DATABASE_VERSION)
|
|
throw core::LmsException{ "Server binary outdated, please upgrade it to handle this database" };
|
|
|
|
if (version < migrationFunctions.begin()->first)
|
|
throw core::LmsException{ outdatedMsg };
|
|
|
|
while (version < LMS_DATABASE_VERSION)
|
|
{
|
|
LMS_SCOPED_TRACE_DETAILED("Database", "MigrationStep");
|
|
LMS_LOG(DB, INFO, "Migrating database from version " << version << " to " << version + 1 << "...");
|
|
|
|
auto itMigrationFunc{ migrationFunctions.find(version) };
|
|
if (itMigrationFunc == std::cend(migrationFunctions))
|
|
throw core::LmsException{ "No code found to upgrade database!" };
|
|
|
|
itMigrationFunc->second(session);
|
|
|
|
VersionInfo::get(session).modify()->setVersion(++version);
|
|
|
|
LMS_LOG(DB, INFO, "Migration complete to version " << version);
|
|
migrationPerformed = true;
|
|
}
|
|
}
|
|
|
|
return migrationPerformed;
|
|
}
|
|
} // namespace lms::db::Migration
|