Commit | Line | Data |
---|---|---|
93bdab9d | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
cf29e8a8 | 2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
93bdab9d JW |
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 | ||
25e39842 | 17 | from tempfile import NamedTemporaryFile |
e9c1b938 | 18 | import logging |
93bdab9d | 19 | |
93bdab9d | 20 | from mediagoblin import mg_globals as mgg |
196a5181 | 21 | from mediagoblin.processing import \ |
64712915 | 22 | create_pub_filepath, FilenameBuilder, BaseProcessingFail, ProgressCallback |
51eb0267 JW |
23 | from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ |
24 | ||
26729e02 | 25 | from . import transcoders |
5c754fda JW |
26 | from .util import skip_transcode |
27 | ||
1f255101 | 28 | |
8e5f9746 JW |
29 | _log = logging.getLogger(__name__) |
30 | _log.setLevel(logging.DEBUG) | |
93bdab9d JW |
31 | |
32 | ||
51eb0267 JW |
33 | class VideoTranscodingFail(BaseProcessingFail): |
34 | ''' | |
35 | Error raised if video transcoding fails | |
36 | ''' | |
37 | general_message = _(u'Video transcoding failed') | |
38 | ||
39 | ||
ec4261a4 | 40 | def sniff_handler(media_file, **kw): |
10085b77 | 41 | transcoder = transcoders.VideoTranscoder() |
4f4f2531 | 42 | data = transcoder.discover(media_file.name) |
10085b77 | 43 | |
4f4f2531 | 44 | _log.debug('Discovered: {0}'.format(data)) |
10085b77 | 45 | |
4f4f2531 JW |
46 | if not data: |
47 | _log.error('Could not discover {0}'.format( | |
10085b77 | 48 | kw.get('media'))) |
4f4f2531 | 49 | return False |
26729e02 | 50 | |
4f4f2531 JW |
51 | if data['is_video'] == True: |
52 | return True | |
26729e02 | 53 | |
ec4261a4 | 54 | return False |
93bdab9d | 55 | |
bfd68cce | 56 | |
fb46fa66 | 57 | def process_video(proc_state): |
93bdab9d | 58 | """ |
5a34a80d JW |
59 | Process a video entry, transcode the queued media files (originals) and |
60 | create a thumbnail for the entry. | |
45ab3e07 SS |
61 | |
62 | A Workbench() represents a local tempory dir. It is automatically | |
63 | cleaned up when this function exits. | |
93bdab9d | 64 | """ |
fb46fa66 | 65 | entry = proc_state.entry |
bfd68cce | 66 | workbench = proc_state.workbench |
23caf305 CAW |
67 | video_config = mgg.global_config['media_type:mediagoblin.media_types.video'] |
68 | ||
8545cfc9 | 69 | queued_filepath = entry.queued_media_file |
bfd68cce | 70 | queued_filename = proc_state.get_queued_filename() |
28f364bd | 71 | name_builder = FilenameBuilder(queued_filename) |
93bdab9d | 72 | |
e9c1b938 | 73 | medium_filepath = create_pub_filepath( |
28f364bd | 74 | entry, name_builder.fill('{basename}-640p.webm')) |
93bdab9d | 75 | |
e9c1b938 | 76 | thumbnail_filepath = create_pub_filepath( |
28f364bd | 77 | entry, name_builder.fill('{basename}.thumbnail.jpg')) |
93bdab9d | 78 | |
c00e18fe SS |
79 | # Create a temporary file for the video destination (cleaned up with workbench) |
80 | tmp_dst = NamedTemporaryFile(dir=workbench.dir, delete=False) | |
e9c1b938 JW |
81 | with tmp_dst: |
82 | # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square | |
64712915 | 83 | progress_callback = ProgressCallback(entry) |
93bdab9d | 84 | |
5c754fda JW |
85 | dimensions = ( |
86 | mgg.global_config['media:medium']['max_width'], | |
87 | mgg.global_config['media:medium']['max_height']) | |
88 | ||
29adab46 | 89 | # Extract metadata and keep a record of it |
5c754fda | 90 | metadata = transcoders.VideoTranscoder().discover(queued_filename) |
29adab46 | 91 | store_metadata(entry, metadata) |
5c754fda | 92 | |
29adab46 CAW |
93 | # Figure out whether or not we need to transcode this video or |
94 | # if we can skip it | |
5c754fda JW |
95 | if skip_transcode(metadata): |
96 | _log.debug('Skipping transcoding') | |
97 | # Just push the submitted file to the tmp_dst | |
98 | open(tmp_dst.name, 'wb').write(open(queued_filename, 'rb').read()) | |
99 | ||
100 | dst_dimensions = metadata['videowidth'], metadata['videoheight'] | |
fd693e36 CAW |
101 | |
102 | # Push original file to public storage | |
103 | _log.debug('Saving original...') | |
104 | proc_state.copy_original(queued_filepath[-1]) | |
105 | ||
106 | did_transcode = False | |
5c754fda JW |
107 | else: |
108 | transcoder = transcoders.VideoTranscoder() | |
109 | ||
110 | transcoder.transcode(queued_filename, tmp_dst.name, | |
111 | vp8_quality=video_config['vp8_quality'], | |
112 | vp8_threads=video_config['vp8_threads'], | |
113 | vorbis_quality=video_config['vorbis_quality'], | |
114 | progress_callback=progress_callback, | |
115 | dimensions=dimensions) | |
116 | ||
117 | dst_dimensions = transcoder.dst_data.videowidth,\ | |
118 | transcoder.dst_data.videoheight | |
119 | ||
fd693e36 CAW |
120 | # Push transcoded video to public storage |
121 | _log.debug('Saving medium...') | |
122 | mgg.public_store.copy_local_to_storage(tmp_dst.name, medium_filepath) | |
123 | _log.debug('Saved medium') | |
124 | ||
125 | entry.media_files['webm_640'] = medium_filepath | |
e9c1b938 | 126 | |
fd693e36 | 127 | did_transcode = True |
e9c1b938 | 128 | |
5c754fda JW |
129 | # Save the width and height of the transcoded video |
130 | entry.media_data_init( | |
131 | width=dst_dimensions[0], | |
132 | height=dst_dimensions[1]) | |
93bdab9d | 133 | |
c00e18fe SS |
134 | # Temporary file for the video thumbnail (cleaned up with workbench) |
135 | tmp_thumb = NamedTemporaryFile(dir=workbench.dir, suffix='.jpg', delete=False) | |
93bdab9d | 136 | |
e9c1b938 JW |
137 | with tmp_thumb: |
138 | # Create a thumbnail.jpg that fits in a 180x180 square | |
e4a1b6d2 JW |
139 | transcoders.VideoThumbnailerMarkII( |
140 | queued_filename, | |
141 | tmp_thumb.name, | |
142 | 180) | |
1f255101 | 143 | |
5c754fda JW |
144 | # Push the thumbnail to public storage |
145 | _log.debug('Saving thumbnail...') | |
146 | mgg.public_store.copy_local_to_storage(tmp_thumb.name, thumbnail_filepath) | |
147 | entry.media_files['thumb'] = thumbnail_filepath | |
93bdab9d | 148 | |
fd693e36 CAW |
149 | # save the original... but only if we did a transcoding |
150 | # (if we skipped transcoding and just kept the original anyway as the main | |
151 | # media, then why would we save the original twice?) | |
152 | if video_config['keep_original'] and did_transcode: | |
23caf305 | 153 | # Push original file to public storage |
c00e18fe | 154 | _log.debug('Saving original...') |
715ea495 | 155 | proc_state.copy_original(queued_filepath[-1]) |
93bdab9d | 156 | |
bfd68cce E |
157 | # Remove queued media file from storage and database |
158 | proc_state.delete_queue_file() | |
29adab46 CAW |
159 | |
160 | ||
161 | def store_metadata(media_entry, metadata): | |
162 | """ | |
163 | Store metadata from this video for this media entry. | |
164 | """ | |
165 | # Let's pull out the easy, not having to be converted ones first | |
166 | stored_metadata = dict( | |
167 | [(key, metadata[key]) | |
168 | for key in [ | |
169 | "videoheight", "videolength", "videowidth", | |
170 | "audiorate", "audiolength", "audiochannels", "audiowidth", | |
171 | "mimetype", "tags"] | |
172 | if key in metadata]) | |
173 | ||
174 | # We have to convert videorate into a sequence because it's a | |
175 | # special type normally.. | |
176 | ||
177 | if "videorate" in metadata: | |
178 | videorate = metadata["videorate"] | |
179 | stored_metadata["videorate"] = [videorate.num, videorate.denom] | |
180 | ||
181 | media_entry.media_data_init( | |
182 | orig_metadata=stored_metadata) |