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階層が疑似的に表示されるようになります。