pythonでfoobarのalternativeを作る 7 version 9
自分のPCの音楽ファイルをタグでデータベース化 7
- 190514 の開発日記
## Mutagen の使い方
mutagen の関数がファイル毎になっていて面倒でどうにかならんかなあと思って調べたら`mutagen.File`がオールマイティみたいだったので試したらいけた
```
import glob ,re , os ,mutagen
import sqlite3
import time
# mutagen.File 利用
count={'all':0 , 'mfile':0 , 'nonemusic':0 , 'file':0}
tags={'title':'','album':'','artist':'','track':''}
fo=r'i:\Music\Jazz\**'
conn = sqlite3.connect(<<db-pass>>)
c = conn.cursor()
c.execute('create table IF NOT EXISTS musics(fullpath text, mdata,title,track,album ,artist)')
start = time.time()
for p in glob.iglob(fo , recursive=True):
count['all']+=1
if os.path.isfile(p):
count['file']+=1
if re.search('\.(mp3|ape|ogg|flac|aac|mp4|m4a)$', p):
count['mfile']+=1
m = mutagen.File(p, easy=True)
for tag in tags.keys():
try :
tags[tag]=m[tag][0]
except:
tags[tag]=None
c.execute( "INSERT INTO musics (fullpath, title,track,album ,artist) VALUES (?,?,?,?,?)" ,
( p , tags['title'],tags['track'],tags['album'],tags['artist'] ))
for tag in tags.keys():
tags[tag]=None
else:
count['nonemusic']+=1
continue
elapsed_time = time.time() - start
print ("elapsed_time:{0}".format(elapsed_time) + "[sec]")
c.execute('select count (*) from musics')
print('data--{0}'.format(c.fetchall()))
print(count)
conn.commit()
conn.close()
```
- これは列挙ファイルから拡張子で音楽ファイルをフィルタしてmutagenに投げてタグ取得、それをDBに追加していくだけ。mutagenへの投げ方を簡単にしたもの
- DB更新のルーチンはないので前に作ったものと合わせる必要がある。その際に二段階にする方向性だったのでその二段階目のコードがこれになる
- m = mutagen.File(p, easy=True) とするとmに辞書でタグが入ってくる。そこからアルバムタグを取得するために`m['album'][0]`とすると、あれば良いけど無いとエラーで返される。無いときはNULLを返して欲しいので、そのために`try`文としている。こういうtryの使い方はどうなんだろう?
- 取得したいタグをあらかじめ辞書で作っておいて、そのkeyでfor文を回した。forのイテレータの作り方としては結構スマートなつもり( ´∀`)。なにより取得したいタグがあとで増えてもforを書き換えなくても良いので便利。ただしちょっと問題もあってそれは以下
## 新たな問題
- タグの種類だけど、トラックナンバーについて、ファイルによって`track`としてるものと`tracknumber`としているものがある。こういう微妙な違いって、プログラム上は明確な違いなので処理が難しい。人間の目で見れば同じものってわかるんだけど。こういう違いがこれだけなら対応は簡単だが、それぞれの項目で微妙な方言があるようだと難しくなってくる。foobarの偉大さを改めて感じます
- DBをいじってるとやはりどう書き込まれたかをすぐ見たくなる。当然だと思うが。しかし一気に千項目以上を書き込んでいるので気軽に列挙も出来ない。そうなるとGUIのDBビューアが必須だ。まずこれを作ろうと改めて考えた
- DDでテーブルとカラム取得して、データ総数、カラム毎のデータ数とNULLの数、これらを一覧する。できれば自由にselect文を発行して結果も表示したいけどこれは難しいかな
- ちょっと調べたらDBのビューアはフリーのものがありますね。でも落としてみたけどうまく動かなかった(・∀・)ノ。需要はあるんだよなあ、意外とビューアの方が需要あったりして。考えよう。
## ビューアのコード
- 一度selectを発行して結果を表示するものを作ったんだけど0からの方が良いかもだなあ
-
import sqlite3
con = sqlite3.connect("test.db")
cursor = con.cursor()
cursor.execute("select * from sqlite_master where type='table'")
table=cursor.fetchall()
print(table)
table=table[][] # どう書けば委員ジャロ?
cur.execute("PRAGMA TABLE_INFO(?)" , (table,)) ### テーブルのカラム情報を取得
cols = cur.fetchall() ### 1つのカラム情報は6要素を含むタプルで,複数のカラム情報のリストが得られる
print(cols)
print([item[1] for item in cols]) # これでカラムだけ リスト内包表記
## ここからカラム情報のリストを作る col
cursor.execute('select count(*) from ?' , (table , )) # まず総数
print('totaldata--{0}'.format(cursor.fetchall()))
for i in col :
cursor.execute('select count(?) from ? where ? IS NULL' , (i , table))
>>> cur.close()
>>> conn.close()
---------------
import sqlite3
connection = sqlite3.connect('~/foo.sqlite')
cursor = connection.execute('select * from bar')
cursor.descriptionは列の説明です
names = list(map(lambda x: x[0], cursor.description))
あるいは、リスト内包表記を使用することもできます。
names = [description[0] for description in cursor.description]
ここみて
https://codeday.me/jp/qa/20190222/300516.htmlhttps://codeday.me/jp/qa/20190222/300516.html
「列名またはテーブル名にプレースホルダを使用することはできません」どこにもはっきり書いてないらしいけどそういうことらしい
で、代替案として
`select * from {}'.format(table)`
普通にこれでいけるって書いてある
まあそのとおりですね
- 190514 の開発日記
Mutagen の使い方
mutagen の関数がファイル毎になっていて面倒でどうにかならんかなあと思って調べたらmutagen.File
がオールマイティみたいだったので試したらいけた
import glob ,re , os ,mutagen
import sqlite3
import time
# mutagen.File 利用
count={'all':0 , 'mfile':0 , 'nonemusic':0 , 'file':0}
tags={'title':'','album':'','artist':'','track':''}
fo=r'i:\Music\Jazz\**'
conn = sqlite3.connect(<<db-pass>>)
c = conn.cursor()
c.execute('create table IF NOT EXISTS musics(fullpath text, mdata,title,track,album ,artist)')
start = time.time()
for p in glob.iglob(fo , recursive=True):
count['all']+=1
if os.path.isfile(p):
count['file']+=1
if re.search('\.(mp3|ape|ogg|flac|aac|mp4|m4a)$', p):
count['mfile']+=1
m = mutagen.File(p, easy=True)
for tag in tags.keys():
try :
tags[tag]=m[tag][0]
except:
tags[tag]=None
c.execute( "INSERT INTO musics (fullpath, title,track,album ,artist) VALUES (?,?,?,?,?)" ,
( p , tags['title'],tags['track'],tags['album'],tags['artist'] ))
for tag in tags.keys():
tags[tag]=None
else:
count['nonemusic']+=1
continue
elapsed_time = time.time() - start
print ("elapsed_time:{0}".format(elapsed_time) + "[sec]")
c.execute('select count (*) from musics')
print('data--{0}'.format(c.fetchall()))
print(count)
conn.commit()
conn.close()
- これは列挙ファイルから拡張子で音楽ファイルをフィルタしてmutagenに投げてタグ取得、それをDBに追加していくだけ。mutagenへの投げ方を簡単にしたもの
- DB更新のルーチンはないので前に作ったものと合わせる必要がある。その際に二段階にする方向性だったのでその二段階目のコードがこれになる
- m = mutagen.File(p, easy=True) とするとmに辞書でタグが入ってくる。そこからアルバムタグを取得するために
m['album'][0]
とすると、あれば良いけど無いとエラーで返される。無いときはNULLを返して欲しいので、そのためにtry
文としている。こういうtryの使い方はどうなんだろう? - 取得したいタグをあらかじめ辞書で作っておいて、そのkeyでfor文を回した。forのイテレータの作り方としては結構スマートなつもり( ´∀`)。なにより取得したいタグがあとで増えてもforを書き換えなくても良いので便利。ただしちょっと問題もあってそれは以下
新たな問題
- タグの種類だけど、トラックナンバーについて、ファイルによって
track
としてるものとtracknumber
としているものがある。こういう微妙な違いって、プログラム上は明確な違いなので処理が難しい。人間の目で見れば同じものってわかるんだけど。こういう違いがこれだけなら対応は簡単だが、それぞれの項目で微妙な方言があるようだと難しくなってくる。foobarの偉大さを改めて感じます - DBをいじってるとやはりどう書き込まれたかをすぐ見たくなる。当然だと思うが。しかし一気に千項目以上を書き込んでいるので気軽に列挙も出来ない。そうなるとGUIのDBビューアが必須だ。まずこれを作ろうと改めて考えた
- DDでテーブルとカラム取得して、データ総数、カラム毎のデータ数とNULLの数、これらを一覧する。できれば自由にselect文を発行して結果も表示したいけどこれは難しいかな
- ちょっと調べたらDBのビューアはフリーのものがありますね。でも落としてみたけどうまく動かなかった(・∀・)ノ。需要はあるんだよなあ、意外とビューアの方が需要あったりして。考えよう。
ビューアのコード
- 一度selectを発行して結果を表示するものを作ったんだけど0からの方が良いかもだなあ
import sqlite3
con = sqlite3.connect("test.db")
cursor = con.cursor()
cursor.execute("select * from sqlite_master where type='table'")
table=cursor.fetchall()
print(table)
table=table[][] # どう書けば委員ジャロ?
cur.execute("PRAGMA TABLE_INFO(?)" , (table,)) ### テーブルのカラム情報を取得
cols = cur.fetchall() ### 1つのカラム情報は6要素を含むタプルで,複数のカラム情報のリストが得られる
print(cols)
print([item[1] for item in cols]) # これでカラムだけ リスト内包表記
ここからカラム情報のリストを作る col
cursor.execute('select count(*) from ?' , (table , )) # まず総数
print('totaldata--{0}'.format(cursor.fetchall()))
for i in col :
cursor.execute('select count(?) from ? where ? IS NULL' , (i , table))
cur.close()
conn.close()
import sqlite3
connection = sqlite3.connect('~/foo.sqlite')
cursor = connection.execute('select * from bar')
cursor.descriptionは列の説明です
names = list(map(lambda x: x[0], cursor.description))
あるいは、リスト内包表記を使用することもできます。
names = [description[0] for description in cursor.description]
ここみて
https://codeday.me/jp/qa/20190222/300516.html
「列名またはテーブル名にプレースホルダを使用することはできません」どこにもはっきり書いてないらしいけどそういうことらしい
で、代替案として
select * from {}'.format(table)
普通にこれでいけるって書いてある
まあそのとおりですね