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/>.
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
42 @allowed_methods(['POST', 'GET'])
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', '')
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 ''))
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
!= ''):
76 update_chapters(req
, request
.user
)
77 except ParameterMissing
, e
:
78 return HttpResponseBadRequest(e
)
81 'update_url': update_url
,
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)
97 since
= datetime
.fromtimestamp(float(since_
)) if since_
else None
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
)
105 episode
= Episode
.objects
.filter(podcast__urls__url
=podcast_url
,
106 urls__url
=episode_url
).get()
107 except Episode
.DoesNotExist
:
110 e_state
= episode_state_for_user_episode(request
.user
, episode
)
112 chapterlist
= sorted(e_state
.chapters
, key
=lambda c
: c
.start
)
115 chapterlist
= filter(lambda c
: c
.created
>= since
, 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
129 'advertisement': c
.advertisement
,
130 'timestamp': c
.created
,
134 return JsonResponse({
135 'chapters': chapters
,
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
)
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
):
166 raise ParameterMissing('start parameter missing')
167 start
= parse_time(c
['start'])
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)
178 device_id
= get_device(user
, device_uid
,
179 request
.META
.get('HTTP_USER_AGENT', ''), undelete
=True).id
184 chapter
.device
= device_id
185 chapter
.created
= timestamp
186 chapter
.start
= start
188 chapter
.label
= label
189 chapter
.advertisement
= adv
194 def parse_rem_chapters(chapers
):
197 raise ParameterMissing('start parameter missing')
198 start
= parse_time(c
['start'])
201 raise ParameterMissing('end parameter missing')
202 end
= parse_time(c
['end'])