Exporting a gmusicbrowser queue

I was looking to brush up on my C and C++ skills and become acquainted with the GNU GCC compiler, and this was one of the first topics I chose.

The idea is, take the queue of songs from gmusicbrowser and write a shell script to copy each music file to the current directory, prepending the number position in queue to the file name.

This permits the user to place all the files on a thumb drive or other device, and have them play in the same order, when the playing device plays in order of an alphabetical sorting of the directory. Two examples of devices that do this are an Onkyo TX-8050 receiver and a Kenwood DPX-500BT head unit.

The general procedure is:

1. Open the gmbrc file, ~/.config/gmusicbrowser/gmbrc
2. Find the line with SongArray_Queue which lists the indices of each queue file
3. Fast forward to the songs database, beginning with [Songs]
4. Read each song, associating each file with an index, sorting along the way
5. Copy each file to the destination (or create a script that will)

First, the program in C++:

/*  This program 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.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

    Description: Take a gmbrc file and create a script to copy the queue files
       into the current directory, with position number in front.
    by Derek Yerger
    */

#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include "UriCodec.h"

using namespace std;

ifstream gmbrc;

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}


std::vector<std::string> split(const std::string &s, char delim) {
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}

string skiptonext(string searchstr)
{
    string line;
    while (getline(gmbrc, line)) {
        if (line.length() >= searchstr.length()) {
            if (line.substr(0,searchstr.length()) == searchstr) break;
        }
    }
    return(line);
}

int main()
{
    string line;
    string plist;
    string srcfile;
    // std::vector<std::string> listitems;
    // std::vector<long> listitemsidx;
    std::map<string,string> listitems, listitems2;
    // int n = 0;
    gmbrc.open ("/home/derek/.config/gmusicbrowser/gmbrc");
    if (gmbrc.is_open()) {
        line = skiptonext("SongArray_Queue");
        plist = line.substr(27) + " ";
        // cout << plist;
        skiptonext("[Songs]");
        while (getline(gmbrc, line)) {
            if (line.length() >= 7) {
                if (line.substr(0,7) == "[album]") break;
                std::vector<std::string> splt = split(line, 't');
                if (plist.find(' '+splt[0]+' ') != string::npos) {
                    srcfile = splt[21] + '/' + splt[10];
                    listitems[splt[0]] = UriDecode(srcfile);
                    listitems2[splt[0]] = UriDecode(splt[10]);
                }
            }
        }
    }
    gmbrc.close();
    ofstream batchfile;
    batchfile.open("gmbexport.sh");
    std::vector<std::string> splt = split(plist.substr(1, plist.length()-2), ' ');
    int counter = 100;
    for (std::vector<std::string>::iterator c = splt.begin() ; c!=splt.end(); ++c) {
        counter++;
        batchfile << "cp "" << listitems[*c] << "" "./" << counter << ' ' << listitems2[*c] << ""n";
    }
    batchfile << "~/Documents/scripts/old/flacmp3.shn";
    batchfile << "rm *.flacn";
    batchfile.close();
    return 0;
}

Execution time: 0.061s. Easy enough. I opted to create a script because writing or borrowing a file copy routine was out of the scope of this exercise. Note that at the end of the script, the flacmp3 tool is used to convert any flac files to mp3, since the aforementioned devices don’t support flac audio.

In this case, queue ordering is handled by creating a list from the song database containing only the queue songs (line 77), then go through the queue pulling our song index and filenames by using a string-indexed vector (line 90-93).

Then, the same program in C:

/*  This program 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.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

    Description: Take a gmbrc file and create a script to copy the queue files
       into the current directory, with position number in front.
    by Derek Yerger
    */

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

int main(void)
{
    FILE * fp;
    size_t len = 0;
    ssize_t read;
    int count = 0;
    char *line, *splt, *splt2, *path, *fname, *plist, *pcopy, *trk, *trk2;
    int j;
    fp = fopen("/home/derek/.config/gmusicbrowser/gmbrc", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);
    while (((read = getline(&line, &len, fp)) != -1) && (strncmp(line,"SongArray_Queue",15)!=0));
    plist = strdup(line+27);
    int strp = strlen(plist)-1;
    memset(plist+strp,32,1);
    printf("Song queue: [%s]", plist);
    while (((read = getline(&line, &len, fp)) != -1) && (strcmp(line,"[Songs]n")!=0));
    while (((read = getline(&line, &len, fp)) != -1) && (count >=0) && (strcmp(line,"[album]n")!=0)) {
           // printf("%s", line);
           int i = 0;
           char *linep = line;
           while ((splt = strsep(&linep, "t")) != NULL)
           {
                if (i == 0) {
                   trk=malloc(500);
                   trk[0]=' ';
                   trk[1]='\0';
                   strcat(trk,splt);
                   int tkpt = strlen(trk);
                   memset(trk+tkpt,32,1); tkpt++;
                   memset(trk+tkpt,0,1);
                   trk2=strdup(splt);
                   // printf("track=[%s]n ", trk);
                }
                if (i == 10) fname = strdup(splt);
                if (i == 21) {
                   path = strdup(splt);
                   if (strstr(plist, trk)) {
                        j=0;
                        pcopy=strdup(plist);
                        char *pcopyp = pcopy;
                        while ((splt2 = strsep(&pcopyp, " ")) != NULL)
                        {
                           if (strcmp(splt2,trk2)==0)
                                {
                                printf("[ %i ] ",j);
                                printf("%s/",path);
                                printf("%sn",fname);

                                }
                           j++;
                        }
                        free(pcopy);
                   }
                   free(fname);
                   free(path);
                }
                i++;
           }
           if (i>0) {free(trk); free(trk2);}
           count++;
        }
        fclose(fp);
        free(line);
        free(plist);
        free(splt);
        if (j>0) free(splt2);
        exit(EXIT_SUCCESS);
}

Note, dealing with strings is a bit trickier, and for someone who hasn’t written C for non-MCU’s in, well, ever, it took a bit of debugging and tracing to ensure every free() belonged to a malloc(). The tool valgrind was helpful for this. Sample output:

Song queue: [ 5551 5552 5545 5548 5553 5546 5549 5550 5554 5547 5539 5537 5536 5540 5535 5542 5541 5538 5543 ][ 15 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/05- Moshic - Do You Remember.mp3
[ 13 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/03- Moshic - The Clock Of Life (Chill mix).mp3
[ 12 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/02- Moshic - Blindness In Your Eyes.mp3
[ 18 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/08- Moshic - No Woman Can Beat You (Argonout Can Make Commercial mix).mp3
[ 11 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/01- Moshic - No Woman Can Beat You.mp3
[ 14 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/04- Moshic - I Remember You.mp3
[ 17 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/07- Moshic - Spin Away.mp3
[ 16 ] /home/derek/Music/Moshic - Argaman (V0)/CD2/06- Moshic - My Life Is For You.mp3
[ 19 ] /home/derek/Music/Moshic - Argaman (V0)/Continuous Mix/01- Moshic - Argaman Continuous Mix.mp3
[ 3 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/04- Moshic - The Persian Dancing Cat.mp3
[ 6 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/07- Moshic - Rams Horn Space.mp3
[ 10 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/11- Moshic - Souls Plug.mp3
[ 4 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/05- Moshic - Una Hamm.mp3
[ 7 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/08- Moshic - Clock Of Life.mp3
[ 8 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/09- Moshic - Orphaned Steps.mp3
[ 1 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/02- Moshic - Who Wants To Rule The World.mp3
[ 2 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/03- Moshic - Reality Illustrated.mp3
[ 5 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/06- Moshic - Argaman.mp3
[ 9 ] /home/derek/Music/Moshic - Argaman (V0)/CD1/10- Moshic - How Far So Long.mp3
==32313==
==32313== HEAP SUMMARY:
==32313== in use at exit: 0 bytes in 0 blocks
==32313== total heap usage: 22,253 allocs, 22,253 frees, 3,352,549 bytes allocated
==32313==
==32313== All heap blocks were freed -- no leaks are possible

Execution time 0.018s. Obviously the C version is a bit more limited, having skipped the sorting and script creation altogether. But this has been a great reintroduction to simple parsing in both languages, and the strengths and weaknesses of each.