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
43 @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
)
103 episode
= episode_for_podcast_url(podcast_url
, episode_url
)
108 e_state
= episode_state_for_user_episode(request
.user
, episode
)
110 chapterlist
= sorted(e_state
.chapters
, key
=lambda c
: c
.start
)
113 chapterlist
= filter(lambda c
: c
.created
>= since
, chapters
)
116 for c
in chapterlist
:
117 if c
.device
is not None:
118 device
= request
.user
.get_device(c
.device
)
119 device_uid
= device
.uid
127 'advertisement': c
.advertisement
,
128 'timestamp': c
.created
,
132 return JsonResponse({
133 'chapters': chapters
,
138 def update_chapters(req
, user
):
139 podcast_url
= normalize_feed_url(req
['podcast'])
140 episode_url
= normalize_feed_url(req
['episode'])
142 episode
= episode_for_podcast_url(podcast_url
, episode_url
,
145 e_state
= episode_state_for_user_episode(request
.user
, episode
)
149 device
= get_device(request
.user
, req
['device'],
150 request
.META
.get('HTTP_USER_AGENT', ''), undelete
=True)
152 timestamp
= dateutil
.parser
.parse(req
['timestamp']) if 'timestamp' in req
else datetime
.utcnow()
154 new_chapters
= parse_new_chapters(request
.user
, req
.get('chapters_add', []))
155 rem_chapters
= parse_rem_chapters(req
.get('chapters_remove', []))
157 e_state
.update_chapters(new_chapters
, rem_chapters
)
161 def parse_new_chapters(user
, chapters
):
164 raise ParameterMissing('start parameter missing')
165 start
= parse_time(c
['start'])
168 raise ParameterMissing('end parameter missing')
169 end
= parse_time(c
['end'])
171 label
= c
.get('label', '')
172 adv
= c
.get('advertisement', False)
174 device_uid
= c
.get('device', None)
176 device_id
= get_device(user
, device_uid
,
177 request
.META
.get('HTTP_USER_AGENT', ''), undelete
=True).id
182 chapter
.device
= device_id
183 chapter
.created
= timestamp
184 chapter
.start
= start
186 chapter
.label
= label
187 chapter
.advertisement
= adv
192 def parse_rem_chapters(chapers
):
195 raise ParameterMissing('start parameter missing')
196 start
= parse_time(c
['start'])
199 raise ParameterMissing('end parameter missing')
200 end
= parse_time(c
['end'])