cis: torrent files creation
[living-lab-site.git] / cis / api / base.py
1 #!/usr/bin/env python
2
3 """
4 Base classes for the external programs API.
5 """
6
7 import api_exceptions
8 import re
9 import cis_util
10 import random
11
12 class BaseTranscoder:
13     """
14     Abstraction of the API class for the transcoder program. 
15     """
16
17     prog_bin = None
18     input_file = None
19     output_file = None
20     dest_path = ''
21     name = None
22
23     # Recommended formats.
24     containers = {
25         "avi": None,
26         "flv": None,
27         "mp4": None,
28         "ogg": None,
29         "webm": None,
30         "mpegts": None
31     }
32     # File extensions by container. First value is for audio files and the
33     # second one is for (audio-)video files.
34     extensions = {
35         "avi": ["avi", "avi"],
36         "flv": ["flv", "flv"],
37         "mp4": ["mp4", "mp4"],
38         "ogg": ["oga", "ogv"],
39         "webm": ["webm", "webm"],
40         "mpegts": ["mts", "mts"]
41     }
42     a_codecs = {
43         "mp3": None,
44         "vorbis": None
45     }
46     v_codecs = {
47         "h264": None,
48         "theora": None,
49         "vp8": None
50     }
51
52     def __init__(self, input_file, name=None, prog_bin=None):
53         self.input_file = input_file
54         if prog_bin is not None:
55             self.prog_bin = prog_bin
56         
57         if name is None:
58             name = cis_util.get_name(input_file)
59
60         self.name = name
61
62     def transcode(self, container, a_codec=None, v_codec=None,
63             a_bitrate=None, a_samplingrate=None, a_channels=None,
64             v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
65         """
66         Transcodes the input file to an audio-video file.
67
68         container: possible values are listed in containers member as keys
69         a_codec: possible values are listed in a_codecs member as keys
70         v_codec: possible values are listed in v_codecs member as keys
71         a_bitrate: (numeric) audio bit rate
72         a_samplingrate: (numeric) audio sampling rate in Hz
73         a_channels: (numeric) number of audio channels
74         v_bitrate: (numeric) video bit rate
75         v_framerate: (numeric) number of frames per second for a video
76         v_resolution: (string) video image size as <width>x<height>
77         v_dar: video display aspect ratio as <den>x<num> or float
78         """
79
80         # Check parameters.
81         if a_codec is None and v_codec is None:
82             raise ValueError('No audio or video codec specified.')
83
84         if a_codec is not None and type(a_codec) is not str:
85             raise TypeError('Audio codec must be string.')
86
87         if v_codec is not None and type(v_codec) is not str:
88             raise TypeError('Video codec must be string.')
89
90         if a_samplingrate is not None and type(a_samplingrate) is not int:
91             raise TypeError('Audio sampling rate must be an integer.')
92
93         if a_channels is not None and type(a_channels) is not int:
94             raise TypeError('Audio channels parameter must be an integer.')
95
96         if v_framerate is not None and type(v_framerate) is not int:
97             raise TypeError('Video frate rate must be an integer.')
98
99         if v_resolution is not None \
100                 and re.match('[\d]+x[\d]+', v_resolution) is None:
101             raise ValueError('Video resolution must be a string like <width>x<height>.')
102
103         if v_dar is not None and (type(v_dar) is not float \
104                 and re.match('[\d]+:[\d]+', v_dar) is None):
105             raise ValueError('Video display aspect ratio must be a float or a string like <den>:<num>.')
106
107         self.output_file = self.dest_path + self.name
108         if v_resolution is not None:
109             self.output_file += '_'
110             self.output_file += v_resolution[(v_resolution.rindex('x')+1):]
111             self.output_file += 'p'
112         ext = self.tr_extension(container, (v_codec is not None))
113         if ext is not None:
114             self.output_file += '.' + ext
115
116         self._transcode(self.tr_container(container),
117                 self.tr_a_codec(a_codec), self.tr_v_codec(v_codec),
118                 a_bitrate, a_samplingrate, a_channels,
119                 v_bitrate, v_framerate, v_resolution, v_dar)
120
121     def _transcode(self, container, a_codec=None, v_codec=None,
122             a_bitrate=None, a_samplingrate=None, a_channels=None,
123             v_bitrate=None, v_framerate=None, v_resolution=None, v_dar=None):
124         """
125         Called by transcode; must be overridden by a child class which
126         effectively transcodes the input file.
127         """
128         pass
129
130     def tr_container(self, name):
131         """ Translates container API name into external program identifier."""
132
133         if not self.containers.has_key(name) or self.containers[name] is None:
134             raise api_exceptions.NotImplementedException("Container " + name)
135
136         return self.containers[name]
137
138     def tr_extension(self, name, video=True):
139         """ Translates container API name into file extension."""
140
141         if video is True:
142             i = 1
143         else:
144             i = 0
145
146         if not self.extensions.has_key(name) or self.extensions[name] is None:
147             return None
148
149         return self.extensions[name][i]
150
151     def tr_a_codec(self, name):
152         """ Translates audio codec API name into external program identifier."""
153
154         if not self.a_codecs.has_key(name) or self.a_codecs[name] is None:
155             raise api_exceptions.NotImplementedException("Audio Codec " + name)
156
157         return self.a_codecs[name]
158
159     def tr_v_codec(self, name):
160         """ Translates video codec API name into external program identifier."""
161
162         if not self.v_codecs.has_key(name) or self.v_codecs[name] is None:
163             raise api_exceptions.NotImplementedException("Video Codec " + name)
164
165         return self.v_codecs[name]
166
167
168 class BaseThumbExtractor:
169     """
170     Abstraction of the API class for the thumbnail extraction program. 
171
172     Thumbnail extracted are in JPEG format.
173     """
174
175     prog_bin = None
176     input_file = None
177     dest_path = ''
178     name = None
179     
180     def __init__(self, input_file, name=None, prog_bin=None):
181         self.input_file = input_file
182         if prog_bin is not None:
183             self.prog_bin = prog_bin
184         
185         if name is None:
186             name = cis_util.get_name(input_file)
187
188         self.name = name
189
190     def extract_thumb(self, seek_pos, resolution="120x90", index=0):
191         """
192         Extracts a thumbnail from the video from a specified position
193         expressed in seconds (int/float).
194
195         index: an index appended to the image name in order to avoid
196         overwriting.
197         """
198         pass
199
200     def extract_random_thumb(self, resolution="120x90", index=0):
201         """
202         Extracts a thumbnail from the video from a random position.
203         """
204         duration = self.get_video_duration()
205         seek_pos = random.random() * duration
206         self.extract_thumb(seek_pos, resolution, index)
207
208     def extract_summary_thumbs(self, count, resolution="120x90"):
209         """
210         Extracts a series of thumbnails from a video by taking several
211         snapshots.
212
213         The snapshots are taken from equally spaced positions such that
214         `count` thumbs are extracted.
215         """
216         duration = self.get_video_duration()
217         interval = duration / (count + 1)
218         
219         n_thumbs_extracted = 0
220         seek_pos = interval
221         for index in range (0, count):
222             thumb_extracted = True
223             try:
224                 self.extract_thumb(seek_pos, resolution, index)
225             except api_exceptions.ThumbExtractionException as e:
226                 thumb_extracted = False
227
228             if thumb_extracted:
229                 n_thumbs_extracted += 1
230
231             seek_pos += interval
232
233         return n_thumbs_extracted
234
235     def get_output_file_name(self, index):
236         """ Returns the name required as output file name based on index. """
237         output_file_name = self.dest_path + self.name \
238                 + '_t' + ("%02d" % index) + '.jpg'
239         return output_file_name
240
241     def get_video_duration(self):
242         pass
243
244
245 class BaseFileTransferer:
246     """
247     Ensures file transfer from the Web Server to the CIS (here).
248     
249     Several implementations can be done by extending this class for
250     file transfer protocol such as FTP, SCP, RSYNC, HTTP etc.
251     """
252     
253     local_path = ''
254     remote_path = ''
255     
256     def __init__(self, local_path='', remote_path=''):
257         """ Initialize by setting local and remote paths for file transfer. """
258         self.local_path = local_path
259         self.remote_path = remote_path
260
261     def __del__(self):
262         self.close()
263
264     def get(self, files):
265         """
266         Transfers files locally from the Web Server.
267
268         files: a list of file name strings
269         """
270         pass
271
272     def put(self, files):
273         """
274         Transfers files from the Web Server locally.
275
276         files: a list of file name strings
277         """
278         pass
279
280     def close(self):
281         """
282         This method should be called when the instance is no longer required.
283
284         Class's destructor calls this method.
285         """
286         pass
287
288
289 class BaseAVInfo:
290     @staticmethod
291     def get_video_duration(input_file):
292         """
293         Returns the number of seconds of a video (int/float).
294         """
295         pass