[HowTo] Migrate your music ratings from one Plex server to another

Use case

I wanted to run new Plex Media Server in Docker container for easier management and migration in the future. New server did not migrate old Plex files but scanned and re-index everything all over again. Plex was nice enough to sync all watch stats, but my music ratings was not synced. For this I needed a way to migrate all track ratings to the new plex server.

Solution

In a nutshell here is what needs to happen:

  • Turn off old Plex server
  • Copy the library database locally (or if it’s already local - for safety reasons). On my Synology, the location is: /volume1/PlexMediaServer/AppData/Plex Media Server/Plug-in Support/Database/com.plexapp.plugins.library.db.
  • Extract the rating data for each music track into csv file
  • Use a python script to rate all matching music tracks in the new Plex server

Things to note:

  • New plex server uses exactly the same data folder for my music as the old server (file names are the same)
  • The music folder path was changed on the new server, therefore filesystem paths to music tracks are not the same

What’s needed:

  • sqlite3 client
  • python3
  • plexapi (python library - pip install plexapi)

Step-by-step

An SQL query to retrieve rating data from old Plex library database (edit upon need):

select
	-- Rounding the half-star ratings
    CAST(IIF(mis.rating % 2 == 0, mis.rating, mis.rating + 1) AS INT) as 'Rating',
    -- Rewriting track file path to new Plex mount point
    replace(mp.file, '/old/plex/music-collection', '/new/plex/music/location') as 'File'
from media_items mi
    join media_parts mp on mi.id = mp.media_item_id
    join metadata_items mda on mi.metadata_item_id = mda.id
    join metadata_item_settings mis on mis.guid = mda.guid
    join accounts a on mis.account_id = a.id
    join library_sections ls on mda.library_section_id = ls.id
where
    a.name = 'username'         -- Which user ratings to get
    and ls.name = 'Music'       -- Which Plex library to query
    and mis.rating is not null  -- Get only tracks with rating
;

After edit the query to suit your need, remove the comments and make it a one liner command, so we could pass it in the sqlite3 CLI client easily:

sqlite3 -csv <path-to-library.db> "select CAST(IIF(mis.rating % 2 == 0, mis.rating, mis.rating + 1) AS INT) as 'Rating',replace(mp.file, '/old/plex/music-collection', '/new/plex/music/location') as 'File' from media_items mi join media_parts mp on mi.id = mp.media_item_id join metadata_items mda on mi.metadata_item_id = mda.id join metadata_item_settings mis on mis.guid = mda.guid join accounts a on mis.account_id = a.id join library_sections ls on mda.library_section_id = ls.id where a.name = 'username' and ls.name = 'Music' and mis.rating is not null;" > ratings.csv

Having the ratings.csv file, we already got all the data from the old Plex we need. It’s time to rate the tracks in the new Plex server. Here is a python script which will read the ratings.csv and rate each track in Plex which matches files in ratings.csv. Change the configuration variables as needed.

from plexapi.server import PlexServer
import csv

# Configuration variables
baseurl = 'http://localhost:32400'
token = 'PLEX_TOKEN'
ratingsPath = 'ratings.csv'

# Implementation details
db = {}
with open(ratingsPath) as csvfile:
    reader = csv.reader(csvfile, delimiter=',')
    for row in reader:
        rating = row[0]
        file = row[1]
        db[file] = rating

plextracks = PlexServer(baseurl, token).library.section('Music').searchTracks()
for track in plextracks:
    # Uncomment to clear all tracks rating before rate them.
    # track.rate(None)
    file = track.media[0].parts[0].file
    if file in db:
        track.rate(int(db[file]))
    else:
        print(file)

Execute via (assuming the script is named rating-migration.py and python executable is named python):

python rating-migration.py > not-rated.out

The script will print on stdout all unmatched tracks from the new Plex server.
Note: To obtain Plex Token check this guide.

That’s all - all tracks should be rated now.

Cheers!

Maybe I’m missing some detail from your use case but in my experience ratings just stored in metadata_item_settings so an export from source and import into target of that has given me all ratings/watch status etc… when moving from one server to another. Basically I’ve been doing this and never run into problems …

Export (source):

“/usr/lib/plexmediaserver/Plex Media Server” --sqlite com.plexapp.plugins.library.db “.output settings-views.sql” “.dump metadata_item_settings” “.dump metadata_item_views” “.dump accounts” “.dump devices” “.dump statistics_media” “.dump statistics_bandwidth”

Import (target):

“/usr/lib/plexmediaserver/Plex Media Server” --sqlite com.plexapp.plugins.library.db “.read settings-views.sql”

BTW… metadata_item_views, devices, statistics_media and statistics_bandwidth are needed to maintain dashboard stats.

Assumption: Libraries are setup the same AND everything is matched 100% between source & target.

That’s what’s different in my case - I keep the music files as they are but the directory path is different. I observed the GUIDs are also different therefore I concluded I need to rate each track fresh and mapping by filename as the only stable mapping between old/new server. That’s of course if I’m not missing some detail.

1 Like

Mmmm, metadata_item_settings does not contain media path information so that should not be a factor for ratings/watch status however …

will certainly mess things up as plex will see them as different if they change. Are you using Prefer local metadata for your music library and is your music tagged with musicbrainz picard including these tags …

Screenshot from 2023-01-09 18-35-36

… as plex (at least in my experience) will rematch these correctly giving them same guid when used. Just some food for thought.

Anyhow, was just curious on your post and am glad it all worked for you. Was a bit more simple for me.

Yes, I do use Prefer local metadata otherwise matching is a mess and I’ve tagged my music via beets but honestly, I tried to verify if those music brainz tags exist and it’s kind of a mess.

Here is the output of mediainfo (ommitting lyrics and acoustic ID):

General
Complete name                            : Adele - Hello.flac
Format                                   : FLAC
Format/Info                              : Free Lossless Audio Codec
File size                                : 29.2 MiB
Duration                                 : 4 min 55 s
Overall bit rate mode                    : Variable
Overall bit rate                         : 829 kb/s
Album                                    : 25
Album/Performer                          : Adele / Adele
Part                                     : 1
Part/Total                               : 1
Track name                               : Hello
Track name/Position                      : 1
Track name/Total                         : 14
Performer                                : Adele
Label                                    : Columbia
Genre                                    : Soul, Pop
Recorded date                            : 2015-11-20 / 2015
BPM                                      : 0
Cover                                    : Yes
Cover type                               : Cover (front)
Cover MIME                               : image/jpeg
ALBUMARTIST_CREDIT                       : Adele
ALBUMARTISTSORT                          : Adele
MUSICBRAINZ_ALBUMCOMMENT                 : Target exclusive
RELEASESTATUS                            : Official
MUSICBRAINZ_ALBUMSTATUS                  : Official
RELEASETYPE                              : album
MUSICBRAINZ_ALBUMTYPE                    : album
ARTIST_CREDIT                            : Adele
ARTISTSORT                               : Adele
ASIN                                     : B0189BE2QE
CATALOGNUMBER                            : 88875176782
COMPILATION                              : 0
RELEASECOUNTRY                           : US
DISCC                                    : 1
PUBLISHER                                : Columbia
MUSICBRAINZ_RELEASEGROUPID               : 5537624c-3d2f-4f5c-8099-df916082c85c
MEDIA                                    : CD
ORIGINALDATE                             : 2015-11-20
SCRIPT                                   : Latn
TRACK                                    : 1
TRACKC                                   : 14

Seems there are some tags visible, but certainly not all of the tags needed.

Then getting track info from beet info:

           album: 25
       albumartist: Adele
albumartist_credit: Adele
  albumartist_sort: Adele
     albumdisambig: Target exclusive
       albumstatus: Official
         albumtype: album
        albumtypes: album
          arranger:
               art: True
            artist: Adele
     artist_credit: Adele
       artist_sort: Adele
              asin: B0189BE2QE
          bitdepth: 16
           bitrate: 788787
      bitrate_mode:
               bpm: 0
        catalognum: 88875176782
       catalognums: 88875176782
          channels: 2
              comp: False
          composer:
     composer_sort:
           country: US
              date: 2015-11-20
               day: 20
              disc: 1
         disctitle:
         disctotal: 1
           encoder:
      encoder_info:
  encoder_settings:
            format: FLAC
             genre: Soul, Pop
            genres: Soul, Pop
          grouping:
             label: Columbia
          language: eng
         languages: eng
            length: 295.49333333333334
          lyricist:
  mb_albumartistid: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
 mb_albumartistids: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
        mb_albumid: b2dbd919-229f-41df-a6bf-eaa042c1■■■0
       mb_artistid: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
      mb_artistids: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
 mb_releasegroupid: 5537624c-3d2f-4f5c-8099-df916082c85c
 mb_releasetrackid:
        mb_trackid: 0a8e8d55-4b83-4f8a-9732-fbb5ded9f344
         mb_workid:
             media: CD
             month: 11
     original_date: 2015-11-20
      original_day: 20
    original_month: 11
     original_year: 2015
     rg_track_gain: -10.38
     rg_track_peak: 0.99884
        samplerate: 44100
            script: Latn
             title: Hello
             track: 1
        tracktotal: 14
              year: 2015

I see a lot more info here, including mb_ IDs. But is that info from the file itself or beets database - beats me.

Doing the same via ffprobe:

Input #0, flac, from 'Adele - Hello.flac':
  Metadata:
    ACOUSTID_FINGERPRINT: AQADtFSSLMo64YqP2od_9LiIp0mP5kJ__LhzHb_wH8mQMvvhF-UVfDg8LsGz46KOXkGjZsGV4_rxD7fxo_lR4_nw4EGoPEfCOEGt49sT1Deav_jwHDUdNM84_Cd-Cf6SMniFnhKa_sGTlMQfHv2I5sezD-8iI8wP7chz4VnK4edw92iK_iEeEUc4J4dq
HdGPH_0RXUPy48ePRzv6C66Yolaio06G5oee40L5nLhyBc2YHEfzHLW0CFqaTiizHD8
    ACOUSTID_ID     : 0710e87d-f324-4a30-9361-560185fb8469
    ALBUM           : 25
    ALBUM ARTIST    : Adele
    album_artist    : Adele
    ALBUMARTIST_CREDIT: Adele
    ALBUMARTISTSORT : Adele
    MUSICBRAINZ_ALBUMCOMMENT: Target exclusive
    RELEASESTATUS   : Official
    MUSICBRAINZ_ALBUMSTATUS: Official
    RELEASETYPE     : album
    MUSICBRAINZ_ALBUMTYPE: album
    ARTIST          : Adele
    ARTIST_CREDIT   : Adele
    ARTISTSORT      : Adele
    ASIN            : B0189BE2QE
    BPM             : 0
    CATALOGNUMBER   : 88875176782
    COMPILATION     : 0
    RELEASECOUNTRY  : US
    DATE            : 2015-11-20
    YEAR            : 2015
    disc            : 1
    DISCTOTAL       : 1
    DISCC           : 1
    TOTALDISCS      : 1
    GENRE           : Soul, Pop
    LABEL           : Columbia
    PUBLISHER       : Columbia
    LANGUAGE        : eng
    MUSICBRAINZ_ALBUMARTISTID: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
    MUSICBRAINZ_ALBUMID: b2dbd919-229f-41df-a6bf-eaa042c1■■■0
    MUSICBRAINZ_ARTISTID: cc2c9c3c-b7bc-4b8b-84d8-4fbd8779e493
    MUSICBRAINZ_RELEASEGROUPID: 5537624c-3d2f-4f5c-8099-df916082c85c
    MUSICBRAINZ_TRACKID: 0a8e8d55-4b83-4f8a-9732-fbb5ded9f344
    MEDIA           : CD
    ORIGINALDATE    : 2015-11-20
    REPLAYGAIN_TRACK_GAIN: -10.38 dB
    REPLAYGAIN_TRACK_PEAK: 0.998840
    SCRIPT          : Latn
    TITLE           : Hello
    track           : 1
    TRACKTOTAL      : 14
    TRACKC          : 14
    TOTALTRACKS     : 14

Here, we see the tags.

Finally, getting that info via id3v2 I got nothing. id3info also gives me nothing.

Adele - Hello.flac: No ID3 tag

All that seems like a mess to me. I will assume the tracks are tagged properly and ignore the manual checks I did to see if I can match track info by GUID. I’m open to check your solution. I will backup my database, delete all ratings from my library and do the export and migration. Will compare the ratings before and after migration. Will post back the results.

1 Like

Didn’t have the time to dig deep but my test didn’t show any positive result. Here is what I did:

  1. Export data from old Plex db
sqlite3 com.plexapp.plugins.library.db -cmd \
  ".output settings-views.sql" \
  ".dump metadata_item_settings" \
  ".dump metadata_item_views" \
  ".dump accounts" \
  ".dump devices" \
  ".dump statistics_media" \
  ".dump statistics_bandwidth"
  1. Stop new Plex server
  2. Import data (cleaning up tables first as there were already records in them):
sqlite3 com.plexapp.plugins.library.db -cmd \
  "DROP TABLE IF EXISTS metadata_item_settings" \
  "DROP TABLE IF EXISTS metadata_item_views" \
  "DROP TABLE IF EXISTS accounts" \
  "DROP TABLE IF EXISTS devices" \
  "DROP TABLE IF EXISTS statistics_media" \
  "DROP TABLE IF EXISTS statistics_bandwidth" \
  ".read settings-views.sql"
  1. Run Plex and verify ratings - nothing is shown as rated

Obviously there is a misalignment between old/new Plex metadata, but I’m not aware where that might be.

Maybe this is something to ask for? There is a valid point to also including syncing of ratings.

I will ask Engineering.

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.