1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
17 from tempfile
import NamedTemporaryFile
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 _
26 from . import transcoders
28 _log
= logging
.getLogger(__name__
)
29 _log
.setLevel(logging
.DEBUG
)
32 class VideoTranscodingFail(BaseProcessingFail
):
34 Error raised if video transcoding fails
36 general_message
= _(u
'Video transcoding failed')
39 def sniff_handler(media_file
, **kw
):
40 transcoder
= transcoders
.VideoTranscoder()
41 data
= transcoder
.discover(media_file
.name
)
43 _log
.debug('Discovered: {0}'.format(data
))
46 _log
.error('Could not discover {0}'.format(
50 if data
['is_video'] == True:
56 def process_video(entry
, workbench
=None):
58 Process a video entry, transcode the queued media files (originals) and
59 create a thumbnail for the entry.
61 A Workbench() represents a local tempory dir. It is automatically
62 cleaned up when this function exits.
64 video_config
= mgg
.global_config
['media_type:mediagoblin.media_types.video']
66 queued_filepath
= entry
.queued_media_file
67 queued_filename
= workbench
.localized_file(
68 mgg
.queue_store
, queued_filepath
,
70 name_builder
= FilenameBuilder(queued_filename
)
72 medium_filepath
= create_pub_filepath(
73 entry
, name_builder
.fill('{basename}-640p.webm'))
75 thumbnail_filepath
= create_pub_filepath(
76 entry
, name_builder
.fill('{basename}.thumbnail.jpg'))
78 # Create a temporary file for the video destination
79 tmp_dst
= NamedTemporaryFile(dir=workbench
.dir)
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
)
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(
96 _log
.debug('Saved medium')
98 entry
.media_files
['webm_640'] = medium_filepath
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
)
105 # Create a temporary file for the video thumbnail
106 tmp_thumb
= NamedTemporaryFile(dir=workbench
.dir, suffix
='.jpg')
109 # Create a thumbnail.jpg that fits in a 180x180 square
110 transcoders
.VideoThumbnailerMarkII(
115 # Push the thumbnail to public storage
116 _log
.debug('Saving thumbnail...')
117 mgg
.public_store
.get_file(thumbnail_filepath
, 'wb').write(
119 _log
.debug('Saved thumbnail')
121 entry
.media_files
['thumb'] = thumbnail_filepath
123 if video_config
['keep_original']:
124 # Push original file to public storage
125 queued_file
= file(queued_filename
, 'rb')
128 original_filepath
= create_pub_filepath(
132 with mgg
.public_store
.get_file(original_filepath
, 'wb') as \
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')
139 entry
.media_files
['original'] = original_filepath
141 mgg
.queue_store
.delete_file(queued_filepath
)
144 workbench
.destroy_self()