1 from __future__ import print_function
10 from django.core.management.base import BaseCommand, CommandError
12 from ...models import SoundFile
15 def get_bitrate(filename):
16 p = subprocess.Popen(['mediainfo', '--Inform=Audio;%BitRate%', filename],
18 stdin=subprocess.PIPE,
19 stdout=subprocess.PIPE,
20 stderr=subprocess.PIPE)
21 stdout, stderr = p.communicate()
23 return int(stdout) / 1000
28 class Command(BaseCommand):
30 def add_arguments(self, parser):
36 help='Create files even if they exist')
37 parser.add_argument('--reset-metadata',
39 dest='reset_metadata',
41 help='Reset metadata on all files')
42 parser.add_argument('--emission',
46 help='Process files belonging to emission only')
47 parser.add_argument('--episode',
51 help='Process files belonging to episode only')
52 parser.add_argument('--formats',
56 parser.add_argument('--copy',
60 help='Copy initial file')
61 parser.add_argument('--link',
65 help='Link initial file')
67 def handle(self, force, reset_metadata, copy, link, emission, episode, verbosity, formats, **kwargs):
68 self.verbose = (verbosity > 1)
72 for soundfile in SoundFile.objects.select_related():
73 if emission and soundfile.episode.emission.slug != emission:
75 if episode and soundfile.episode.slug != episode:
78 if soundfile.file is None or not os.path.exists(soundfile.file.path):
80 except ValueError: # no file associated with it
82 if not soundfile.podcastable:
83 # get duration using initial file
84 if not soundfile.duration:
85 cmd = ['soxi', '-D', soundfile.file.path]
87 soundfile.duration = int(float(subprocess.check_output(cmd)))
88 except (ValueError, subprocess.CalledProcessError):
93 for format in formats.split(','):
94 file_path = soundfile.get_format_path(format)
96 if not os.path.exists(file_path) or force:
97 created = self.create(soundfile, format)
98 if created or reset_metadata:
99 self.set_metadata(soundfile, format)
100 if (force or not soundfile.duration):
101 for extension in ('ogg', 'mp3'):
102 soundfile_name = soundfile.get_format_path(extension)
103 if os.path.exists(soundfile_name):
104 cmd = ['soxi', '-D', soundfile_name]
105 soundfile.duration = int(float(subprocess.check_output(cmd)))
110 def create(self, soundfile, format):
111 file_path = soundfile.get_format_path(format)
112 if not os.path.exists(os.path.dirname(file_path)):
113 os.makedirs(os.path.dirname(file_path))
115 if self.copy and os.path.splitext(soundfile.file.path)[-1].strip('.') == format:
116 shutil.copy(soundfile.file.path, file_path)
119 if self.link and os.path.splitext(soundfile.file.path)[-1].strip('.') == format:
120 os.symlink(soundfile.file.path, file_path)
123 vorbis_rates = [64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 500]
124 orig_bitrate = get_bitrate(soundfile.file.path)
127 if orig_bitrate is not None:
128 if soundfile.episode.emission.podcast_sound_quality == 'high':
131 # cap quality to original bitrate (using vorbis quality mapping)
132 for i, rate in enumerate(vorbis_rates):
133 if orig_bitrate < rate:
134 vorbis_q = min((i, vorbis_q))
135 mp3_q = min((i, mp3_q))
138 cmd = ['ffmpeg', '-y', '-i', soundfile.file.path]
140 cmd.extend(['-q:a', str(vorbis_q)])
141 elif format == 'mp3':
142 cmd.extend(['-q:a', str(mp3_q)])
144 cmd.append(file_path)
147 print('creating', file_path)
148 print(' ', ' '.join(cmd))
150 cmd[1:1] = ['-loglevel', 'quiet']
153 return os.path.exists(file_path)
155 def set_metadata(self, soundfile, format):
156 file_path = soundfile.get_format_path(format)
158 audio = mutagen.File(file_path, easy=True)
160 if 'comment' in audio:
162 if soundfile.fragment is True and soundfile.title:
163 audio['title'] = '%s - %s' % (
164 soundfile.episode.title,
167 audio['title'] = soundfile.episode.title
168 audio['album'] = soundfile.episode.emission.title
169 audio['artist'] = 'Radio Panik'
171 if soundfile.episode.image or soundfile.episode.emission.image:
172 image = (soundfile.episode.image or soundfile.episode.emission.image)
174 if os.path.splitext(image.path)[1].lower() in ('.jpeg', '.jpg'):
175 mimetype = 'image/jpeg'
176 elif os.path.splitext(image.path)[1].lower() == '.png':
177 mimetype = 'image/png'
182 audio['coverartmime'] = mimetype
183 audio['coverartdescription'] = 'image'
184 audio['coverart'] = base64.encodebytes(image.read()).replace(b'\n', b'').decode('ascii')
185 elif format == 'mp3':
187 audio = mutagen.mp3.MP3(file_path)
188 audio.tags.add(mutagen.id3.APIC(
189 encoding=3, description='image',
190 type=3, mime=mimetype, data=image.read()))