Mongo removal: Remove the validate=True arg to obj.save()
[mediagoblin.git] / mediagoblin / plugins / api / views.py
CommitLineData
a062149e
JW
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
17import json
4504dbba
JW
18import logging
19import uuid
20
21from os.path import splitext
74af60bb 22from werkzeug.datastructures import FileStorage
62d14bf5 23from werkzeug.exceptions import BadRequest, Forbidden
4504dbba 24from werkzeug.utils import secure_filename
74af60bb 25from werkzeug.wrappers import Response
4504dbba 26from celery import registry
a062149e 27
4504dbba
JW
28from mediagoblin.db.util import ObjectId
29from mediagoblin.decorators import require_active_login
30from mediagoblin.processing import mark_entry_failed
31from mediagoblin.processing.task import ProcessMedia
32from mediagoblin.meddleware.csrf import csrf_exempt
111a609d 33from mediagoblin.media_types import sniff_media
42c83752
JW
34from mediagoblin.plugins.api.tools import api_auth, get_entry_serializable, \
35 json_response
a062149e 36
4504dbba
JW
37_log = logging.getLogger(__name__)
38
39
40@csrf_exempt
41@api_auth
42@require_active_login
43def post_entry(request):
44 _log.debug('Posting entry')
09e528ac
JW
45
46 if request.method == 'OPTIONS':
47 return json_response({'status': 200})
48
4504dbba 49 if request.method != 'POST':
09e528ac 50 _log.debug('Must POST against post_entry')
cfa92229 51 raise BadRequest()
4504dbba 52
111a609d
JW
53 if not 'file' in request.files \
54 or not isinstance(request.files['file'], FileStorage) \
55 or not request.files['file'].stream:
09e528ac 56 _log.debug('File field not found')
cfa92229 57 raise BadRequest()
4504dbba 58
111a609d 59 media_file = request.files['file']
4504dbba
JW
60
61 media_type, media_manager = sniff_media(media_file)
62
63 entry = request.db.MediaEntry()
64 entry.id = ObjectId()
65 entry.media_type = unicode(media_type)
111a609d 66 entry.title = unicode(request.form.get('title')
4504dbba
JW
67 or splitext(media_file.filename)[0])
68
111a609d
JW
69 entry.description = unicode(request.form.get('description'))
70 entry.license = unicode(request.form.get('license', ''))
4504dbba
JW
71
72 entry.uploader = request.user.id
73
74 entry.generate_slug()
75
76 task_id = unicode(uuid.uuid4())
77
78 # Now store generate the queueing related filename
79 queue_filepath = request.app.queue_store.get_unique_filepath(
80 ['media_entries',
81 task_id,
82 secure_filename(media_file.filename)])
83
84 # queue appropriately
85 queue_file = request.app.queue_store.get_file(
86 queue_filepath, 'wb')
87
88 with queue_file:
111a609d 89 queue_file.write(request.files['file'].stream.read())
4504dbba
JW
90
91 # Add queued filename to the entry
92 entry.queued_media_file = queue_filepath
93
94 entry.queued_task_id = task_id
95
96 # Save now so we have this data before kicking off processing
b39d1f23 97 entry.save()
4504dbba 98
111a609d 99 if request.form.get('callback_url'):
5354f954
JW
100 metadata = request.db.ProcessingMetaData()
101 metadata.media_entry = entry
111a609d 102 metadata.callback_url = unicode(request.form['callback_url'])
5354f954
JW
103 metadata.save()
104
4504dbba
JW
105 # Pass off to processing
106 #
107 # (... don't change entry after this point to avoid race
108 # conditions with changes to the document via processing code)
109 process_media = registry.tasks[ProcessMedia.name]
110 try:
111 process_media.apply_async(
5c2b8486 112 [unicode(entry.id)], {},
4504dbba
JW
113 task_id=task_id)
114 except BaseException as e:
115 # The purpose of this section is because when running in "lazy"
116 # or always-eager-with-exceptions-propagated celery mode that
117 # the failure handling won't happen on Celery end. Since we
118 # expect a lot of users to run things in this way we have to
119 # capture stuff here.
120 #
121 # ... not completely the diaper pattern because the
122 # exception is re-raised :)
5c2b8486 123 mark_entry_failed(entry.id, e)
4504dbba
JW
124 # re-raise the exception
125 raise
126
127 return json_response(get_entry_serializable(entry, request.urlgen))
128
a062149e
JW
129
130@api_auth
131def api_test(request):
132 if not request.user:
cfa92229 133 raise Forbidden()
a062149e
JW
134
135 user_data = {
136 'username': request.user.username,
137 'email': request.user.email}
138
74af60bb
SS
139 # TODO: This is the *only* thing using Response() here, should that
140 # not simply use json_response()?
a062149e 141 return Response(json.dumps(user_data))
42c83752
JW
142
143
144def get_entries(request):
145 entries = request.db.MediaEntry.query
146
c92aa0d0 147 # TODO: Make it possible to fetch unprocessed media, or media in-processing
42c83752
JW
148 entries = entries.filter_by(state=u'processed')
149
c92aa0d0
JW
150 # TODO: Add sort order customization
151 entries = entries.order_by(request.db.MediaEntry.created.desc())
152
153 # TODO: Fetch default and upper limit from config
154 entries = entries.limit(int(request.GET.get('limit') or 10))
155
42c83752
JW
156 entries_serializable = []
157
158 for entry in entries:
85726f73 159 entries_serializable.append(get_entry_serializable(entry, request.urlgen))
42c83752
JW
160
161 return json_response(entries_serializable)