Gerbera 1.11.0のインポートスクリプトをいじる

Gerbera 1.4.0相当までは、旧mediatombのインポートスクリプトがそのまま使えました。

しかし、Gerbera 1.11.0になってから、インポート方法が変わり、インポートそのものは今まで通りaddCdsObject()を使ってインポートするのですが、第二引数であるコンテナツリー指定の形式が変わったようで、今までは単なる配列を指定するだけでしたが、これからはいったんaddContainerTree()を使って変数にコンテナツリーを代入してから、addCdsObject()の第二引数にその変数を指定する方法に変わりました。

面倒そうな話ですが、こうなった理由はとても合理的で、複数値を持ったメタデータに対応するためです。
一番わかりやすいのがジャンル(genre)
音楽のジャンルが多様化しているので、テクノポップなんかはGENRE=["POP","TECNO","TECNO_POP"]みたいな感じになると思うんですが、今までだとこの曲は
Audio - Genre - POP TECNO TECNO_POP
となるか
Audio - Genre - POP
となって、TECNO以下切り捨てみたいになっていたと思います。
(ジャンルを例に出しましたが、それほどジャンル検索をしたことがないです。)

それを、
Audio - Genre - POP
Audio - Genre - TECNO
Audio - Genre - TECNO_POP
どこからでもアクセスできるようにする、という設計思想になっているようです。
理にかなっていますが、配列の中に配列を入れられるよう、コンテナツリーをaddContainerTree()を使って代入することになったようです。

更に、これらのコンテナツリーは、どのようなオブジェクトなのか、オブジェクトタイプを予め定義する必要があるようです。

んで、本題。
うちの音楽ライブラリは4つのフォルダ(ディレクトリ)に分けて格納しています。
gentoolinux.hatenablog.com
今回、HDD交換を機に、ディレクトリがツリー分下がっています。つまり、
/home/files/mp3/Album
/home/files/mp3/Single
/home/files/mp3/SuperEurobeat
/home/files/mp3/Wife
という構造に変えました。
この第4階層をAudioコンテナの次に表示し、DLNA上では
Audio - Album
Audio - Aingle
Audio - SuperEurobeat
Ausio - Wife
と、一つ階層を増やし、膨大な音楽ファイルを選びやすくしたいと思います。

これを実現するため、今まではimport.jsを改変していましたが、Gerbera 1. 11.0からはcommon.jsを改変する必要があります。
import.jsもcommon.jsも、1.11.0デフォルトのスクリプトとし、common.jsを、この1.11.0のデフォルトのものから改変していきます。
まずは、音楽ファイルのフルパスを変数tmpfilelocに格納します。パスが格納されているのはorigオブジェクトのlocationプロパティです。なので、
var tmpfileloc = orig.location;
そこから、/で分割した値をfilelocの変数に配列として代入します。
var fileloc = tmpfileloc.split('/');
最終的にディレクトリの第4階層をコンテナ名の変数u_pathに代入します。
var u_path = fileloc[4];

これらを、できるだけ音楽ファイルが読み込まれるインポートファンクションの前段に記載します。
今回、音楽ファイルのインポートはaddAudio()とaddAudioStructured()がありますが、今回はaddAudio()を使います。

// doc-add-audio-begin
function addAudio(obj)
の直後くらいに記述しました

// doc-add-audio-begin
function addAudio(obj) {
    // Note the difference between obj.title and obj.metaData[M_TITLE] -
    // while object.title will originally be set to the file name,
    // obj.metaData[M_TITLE] will contain the parsed title - in this
    // particular example the ID3 title of an MP3.
    var title = obj.title;
    var tmpfileloc = orig.location;
    var fileloc = tmpfileloc.split('/');
    var u_path = fileloc[4];

続いて、今までにないコンテナとなるわけなので、コンテナ定義を追加します。
コンテナの定義は
const chain = {
という変数代入に続いて、1行づつ定義されています。例えば
audio: { title: 'Audio', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: },
という定義は、Audioという固定名称のコンテナです。オブジェクトの内容によって変更されることはありません。
対して、
composer: { title: composer, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_COMPOSER, metaData:
, res: obj.res, aux: obj.aux, refID: obj.id },
は、作曲家名のコンテナになるので、オブジェクト(=音楽ファイル)によって、コンテナ名は変動します。composer変数に代入されている作曲家名がコンテナ名になります。どのオブジェクト(=音楽ファイル)の作曲家名をコンテナにするのか? を、後半のres: obj.res, aux: obj.aux, refID: obj.idで、定義しています。(多分obj.idが重要なのだと思う・・・。)
なので、先ほどのu_pathに代入されている値をコンテナ名=titleにします。
u_path: { title: u_path, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },

このあたりのコンテナ定義をさらすと

    // The UPnP class argument to addCdsObject() is optional, if it is
    // not supplied the default UPnP class will be used. However, it
    // is suggested to correctly set UPnP classes of containers and
    // objects - this information may be used by some renderers to
    // identify the type of the container and present the content in a
    // different manner.

    // Remember, the server will sort all items by ID3 track if the
    // container class is set to UPNP_CLASS_CONTAINER_MUSIC_ALBUM.

    const chain = {
        audio: { title: 'Audio', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [] },
        allAudio: { title: 'All Audio', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allArtists: { title: 'Artists', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allGenres: { title: 'Genres', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allAlbums: { title: 'Albums', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allYears: { title: 'Year', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allComposers: { title: 'Composers', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allSongs: { title: 'All Songs', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allFull: { title: 'All - full name', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        artist: { searchable: false, title: artist[0], objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ARTIST, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        album: { searchable: false, title: album, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ALBUM, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        genre: { title: genre, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_GENRE, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        year: { title: date, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        composer: { title: composer, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_COMPOSER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        u_path: { title: u_path, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },

   };

定義したら、いよいよaddContainerTree()にu_pathを定義していきます。順番としてはAll Audioの次に、第4ディレクトリを表示してもらいます。
例として、All Audioにパスのコンテナを追加します。

var container = addContainerTree([chain.audio, chain.u_path, chain.allAudio]);
addCdsObject(obj, container);

このあたりのデータベースを代入数するスクリプトを表示します。

    const chain = {
        audio: { title: 'Audio', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [] },
        allAudio: { title: 'All Audio', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allArtists: { title: 'Artists', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allGenres: { title: 'Genres', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allAlbums: { title: 'Albums', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allYears: { title: 'Year', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allComposers: { title: 'Composers', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allSongs: { title: 'All Songs', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        allFull: { title: 'All - full name', objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER },
        artist: { searchable: false, title: artist[0], objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ARTIST, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        album: { searchable: false, title: album, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_ALBUM, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        genre: { title: genre, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_GENRE, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        year: { title: date, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        composer: { title: composer, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER_MUSIC_COMPOSER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },
        u_path: { title: u_path, objectType: OBJECT_TYPE_CONTAINER, upnpclass: UPNP_CLASS_CONTAINER, metaData: [], res: obj.res, aux: obj.aux, refID: obj.id },

   };

    chain.audio.metaData[M_CONTENT_CLASS] = [ UPNP_CLASS_AUDIO_ITEM ];
    chain.album.metaData[M_ARTIST] = artist;
    chain.album.metaData[M_ALBUMARTIST] = artist;
    chain.album.metaData[M_GENRE] = [ genre ];
    chain.album.metaData[M_DATE] = obj.metaData[M_DATE];
    chain.album.metaData[M_UPNP_DATE] = obj.metaData[M_UPNP_DATE];
    chain.album.metaData[M_ALBUM] = [ album ];
    chain.artist.metaData[M_ARTIST] = artist;
    chain.artist.metaData[M_ALBUMARTIST] = artist;
    chain.genre.metaData[M_GENRE] = [ genre ];
    chain.year.metaData[M_DATE] = [ date ];
    chain.year.metaData[M_UPNP_DATE] = [ date ];
    chain.composer.metaData[M_COMPOSER] = [ composer ];
//    chain.u_path.metaData[M_U_PATH] = [ u_path ];

    var container = addContainerTree([chain.audio, chain.u_path, chain.allAudio]);
    addCdsObject(obj, container);

    container = addContainerTree([chain.audio, chain.u_path, chain.allArtists, chain.artist, chain.allSongs]);
    addCdsObject(obj, container);

    var temp = '';
    if (artist_full) {
        temp = artist_full;
    }

    if (album_full) {
        temp = temp + ' - ' + album_full + ' - ';
    } else {
        temp = temp + ' - ';
    }

    obj.title = temp + title;
    container = addContainerTree([chain.audio, chain.u_path, chain.allFull]);
    addCdsObject(obj, container);

    const artCnt = artist.length;
    var i;
    for (i = 0; i < artCnt; i++) {
        chain.artist.title = artist[i];
        container = addContainerTree([chain.audio, chain.u_path, chain.allArtists, chain.artist, chain.allFull]);
        addCdsObject(obj, container);
    }

    obj.title = track + title;
    for (i = 0; i < artCnt; i++) {
        chain.artist.title = artist[i];
        chain.artist.searchable = true;
        container = addContainerTree([chain.audio, chain.u_path, chain.allArtists, chain.artist, chain.album]);
        addCdsObject(obj, container);
    }

    chain.album.searchable = true;
    container = addContainerTree([chain.audio, chain.u_path, chain.allAlbums, chain.album]);
    obj.title = track + title;
    addCdsObject(obj, container);

    chain.genre.searchable = true;
    if (obj.metaData[M_GENRE]) {
        for (var oneGenre in obj.metaData[M_GENRE]) {
            chain.genre.title = obj.metaData[M_GENRE][oneGenre];
            chain.genre.metaData[M_GENRE] = [ oneGenre ];
            container = addContainerTree([chain.audio, chain.u_path, chain.allGenres, chain.genre]);
            addCdsObject(obj, container);
        }
    }

    container = addContainerTree([chain.audio, chain.u_path, chain.allYears, chain.year]);
    addCdsObject(obj, container);

    container = addContainerTree([chain.audio, chain.u_path, chain.allComposers, chain.composer]);
    addCdsObject(obj, container);
}
// doc-add-audio-end

これで、Audioの下に、フォルダ第4階層が疑似的に表示されるようになります。