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