[Migration] remove references to main database
[mygpo.git] / mygpo / api / advanced / episode.py
blobca9318f8d3ac14a1ae778cc92ccd1ba719a766c8
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.api.httpresponse import JsonResponse
28 from mygpo.api.exceptions import ParameterMissing
29 from mygpo.api.backend import get_device
30 from mygpo.users.models import Chapter
31 from mygpo.utils import parse_time, parse_request_body, normalize_feed_url
32 from mygpo.decorators import allowed_methods, cors_origin
33 from mygpo.api.basic_auth import require_valid_user, check_username
34 from mygpo.db.couchdb.episode_state import episode_state_for_user_episode, \
35 update_episode_chapters
38 @csrf_exempt
39 @require_valid_user
40 @check_username
41 @never_cache
42 @allowed_methods(['POST', 'GET'])
43 @cors_origin()
44 def chapters(request, username):
46 now = datetime.utcnow()
47 now_ = int(time.mktime(now.timetuple()))
49 if request.method == 'POST':
50 req = parse_request_body(request)
52 if not 'podcast' in req:
53 return HttpResponseBadRequest('Podcast URL missing')
55 if not 'episode' in req:
56 return HttpResponseBadRequest('Episode URL missing')
58 podcast_url = req.get('podcast', '')
59 episode_url = req.get('episode', '')
60 update_urls = []
62 # podcast sanitizing
63 s_podcast_url = normalize_feed_url(podcast_url)
64 if s_podcast_url != podcast_url:
65 req['podcast'] = s_podcast_url
66 update_urls.append((podcast_url, s_podcast_url or ''))
68 # episode sanitizing
69 s_episode_url = normalize_feed_url(episode_url, 'episode')
70 if s_episode_url != episode_url:
71 req['episode'] = s_episode_url
72 update_urls.append((episode_url, s_episode_url or ''))
74 if (s_podcast_url != '') and (s_episode_url != ''):
75 try:
76 update_chapters(req, request.user)
77 except ParameterMissing, e:
78 return HttpResponseBadRequest(e)
80 return JsonResponse({
81 'update_url': update_url,
82 'timestamp': now_
85 elif request.method == 'GET':
86 if not 'podcast' in request.GET:
87 return HttpResponseBadRequest('podcast URL missing')
89 if not 'episode' in request.GET:
90 return HttpResponseBadRequest('Episode URL missing')
92 podcast_url = request.GET['podcast']
93 episode_url = request.GET['episode']
95 since_ = request.GET.get('since', None)
96 try:
97 since = datetime.fromtimestamp(float(since_)) if since_ else None
98 except ValueError:
99 return HttpResponseBadRequest('since-value is not a valid timestamp')
101 podcast_url = normalize_feed_url(podcast_url)
102 episode_url = normalize_feed_url(episode_url)
104 try:
105 episode = Episode.objects.filter(podcast__urls__url=podcast_url,
106 urls__url=episode_url).get()
107 except Episode.DoesNotExist:
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 = normalize_feed_url(req['podcast'])
142 episode_url = normalize_feed_url(req['episode'])
144 podcast = Podcast.objects.get_or_create_for_url(podcast_url)
145 episode = Episode.objects.get_or_create_for_url(podcast, episode_url)
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 update_episode_chapters(e_state, 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)