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
.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
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', '')
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
))
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
!= ''):
78 update_chapters(req
, request
.user
)
79 except ParameterMissing
, e
:
80 return HttpResponseBadRequest(e
)
83 'update_url': update_url
,
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)
99 since
= datetime
.fromtimestamp(float(since_
)) if since_
else None
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
)
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
= sanitize_url(req
['podcast'])
142 episode_url
= sanitize_url(req
['episode'], 'episode')
144 episode
= episode_for_podcast_url(podcast_url
, 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 e_state
.update_chapters(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'])