0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Python]持っている音楽ファイルのアーティスト割合などを取得する

Posted at

はじめに

  • ライブラリだと文字化けするので使わずに作成
  • 持っている音楽のアーティスト、歌手、曲の重複数をコマンドプロンプトに表示
  • 音楽ジャンルはVOCALOIDを想定していますが、他ジャンルが入っていても動きます

環境

  • Windows11
  • Visual Studio 2022 (Community)
  • Python 3.9(64bit)

VOCALOIDジャンル特有のファイル構造(他ジャンルでも同じ?)

  • mp3タグの文字コードがバラバラ(タグを解析するライブラリを使うと文字化けする)
  • アーティスト名に別名が存在する人が多い(例:ラマーズPとゴジマジP) また、表記ゆれもある
  • アーティスト名の表記形式は、○○ Feat.△△ (だいたいこれ) ○○ loves △△(デッドボールPのアルバム) の2種類が存在
  • 名前の区切りは、 X,Y(だいたいこれ) X+Y X・Y X&Y X、Y(一部のアルバム)

変数の準備など

-- coding: shift-jis -- は、パスに日本語(マルチバイト文字)がある場合に必要
import os を先に書く

Python
import os
# -*- coding: shift-jis -*-
dir_path = r"ミュージックフォルダまでのパス"
files = []
res = {}
res2 = {}
songs = {}

mp3ファイルを取得

ジャケット画像を設定している場合はjpgファイル、ミュージックフォルダにはdesktop.iniがあるので拡張子からそれらを弾く

自分の持っているファイルの曲番号は2桁か3桁(01,001など)なので、空白位置からどっちか調べ、番号を消した状態で曲名をsongsに追加する(曲別ランキングに使用)

Python
def add_file(path):
	for name in os.listdir(path):
		if len(name) > 4:
			if name[-4:] == ".mp3":
				if name[2] == ' ':
					if name[3:-4] in songs:
						songs[name[3:-4]] += 1
					else:
						songs[name[3:-4]] = 1
				elif name[3] == ' ':
					if name[4:-4] in songs:
						songs[name[4:-4]] += 1
					else:
						songs[name[4:-4]] = 1
				files.append(path + "\\" + name)
				continue
			elif name[-4:] == ".jpg" or name[-4:] == ".ini":
				continue
		add_file(path + "\\" + name)

mp3ファイルのTPE1タグを取得

TPE1タグにアーティスト情報が載っている(○○ Feat.△△ など)
TPE1タグを取得するライブラリは存在するが、文字化けするので使用しない。
今のところ、文字コードは UTF-8,Shift-JIS,UTF-16のいずれかで対応できたので、try-exceptを使いしらみつぶしに変換する
ヌル文字はここで削除する(utf-16を除く)

Python
def get_tpe1(path):
target = "TPE1".encode("utf-8")
with open(path,"br") as f:
	data = f.read()
	i = data.find(target) + 7
	tmp = data[i + 4:i + 3 + data[i]]
	rt = ""
	if not i == -1:
		try:
			return tmp.replace(b"\x00",b"").decode("utf-8")
		except Exception:
			try:
				return tmp.replace(b"\x00",b"").decode("shift-jis")
			except Exception:
				return tmp.decode("utf-16")
	else:
		return ""

最終仕上げ

取得したデータをソートして、ランキング(多い順)で表示する
アーティスト名とFeat以降の区切りを見つける
アーティスト名のみ(l = -1 + 5 つまり4)の場合は、アーティスト名のみを取得
replace()を使いアーティスト名の統一、名前の区切りの統一をしておく(例:ゴジマジP→ラマーズP Feat.初音ミク&鏡音リン→Feat.初音ミク,鏡音リン)
鏡音リン・レンを鏡音リン・鏡音レンに直す(後でランキングをつけるため)

Python
add_file(dir_path)
print("曲数: ",len(files))
print("種類: ",len(songs))
for path in files:
	ats = get_tpe1(path)
	if not ats == "":
		if "loves" in ats:
			l = ats.lower().find("loves") + 5
		else:
			l = ats.lower().find("feat.") + 5
		if not l == 4:
			artist = ats[:l - 6] if ats[l - 6] == " " else ats[:l - 5]
			artist = artist.replace("ryo","supercell").replace("ゴジマジP","ラマーズP").replace("ピノキオP","ピノキオピー").replace("ちょむP","TakeponG (ちょむP)").replace("1640mP","40mP").replace("じん (自然の敵P)","じん").replace("wowaka (現実逃避P)","wowaka").replace("マチゲリータP","マチゲリータ")
			feats = ats[l:].replace(" ","").replace("x",",").replace("&",",").replace("+",",").replace("",",").replace("",",")
			if ("鏡音リン" in feats) and (not ("鏡音レン" in feats)) and ("レン" in feats):
				feats = feats.replace("レン","鏡音レン")
			featsl = feats.split(",")
			if artist in res2:
				res2[artist] += 1
			else:
				res2[artist] = 1
			for feat in featsl:
				if feat in res:
					res[feat] += 1
				else:
					res[feat] = 1
		elif not len(ats) == 0:
			artist = ats[:-1] if ats[-1] == " " else ats
			artist = artist.replace("ryo","supercell").replace("ゴジマジP","ラマーズP").replace("ピノキオP","ピノキオピー").replace("ちょむP","TakeponG (ちょむP)").replace("1640mP","40mP").replace("じん (自然の敵P)","じん").replace("wowaka (現実逃避P)","wowaka").replace("マチゲリータP","マチゲリータ")
			if artist in res2:
				res2[artist] += 1
			else:
				res2[artist] = 1
sigma = [sum(res.values()),sum(res2.values())]
res = sorted(res.items(), key = lambda x : x[1], reverse = True)
res2 = sorted(res2.items(), key = lambda x : x[1], reverse = True)
songs = sorted(songs.items(), key = lambda x : x[1], reverse = True)
print("")
print("歌手別ランキング")
for result in res:
	print(result,"  ",result[1] / sigma[0] * 100,"%")
print("")
print("アーティスト名ランキング")
for result2 in res2:
	print(result2,"  ",result2[1] / sigma[1] * 100,"%")
print("")
print("曲別ランキング")
for song in songs:
	print(song)
input()

完成形

Python
import os
# -*- coding: shift-jis -*-
dir_path = r"ミュージックフォルダまでのパス"
files = []
res = {}
res2 = {}
songs = {}
def add_file(path):
	for name in os.listdir(path):
		if len(name) > 4:
			if name[-4:] == ".mp3":
				if name[2] == ' ':
					if name[3:-4] in songs:
						songs[name[3:-4]] += 1
					else:
						songs[name[3:-4]] = 1
				elif name[3] == ' ':
					if name[4:-4] in songs:
						songs[name[4:-4]] += 1
					else:
						songs[name[4:-4]] = 1
				files.append(path + "\\" + name)
				continue
			elif name[-4:] == ".jpg" or name[-4:] == ".ini":
				continue
		add_file(path + "\\" + name)

def get_tpe1(path):
	target = "TPE1".encode("utf-8")
	with open(path,"br") as f:
		data = f.read()
		i = data.find(target) + 7
		tmp = data[i + 4:i + 3 + data[i]]
		rt = ""
		if not i == -1:
			try:
				return tmp.replace(b"\x00",b"").decode("utf-8")
			except Exception:
				try:
					return tmp.replace(b"\x00",b"").decode("shift-jis")
				except Exception:
					return tmp.decode("utf-16")
		else:
			return ""

add_file(dir_path)
print("曲数: ",len(files))
print("種類: ",len(songs))
for path in files:
	ats = get_tpe1(path)
	if not ats == "":
		if "loves" in ats:
			l = ats.lower().find("loves") + 5
		else:
			l = ats.lower().find("feat.") + 5
		if not l == 4:
			artist = ats[:l - 6] if ats[l - 6] == " " else ats[:l - 5]
			artist = artist.replace("ryo","supercell").replace("ゴジマジP","ラマーズP").replace("ピノキオP","ピノキオピー").replace("ちょむP","TakeponG (ちょむP)").replace("1640mP","40mP").replace("じん (自然の敵P)","じん").replace("wowaka (現実逃避P)","wowaka").replace("マチゲリータP","マチゲリータ")
			feats = ats[l:].replace(" ","").replace("x",",").replace("&",",").replace("+",",").replace("",",").replace("",",")
			if ("鏡音リン" in feats) and (not ("鏡音レン" in feats)) and ("レン" in feats):
				feats = feats.replace("レン","鏡音レン")
			featsl = feats.split(",")
			if artist in res2:
				res2[artist] += 1
			else:
				res2[artist] = 1
			for feat in featsl:
				if feat in res:
					res[feat] += 1
				else:
					res[feat] = 1
		elif not len(ats) == 0:
			artist = ats[:-1] if ats[-1] == " " else ats
			artist = artist.replace("ryo","supercell").replace("ゴジマジP","ラマーズP").replace("ピノキオP","ピノキオピー").replace("ちょむP","TakeponG (ちょむP)").replace("1640mP","40mP").replace("じん (自然の敵P)","じん").replace("wowaka (現実逃避P)","wowaka").replace("マチゲリータP","マチゲリータ")
			if artist in res2:
				res2[artist] += 1
			else:
				res2[artist] = 1
sigma = [sum(res.values()),sum(res2.values())]
res = sorted(res.items(), key = lambda x : x[1], reverse = True)
res2 = sorted(res2.items(), key = lambda x : x[1], reverse = True)
songs = sorted(songs.items(), key = lambda x : x[1], reverse = True)
print("")
print("歌手別ランキング")
for result in res:
	print(result,"  ",result[1] / sigma[0] * 100,"%")
print("")
print("アーティスト名ランキング")
for result2 in res2:
	print(result2,"  ",result2[1] / sigma[1] * 100,"%")
print("")
print("曲別ランキング")
for song in songs:
	print(song)
input()

結果

曲数:  2937
種類:  2275

歌手別ランキング
('初音ミク', 1359)    52.5725338491296 %
('鏡音リン', 294)    11.37330754352031 %
('鏡音レン', 200)    7.7369439071566735 %
...(略)...
アーティスト名ランキング
('DECO*27', 119)    4.051753489955737 %
('OSTER project', 97)    3.302689819543752 %
('40mP', 83)    2.826012938372489 %
...(略)...
曲別ランキング
('[Secret Track]', 10)
('ロミオとシンデレラ', 10)
('ココロ', 10)
...(略)...
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?