Switch over media processor to use the get_workbench decorator (#565)
[mediagoblin.git] / mediagoblin / media_types / video / processing.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 from tempfile import NamedTemporaryFile
18 import logging
19
20 from mediagoblin import mg_globals as mgg
21 from mediagoblin.decorators import get_workbench
22 from mediagoblin.processing import \
23 create_pub_filepath, FilenameBuilder, BaseProcessingFail, ProgressCallback
24 from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
25
26 from . import transcoders
27
28 _log = logging.getLogger(__name__)
29 _log.setLevel(logging.DEBUG)
30
31
32 class VideoTranscodingFail(BaseProcessingFail):
33 '''
34 Error raised if video transcoding fails
35 '''
36 general_message = _(u'Video transcoding failed')
37
38
39 def sniff_handler(media_file, **kw):
40 transcoder = transcoders.VideoTranscoder()
41 data = transcoder.discover(media_file.name)
42
43 _log.debug('Discovered: {0}'.format(data))
44
45 if not data:
46 _log.error('Could not discover {0}'.format(
47 kw.get('media')))
48 return False
49
50 if data['is_video'] == True:
51 return True
52
53 return False
54
55 @get_workbench
56 def process_video(entry, workbench=None):
57 """
58 Process a video entry, transcode the queued media files (originals) and
59 create a thumbnail for the entry.
60
61 A Workbench() represents a local tempory dir. It is automatically
62 cleaned up when this function exits.
63 """
64 video_config = mgg.global_config['media_type:mediagoblin.media_types.video']
65
66 queued_filepath = entry.queued_media_file
67 queued_filename = workbench.localized_file(
68 mgg.queue_store, queued_filepath,
69 'source')
70 name_builder = FilenameBuilder(queued_filename)
71
72 medium_filepath = create_pub_filepath(
73 entry, name_builder.fill('{basename}-640p.webm'))
74
75 thumbnail_filepath = create_pub_filepath(
76 entry, name_builder.fill('{basename}.thumbnail.jpg'))
77
78 # Create a temporary file for the video destination
79 tmp_dst = NamedTemporaryFile(dir=workbench.dir)
80
81 with tmp_dst:
82 # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square
83 progress_callback = ProgressCallback(entry)
84 transcoder = transcoders.VideoTranscoder()
85 transcoder.transcode(queued_filename, tmp_dst.name,
86 vp8_quality=video_config['vp8_quality'],
87 vp8_threads=video_config['vp8_threads'],
88 vorbis_quality=video_config['vorbis_quality'],
89 progress_callback=progress_callback)
90
91 # Push transcoded video to public storage
92 _log.debug('Saving medium...')
93 # TODO (#419, we read everything in RAM here!)
94 mgg.public_store.get_file(medium_filepath, 'wb').write(
95 tmp_dst.read())
96 _log.debug('Saved medium')
97
98 entry.media_files['webm_640'] = medium_filepath
99
100 # Save the width and height of the transcoded video
101 entry.media_data_init(
102 width=transcoder.dst_data.videowidth,
103 height=transcoder.dst_data.videoheight)
104
105 # Create a temporary file for the video thumbnail
106 tmp_thumb = NamedTemporaryFile(dir=workbench.dir, suffix='.jpg')
107
108 with tmp_thumb:
109 # Create a thumbnail.jpg that fits in a 180x180 square
110 transcoders.VideoThumbnailerMarkII(
111 queued_filename,
112 tmp_thumb.name,
113 180)
114
115 # Push the thumbnail to public storage
116 _log.debug('Saving thumbnail...')
117 mgg.public_store.get_file(thumbnail_filepath, 'wb').write(
118 tmp_thumb.read())
119 _log.debug('Saved thumbnail')
120
121 entry.media_files['thumb'] = thumbnail_filepath
122
123 if video_config['keep_original']:
124 # Push original file to public storage
125 queued_file = file(queued_filename, 'rb')
126
127 with queued_file:
128 original_filepath = create_pub_filepath(
129 entry,
130 queued_filepath[-1])
131
132 with mgg.public_store.get_file(original_filepath, 'wb') as \
133 original_file:
134 _log.debug('Saving original...')
135 # TODO (#419, we read everything in RAM here!)
136 original_file.write(queued_file.read())
137 _log.debug('Saved original')
138
139 entry.media_files['original'] = original_filepath
140
141 mgg.queue_store.delete_file(queued_filepath)
142
143 # clean up workbench
144 workbench.destroy_self()