Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
271 lines (213 sloc) 8.23 KB
#!/usr/bin/python
# -*- coding: utf-8 -*-
####
# 01/2011 Bernd Schlapsi <brot@gmx.info>
#
# This script is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# gPodder is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Dependencies:
# * python-mutagen (Mutagen is a Python module to handle audio metadata)
#
# This extension script adds episode title and podcast title to the audio file
# The episode title is written into the title tag
# The podcast title is written into the album tag
import base64
import datetime
import logging
import mimetypes
import os
from mutagen import File
from mutagen.flac import Picture
from mutagen.id3 import APIC, ID3
from mutagen.mp3 import MP3
from mutagen.mp4 import MP4Cover, MP4Tags
import gpodder
from gpodder import coverart
logger = logging.getLogger(__name__)
# workaround for https://github.com/quodlibet/mutagen/issues/334
# can't add_tags to MP4 when file has no tag
MP4Tags._padding = 0
_ = gpodder.gettext
__title__ = _('Tag downloaded files using Mutagen')
__description__ = _('Add episode and podcast titles to MP3/OGG tags')
__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
__doc__ = 'http://wiki.gpodder.org/wiki/Extensions/Tagging'
__payment__ = 'https://flattr.com/submit/auto?user_id=BerndSch&url=http://wiki.gpodder.org/wiki/Extensions/Tagging'
__category__ = 'post-download'
DefaultConfig = {
'strip_album_from_title': True,
'genre_tag': 'Podcast',
'always_remove_tags': False,
'auto_embed_coverart': False,
'set_artist_to_album': False,
}
class AudioFile(object):
def __init__(self, filename, album, title, genre, pubDate, cover):
self.filename = filename
self.album = album
self.title = title
self.genre = genre
self.pubDate = pubDate
self.cover = cover
def remove_tags(self):
audio = File(self.filename, easy=True)
if audio.tags is not None:
audio.delete()
audio.save()
def write_basic_tags(self, set_artist_to_album):
audio = File(self.filename, easy=True)
if audio.tags is None:
audio.add_tags()
if self.album is not None:
audio.tags['album'] = self.album
if self.title is not None:
audio.tags['title'] = self.title
if self.genre is not None:
audio.tags['genre'] = self.genre
if self.pubDate is not None:
audio.tags['date'] = self.pubDate
if set_artist_to_album:
audio.tags['artist'] = self.album
audio.save()
def insert_coverart(self):
""" implement the cover art logic in the subclass
"""
None
def get_cover_picture(self, cover):
""" Returns mutagen Picture class for the cover image
Usefull for OGG and FLAC format
Picture type = cover image
see http://flac.sourceforge.net/documentation_tools_flac.html#encoding_options
"""
f = file(cover)
p = Picture()
p.type = 3
p.data = f.read()
p.mime = mimetypes.guess_type(cover)[0]
f.close()
return p
class OggFile(AudioFile):
def __init__(self, filename, album, title, genre, pubDate, cover):
super(OggFile, self).__init__(filename, album, title, genre, pubDate, cover)
def insert_coverart(self):
audio = File(self.filename, easy=True)
p = self.get_cover_picture(self.cover)
audio['METADATA_BLOCK_PICTURE'] = base64.b64encode(p.write())
audio.save()
class Mp4File(AudioFile):
def __init__(self, filename, album, title, genre, pubDate, cover):
super(Mp4File, self).__init__(filename, album, title, genre, pubDate, cover)
def insert_coverart(self):
audio = File(self.filename)
if self.cover.endswith('png'):
cover_format = MP4Cover.FORMAT_PNG
else:
cover_format = MP4Cover.FORMAT_JPEG
data = open(self.cover, 'rb').read()
audio.tags['covr'] = [MP4Cover(data, cover_format)]
audio.save()
class Mp3File(AudioFile):
def __init__(self, filename, album, title, genre, pubDate, cover):
super(Mp3File, self).__init__(filename, album, title, genre, pubDate, cover)
def insert_coverart(self):
audio = MP3(self.filename, ID3=ID3)
if audio.tags is None:
audio.add_tags()
audio.tags.add(
APIC(
encoding=3, # 3 is for utf-8
mime=mimetypes.guess_type(self.cover)[0],
type=3,
desc='Cover',
data=open(self.cover, 'rb').read()
)
)
audio.save()
class gPodderExtension:
def __init__(self, container):
self.container = container
def on_episode_downloaded(self, episode):
info = self.read_episode_info(episode)
if info['filename'] is None:
return
self.write_info2file(info, episode)
def get_audio(self, info, episode):
audio = None
cover = None
audioClass = None
if self.container.config.auto_embed_coverart:
cover = self.get_cover(episode.channel)
if info['filename'].endswith('.mp3'):
audioClass = Mp3File
elif info['filename'].endswith('.ogg'):
audioClass = OggFile
elif info['filename'].endswith('.m4a') or info['filename'].endswith('.mp4'):
audioClass = Mp4File
elif File(info['filename'], easy=True):
# mutagen can work with it: at least add basic tags
audioClass = AudioFile
if audioClass:
audio = audioClass(info['filename'],
info['album'],
info['title'],
info['genre'],
info['pubDate'],
cover)
return audio
def read_episode_info(self, episode):
info = {
'filename': None,
'album': None,
'title': None,
'genre': None,
'pubDate': None
}
# read filename (incl. file path) from gPodder database
info['filename'] = episode.local_filename(create=False, check_only=True)
if info['filename'] is None:
return
# read title+album from gPodder database
info['album'] = episode.channel.title
title = episode.title
if (self.container.config.strip_album_from_title and title and info['album'] and title.startswith(info['album'])):
info['title'] = title[len(info['album']):].lstrip()
else:
info['title'] = title
if self.container.config.genre_tag is not None:
info['genre'] = self.container.config.genre_tag
# convert pubDate to string
try:
pubDate = datetime.datetime.fromtimestamp(episode.pubDate)
info['pubDate'] = pubDate.strftime('%Y-%m-%d %H:%M')
except:
try:
# since version 3 the published date has a new/other name
pubDate = datetime.datetime.fromtimestamp(episode.published)
info['pubDate'] = pubDate.strftime('%Y-%m-%d %H:%M')
except:
info['pubDate'] = None
return info
def write_info2file(self, info, episode):
audio = self.get_audio(info, episode)
if self.container.config.always_remove_tags:
audio.remove_tags()
else:
audio.write_basic_tags(self.container.config.set_artist_to_album)
if self.container.config.auto_embed_coverart:
audio.insert_coverart()
logger.info('tagging.on_episode_downloaded(%s/%s)', episode.channel.title, episode.title)
def get_cover(self, podcast):
downloader = coverart.CoverDownloader()
return downloader.get_cover(podcast.cover_file, podcast.cover_url,
podcast.url, podcast.title, None, None, True)