unstable
This commit is contained in:
parent
08098695cc
commit
fdeb1c4289
140
mobject.py
Normal file
140
mobject.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB, Encoding, PictureType
|
||||||
|
from mutagen.flac import FLAC, Picture
|
||||||
|
from pathlib import Path
|
||||||
|
import io
|
||||||
|
|
||||||
|
class MObject:
|
||||||
|
_file_bytes : bytes
|
||||||
|
_file_io : io.BytesIO
|
||||||
|
|
||||||
|
def __init__(self, fb : bytes):
|
||||||
|
self._file_bytes = fb
|
||||||
|
self._file_io = io.BytesIO(fb)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_bytes(self) -> bytes:
|
||||||
|
return self._file_io.getbuffer()
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def get_album(self) -> str:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def get_artist(self) -> str:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def get_cover(self) -> bytes:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def set_artist(self, val : str) -> None:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def set_album(self, val : str) -> None:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def set_title(self, val : str) -> None:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
def set_cover(self, val : bytes, png : bool = False) -> None:
|
||||||
|
raise Exception("unimplemented")
|
||||||
|
|
||||||
|
class MMP3Object(MObject):
|
||||||
|
_id3 : ID3
|
||||||
|
def __init__(self, file : bytes):
|
||||||
|
super().__init__(self, file)
|
||||||
|
|
||||||
|
self._id3 = ID3()
|
||||||
|
self._id3.load(self._file_io, translate = True)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return self._id3.get("TIT2", default = TIT2(text = None)).text
|
||||||
|
|
||||||
|
def set_title(self, val : str) -> None:
|
||||||
|
self._id3.delall("TIT2")
|
||||||
|
self._id3.add(TIT2(text = val))
|
||||||
|
self._id3.save()
|
||||||
|
|
||||||
|
def get_album(self) -> str:
|
||||||
|
return self._id3.get("TALB", default = TALB(text = None)).text
|
||||||
|
|
||||||
|
def set_album(self, val : str) -> None:
|
||||||
|
self._id3.delall("TALB")
|
||||||
|
self._id3.add(TALB(text = val))
|
||||||
|
self._id3.save()
|
||||||
|
|
||||||
|
def get_artist(self) -> str:
|
||||||
|
return self._id3.get("TPE1", default = TPE1(text = None)).text
|
||||||
|
|
||||||
|
def set_artist(self, val : str):
|
||||||
|
self._id3.delall("TPE1")
|
||||||
|
self._id3.add(TPE1(text = val))
|
||||||
|
self._id3.save()
|
||||||
|
|
||||||
|
def get_cover(self) -> bytes:
|
||||||
|
cover = self._id3.get("APIC", default=APIC(encoding = Encoding.UTF8, mime = "image/jpeg", type=PictureType.COVER_FRONT, desc=u"Cover", data = None))
|
||||||
|
return cover.data
|
||||||
|
|
||||||
|
def set_cover(self, val : bytes, png : bool = False) -> None:
|
||||||
|
self._id3.delall("APIC")
|
||||||
|
self._id3.add(APIC(encoding = Encoding.UTF8, mime = "image/png" if png else "image/jpeg", type=PictureType.COVER_FRONT, desc=u"Cover", data = val))
|
||||||
|
self._id3.save()
|
||||||
|
|
||||||
|
|
||||||
|
class MFLACObject(MObject):
|
||||||
|
_flac : FLAC
|
||||||
|
def __init__(self, file : bytes):
|
||||||
|
super().__init__(self, file)
|
||||||
|
|
||||||
|
self._flac = FLAC(self._file_io)
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return self._flac.get("TITLE", default = None)
|
||||||
|
|
||||||
|
def set_title(self, val : str) -> None:
|
||||||
|
self._flac["TITLE"] = val
|
||||||
|
self._flac.save()
|
||||||
|
|
||||||
|
def get_album(self) -> str:
|
||||||
|
return self._flac.get("ALBUM", default = None)
|
||||||
|
|
||||||
|
def set_album(self, val : str) -> None:
|
||||||
|
self._flac["ALBUM"] = val
|
||||||
|
self._flac.save()
|
||||||
|
|
||||||
|
def get_artist(self) -> str:
|
||||||
|
return self._flac.get("ARTIST", default = None)
|
||||||
|
|
||||||
|
def set_artist(self, val : str):
|
||||||
|
self._flac["ARTIST"] = val
|
||||||
|
self._flac.save()
|
||||||
|
|
||||||
|
def get_cover(self) -> bytes:
|
||||||
|
pics = self._flac.pictures
|
||||||
|
if len(pics) > 0:
|
||||||
|
return pics[0].data
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def set_cover(self, val : bytes, png : bool = False) -> None:
|
||||||
|
self._flac.clear_pictures()
|
||||||
|
pic = Picture()
|
||||||
|
pic.type = PictureType.COVER_FRONT
|
||||||
|
pic.mime = "image/png" if png else "image/jpeg"
|
||||||
|
pic.desc = "Cover"
|
||||||
|
pic.data = val
|
||||||
|
self._flac.add_picture(pic)
|
||||||
|
self._flac.save()
|
||||||
|
|
||||||
|
|
||||||
|
def create_mobject(filepath : str) -> MObject:
|
||||||
|
fp = Path(filepath)
|
||||||
|
with open(fp, "rb") as f:
|
||||||
|
buf = f.read()
|
||||||
|
if (fp.suffix == "mp3"):
|
||||||
|
return MMP3Object(buf)
|
||||||
|
if (fp.suffix == "flac"):
|
||||||
|
return MFLACObject(buf)
|
||||||
|
return None
|
71
musick.py
71
musick.py
@ -1,15 +1,14 @@
|
|||||||
import os
|
import os
|
||||||
import mutagen
|
|
||||||
from mutagen.easyid3 import EasyID3
|
|
||||||
from mutagen.id3 import ID3, APIC, TIT2, TPE1, TALB
|
|
||||||
import getopt
|
import getopt
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import mobject
|
||||||
|
|
||||||
total_proc = 0
|
total_proc = 0
|
||||||
total_succ = 0
|
total_succ = 0
|
||||||
total_error = 0
|
total_error = 0
|
||||||
total_cover = 0
|
total_missing_cover = 0
|
||||||
|
|
||||||
input_dir = None
|
input_dir = None
|
||||||
output_dir = None
|
output_dir = None
|
||||||
@ -108,10 +107,8 @@ def usage():
|
|||||||
-o output : output directory\n\
|
-o output : output directory\n\
|
||||||
-c cover : cover arts page\n")
|
-c cover : cover arts page\n")
|
||||||
|
|
||||||
no_match = []
|
|
||||||
|
|
||||||
def process_file(f: str, odir: str):
|
def process_file(f: str, odir: str):
|
||||||
global total_cover
|
global total_missing_cover
|
||||||
global total_succ
|
global total_succ
|
||||||
global total_error
|
global total_error
|
||||||
global total_proc
|
global total_proc
|
||||||
@ -120,51 +117,41 @@ def process_file(f: str, odir: str):
|
|||||||
total_proc += 1
|
total_proc += 1
|
||||||
|
|
||||||
artist, album, title = parse_triple(f)
|
artist, album, title = parse_triple(f)
|
||||||
|
|
||||||
# id3 = ID3(f)
|
|
||||||
|
|
||||||
# album = str(id3.get("TALB") if id3.get("TALB") != None else "")
|
|
||||||
if album == "":
|
if album == "":
|
||||||
album = UNKNOWN_ALBUM_NAME
|
album = UNKNOWN_ALBUM_NAME
|
||||||
|
|
||||||
# artist = str(id3.get("TPE1") if id3.get("TPE1") != None else "")
|
|
||||||
if artist == "":
|
if artist == "":
|
||||||
artist = UNKNOWN_ARTIST_NAME
|
artist = UNKNOWN_ARTIST_NAME
|
||||||
|
|
||||||
# title = str(id3.get("TIT2") if id3.get("TIT2") != None else "")
|
|
||||||
|
|
||||||
if title == "":
|
if title == "":
|
||||||
raise Exception("No title")
|
raise Exception("No title")
|
||||||
|
|
||||||
print(" Identified artist: " + artist + " album: " + album + " title: " + title)
|
mobj = mobject.create_mobject(f)
|
||||||
|
|
||||||
|
print(f" Artist: \"{mobj.get_artist()}\" -> \"{artist}\"\n" +
|
||||||
|
f" Album: \"{mobj.get_album()}\" -> \"{album}\"\n" +
|
||||||
|
f" Title: \"{mobj.get_title()}\" -> \"{title}\"")
|
||||||
|
|
||||||
|
mobj.set_artist(artist)
|
||||||
|
mobj.set_album(album)
|
||||||
|
mobj.set_title(title)
|
||||||
|
|
||||||
|
if (mobj.get_cover() == None):
|
||||||
|
cover_idx = find_best_cover(artist, album, title)
|
||||||
|
if (cover_idx >= 0):
|
||||||
|
mobj.set_cover(cover_arts[cover_idx][3])
|
||||||
|
print(" Cover: <None> -> <New>")
|
||||||
|
else:
|
||||||
|
total_missing_cover += 1
|
||||||
|
print(" Cover: <None> -> <None>")
|
||||||
|
else:
|
||||||
|
print(" Cover: <Exists>")
|
||||||
|
|
||||||
target_dir = os.path.join(odir, artist, album)
|
target_dir = os.path.join(odir, artist, album)
|
||||||
os.makedirs(target_dir, exist_ok = True)
|
os.makedirs(target_dir, exist_ok = True)
|
||||||
|
|
||||||
target_f = os.path.join(target_dir, os.path.basename(f))
|
target_f = os.path.join(target_dir, os.path.basename(f))
|
||||||
try:
|
with open(target_f, "wb") as tf:
|
||||||
shutil.copyfile(f, target_f)
|
tf.write(mobj.get_bytes())
|
||||||
except shutil.SameFileError:
|
print(" Written file to: " + target_f)
|
||||||
pass
|
|
||||||
print(" Copied file to: " + target_f)
|
|
||||||
|
|
||||||
cover_idx = find_best_cover(artist, album, title)
|
|
||||||
|
|
||||||
audio = mutagen.File(f)
|
|
||||||
print(f" File info: {audio.info.pprint()}")
|
|
||||||
audio["title"] = title
|
|
||||||
audio["artist"] = artist
|
|
||||||
audio["album"] = album
|
|
||||||
audio.save()
|
|
||||||
|
|
||||||
# if cover_idx >= 0:
|
|
||||||
# # install cover
|
|
||||||
# id3.delall("APIC")
|
|
||||||
# id3.add(APIC(3, "image/jpeg", 3, "front cover", cover_arts[cover_idx][3]))
|
|
||||||
# total_cover += 1
|
|
||||||
# print(f" Written metadata for {f} with cover art - " + cover_arts[cover_idx][4])
|
|
||||||
# else:
|
|
||||||
# no_match.append(target_f)
|
|
||||||
|
|
||||||
total_succ += 1
|
total_succ += 1
|
||||||
|
|
||||||
@ -218,8 +205,6 @@ def main():
|
|||||||
load_cover_arts()
|
load_cover_arts()
|
||||||
process_directory(input_dir, output_dir)
|
process_directory(input_dir, output_dir)
|
||||||
|
|
||||||
print("\nSummary - Total Files: " + str(total_proc) + " Total Errors: " + str(total_error) + " Total Covers: " + str(total_cover))
|
print("\nSummary - Total Files: " + str(total_proc) + " Total Errors: " + str(total_error) + " Total Missing Covers: " + str(total_missing_cover))
|
||||||
for each in no_match:
|
|
||||||
print("No match: " + each)
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user