0

My PC is half broken (Ubuntu 24.04.01). I can still access the file system. Luckily I was able transfer all the music files.

But I had put stars on those songs in Rhythmbox (took 2 - 3 days to do). And using them I could select the 4 and 5 stars in "My Top Rated" list.

I found this: Transferring music ratings from rhythmbox to gmusicbrowser

and copied over ~/.local/share/rhythmbox/rhythmdb.xml file to the new machine.

I did a diff (meld) between the old rhythmdb.xml and the new one and it is not a simple copy over.

Any suggestions on how to proceed?

JohnA
  • 119

1 Answers1

0

Wrote a script to copy the ratings over from a previous version of rhythmdb.xml.

In short, it copies over the rating field from the old previous rhythmdb.xml file for each song in the current xml file.

NOTE: Backup your xml files BEFORE you run any of these!

  • ensure RhythmBox is not running. It may interact with the db while this script is modifying it.

  • run ./do_db_get_current ; do only once

  • copy the previous rhythmdb.xml file to this directory; name it "rhythmdb-prev.xml"

  • at this point, these files should exist:

    • rhythmdb-curr.xml: the current db file with no ratings in it
    • rhythmdb-prev.xml: the previous db file with ratings in it
  • run ./doit

    • check rhythmdb-update.xml exists
    • check the contents of rhythmdb-update.xml for expected ratings
    • you can use ./doit --verbose to see a bit more information about the XML content
  • run ./do_db_update

  • start RhythmBox and check ratings are as expected

Here's the bash script ./do_db_get_current

#! /usr/bin/env bash

CURR_FILE=rhythmdb-curr.xml if [ -f "$CURR_FILE" ]; then echo "$CURR_FILE already exists. skipping" else echo "Copying rhythmdb.xml..." cp ~/.local/share/rhythmbox/rhythmdb.xml "$CURR_FILE" fi

import xml.etree.ElementTree as ET


# --------------------
## holds all app related functions
class App:
    # --------------------
    ## constructor
    def __init__(self):
        ## holds previous music data using location as a key (has rating field)
        self._prev_data = {}
        ## holds current music data using location as a key (does not have rating field)
        self._curr_data = {}

    # --------------------
    ## load data and generate an updated rhythmdb.xml
    #
    # @return None
    def run(self):
        curr_path = 'rhythmdb-curr.xml'
        prev_path = 'rhythmdb-prev.xml'
        updt_path = 'rhythmdb-update.xml'

        self._load_info('Current', curr_path, self._curr_data)
        self._load_info('Previous', prev_path, self._prev_data)

        updt_tree = ET.parse(curr_path)
        for entry in updt_tree.iter('entry'):
            if entry.attrib['type'] != 'song':
                continue

            location = entry.find('location')
            prev_entry = self._prev_data[location.text]
            prev_rating = prev_entry.find('rating')

            # it's a song
            rating = ET.SubElement(entry, 'rating')
            rating.text = prev_rating.text
        updt_tree.write(updt_path)

    # --------------------
    ## load the given XML file and save it in the given dict
    #
    # @param tag    logging tag
    # @param path   the XML file to load
    # @param info   the dict to fill
    # @return None
    def _load_info(self, tag, path, info):
        tree = ET.parse(path)
        root = tree.getroot()

        radios = 0
        ignores = 0
        unknowns = 0
        songs = 0
        rated = 0
        duplicates = 0
        for idx, entry in enumerate(root.findall('entry')):
            if entry.attrib['type'] == 'iradio':
                radios += 1
                continue
            if entry.attrib['type'] == 'ignore':
                ignores += 1
                continue
            if entry.attrib['type'] != 'song':
                unknowns += 1
                continue

            songs += 1
            title = entry.find('title')

            rating = entry.find('rating')
            if rating is None:
                rating = 'none'
            else:
                rating = rating.text
                rated += 1

            location = entry.find('location')
            if location in info:
                duplicates += 1
                print(f'{idx}) Duplicate title: {title.text} {rating}')
                print(f'{idx})   loc1 : {location.text}')
                loc2 = self._curr_data[location].find('location')
                print(f'{idx})   loc2 : {loc2.text}')
            else:
                # print(f'{idx}) Song title     : {title.text} {rating}')
                info[location.text] = entry

        print(f'{tag} data:')
        print(f'   radios    : {radios: >4}')
        print(f'   ignores   : {ignores: >4}')
        print(f'   songs     : {songs: >4}')
        print(f'   rated     : {rated: >4}')
        print(f'   duplicates: {duplicates: >4}')
        print(f'   unknowns  : {unknowns: >4}')
        print(f'   #keys     : {len(info): >4}')

Here's the ./do_db_update script

#! /usr/bin/env bash

UPDT_FILE=rhythmdb-update.xml if [ -f "$UPDT_FILE" ]; then echo "Updating rhythmdb.xml from $UPDT_FILE" cp "$UPDT_FILE" ~/.local/share/rhythmbox/rhythmdb.xml else echo "$UPDT_FILE does not exist. Did you run ./doit?" fi

JohnA
  • 119