Skip to content

Instantly share code, notes, and snippets.

@jangler
Last active August 13, 2020 16:55
Show Gist options
  • Save jangler/3956997 to your computer and use it in GitHub Desktop.
Save jangler/3956997 to your computer and use it in GitHub Desktop.
CLI python2 / mutagen audio tag editor
#!/usr/bin/env python2
from argparse import ArgumentParser
from sys import stderr
import mutagen
def get_args():
parser = ArgumentParser(description='view and edit audio tags')
parser.add_argument('-t', '--title', type=str, help='set title')
parser.add_argument('-r', '--artist', type=str, help='set artist')
parser.add_argument('-l', '--album', type=str, help='set album')
parser.add_argument('-n', '--tracknumber', type=str,
help='set tracknumber')
parser.add_argument('-d', '--date', type=str, help='set date')
parser.add_argument('-v', '--view', action='store_true',
help='view tags after edits are made')
parser.add_argument('FILE', type=str, nargs='*',
help='files on which to operate')
return parser.parse_args()
def set_tag(audio, key, value, filename):
if value:
try:
audio[key] = value
except KeyError:
stderr.write('failed to set %s for %s\n' % (key, filename))
def run():
args = get_args()
tags = {
'title': args.title,
'artist': args.artist,
'album': args.album,
'tracknumber': args.tracknumber,
'date': args.date,
}
for filename in args.FILE:
try:
audio = mutagen.File(filename, easy=True)
except BaseException as e:
stderr.write(str(e) + '\n')
continue
if audio:
for key, value in tags.items():
set_tag(audio, key, value, filename)
if args.view:
print(filename + ':')
print(audio.pprint())
audio.save()
else:
stderr.write('failed to open file: %s\n' % filename)
if __name__ == '__main__':
run()
@wbob
Copy link

wbob commented Aug 13, 2020

in your past revisions, the audio.save() got lost, so changes are never written. Not sure about the .decode(), I thought argparse can handle the encoding.

--- a/tag.py
+++ b/tag.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 
 from argparse import ArgumentParser
 from sys import stderr
@@ -21,7 +21,6 @@ def get_args():
 
 def set_tag(audio, key, value, filename):
     if value:
-        value = value.decode('utf-8', 'replace')
         try:
             audio[key] = value
         except KeyError:
@@ -43,11 +42,13 @@ def run():
             stderr.write(str(e) + '\n')
             continue
         if audio:
-            for key, value in tags.items():
+            for key, value in list(tags.items()):
                 set_tag(audio, key, value, filename)
             if args.view:
                 print(filename + ':')
                 print(audio.pprint())
+            audio.save()
+

@jangler
Copy link
Author

jangler commented Aug 13, 2020

Yeah, a string can't be decoded, so that line of code shouldn't be there. Is there a reason for making tags.items() into a list? The object is iterable without doing that.

@wbob
Copy link

wbob commented Aug 13, 2020

list() is proposed by the 2to3 conversion util, and according to this answer errs on the safe side for python3 to provide the same snapshotting character of items() in python2.

But as the loop just set_tag()'s and no change occurs on the iterable, there's practical no difference for the code at hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment