Compare commits
2 commits
6c1ca37fd3
...
00872dfed2
Author | SHA1 | Date | |
---|---|---|---|
coderkun | 00872dfed2 | ||
coderkun | e197a579ed |
225
clean-tags.py
225
clean-tags.py
|
@ -1,225 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
# Dependencies:
|
||||
# – python-mutagen
|
||||
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
|
||||
from mutagen.id3 import ID3
|
||||
from mutagen.flac import FLAC
|
||||
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', '.flac', '.ogg']
|
||||
TAGS_ID3 = ['TALB', 'TDRC', 'TIT2', 'TPE1', 'TRCK']
|
||||
TAGS_VORBIS_COMMENTS = ['ALBUM', 'ARTIST', 'DATE', 'TITLE', 'TRACKNUMBER']
|
||||
|
||||
|
||||
def __init__(self, folder):
|
||||
"""Construct a new tag cleaner instancce."""
|
||||
self._logger = logging.getLogger(__class__.__name__)
|
||||
self._dry = False
|
||||
self._id3 = True
|
||||
self._flac = True
|
||||
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 set_id3(self, do):
|
||||
"""Set whether to clean ID3 tags or not."""
|
||||
self._id3 = do
|
||||
|
||||
|
||||
def set_flac(self, do):
|
||||
"""Set whether to clean FLAC vorbis comments or not."""
|
||||
self._flac = do
|
||||
|
||||
|
||||
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", len(self._files))
|
||||
|
||||
|
||||
def _clean_file(self, filename):
|
||||
"""Clean a file."""
|
||||
self._logger.info("Clean file \"%s\"", filename)
|
||||
if os.path.isfile(filename):
|
||||
# ID3
|
||||
if self._id3:
|
||||
self._clean_id3(filename)
|
||||
# FLAC
|
||||
if self._flac:
|
||||
self._clean_flac(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.info("Cleaning of ID3 tags failed: %s", e)
|
||||
|
||||
|
||||
def _clean_flac(self, filename):
|
||||
"""Clean FLAC vorbis comments."""
|
||||
self._logger.info("Clean FLAC vorbis comments")
|
||||
try:
|
||||
flac = FLAC(filename)
|
||||
invalid_comments = {}
|
||||
if flac.tags:
|
||||
invalid_comments = self._clean_vorbis_comments(flac.tags)
|
||||
# Delete comments
|
||||
if invalid_comments:
|
||||
for key in invalid_comments.keys():
|
||||
print("Delete", key)
|
||||
del flac.tags[key]
|
||||
if not self._dry:
|
||||
flac.save()
|
||||
print("File saved")
|
||||
else:
|
||||
print("File not saved (running in dry mode)")
|
||||
else:
|
||||
self._logger.info("Clean, nothing to do")
|
||||
except MutagenError as e:
|
||||
self._logger.info("Cleaning of FLAC vorbis comments failed: %s", e)
|
||||
|
||||
|
||||
def _clean_vorbis_comments(self, comments):
|
||||
invalid_comments = {}
|
||||
for key, value in comments:
|
||||
if key not in TagCleaner.TAGS_VORBIS_COMMENTS:
|
||||
invalid_comments[key] = value
|
||||
if invalid_comments:
|
||||
print("Unwanted comments:")
|
||||
for key in invalid_comments.keys():
|
||||
print("{}{}: {}".format(TagCleaner.INDENTATION, key, invalid_comments[key]))
|
||||
|
||||
return invalid_comments
|
||||
|
||||
|
||||
|
||||
|
||||
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('--no-id3', dest='id3', action='store_false', help="disable cleaning of ID3 tags")
|
||||
parser.add_argument('--no-fac', dest='flac', action='store_false', help="disable cleaning of FLAC vorbis comments")
|
||||
parser.add_argument('folder', help="source folder to read audio files from")
|
||||
parser.set_defaults(id3=True, flac=True)
|
||||
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)
|
||||
tag_cleaner.set_id3(args.id3)
|
||||
tag_cleaner.set_flac(args.flac)
|
||||
|
||||
# Run action
|
||||
tag_cleaner.clean_files()
|
|
@ -1,17 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Count the number of unread emails and return the count in the i3/sway bar
|
||||
# format.
|
||||
|
||||
# Settings
|
||||
MAILDIRNEW="$HOME/Dokumente/eMails/*/INBOX/new/"
|
||||
|
||||
# Count new mails
|
||||
MAILNEW="$(find $MAILDIRNEW -type f | wc -l)"
|
||||
|
||||
# Print count in i3/sway bar format
|
||||
echo $MAILNEW
|
||||
echo $MAILNEW
|
||||
[ ${MAILNEW} -ge 1 ] && exit 33
|
||||
|
||||
exit 0
|
28
pa.bash
28
pa.bash
|
@ -1,28 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
|
||||
# Detect current profile
|
||||
function profile {
|
||||
CURRENT_PROFILE=$(LC_ALL=C pactl list sinks | grep -i "active port" | cut -d " " -f 3)
|
||||
}
|
||||
|
||||
|
||||
# If either i3’s button is 1 or the first argument is “switch”, switch profile
|
||||
if [[ $BLOCK_BUTTON -eq 1 ]] || [ "$1" == "switch" ] ; then
|
||||
profile
|
||||
if ! [[ "$CURRENT_PROFILE" == *"iec958"* ]] ; then
|
||||
pacmd set-card-profile 0 output:iec958-stereo
|
||||
else
|
||||
pacmd set-card-profile 0 output:hdmi-stereo
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print out current profile
|
||||
profile
|
||||
if [[ "$CURRENT_PROFILE" == *"iec958"* ]] ; then
|
||||
echo "digital"
|
||||
elif [[ "$CURRENT_PROFILE" == *"hdmi"* ]] ; then
|
||||
echo "hdmi"
|
||||
else
|
||||
echo "${CURRENT_PROFILE%%-*}"
|
||||
fi
|
28
pa.sh
28
pa.sh
|
@ -1,28 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
|
||||
# Detect current profile
|
||||
profile() {
|
||||
CURRENT_PROFILE=$(LC_ALL=C pactl list sinks | grep -i "active port" | cut -d " " -f 3)
|
||||
}
|
||||
|
||||
|
||||
# If either i3’s button is 1 or the first argument is “switch”, switch profile
|
||||
if [[ $BLOCK_BUTTON -eq 1 ]] || [ "$1" == "switch" ] ; then
|
||||
profile
|
||||
if ! [[ "$CURRENT_PROFILE" == *"iec958"* ]] ; then
|
||||
pacmd set-card-profile 0 output:iec958-stereo
|
||||
else
|
||||
pacmd set-card-profile 0 output:hdmi-stereo
|
||||
fi
|
||||
fi
|
||||
|
||||
# Print out current profile
|
||||
profile
|
||||
if [[ "$CURRENT_PROFILE" == *"iec958"* ]] ; then
|
||||
echo "digital"
|
||||
elif [[ "$CURRENT_PROFILE" == *"hdmi"* ]] ; then
|
||||
echo "hdmi"
|
||||
else
|
||||
echo "${CURRENT_PROFILE%%-*}"
|
||||
fi
|
17
passmenu
17
passmenu
|
@ -1,17 +0,0 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
shopt -s nullglob globstar
|
||||
|
||||
# Present menu to select password
|
||||
prefix=${PASSWORD_STORE_DIR-~/.password-store}
|
||||
password_files=( "$prefix"/**/*.gpg )
|
||||
password_files=( "${password_files[@]#"$prefix"/}" )
|
||||
password_files=( "${password_files[@]%.gpg}" )
|
||||
password=$(printf '%s\n' "${password_files[@]}" | bemenu $@)
|
||||
|
||||
# Copy password to clipboard
|
||||
pass show -c "$password" 2>/dev/null
|
||||
|
||||
# Send notification
|
||||
notify-send -u critical -t 45000 "Password in clipboard" \
|
||||
"The password for \"$password\" is currently in the clipboard."
|
17
passusermenu
17
passusermenu
|
@ -1,17 +0,0 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
shopt -s nullglob globstar
|
||||
|
||||
# Present menu to select password
|
||||
prefix=${PASSWORD_STORE_DIR-~/.password-store}
|
||||
password_files=( "$prefix"/**/*.gpg )
|
||||
password_files=( "${password_files[@]#"$prefix"/}" )
|
||||
password_files=( "${password_files[@]%.gpg}" )
|
||||
password=$(printf '%s\n' "${password_files[@]}" | bemenu $@)
|
||||
|
||||
# Copy password to clipboard
|
||||
pass user "$password" 2>/dev/null
|
||||
|
||||
# Send notification
|
||||
notify-send -u critical -t 45000 "Username in clipboard" \
|
||||
"The username for \"$password\" is currently in the clipboard."
|
13
timewmenu
Executable file
13
timewmenu
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
shopt -s nullglob globstar
|
||||
|
||||
# Present menu to select tag
|
||||
tag=$(timew tags | tail +4 | cut -d " " -f 1 | bemenu $@)
|
||||
tag=$(echo "${tag%-*}" | xargs echo -n)
|
||||
|
||||
# Start timetracking
|
||||
output=$(timew start "$tag")
|
||||
|
||||
# Send notification
|
||||
notify-send -t 5000 "timew" "${output}"
|
Loading…
Reference in a new issue