genre/artist/title
⇒
genre/artist/album/title
ジャンル下のフォルダ構成の変更の手法
前の記事
◇RaspiにminiDLNAをソースから入れる
ではジャンル下とアーティスト・リストしたのmusicAtristレコードを交換しました。
これを、交換ではなくアーティスト・リストしたの構成をジャンル下に複製する方法とします。
前回の手法
今回の手法
今回はジャンル下のmusicAtristの下のmusicTrackレコードを一旦全て削除します。
アーティスト・リスト下のmusicAtristのmusicAlbum/misicTrackを複製し、ジャンル下のmusicAtristに繋ぎます。
実際のコード
置き換えはDB作成途中ではなく完成後に行うようにしました。
scanner.cにあるstart_scanner()メソッドの最後に追加しました。
// scanner.c
extern void dup_musicAlbum(void);
void
start_scanner(void)
{
struct media_dir_s *media_path;
char path[MAXPATHLEN];
・・・
//JM: Set up a db version number, so we know if we need to rebuild due to a new structure.
sql_exec(db, "pragma user_version = %d;", DB_VERSION);
//
dup_musicAlbum();
DPRINTF(E_WARN, L_GENERAL, "create DB done... may be.\n");
}
#define DBG_SQL 0
#define STR_BUF_LEN 512
char SQL[STR_BUF_LEN];
char NAME[STR_BUF_LEN+6];
// シングル・クオート,%をエスケープする.
const char* escape(const char*text){
if( !strchr(text,'\'')&&!strchr(text,'%') ) return text;
int idx = 0;
char* dst = NAME;
const char* src=text;
for(idx=0; idx<STR_BUF_LEN && *src;++idx,++src ){
dst[idx]=*src;
if( *src=='\'') dst[++idx]='\'';// SQLエスケープ
else if( *src=='%') dst[++idx] ='%';// formatエスケープ
}
dst[idx]=0;
return NAME;
}
// OBJECTSレコードのカラム
#define REC_SIZE 7
#define ID 0
#define OBJECT_ID 1
#define PARENT_ID 2
#define REF_ID 3
#define CLASS 4
#define DETAIL_ID 5
#define NAME 6
void dup_musicAlbum(void){
DPRINTF(E_WARN, L_GENERAL, "duplicate musicAlbums under folder(atrist) to under folder(genre)\n");
//-----------------------------------------------------
// ジャンルの下の"全ての曲"以外のmusicTrackを削除する
//-----------------------------------------------------
sql_exec(db,"delete from OBJECTS"
" where CLASS='item.audioItem.musicTrack'"
" and PARENT_ID like '1$5$%%'"
" and PARENT_ID not like '%%$0';");
//-----------------------------------------------------
// Folder(Artist)下のmusicArtistレコードを全て得る
//-----------------------------------------------------
snprintf(SQL,STR_BUF_LEN
,"select * from OBJECTS"
" where CLASS='container.person.musicArtist'"
" and PARENT_ID='1$6';");
if(DBG_SQL) DPRINTF(E_WARN, L_GENERAL, "SQL=%s\n",SQL);
char** result;int row;int col;char* err;
sqlite3_get_table(db,SQL,&result, &row, &col, &err);
int row_idx;
for(row_idx=1;row_idx<=row;++row_idx){
int idx = row_idx*REC_SIZE;
char* object_id = result[idx+OBJECT_ID];
char* name = result[idx+NAME];
//---------------------------------------------------
// 各musicArtistに対応するジャンル下のmusicArtistを得る
//---------------------------------------------------
snprintf(SQL,STR_BUF_LEN
,"select OBJECT_ID from OBJECTS"
" where CLASS='container.person.musicArtist'"
" and PARENT_ID like '1$5%%%%'" // 2段escape
" and NAME='%s';"
,escape(name));
if(DBG_SQL) DPRINTF(E_WARN, L_GENERAL, "SQL=%s\n",SQL);
char* object_id_GA= sql_get_text_field(db,SQL);
if(DBG_SQL) DPRINTF(E_WARN, L_GENERAL, "%s -> %s NAME=%s\n",object_id,object_id_GA,name);
//---------------------------------------------------
// 各musicArtist下のmusicAlbumを得る
//---------------------------------------------------
snprintf(SQL,STR_BUF_LEN
,"select * from OBJECTS"
" where PARENT_ID='%s';"
// " and CLASS='container.album.musicAlbum';"
,object_id);
if(DBG_SQL) DPRINTF(E_WARN, L_GENERAL, "SQL=%s\n",SQL);
char** result_AA;int row_AA;int col_AA;char* err_AA;
sqlite3_get_table(db,SQL,&result_AA, &row_AA, &col_AA, &err_AA);
int row_idx_AA;
for(row_idx_AA=1;row_idx_AA<=row_AA;++row_idx_AA){
//---------------------------------------------------------
// 各musicAlbumを複製し、ジャンル下のmusicArtist下に付ける
//---------------------------------------------------------
int idx_AA = row_idx_AA*REC_SIZE;
char* objct_id_AA = result_AA[idx_AA+OBJECT_ID];
char* name_AA = result_AA[idx_AA+NAME];
char* detail_id_AA = result_AA[idx_AA+DETAIL_ID];
snprintf(SQL,STR_BUF_LEN
,"insert into OBJECTS"
" (OBJECT_ID,PARENT_ID,CLASS,DETAIL_ID,NAME)"
" values ('%s$%d','%s','%s','%s','%s');"
,object_id_GA,row_idx_AA
,object_id_GA
,"container.album.musicAlbum"
,detail_id_AA
,escape(name_AA)
);
if(DBG_SQL)DPRINTF(E_WARN, L_GENERAL, "* SQL=%s\n",SQL);
sql_exec(db,SQL);
//---------------------------------------------------------
// 各musicAlbum下のmusicTrackを取得する
//---------------------------------------------------------
snprintf(SQL,STR_BUF_LEN
,"select * from OBJECTS"
" where CLASS='item.audioItem.musicTrack'"
" and PARENT_ID='%s';"
,objct_id_AA);
if(row_idx<=DBG_SQL)DPRINTF(E_WARN, L_GENERAL, "SQL=%s\n",SQL);
char** result_AAT;int row_AAT;int col_AAT;char* err_AAT;
sqlite3_get_table(db,SQL,&result_AAT, &row_AAT, &col_AAT, &err_AAT);
int row_idx_AAT;
for(row_idx_AAT=1;row_idx_AAT<=row_AAT;++row_idx_AAT){
//---------------------------------------------------------
// musicTrackの複製をジャンル下のmusicArtist下に追加すす
//---------------------------------------------------------
int idx_AAT = row_idx_AAT*REC_SIZE;
char* ref_id_AAT = result_AAT[idx_AAT+REF_ID];
char* detail_id_AAT = result_AAT[idx_AAT+DETAIL_ID];
char* name_AAT = result_AAT[idx_AAT+NAME];
snprintf(SQL,STR_BUF_LEN
,"insert into OBJECTS"
" (OBJECT_ID,PARENT_ID,REF_ID,CLASS,DETAIL_ID,NAME)"
" values ('%s$%d$%d','%s$%d','%s','%s','%s','%s');"
,object_id_GA,row_idx_AA,row_idx_AAT
,object_id_GA,row_idx_AA
,ref_id_AAT
,"item.audioItem.musicTrack"
,detail_id_AAT
,escape(name_AAT)
);
if(row_idx<=DBG_SQL)DPRINTF(E_WARN, L_GENERAL, "SQL=%s\n",SQL);
sql_exec(db,SQL);
}
sqlite3_free_table(result_AAT);
}
sqlite3_free_table(result_AA);
sqlite3_free(object_id_GA);
}
sqlite3_free_table(result);
}
ビルドし起動しなおします。
$ sudo service minidlna stop
$ make
$ sudo make install
$ sudo rm -r /var/cache/minidlna
$ sudo rm /var/log/minidlna.log
$ sudo service minidlna start
無事ジャンルの下のアーティストの下にアルバムが置かれるようになりました。
注意点
sqlite3のAPIを使うに当たって名称部の文字のエスケープが必要となります。
| ' | | SQLの文字列内では2個並べ''とします |
| % | | 文字列フォーマット内では2個並べ%%とします
sqlite-APIでは最終SQL文字列ではなく、format文(%sだとか%dが可能)を与えることに注意が必要です。
format文を作るformat文では%%%%と2段のエスケープが必要です。
SQLのlikeのパターンで使う場合はさらにSQLのエスケープも必要となりますが、今回はlikeでは普通の文字としての%使いませんのでSQL用エスケープ処理は入っていません。
|
本文とは全く関係ないことですが、プロポーショナルフォントと呼ばれる空間のリニアリティーを無視したいびつなフォントは大嫌いです。
上の例では標準シングルクオートが極端に小さく、パーセントが極端に大きくなります。
僕の目はこういうのに耐えられませんので
ここでは<table>に次のstyleを設定しました。これをデフォルトにしようかとも考えていますが、ブラウザ共通で"monoapce"を上手く設定できないので躊躇しています。
style="font-family:'MS ゴシック', monospace;"
環境にもよると思いますが、デフォルト表示では''''%%%%です。
等幅指定では''''%%%%となります。
上の行の表示キャプチャしたものが次のものです。
プロポーショナルフォント完全撤廃を訴えたい!
ソース変更部ダウンロード
念のため、ソース1.2.1の変更部を用意しました。
minidlna121_patches_02.zip
修正済みソースと、オリジナルソース、オリジナルからのdiff(パッチ)が入っています。
修正済みソース
オリジナルソース(org)と修正分(patch)
| scanner.c.org |
: ジャンル階層修正 |
|
| scanner.c.patch02 |
: ジャンル階層修正 |
|
次の様にすれば.orgと.patchから変換後ソースを得る事が出来ます。
$ cp scanner.c.org scanner.c
$ patch scanner.c scanner.c.patch_02
残された問題点。
多くはありませんがアートワークの欠落が起きています。
原因は分かっていません。
ログ、/var/log/minidlna.logに次のメッセージがでます。
[・・・] sql.c:117: warn: sql_get_int_field: step failed: SQL logic error or missing database
SELECT count(*) from OBJECTS where PARENT_ID = '1';
原因は分かっていません。