1# -*- coding: utf-8 -*-
2# This extension adjusts the volume of audio files to a standard level
3# Supported file formats are mp3 and ogg
4#
5# Requires: normalize-audio, mpg123
6#
7# (c) 2011-11-06 Bernd Schlapsi <brot@gmx.info>
8# Released under the same license terms as gPodder itself.
9
10import logging
11import os
12import subprocess
13
14import gpodder
15from gpodder import util
16
17logger = logging.getLogger(__name__)
18
19_ = gpodder.gettext
20
21__title__ = _('Normalize audio with re-encoding')
22__description__ = _('Normalize the volume of audio files with normalize-audio')
23__authors__ = 'Bernd Schlapsi <brot@gmx.info>'
24__doc__ = 'https://gpodder.github.io/docs/extensions/normalizeaudio.html'
25__payment__ = 'https://flattr.com/submit/auto?user_id=BerndSch&url=http://wiki.gpodder.org/wiki/Extensions/NormalizeAudio'
26__category__ = 'post-download'
27
28
29DefaultConfig = {
30    'context_menu': True,  # Show action in the episode list context menu
31}
32
33# a tuple of (extension, command)
34CONVERT_COMMANDS = {
35    '.ogg': 'normalize-ogg',
36    '.mp3': 'normalize-mp3',
37}
38
39
40class gPodderExtension:
41    MIME_TYPES = ('audio/mpeg', 'audio/ogg', )
42    EXT = ('.mp3', '.ogg', )
43
44    def __init__(self, container):
45        self.container = container
46
47        # Dependency check
48        self.container.require_command('normalize-ogg')
49        self.container.require_command('normalize-mp3')
50        self.container.require_command('normalize-audio')
51
52    def on_load(self):
53        logger.info('Extension "%s" is being loaded.' % __title__)
54
55    def on_unload(self):
56        logger.info('Extension "%s" is being unloaded.' % __title__)
57
58    def on_episode_downloaded(self, episode):
59        self._convert_episode(episode)
60
61    def on_episodes_context_menu(self, episodes):
62        if not self.container.config.context_menu:
63            return None
64
65        if not any(self._check_source(episode) for episode in episodes):
66            return None
67
68        return [(self.container.metadata.title, self.convert_episodes)]
69
70    def _check_source(self, episode):
71        if not episode.file_exists():
72            return False
73
74        if episode.mime_type in self.MIME_TYPES:
75            return True
76
77        if episode.extension() in self.EXT:
78            return True
79
80        return False
81
82    def _convert_episode(self, episode):
83        if episode.file_type() != 'audio':
84            return
85
86        filename = episode.local_filename(create=False)
87        if filename is None:
88            return
89
90        basename, extension = os.path.splitext(filename)
91
92        cmd = [CONVERT_COMMANDS.get(extension, 'normalize-audio'), filename]
93
94        if gpodder.ui.win32:
95            p = util.Popen(cmd)
96            p.wait()
97            stdout, stderr = ("<unavailable>",) * 2
98        else:
99            p = util.Popen(cmd, stdout=subprocess.PIPE,
100                    stderr=subprocess.PIPE)
101            stdout, stderr = p.communicate()
102
103        if p.returncode == 0:
104            logger.info('normalize-audio processing successful.')
105            gpodder.user_extensions.on_notification_show(_('File normalized'),
106                    episode.title)
107        else:
108            logger.warn('normalize-audio failed: %s / %s', stdout, stderr)
109
110    def convert_episodes(self, episodes):
111        for episode in episodes:
112            self._convert_episode(episode)
113