add script “clean-tags.py”
This commit is contained in:
parent
9a2776b53e
commit
51e703e41c
1 changed files with 165 additions and 0 deletions
165
clean-tags.py
Executable file
165
clean-tags.py
Executable file
|
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# Dependencies:
|
||||
# – python-mutagen
|
||||
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from mutagen.id3 import ID3
|
||||
from mutagen import MutagenError
|
||||
|
||||
|
||||
|
||||
|
||||
class Styling:
|
||||
"""Output styling constants."""
|
||||
ENDC = '\x1b[0m'
|
||||
BOLD = '\x1b[1m'
|
||||
|
||||
|
||||
|
||||
|
||||
class TagCleaner:
|
||||
"""
|
||||
Class to check audio files for unwanted tags and provide routines to clean
|
||||
them.
|
||||
"""
|
||||
INDENTATION = " "
|
||||
FILE_EXTENSIONS = ['.mp3']
|
||||
TAGS_ID3 = ['TALB', 'TDRC', 'TIT2', 'TPE1', 'TRCK']
|
||||
|
||||
|
||||
def __init__(self, folder):
|
||||
"""Construct a new tag cleaner instancce."""
|
||||
self._logger = logging.getLogger(__class__.__name__)
|
||||
self._dry = False
|
||||
self._folder = folder
|
||||
self._files = []
|
||||
self._read_files(folder)
|
||||
|
||||
|
||||
def set_dry(self, dry):
|
||||
"""Set dry mode (do not save any file)."""
|
||||
self._dry = dry
|
||||
|
||||
|
||||
def clean_files(self):
|
||||
"""Clean all found files."""
|
||||
self._logger.info("Clean files")
|
||||
for filename in self._files:
|
||||
self._clean_file(filename)
|
||||
|
||||
|
||||
def _read_files(self, folder):
|
||||
self._logger.info("Read files from \"{}\"".format(folder))
|
||||
for dirname, dirnames, filenames in os.walk(folder):
|
||||
for filename in filenames:
|
||||
if filename.startswith("."):
|
||||
continue
|
||||
(name, ext) = os.path.splitext(filename)
|
||||
if ext.lower() not in TagCleaner.FILE_EXTENSIONS:
|
||||
continue
|
||||
self._logger.debug("Found file \"%s\"", os.path.join(dirname, filename))
|
||||
self._files.append(os.path.join(dirname, filename))
|
||||
self._logger.info("Found %d files".format(len(self._files)))
|
||||
|
||||
|
||||
def _clean_file(self, filename):
|
||||
"""Clean a file."""
|
||||
self._logger.info("Clean file \"%s\"", filename)
|
||||
if os.path.isfile(filename):
|
||||
# ID3
|
||||
self._clean_id3(filename)
|
||||
else:
|
||||
self._logger.info("Not a file: \"%s\"", filename)
|
||||
|
||||
|
||||
def _clean_id3(self, filename):
|
||||
"""Clean ID3 tags."""
|
||||
self._logger.info("Clean ID3")
|
||||
try:
|
||||
tags = ID3(filename)
|
||||
print(Styling.BOLD + filename[len(self._folder):] + Styling.ENDC)
|
||||
print("ID3", "v{}.{}.{}".format(*tags.version))
|
||||
valid = True
|
||||
|
||||
# Check version
|
||||
if tags.version != (2, 4, 0):
|
||||
valid = False
|
||||
|
||||
# Unknown tags
|
||||
if tags.unknown_frames:
|
||||
valid = False
|
||||
print("Unknown frames:")
|
||||
for frame in tags.unknown_frames:
|
||||
print(frame)
|
||||
|
||||
# Invalid tags
|
||||
invalid_tags = []
|
||||
for tag in tags:
|
||||
if len(tag) > 4:
|
||||
tag = tag[0:4]
|
||||
if tag not in TagCleaner.TAGS_ID3:
|
||||
invalid_tags.append(tag)
|
||||
if invalid_tags:
|
||||
valid = False
|
||||
print("Unwanted tags:")
|
||||
for tag in invalid_tags:
|
||||
for frame in tags.getall(tag):
|
||||
if hasattr(frame, 'text'):
|
||||
for value in frame.text:
|
||||
print("{}{}: {}".format(TagCleaner.INDENTATION, tag, value))
|
||||
else:
|
||||
print("{}{}:".format(TagCleaner.INDENTATION, tag), frame)
|
||||
|
||||
# Save
|
||||
if not valid:
|
||||
# Delete tags
|
||||
for tag in invalid_tags:
|
||||
print("Delete", tag)
|
||||
tags.delall(tag)
|
||||
|
||||
# Save file
|
||||
if not self._dry:
|
||||
try:
|
||||
tags.save()
|
||||
print("File saved")
|
||||
except Exception as e:
|
||||
self._logger.error("Saving of file \"%s\" failed: %s", filename, e)
|
||||
else:
|
||||
print("File not saved (running in dry mode)")
|
||||
else:
|
||||
self._logger.info("Clean, nothing to do")
|
||||
except MutagenError as e:
|
||||
self._logger.error("Cleaning of ID3 tags failed: %s", e)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Setup command line
|
||||
parser = argparse.ArgumentParser("Clean unwanted tags from audio files to keep your music library clean.")
|
||||
parser.add_argument('-d', '--dry', dest='dry', action='store_true', default=False, help="dry run, do not modify any file, just print information")
|
||||
parser.add_argument('-v', '--verbose', dest='verbosity', action='count', default=0, help="be verbose, show more information")
|
||||
parser.add_argument('-l', '--logfile', dest='logfile', help="specify name of logfile")
|
||||
parser.add_argument('folder', help="source folder to read audio files from")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
filename=args.logfile,
|
||||
level=logging.ERROR-(10*args.verbosity),
|
||||
format="%(asctime)s %(levelname)s: %(message)s"
|
||||
)
|
||||
|
||||
# Create tag cleaner instance
|
||||
tag_cleaner = TagCleaner(args.folder)
|
||||
tag_cleaner.set_dry(args.dry)
|
||||
|
||||
# Run action
|
||||
tag_cleaner.clean_files()
|
||||
Loading…
Add table
Add a link
Reference in a new issue