]> git.0d.be Git - django-panik-emissions.git/blob - emissions/management/commands/create-sound-files.py
create-sound-files: extend copy to all source formats
[django-panik-emissions.git] / emissions / management / commands / create-sound-files.py
1 from __future__ import print_function
2
3 import base64
4 import mutagen
5 import mutagen.mp3
6 import shutil
7 import os
8 import subprocess
9
10 from django.core.management.base import BaseCommand, CommandError
11
12 from ...models import SoundFile
13
14
15 class Command(BaseCommand):
16
17     def add_arguments(self, parser):
18         parser.add_argument(
19             '--force',
20             action='store_true',
21             dest='force',
22             default=False,
23             help='Create files even if they exist')
24         parser.add_argument('--reset-metadata',
25             action='store_true',
26             dest='reset_metadata',
27             default=False,
28             help='Reset metadata on all files')
29         parser.add_argument('--emission',
30             dest='emission',
31             metavar='EMISSION',
32             default=None,
33             help='Process files belonging to emission only')
34         parser.add_argument('--episode',
35             dest='episode',
36             metavar='EPISODE',
37             default=None,
38             help='Process files belonging to episode only')
39         parser.add_argument('--copy',
40             action='store_true',
41             dest='copy',
42             default=False,
43             help='Copy initial file')
44
45     def handle(self, force, reset_metadata, copy, emission, episode, verbosity, **kwargs):
46         self.verbose = (verbosity > 1)
47         self.copy = copy
48
49         for soundfile in SoundFile.objects.select_related().exclude(podcastable=False):
50             if emission and soundfile.episode.emission.slug != emission:
51                 continue
52             if episode and soundfile.episode.slug != episode:
53                 continue
54             try:
55                 if soundfile.file is None or not os.path.exists(soundfile.file.path):
56                     continue
57             except ValueError:  # no file associated with it
58                 continue
59             for format in ('ogg', 'mp3'):
60                 file_path = soundfile.get_format_path(format)
61                 created = False
62                 if not os.path.exists(file_path) or force:
63                     created = self.create(soundfile, format)
64                 if created or reset_metadata:
65                     self.set_metadata(soundfile, format)
66             if (force or not soundfile.duration) and os.path.exists(soundfile.get_format_path('ogg')):
67                 cmd = ['soxi', '-D', soundfile.get_format_path('ogg')]
68                 soundfile.duration = int(float(subprocess.check_output(cmd)))
69                 soundfile.save()
70
71
72     def create(self, soundfile, format):
73         file_path = soundfile.get_format_path(format)
74         if not os.path.exists(os.path.dirname(file_path)):
75             os.mkdir(os.path.dirname(file_path))
76
77         cmd = ['ffmpeg', '-y', '-i', soundfile.file.path]
78
79         if self.copy and os.path.splitext(soundfile.file.path)[-1].strip('.') == format:
80             shutil.copy(soundfile.file.path, file_path)
81             return
82
83         if format == 'ogg':
84             cmd.extend(['-q:a', '5'])
85         elif format == 'mp3':
86             cmd.extend(['-q:a', '4'])
87
88         cmd.append(file_path)
89
90         if self.verbose:
91             print('creating', file_path)
92             print('  ', ' '.join(cmd))
93         else:
94             cmd[1:1] = ['-loglevel', 'quiet']
95
96         subprocess.call(cmd)
97         return os.path.exists(file_path)
98
99     def set_metadata(self, soundfile, format):
100         file_path = soundfile.get_format_path(format)
101
102         audio = mutagen.File(file_path, easy=True)
103
104         if 'comment' in audio:
105             del audio['comment']
106         if soundfile.fragment is True and soundfile.title:
107             audio['title'] = '%s - %s' % (
108                     soundfile.episode.title,
109                     soundfile.title)
110         else:
111             audio['title'] = soundfile.episode.title
112         audio['album'] = soundfile.episode.emission.title
113         audio['artist'] = 'Radio Panik'
114
115         if soundfile.episode.image or soundfile.episode.emission.image:
116             image = (soundfile.episode.image or soundfile.episode.emission.image)
117             image.file.open()
118             if os.path.splitext(image.path)[1].lower() in ('.jpeg', '.jpg'):
119                 mimetype = 'image/jpeg'
120             elif os.path.splitext(image.path)[1].lower() == '.png':
121                 mimetype = 'image/png'
122             else:
123                 mimetype = None
124             if mimetype:
125                 if format == 'ogg':
126                     audio['coverartmime'] = mimetype
127                     audio['coverartdescription'] = 'image'
128                     audio['coverart'] = base64.encodebytes(image.read()).replace(b'\n', b'').decode('ascii')
129                 elif format == 'mp3':
130                     audio.save()
131                     audio = mutagen.mp3.MP3(file_path)
132                     audio.tags.add(mutagen.id3.APIC(
133                             encoding=3, description='image',
134                             type=3, mime=mimetype, data=image.read()))
135             image.close()
136
137         audio.save()