remove now unused {% load url from future %}
[mygpo.git] / mygpo / api / advanced / episode.py
blobc585a408a0b0d4ff6c277eda371ae4a2868449d5
2 # This file is part of my.gpodder.org.
4 # my.gpodder.org is free software: you can redistribute it and/or modify it
5 # 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 (at your
7 # option) any later version.
9 # my.gpodder.org is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
12 # License for more details.
14 # You should have received a copy of the GNU Affero General Public License
15 # along with my.gpodder.org. If not, see <http://www.gnu.org/licenses/>.
18 import time
19 from datetime import datetime
21 import dateutil.parser
23 from django.http import HttpResponseBadRequest, Http404
24 from django.views.decorators.csrf import csrf_exempt
25 from django.views.decorators.cache import never_cache
27 from mygpo.core import models
28 from mygpo.api.httpresponse import JsonResponse
29 from mygpo.api.exceptions import ParameterMissing
30 from mygpo.api.sanitizing import sanitize_url
31 from mygpo.api.backend import get_device
32 from mygpo.users.models import Chapter
33 from mygpo.utils import parse_time
34 from mygpo.decorators import allowed_methods
35 from mygpo.core.json import json
36 from mygpo.api.basic_auth import require_valid_user, check_username
37 from mygpo.db.couchdb.episode import episode_for_podcast_url
38 from mygpo.db.couchdb.episode_state import episode_state_for_user_episode
41 @csrf_exempt
42 @require_valid_user
43 @check_username
44 @never_cache
45 @allowed_methods(['POST', 'GET'])
46 def chapters(request, username):
48 now = datetime.utcnow()
49 now_ = int(time.mktime(now.timetuple()))
51 if request.method == 'POST':
52 req = json.loads(request.body)
54 if not 'podcast' in req:
55 return HttpResponseBadRequest('Podcast URL missing')
57 if not 'episode' in req:
58 return HttpResponseBadRequest('Episode URL missing')
60 podcast_url = req.get('podcast', '')
61 episode_url = req.get('episode', '')
62 update_urls = []
64 # podcast sanitizing
65 s_podcast_url = sanitize_url(podcast_url)
66 if s_podcast_url != podcast_url:
67 req['podcast'] = s_podcast_url
68 update_urls.append((podcast_url, s_podcast_url))
70 # episode sanitizing
71 s_episode_url = sanitize_url(episode_url, 'episode')
72 if s_episode_url != episode_url:
73 req['episode'] = s_episode_url
74 update_urls.append((episode_url, s_episode_url))
76 if (s_podcast_url != '') and (s_episode_url != ''):
77 try:
78 update_chapters(req, request.user)
79 except ParameterMissing, e:
80 return HttpResponseBadRequest(e)
82 return JsonResponse({
83 'update_url': update_url,
84 'timestamp': now_
87 elif request.method == 'GET':
88 if not 'podcast' in request.GET:
89 return HttpResponseBadRequest('podcast URL missing')
91 if not 'episode' in request.GET:
92 return HttpResponseBadRequest('Episode URL missing')
94 podcast_url = request.GET['podcast']
95 episode_url = request.GET['episode']
97 since_ = request.GET.get('since', None)
98 try:
99 since = datetime.fromtimestamp(float(since_)) if since_ else None
100 except ValueError:
101 return HttpResponseBadRequest('since-value is not a valid timestamp')
103 podcast_url = sanitize_url(podcast_url)
104 episode_url = sanitize_url(episode_url, 'episode')
105 episode = episode_for_podcast_url(podcast_url, episode_url)
107 if episode is None:
108 raise Http404
110 e_state = episode_state_for_user_episode(request.user, episode)
112 chapterlist = sorted(e_state.chapters, key=lambda c: c.start)
114 if since:
115 chapterlist = filter(lambda c: c.created >= since, chapters)
117 chapters = []
118 for c in chapterlist:
119 if c.device is not None:
120 device = request.user.get_device(c.device)
121 device_uid = device.uid
122 else:
123 device_uid = None
125 chapters.append({
126 'start': c.start,
127 'end': c.end,
128 'label': c.label,
129 'advertisement': c.advertisement,
130 'timestamp': c.created,
131 'device': device_uid
134 return JsonResponse({
135 'chapters': chapters,
136 'timestamp': now_
140 def update_chapters(req, user):
141 podcast_url = sanitize_url(req['podcast'])
142 episode_url = sanitize_url(req['episode'], 'episode')
144 episode = episode_for_podcast_url(podcast_url, episode_url,
145 create=True)
147 e_state = episode_state_for_user_episode(request.user, episode)
149 device = None
150 if 'device' in req:
151 device = get_device(request.user, req['device'],
152 request.META.get('HTTP_USER_AGENT', ''), undelete=True)
154 timestamp = dateutil.parser.parse(req['timestamp']) if 'timestamp' in req else datetime.utcnow()
156 new_chapters = parse_new_chapters(request.user, req.get('chapters_add', []))
157 rem_chapters = parse_rem_chapters(req.get('chapters_remove', []))
159 e_state.update_chapters(new_chapters, rem_chapters)
163 def parse_new_chapters(user, chapters):
164 for c in chapters:
165 if not 'start' in c:
166 raise ParameterMissing('start parameter missing')
167 start = parse_time(c['start'])
169 if not 'end' in c:
170 raise ParameterMissing('end parameter missing')
171 end = parse_time(c['end'])
173 label = c.get('label', '')
174 adv = c.get('advertisement', False)
176 device_uid = c.get('device', None)
177 if device_uid:
178 device_id = get_device(user, device_uid,
179 request.META.get('HTTP_USER_AGENT', ''), undelete=True).id
180 else:
181 device_id = None
183 chapter = Chapter()
184 chapter.device = device_id
185 chapter.created = timestamp
186 chapter.start = start
187 chapter.end = end
188 chapter.label = label
189 chapter.advertisement = adv
191 yield chapter
194 def parse_rem_chapters(chapers):
195 for c in chapters:
196 if not 'start' in c:
197 raise ParameterMissing('start parameter missing')
198 start = parse_time(c['start'])
200 if not 'end' in c:
201 raise ParameterMissing('end parameter missing')
202 end = parse_time(c['end'])
204 yield (start, end)