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
.core
import models
28 from mygpo
.api
.httpresponse
import JsonResponse
29 from mygpo
.api
.exceptions
import ParameterMissing
30 from mygpo
.api
.backend
import get_device
31 from mygpo
.users
.models
import Chapter
32 from mygpo
.utils
import parse_time
, parse_request_body
, normalize_feed_url
33 from mygpo
.decorators
import allowed_methods
34 from mygpo
.api
.basic_auth
import require_valid_user
, check_username
35 from mygpo
.db
.couchdb
.episode
import episode_for_podcast_url
36 from mygpo
.db
.couchdb
.episode_state
import episode_state_for_user_episode
, \
37 update_episode_chapters
44 @allowed_methods(['POST', 'GET'])
45 def chapters(request
, username
):
47 now
= datetime
.utcnow()
48 now_
= int(time
.mktime(now
.timetuple()))
50 if request
.method
== 'POST':
51 req
= parse_request_body(request
)
53 if not 'podcast' in req
:
54 return HttpResponseBadRequest('Podcast URL missing')
56 if not 'episode' in req
:
57 return HttpResponseBadRequest('Episode URL missing')
59 podcast_url
= req
.get('podcast', '')
60 episode_url
= req
.get('episode', '')
64 s_podcast_url
= normalize_feed_url(podcast_url
)
65 if s_podcast_url
!= podcast_url
:
66 req
['podcast'] = s_podcast_url
67 update_urls
.append((podcast_url
, s_podcast_url
or ''))
70 s_episode_url
= normalize_feed_url(episode_url
, 'episode')
71 if s_episode_url
!= episode_url
:
72 req
['episode'] = s_episode_url
73 update_urls
.append((episode_url
, s_episode_url
or ''))
75 if (s_podcast_url
!= '') and (s_episode_url
!= ''):
77 update_chapters(req
, request
.user
)
78 except ParameterMissing
, e
:
79 return HttpResponseBadRequest(e
)
82 'update_url': update_url
,
86 elif request
.method
== 'GET':
87 if not 'podcast' in request
.GET
:
88 return HttpResponseBadRequest('podcast URL missing')
90 if not 'episode' in request
.GET
:
91 return HttpResponseBadRequest('Episode URL missing')
93 podcast_url
= request
.GET
['podcast']
94 episode_url
= request
.GET
['episode']
96 since_
= request
.GET
.get('since', None)
98 since
= datetime
.fromtimestamp(float(since_
)) if since_
else None
100 return HttpResponseBadRequest('since-value is not a valid timestamp')
102 podcast_url
= normalize_feed_url(podcast_url
)
103 episode_url
= normalize_feed_url(episode_url
)
104 episode
= episode_for_podcast_url(podcast_url
, episode_url
)
109 e_state
= episode_state_for_user_episode(request
.user
, episode
)
111 chapterlist
= sorted(e_state
.chapters
, key
=lambda c
: c
.start
)
114 chapterlist
= filter(lambda c
: c
.created
>= since
, chapters
)
117 for c
in chapterlist
:
118 if c
.device
is not None:
119 device
= request
.user
.get_device(c
.device
)
120 device_uid
= device
.uid
128 'advertisement': c
.advertisement
,
129 'timestamp': c
.created
,
133 return JsonResponse({
134 'chapters': chapters
,
139 def update_chapters(req
, user
):
140 podcast_url
= normalize_feed_url(req
['podcast'])
141 episode_url
= normalize_feed_url(req
['episode'])
143 episode
= episode_for_podcast_url(podcast_url
, episode_url
,
146 e_state
= episode_state_for_user_episode(request
.user
, episode
)
150 device
= get_device(request
.user
, req
['device'],
151 request
.META
.get('HTTP_USER_AGENT', ''), undelete
=True)
153 timestamp
= dateutil
.parser
.parse(req
['timestamp']) if 'timestamp' in req
else datetime
.utcnow()
155 new_chapters
= parse_new_chapters(request
.user
, req
.get('chapters_add', []))
156 rem_chapters
= parse_rem_chapters(req
.get('chapters_remove', []))
158 update_episode_chapters(e_state
, new_chapters
, rem_chapters
)
162 def parse_new_chapters(user
, chapters
):
165 raise ParameterMissing('start parameter missing')
166 start
= parse_time(c
['start'])
169 raise ParameterMissing('end parameter missing')
170 end
= parse_time(c
['end'])
172 label
= c
.get('label', '')
173 adv
= c
.get('advertisement', False)
175 device_uid
= c
.get('device', None)
177 device_id
= get_device(user
, device_uid
,
178 request
.META
.get('HTTP_USER_AGENT', ''), undelete
=True).id
183 chapter
.device
= device_id
184 chapter
.created
= timestamp
185 chapter
.start
= start
187 chapter
.label
= label
188 chapter
.advertisement
= adv
193 def parse_rem_chapters(chapers
):
196 raise ParameterMissing('start parameter missing')
197 start
= parse_time(c
['start'])
200 raise ParameterMissing('end parameter missing')
201 end
= parse_time(c
['end'])