Spring naar hoofdtekst

Songbook Pro backup ontleed

Geplaatst op door ,
Laatste aanpassing op .

Inleiding

Tot voor kort maakte ik gebruik van een zelfgebouwde webapplicatie om ChordPro muziekbestanden weer te geven op iPad en tablet-PC. Tijdens band­repetities en optredens is dat veel praktischer dan het meeslepen van dikke mappen met pakken papier die nooit op volgorde liggen. Toch had ook deze aanpak minimaal één nadeel…

Offline

Als mijn iPad of tablet geen internetverbinding had, kon ik de webapplicatie niet gebruiken. Ik was aangewezen op een WLAN ter plaatse of het gebruik maken van een mobiele hotspot. Toen we echter overstapten van analoge naar digitale geluidstechniek, moest mijn apparaat met het WLAN van de mixer worden verbonden – en dus niet meer met internet. Wat nu?

Songbook Pro

Er zijn een groot aantal apps die het ChordPro-formaat ondersteunen en offline functioneren. Ik koos uiteindelijk voor Songbook Pro, beschikbaar voor iOS, Android en Windows. Na het uitproberen van de testversie en de aankoop kon het grote importeren beginnen. Als eerste voegde ik mijn volledige discografie toe.

Liedjes (songs) worden in mappen (folders) ondergebracht en in sets gegroepeerd. Zo'n set heeft een naam en een datum. Jammer genoeg kon de kalender in de app alléén per maand bladeren - vanaf het huidige jaar gerekend. Mijn oudste opname dateert uit 2001, dus ik zou heel wat tijd kwijt zijn geweest aan het opzoeken van al die data…

Backup van binnen

Zoals het een degelijke applicatie betaamt, kunnen alle gegevens binnen Songbook Pro worden gebackupt naar een externe locatie. Zo kun je op z'n minst de app met alle data opnieuw inrichten, mocht dat nodig zijn. Of de app tussen meerdere apparaten synchroniseren. Ik was nieuwsgierig en onderzocht zowel het backup- (SongbookPro Backup.sbpbackup) als het synchronisatiebestand (sbp.sync).

Het zijn gecomprimeerde (ZIP-) bestanden, die onder elk gangbaar besturingssysteem kunnen worden geopend. Als er nog geen PDF-documenten in de app zijn geïmporteerd, zijn er slechts twee bestanden in het archief:

$ unzip -l sbp.sync;
Archive:  sbp.sync
  Length      Date    Time    Name
---------  ---------- -----   ----
   512518  2020-01-19 18:26   dataFile.txt
       32  2020-01-19 18:26   dataFile.hash
---------                     -------
   512550                     2 files

Aan de grootte van de beide bestanden is te zien, dat het .txt-bestand waarschijnlijke de data bevat en het .hash-bestand niet méér dan – waarempel! – een 32 byte (128 bit) hash is. Het eerste waar ik aan dacht was het MD5-algoritme; raak!

$ unzip sbp.sync;
Archive:  sbp.sync
  inflating: dataFile.txt
  inflating: dataFile.hash

$ cat dataFile.hash;
0ab9130e3e306b5f83c39a9671fc9a12

$ md5sum dataFile.txt;
0ab9130e3e306b5f83c39a9671fc9a12  dataFile.txt

dataFile.txt

Het databestand begint met (waarschijnlijk) een versienummer en een CRLF regeleinde. Daarop volgt, in één regel zonder regeleinde, een verzameling JSON-data:

1.0<CR><LF>
{"songs":[ /* whole bunch of JSON data... */ ]}

Hoe kon ik hierin nou mijn set-datums eenvoudig aanpassen?

Script

Ik zocht een manier om het geheel in leesbare vorm in mijn favoriete editor te kunnen openen en schreef onderstaand script. Het maakt gebruik van md5sum, unzip, zip en jq.

#!/usr/bin/env bash
#
# Take apart a SongbookPro backup- or sync-file, open it for editing
# in your favorite texteditor, then put it back together again so the
# app accepts it and can restore it properly.
#
# Saves to a date-stamped filename in the source directory.

EDITOR="$(which geany)";
MD5SUM="$(which md5sum)";
UNZIP="$(which unzip)";
ZIP="$(which zip)";
JQ="$(which jq)";

USAGE="Usage: ${0} (.sync|.sbpbackup)-file";

if [ ${#} -ne 1 ]; then
  echo "${USAGE}";
  exit 1;
fi

OLDPWD="$(pwd)";
INFILE="${1}";
WORKDIR="$(mktemp --directory)";

# Switch to working dir
cd "${WORKDIR}";

# Unzip
"${UNZIP}" "${INFILE}" 2>&1 >/dev/null;

# Capture original first line, remove trailing CR
FIRSTLINE="$(head -n 1 "dataFile.txt" | tr -d '\r')";

# Save second line to separate file
tail -n +2 "dataFile.txt" > "dataFile.txt.json";

# JSON-prettify
"${JQ}" -M '.' "dataFile.txt.json" > "dataFile.txt.tmp.json";

# Open up for editing
"${EDITOR}" "dataFile.txt.tmp.json";

# Truncate original file, add original first and new second line
echo -en "${FIRSTLINE}\r\n" > "dataFile.txt";
SECONDLINE="$( "${JQ}" -c '.' "dataFile.txt.tmp.json" )";
echo -n "${SECONDLINE}" >> "dataFile.txt";

# Calculate file hash
echo -en "$( "${MD5SUM}" "dataFile.txt" | cut -d' ' -f1 )" > "dataFile.hash";

# Rezip to unique filename
OUTFILE="$(basename "${INFILE}" )";
OUTEXT="${OUTFILE#*.}";
OUTFILE="${OUTFILE%.*}-$( date '+%F-%H-%M-%S' ).${OUTEXT}";
"${ZIP}" -r "$(dirname "${INFILE}" )"/"${OUTFILE}" . -x "dataFile.txt.json" "dataFile.txt.tmp.json" 2>&1 >/dev/null;

# Clean up
rm -r "${WORKDIR}";

# Back to old working dir, exit normally
cd "${OLDPWD}";
exit 0;

Poging in PHP

Na verloop van tijd kwam het idee om de collecties van Songbook Pro en mijn eigen app te synchroniseren. In elk geval wilde ik graag mijn app als een soort van backup achter de hand houden – voor het geval dat. Daarvoor moest ik dan wel de backup- of synchronisatiebestanden van Songbook Pro kunnen inlezen in PHP. Ik schreef het volgende script:

<?php
function extractData(string $filename)
{
  $zip = new \ZipArchive();
  if ($zip->open('sbp.sync') !== true) {
    return false;
  }
  for ($i = 0; $i < $zip->numFiles; $i++) {
    if ($zip->getNameIndex($i) == 'dataFile.txt') {

      // Get a resource pointer to the compressed datafile
      $ptr = $zip->getStream($zip->getNameIndex($i));

      // Read the uncompressed file and split it by CRLF
      $json = explode("\r\n", stream_get_contents($ptr));

      // Disregard first line, keep the second line
      $json = array_pop($json);

      // Decode the JSON into PHP objects
      $json = json_decode($json);
    }
  }
  $zip->close();
  return $json;
}

Jammer maar helaas

Toen ik eenmaal alle gegevens kon uitlezen, kwam ik een fundamenteel verschil tussen beide apps op het spoor. Mijn app heeft de volgende (boom-)structuur:

Book > Set > Song2Set > Song

In één Book zitten één of meerdere Sets. Meerdere Songs zijn via een koppeltabel Song2Set met Set verbonden. Songs kunnen dus aan meerdere sets worden toegevoegd.

Songbook Pro gebruikt daarentegen de volgende structuur:

Folder > Song
Set > Song

Songs worden rechtstreeks aan een Folder gekoppeld. Sets bestaan uit één of meerdere gekoppelde Songs. De Set- en Folder-koppeling staat los van elkaar.

Dit verschil in opbouw is niet zomaar te overbruggen. Ik zou ofwel mijn eigen app compleet moeten ombouwen, of de situatie accepteren en verder gaan met andere projecten. Ik koos voor dat laatste :-). Mijn inspanningen zijn zeker niet voor niets geweest; ik heb er een hoop van geleerd!

Inhoudsopgave

Atom-feed Atom-feed van FWiePs weblog

Artikelen


Categorieën

Doorzoek de onderstaande categorieën om de lijst met artikelen te filteren.