Changed media processing delegation to a 'sniffing' method
[mediagoblin.git] / mediagoblin / submit / views.py
CommitLineData
e323a068 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
e323a068
CAW
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
2c437493 17import mediagoblin.mg_globals as mg_globals
4a477e24 18import uuid
bb49e56f 19from os.path import splitext
03afc828
CAW
20from cgi import FieldStorage
21
8e5f9746 22from celery import registry
5b1a7bae 23import urllib,urllib2
77b91efc 24import logging
8e5f9746 25
c03d13cd 26_log = logging.getLogger(__name__)
8e5f9746 27
f6f524bf 28from werkzeug.utils import secure_filename
e323a068 29
f64e5250 30from mediagoblin.db.util import ObjectId
152a3bfa 31from mediagoblin.tools.text import cleaned_markdown_conversion, convert_to_tag_list_of_dicts
ae3bc7fa 32from mediagoblin.tools.translate import pass_to_ugettext as _
152a3bfa 33from mediagoblin.tools.response import render_to_response, redirect
e323a068 34from mediagoblin.decorators import require_active_login
3eeadc92 35from mediagoblin.submit import forms as submit_forms, security
8e5f9746 36from mediagoblin.processing import mark_entry_failed, ProcessMedia
4dc74441 37from mediagoblin.messages import add_message, SUCCESS
ec4261a4 38from mediagoblin.media_types import sniff_media, \
4601c30c 39 InvalidFileType, FileTypeNotSupported
e323a068
CAW
40
41
42@require_active_login
43def submit_start(request):
44 """
45 First view for submitting a file.
46 """
20439236 47 submit_form = submit_forms.SubmitStartForm(request.POST)
e323a068 48
f6f524bf 49 if request.method == 'POST' and submit_form.validate():
285ffedd 50 if not ('file' in request.POST
03afc828
CAW
51 and isinstance(request.POST['file'], FieldStorage)
52 and request.POST['file'].file):
53 submit_form.file.errors.append(
4b1adc13 54 _(u'You must provide a file.'))
03afc828 55 else:
6788b412 56 try:
0bce749b 57 filename = request.POST['file'].filename
ec4261a4
JW
58
59 # Sniff the submitted media to determine which
60 # media plugin should handle processing
61 media_type, media_manager = sniff_media(
62 request.POST['file'])
bb49e56f 63
0bce749b
JW
64 # create entry and save in database
65 entry = request.db.MediaEntry()
66 entry['_id'] = ObjectId()
f4ee8399 67 entry.media_type = unicode(media_type)
ec82fbd8 68 entry.title = (
0bce749b
JW
69 unicode(request.POST['title'])
70 or unicode(splitext(filename)[0]))
93bdab9d 71
1d939966
E
72 entry.description = unicode(request.POST.get('description'))
73 entry.description_html = cleaned_markdown_conversion(
74 entry.description)
4bf8e888 75
2788e6a1 76 entry.license = unicode(request.POST.get('license', "")) or None
99a270e9 77
1ceb4fc8 78 entry.uploader = request.user._id
03afc828 79
0bce749b 80 # Process the user's folksonomy "tags"
de917303 81 entry.tags = convert_to_tag_list_of_dicts(
0bce749b 82 request.POST.get('tags'))
03afc828 83
0bce749b
JW
84 # Generate a slug from the title
85 entry.generate_slug()
4a477e24 86
4dc74441 87
0bce749b
JW
88 # Now store generate the queueing related filename
89 queue_filepath = request.app.queue_store.get_unique_filepath(
90 ['media_entries',
91 unicode(entry._id),
92 secure_filename(filename)])
03afc828 93
0bce749b
JW
94 # queue appropriately
95 queue_file = request.app.queue_store.get_file(
96 queue_filepath, 'wb')
03afc828 97
0bce749b
JW
98 with queue_file:
99 queue_file.write(request.POST['file'].file.read())
03afc828 100
0bce749b 101 # Add queued filename to the entry
8545cfc9 102 entry.queued_media_file = queue_filepath
03afc828 103
0bce749b
JW
104 # We generate this ourselves so we know what the taks id is for
105 # retrieval later.
243c3843 106
0bce749b
JW
107 # (If we got it off the task's auto-generation, there'd be
108 # a risk of a race condition when we'd save after sending
109 # off the task)
110 task_id = unicode(uuid.uuid4())
111 entry['queued_task_id'] = task_id
07934b44 112
0bce749b
JW
113 # Save now so we have this data before kicking off processing
114 entry.save(validate=True)
fa7f9c61 115
0bce749b 116 # Pass off to processing
6788b412 117 #
0bce749b
JW
118 # (... don't change entry after this point to avoid race
119 # conditions with changes to the document via processing code)
120 process_media = registry.tasks[ProcessMedia.name]
121 try:
122 process_media.apply_async(
123 [unicode(entry._id)], {},
124 task_id=task_id)
125 except BaseException as exc:
126 # The purpose of this section is because when running in "lazy"
127 # or always-eager-with-exceptions-propagated celery mode that
128 # the failure handling won't happen on Celery end. Since we
129 # expect a lot of users to run things in this way we have to
130 # capture stuff here.
131 #
132 # ... not completely the diaper pattern because the
133 # exception is re-raised :)
134 mark_entry_failed(entry._id, exc)
135 # re-raise the exception
136 raise
137
bb025ebd 138 if mg_globals.app_config["push_urls"]:
5b1a7bae
MA
139 feed_url=request.urlgen(
140 'mediagoblin.user_pages.atom_feed',
141 qualified=True,user=request.user.username)
142 hubparameters = {
77b91efc
MA
143 'hub.mode': 'publish',
144 'hub.url': feed_url}
5b1a7bae 145 hubdata = urllib.urlencode(hubparameters)
bb025ebd
MA
146 hubheaders = {
147 "Content-type": "application/x-www-form-urlencoded",
148 "Connection": "close"}
149 for huburl in mg_globals.app_config["push_urls"]:
77b91efc
MA
150 hubrequest = urllib2.Request(huburl, hubdata, hubheaders)
151 try:
152 hubresponse = urllib2.urlopen(hubrequest)
153 except urllib2.HTTPError as exc:
154 # This is not a big issue, the item will be fetched
155 # by the PuSH server next time we hit it
c03d13cd 156 _log.warning(
77b91efc
MA
157 "push url %r gave error %r", huburl, exc.code)
158 except urllib2.URLError as exc:
c03d13cd 159 _log.warning(
77b91efc 160 "push url %r is unreachable %r", huburl, exc.reason)
5b1a7bae 161
0bce749b
JW
162 add_message(request, SUCCESS, _('Woohoo! Submitted!'))
163
164 return redirect(request, "mediagoblin.user_pages.user_home",
5a4e3ff1 165 user=request.user.username)
a246ccca
JW
166 except Exception as e:
167 '''
168 This section is intended to catch exceptions raised in
169 mediagobling.media_types
170 '''
4601c30c
JW
171 if isinstance(e, InvalidFileType) or \
172 isinstance(e, FileTypeNotSupported):
173 submit_form.file.errors.append(
174 e)
175 else:
176 raise
f6f524bf 177
9038c9f9
CAW
178 return render_to_response(
179 request,
c9c24934 180 'mediagoblin/submit/start.html',
2c437493
JW
181 {'submit_form': submit_form,
182 'app_config': mg_globals.app_config})