initial commit
BIN
arts/Assassin's Creed OST.jpg
Normal file
After Width: | Height: | Size: 893 KiB |
BIN
arts/Bon Jovi.jpg
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
arts/Borderlands OST.jpg
Normal file
After Width: | Height: | Size: 374 KiB |
BIN
arts/Command and Conquer OST.jpg
Normal file
After Width: | Height: | Size: 354 KiB |
BIN
arts/Counter Strike OST.jpg
Normal file
After Width: | Height: | Size: 209 KiB |
BIN
arts/Dota OST.jpg
Normal file
After Width: | Height: | Size: 258 KiB |
BIN
arts/Dynasty Warriors OST.jpg
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
arts/Eminem.jpg
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
arts/Final Fantasy OST.jpg
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
arts/Flying Steps.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
arts/Fort Minor.jpg
Normal file
After Width: | Height: | Size: 467 KiB |
BIN
arts/Geoplex.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
arts/Granblue Fantasy OST.jpg
Normal file
After Width: | Height: | Size: 842 KiB |
BIN
arts/Hexyz Force OST.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
arts/Immediate Music.jpg
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
arts/Linkin Park.jpg
Normal file
After Width: | Height: | Size: 500 KiB |
BIN
arts/Metal Slug OST.jpg
Normal file
After Width: | Height: | Size: 308 KiB |
BIN
arts/Michael Jackson.jpg
Normal file
After Width: | Height: | Size: 554 KiB |
BIN
arts/Music Instructor.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
arts/Nightwish.jpg
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
arts/Portal OST.jpg
Normal file
After Width: | Height: | Size: 226 KiB |
BIN
arts/Radiant Historia OST.jpg
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
arts/Rammstein.jpg
Normal file
After Width: | Height: | Size: 120 KiB |
BIN
arts/Touhou OST.jpg
Normal file
After Width: | Height: | Size: 2.9 MiB |
BIN
arts/Two Steps From Hell.jpg
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
arts/Vae.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
arts/Xenoblade Chronicles OST.jpg
Normal file
After Width: | Height: | Size: 260 KiB |
BIN
arts/_.jpg
Normal file
After Width: | Height: | Size: 100 KiB |
BIN
arts/_Battlefield.jpg
Normal file
After Width: | Height: | Size: 457 KiB |
BIN
arts/_Digimon.jpg
Normal file
After Width: | Height: | Size: 275 KiB |
BIN
arts/_Dragonball Super.jpg
Normal file
After Width: | Height: | Size: 127 KiB |
BIN
arts/_Metal Gear Rising.jpg
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
arts/_Monster Hunter 3G.jpg
Normal file
After Width: | Height: | Size: 5.2 MiB |
BIN
arts/_Monster Hunter 4G.jpg
Normal file
After Width: | Height: | Size: 669 KiB |
BIN
arts/_Monster Hunter Frontier.jpg
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
arts/_Monster Hunter Stories.jpg
Normal file
After Width: | Height: | Size: 458 KiB |
BIN
arts/_Monster Hunter World.jpg
Normal file
After Width: | Height: | Size: 232 KiB |
BIN
arts/_Monster Hunter XX.jpg
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
arts/_Nier Automata.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
arts/_Pokemon BW2.jpg
Normal file
After Width: | Height: | Size: 324 KiB |
BIN
arts/_Pokemon ORAS.jpg
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
arts/_Pokemon Origins.jpg
Normal file
After Width: | Height: | Size: 380 KiB |
BIN
arts/_Pokemon RSE.jpg
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
arts/_Pokemon SM.jpg
Normal file
After Width: | Height: | Size: 224 KiB |
BIN
arts/_Pokemon SnS.jpg
Normal file
After Width: | Height: | Size: 385 KiB |
BIN
arts/_Pokemon Super Mystery Dungeon.jpg
Normal file
After Width: | Height: | Size: 259 KiB |
BIN
arts/_Pokemon USUM.jpg
Normal file
After Width: | Height: | Size: 224 KiB |
BIN
arts/_Slam Dunk.jpg
Normal file
After Width: | Height: | Size: 444 KiB |
BIN
arts/_Undertale.jpg
Normal file
After Width: | Height: | Size: 682 KiB |
BIN
arts/__Catastrophe.jpg
Normal file
After Width: | Height: | Size: 171 KiB |
BIN
arts/__Dragon Rises.jpg
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
arts/__Gordon Montage.jpg
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
arts/__True Survivor.jpg
Normal file
After Width: | Height: | Size: 143 KiB |
BIN
arts/周杰伦.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
arts/林俊杰.jpg
Normal file
After Width: | Height: | Size: 204 KiB |
204
musick.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
import os
|
||||||
|
from mutagen.easyid3 import EasyID3
|
||||||
|
from mutagen.id3 import ID3, APIC
|
||||||
|
import getopt
|
||||||
|
import sys
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
total_proc = 0
|
||||||
|
total_succ = 0
|
||||||
|
total_error = 0
|
||||||
|
total_cover = 0
|
||||||
|
|
||||||
|
input_dir = None
|
||||||
|
output_dir = None
|
||||||
|
cover_dir = None
|
||||||
|
cover_arts = []
|
||||||
|
|
||||||
|
UNKNOWN_ARTIST_NAME = "Unknown Artists"
|
||||||
|
UNKNOWN_ALBUM_NAME = "Unknown Album"
|
||||||
|
|
||||||
|
WILDCARD_MATCH_SCORE = 1
|
||||||
|
ARTIST_MATCH_SCORE = 10
|
||||||
|
ALBUM_MATCH_SCORE = 100
|
||||||
|
TITLE_MATCH_SCORE = 1000
|
||||||
|
|
||||||
|
# to get a > 0 score, each field either must match or the cover tuple is wildcare (None)
|
||||||
|
def calc_cover_score(artist: str, album: str, title: str, tup : tuple):
|
||||||
|
score = 0
|
||||||
|
|
||||||
|
if (tup[0] == artist):
|
||||||
|
score += ARTIST_MATCH_SCORE
|
||||||
|
elif (tup[0] == ""):
|
||||||
|
score += WILDCARD_MATCH_SCORE
|
||||||
|
else:
|
||||||
|
# mismatched artist, discard
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (tup[1] == album):
|
||||||
|
score += ALBUM_MATCH_SCORE
|
||||||
|
elif (tup[1] == ""):
|
||||||
|
score += WILDCARD_MATCH_SCORE
|
||||||
|
else:
|
||||||
|
# mismatched album, discard
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (tup[2] == title):
|
||||||
|
score += TITLE_MATCH_SCORE
|
||||||
|
elif (tup[2] == ""):
|
||||||
|
score += WILDCARD_MATCH_SCORE
|
||||||
|
else:
|
||||||
|
# mismatched title, discard
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return score
|
||||||
|
|
||||||
|
# returns the idx into cover_arts array.
|
||||||
|
# < 0 means no match
|
||||||
|
def find_best_cover(artist: str, album: str, title: str):
|
||||||
|
best_idx = -1
|
||||||
|
best_score = 0
|
||||||
|
for i in range(0, len(cover_arts)):
|
||||||
|
score = calc_cover_score(artist, album, title, cover_arts[i])
|
||||||
|
if score > best_score:
|
||||||
|
best_idx = i
|
||||||
|
best_score = score
|
||||||
|
|
||||||
|
return best_idx
|
||||||
|
|
||||||
|
|
||||||
|
def load_cover_arts():
|
||||||
|
global cover_arts
|
||||||
|
|
||||||
|
if cover_dir == None:
|
||||||
|
return
|
||||||
|
|
||||||
|
cdir = cover_dir
|
||||||
|
files = os.listdir(cdir)
|
||||||
|
for f in files:
|
||||||
|
try:
|
||||||
|
triple = os.path.splitext(f)[0].split("_")
|
||||||
|
f = os.path.join(cover_dir, f)
|
||||||
|
print("Processing cover art - " + f)
|
||||||
|
artist = triple[0]
|
||||||
|
album = ""
|
||||||
|
title = ""
|
||||||
|
if len(triple) >= 2:
|
||||||
|
album = triple[1]
|
||||||
|
if len(triple) >= 3:
|
||||||
|
title = triple[2]
|
||||||
|
with open(f,'rb') as img:
|
||||||
|
cover_arts.append((artist, album, title, img.read(), os.path.basename(f)))
|
||||||
|
print(" Loading cover art - " + f + " Artist: " + artist + " Album: " + album + " Title: " + title)
|
||||||
|
except Exception as e:
|
||||||
|
print(" Skipping due to Exception - " + str(e))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
print("Usage: python musik.py [options]\n\n\
|
||||||
|
options:\n\
|
||||||
|
-h : show usage.\n\
|
||||||
|
-i input : input directory\n\
|
||||||
|
-o output : output directory\n\
|
||||||
|
-c cover : cover arts page\n")
|
||||||
|
|
||||||
|
|
||||||
|
def process_file(f: str, odir: str):
|
||||||
|
global total_cover
|
||||||
|
global total_succ
|
||||||
|
global total_error
|
||||||
|
global total_proc
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("Processing file - " + f)
|
||||||
|
total_proc += 1
|
||||||
|
id3 = ID3(f)
|
||||||
|
|
||||||
|
album = str(id3.get("TALB") if id3.get("TALB") != None else "")
|
||||||
|
if album == "":
|
||||||
|
album = UNKNOWN_ALBUM_NAME
|
||||||
|
|
||||||
|
artist = str(id3.get("TPE1") if id3.get("TPE1") != None else "")
|
||||||
|
if artist == "":
|
||||||
|
artist = UNKNOWN_ARTIST_NAME
|
||||||
|
|
||||||
|
title = str(id3.get("TIT2") if id3.get("TIT2") != None else "")
|
||||||
|
if artist == "":
|
||||||
|
raise Exception("No title")
|
||||||
|
|
||||||
|
print(" Artist: " + artist + " Album: " + album + " Title: " + title)
|
||||||
|
|
||||||
|
target_dir = os.path.join(odir, artist, album)
|
||||||
|
os.makedirs(target_dir, exist_ok = True)
|
||||||
|
|
||||||
|
target_f = os.path.join(target_dir, os.path.basename(f))
|
||||||
|
shutil.copyfile(f, target_f)
|
||||||
|
print(" Copied file to - " + target_f)
|
||||||
|
|
||||||
|
cover_idx = find_best_cover(artist, album, title)
|
||||||
|
|
||||||
|
if cover_idx >= 0:
|
||||||
|
# install cover
|
||||||
|
id3 = ID3(target_f)
|
||||||
|
id3.delall("APIC")
|
||||||
|
id3.add(APIC(3, "image/jpeg", 3, "Front cover", cover_arts[cover_idx][3]))
|
||||||
|
id3.save(v2_version=3)
|
||||||
|
total_cover += 1
|
||||||
|
print(" Written cover art - " + cover_arts[cover_idx][4])
|
||||||
|
|
||||||
|
total_succ += 1
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
total_error += 1
|
||||||
|
print(" Skipping due to exception - %s" % str(e))
|
||||||
|
|
||||||
|
print("")
|
||||||
|
|
||||||
|
def process_directory(idir: str, odir: str):
|
||||||
|
print("Processing directory - " + idir)
|
||||||
|
files = os.listdir(idir)
|
||||||
|
for f in files:
|
||||||
|
f = os.path.join(idir, f)
|
||||||
|
if os.path.isdir(f):
|
||||||
|
process_directory(f, odir)
|
||||||
|
else:
|
||||||
|
process_file(f, odir)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
global input_dir
|
||||||
|
global output_dir
|
||||||
|
global cover_dir
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts , _ = getopt.getopt(sys.argv[1:], "hi:o:c:")
|
||||||
|
except getopt.GetoptError as err:
|
||||||
|
print(str(err))
|
||||||
|
usage()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
for o, a in opts:
|
||||||
|
if o == "-h":
|
||||||
|
usage()
|
||||||
|
sys.exit(0)
|
||||||
|
elif o == "-i":
|
||||||
|
input_dir = a
|
||||||
|
elif o == "-o":
|
||||||
|
output_dir = a
|
||||||
|
elif o == "-c":
|
||||||
|
cover_dir = a
|
||||||
|
else:
|
||||||
|
print("Unrecognized option: " + o)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if input_dir == None or output_dir == None:
|
||||||
|
print("Must specify both -i and -o")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
load_cover_arts()
|
||||||
|
process_directory(input_dir, output_dir)
|
||||||
|
|
||||||
|
print("\nSummary - Total Files: " + str(total_proc) + " Total Errors: " + str(total_error) + " Total Covers: " + str(total_cover))
|
||||||
|
|
||||||
|
main()
|