diff --git a/core/utils/audio/mp3.py b/core/utils/audio/mp3.py index f2634b0..602e74f 100755 --- a/core/utils/audio/mp3.py +++ b/core/utils/audio/mp3.py @@ -11,29 +11,19 @@ def mp3_length(source_file): except IOError: raise Mp3FileNotFoundException("Audio file not found: %s" % source_file) -def tag_mp3(source_file, artist, title, url="", album="", year="", comment="", image_file="", genre=[]): - #mp3Object.tags.add(APIC(encoding=3, mime=image[1], type=3, desc=u'Cover', data=open(image[0]).read())) +def tag_mp3(source_file, artist, title, url="", album="", year="", comment="", genres=""): try: - audio = ID3(source_file) - except mutagen.id3.error: - audio = ID3() + audio = EasyID3(source_file) + except mutagen.id3.ID3NoHeaderError: + audio = mutagen.File(source_file, easy=True) + audio.add_tags() - audio.add(TPE1(encoding=3, text=unicode(artist))) - audio.add(TIT2(encoding=3, text=unicode(title))) - audio.add(TALB(encoding=3, text=unicode(album))) - audio.add(TCON(encoding=3, text=unicode("Deep House"))) - audio.add(TPE1(encoding=3, text=unicode(artist))) - audio.add(COMM(encoding=3, lang="eng", desc="", text=unicode(comment))) - audio.add(TDRC(encoding=3, text=unicode(year))) - - image = mutagen.id3.APIC( - encoding=3, - mime='image/jpeg', - type=2, - desc=u'Cover', - data=open(image_file, 'rb').read() - ) - audio.add(image) - audio.save(source_file) + audio["artist"] = artist + audio["title"] = title + audio["genre"] = genres + audio["website"] = url + audio["copyright"] = "Deep South Sounds" + audio["album"] = album + audio.save(v1=2) diff --git a/spa/management/commands/tagmix.py b/spa/management/commands/tagmix.py index 2abb50d..592ec98 100644 --- a/spa/management/commands/tagmix.py +++ b/spa/management/commands/tagmix.py @@ -1,17 +1,32 @@ +import logging +import os from django.core.management.base import NoArgsCommand from spa.models.mix import Mix +from utils import query_yes_no class Command(NoArgsCommand): help = "Create tracklists for all mixes" + logger = logging.getLogger(__name__) def handle_noargs(self, **options): - print "Tagging audio files" - mixes = Mix.objects.filter(uid='1fb5bd5d-e32a-4e0d-9321-014587d53327') - #mixes = Mix.objects.all() - for mix in mixes: - print "Tagging: %s" % mix.title - mix.create_mp3_tags() + self.logger.debug("Tagging audio files") + #mixes = Mix.objects.filter(uid='1348680a-507d-4a1e-a828-dffc90191c5b') + mixes = Mix.objects.filter(mp3tags_updated=False) + try: + for mix in mixes: + self.logger.debug("Tagging: %s" % mix.title) + mix_file = mix.get_absolute_path() + if not os.path.isfile(mix_file): + result = query_yes_no("Mix file %s\n\t(%s - %s)\ndoes not exist, delete mix entry?" % ( + mix_file, mix.title, mix.slug)) + if result: + mix.delete() + else: + mix.create_mp3_tags() + mix.mp3tags_updated = True + mix.save() + except Exception: + self.logger.exception("Error tagging mix: %s" % mix.uid) print "Finished tagging" - diff --git a/spa/models/mix.py b/spa/models/mix.py index d8c5f5b..5be7d0a 100755 --- a/spa/models/mix.py +++ b/spa/models/mix.py @@ -60,6 +60,7 @@ class Mix(_BaseModel): is_featured = models.BooleanField(default=True) user = models.ForeignKey(UserProfile, editable=False, related_name='mixes') waveform_generated = models.BooleanField(default=False) + mp3tags_updated = models.BooleanField(default=False) uid = models.CharField(max_length=38, blank=True, unique=True) filetype = models.CharField(max_length=10, blank=False, default="mp3") download_allowed = models.BooleanField(default=False) @@ -96,7 +97,6 @@ class Mix(_BaseModel): def create_mp3_tags(self, prefix=""): try: - image = get_thumbnail(self.mix_image, '300x300', crop='center') tag_mp3( self.get_absolute_path(), artist=self.user.get_nice_name(), @@ -105,14 +105,16 @@ class Mix(_BaseModel): album="Deep South Sounds Mixes", year=self.upload_date.year, comment=self.description, - image_file=os.path.join(image.storage.base_location, image.name), - genre=self.genres) + genres=self.get_nice_genres()) except Exception, ex: self.logger.exception("Mix: error creating tags: %s" % ex.message) pass return '%s/mixes/%s%s.%s' % (settings.MEDIA_ROOT, prefix, self.uid, self.filetype) + def get_nice_genres(self): + return ", ".join(list(self.genres.all().values_list("description", flat=True))) + def get_cache_path(self, prefix=""): return '%s/mixes/%s%s.%s' % (settings.CACHE_ROOT, prefix, self.uid, self.filetype) diff --git a/utils.py b/utils.py index d4ce76b..8458930 100755 --- a/utils.py +++ b/utils.py @@ -1,5 +1,39 @@ import os +import sys def here(x): return os.path.join(os.path.abspath(os.path.dirname(__file__)), x) + + +def query_yes_no(question, default="yes"): + """Ask a yes/no question via raw_input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is one of "yes" or "no". + """ + valid = {"yes": True, "y": True, "ye": True, + "no": False, "n": False} + + if default is None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + sys.stdout.write(question + prompt) + choice = raw_input().lower() + if default is not None and choice == '': + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n") \ No newline at end of file