fix typo
[mygpo.git] / mygpo / web / views / episode.py
blobfb0efb7aa6da60f04494bc36929474dfc7a0537c
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 from datetime import datetime
19 from functools import wraps
21 import dateutil.parser
23 from django.shortcuts import render
24 from django.http import HttpResponseRedirect, Http404
25 from django.contrib.auth.decorators import login_required
26 from django.contrib.sites.models import RequestSite
27 from django.views.decorators.vary import vary_on_cookie
28 from django.views.decorators.cache import never_cache, cache_control
29 from django.contrib import messages
30 from django.utils.translation import ugettext as _
32 from mygpo.api.constants import EPISODE_ACTION_TYPES
33 from mygpo.decorators import repeat_on_conflict
34 from mygpo.core.proxy import proxy_object
35 from mygpo.core.tasks import flattr_thing
36 from mygpo.users.models import Chapter, HistoryEntry, EpisodeAction
37 from mygpo.utils import parse_time, get_timestamp
38 from mygpo.users.settings import FLATTR_TOKEN
39 from mygpo.web.heatmap import EpisodeHeatmap
40 from mygpo.publisher.utils import check_publisher_permission
41 from mygpo.web.utils import get_episode_link_target, fetch_episode_data, \
42 check_restrictions
43 from mygpo.db.couchdb.episode import episode_for_slug_id, episode_for_oldid, \
44 favorite_episodes_for_user, chapters_for_episode, \
45 set_episode_favorite
46 from mygpo.db.couchdb.podcast import podcast_by_id, podcast_for_url, \
47 podcasts_to_dict
48 from mygpo.db.couchdb.episode_state import episode_state_for_user_episode, \
49 add_episode_actions, update_episode_chapters
50 from mygpo.db.couchdb.user import get_latest_episodes
51 from mygpo.userfeeds.feeds import FavoriteFeed
54 @vary_on_cookie
55 @cache_control(private=True)
56 def episode(request, episode):
58 podcast = podcast_by_id(episode.podcast)
60 podcast = check_restrictions(podcast)
62 user = request.user
64 if not podcast:
65 raise Http404
67 if user.is_authenticated():
69 episode_state = episode_state_for_user_episode(user, episode)
70 is_fav = episode_state.is_favorite()
73 # pre-populate data for fetch_data
74 podcasts_dict = {podcast.get_id(): podcast}
75 episodes_dict = {episode._id: episode}
77 has_history = bool(list(episode_state.get_history_entries()))
79 played_parts = EpisodeHeatmap(podcast.get_id(),
80 episode._id, user._id, duration=episode.duration)
82 devices = dict( (d.id, d.name) for d in user.devices )
83 can_flattr = user.get_wksetting(FLATTR_TOKEN) and episode.flattr_url
85 else:
86 has_history = False
87 is_fav = False
88 played_parts = None
89 devices = {}
90 can_flattr = False
92 is_publisher = check_publisher_permission(user, podcast)
94 prev = podcast.get_episode_before(episode)
95 next = podcast.get_episode_after(episode)
97 return render(request, 'episode.html', {
98 'episode': episode,
99 'podcast': podcast,
100 'prev': prev,
101 'next': next,
102 'has_history': has_history,
103 'is_favorite': is_fav,
104 'played_parts': played_parts,
105 'actions': EPISODE_ACTION_TYPES,
106 'devices': devices,
107 'can_flattr': can_flattr,
108 'is_publisher': is_publisher,
112 @never_cache
113 @login_required
114 @vary_on_cookie
115 @cache_control(private=True)
116 def history(request, episode):
117 """ shows the history of the episode """
119 user = request.user
120 podcast = podcast_by_id(episode.podcast)
121 episode_state = episode_state_for_user_episode(user, episode)
123 # pre-populate data for fetch_data
124 podcasts_dict = {podcast.get_id(): podcast}
125 episodes_dict = {episode._id: episode}
127 history = list(episode_state.get_history_entries())
128 HistoryEntry.fetch_data(user, history,
129 podcasts=podcasts_dict, episodes=episodes_dict)
131 devices = dict( (d.id, d.name) for d in user.devices )
133 return render(request, 'episode-history.html', {
134 'episode': episode,
135 'podcast': podcast,
136 'history': history,
137 'actions': EPISODE_ACTION_TYPES,
138 'devices': devices,
142 @never_cache
143 @login_required
144 def toggle_favorite(request, episode):
145 episode_state = episode_state_for_user_episode(request.user, episode)
147 is_fav = episode_state.is_favorite()
148 set_episode_favorite(episode_state, not is_fav)
150 podcast = podcast_by_id(episode.podcast)
152 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
156 @vary_on_cookie
157 @cache_control(private=True)
158 @login_required
159 def list_favorites(request):
160 user = request.user
161 site = RequestSite(request)
163 episodes = favorite_episodes_for_user(user)
165 recently_listened = get_latest_episodes(user)
167 podcast_ids = [episode.podcast for episode in episodes + recently_listened]
168 podcasts = podcasts_to_dict(podcast_ids)
170 recently_listened = fetch_episode_data(recently_listened, podcasts=podcasts)
171 episodes = fetch_episode_data(episodes, podcasts=podcasts)
173 favfeed = FavoriteFeed(user)
174 feed_url = favfeed.get_public_url(site.domain)
176 podcast = podcast_for_url(feed_url)
178 token = request.user.favorite_feeds_token
180 return render(request, 'favorites.html', {
181 'episodes': episodes,
182 'feed_token': token,
183 'site': site,
184 'podcast': podcast,
185 'recently_listened': recently_listened,
189 @never_cache
190 def add_action(request, episode):
192 device = request.user.get_device(request.POST.get('device'))
194 action_str = request.POST.get('action')
195 timestamp = request.POST.get('timestamp', '')
197 if timestamp:
198 try:
199 timestamp = dateutil.parser.parse(timestamp)
200 except (ValueError, AttributeError):
201 timestamp = datetime.utcnow()
202 else:
203 timestamp = datetime.utcnow()
205 action = EpisodeAction()
206 action.timestamp = timestamp
207 action.upload_timestamp = get_timestamp(datetime.utcnow())
208 action.device = device.id if device else None
209 action.action = action_str
211 state = episode_state_for_user_episode(request.user, episode)
212 add_episode_actions(state, [action])
214 podcast = podcast_by_id(episode.podcast)
215 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
218 @never_cache
219 @login_required
220 def flattr_episode(request, episode):
221 """ Flattrs an episode, records an event and redirects to the episode """
223 user = request.user
224 site = RequestSite(request)
226 # Flattr via the tasks queue, but wait for the result
227 task = flattr_thing.delay(user, episode._id, site.domain,
228 request.is_secure(), 'Episode')
229 success, msg = task.get()
231 if success:
232 action = EpisodeAction()
233 action.action = 'flattr'
234 action.upload_timestamp = get_timestamp(datetime.utcnow())
235 state = episode_state_for_user_episode(request.user, episode)
236 add_episode_actions(state, [action])
237 messages.success(request, _("Flattr\'d"))
239 else:
240 messages.error(request, msg)
242 podcast = podcast_by_id(episode.podcast)
243 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
246 # To make all view accessible via either CouchDB-ID for Slugs
247 # a decorator queries the episode and passes the Id on to the
248 # regular views
250 def slug_id_decorator(f):
251 @wraps(f)
252 def _decorator(request, p_slug_id, e_slug_id, *args, **kwargs):
253 episode = episode_for_slug_id(p_slug_id, e_slug_id)
255 if episode is None:
256 raise Http404
258 # redirect when Id or a merged (non-cannonical) slug is used
259 if episode.slug and episode.slug != e_slug_id:
260 podcast = podcast_by_id(episode.podcast)
261 return HttpResponseRedirect(
262 get_episode_link_target(episode, podcast))
264 return f(request, episode, *args, **kwargs)
266 return _decorator
269 def oldid_decorator(f):
270 @wraps(f)
271 def _decorator(request, id, *args, **kwargs):
272 episode = episode_for_oldid(id)
274 if episode is None:
275 raise Http404
277 # redirect to Id or slug URL
278 podcast = podcast_by_id(episode.podcast)
279 return HttpResponseRedirect(get_episode_link_target(episode, podcast))
281 return _decorator
283 show_slug_id = slug_id_decorator(episode)
284 toggle_favorite_slug_id = slug_id_decorator(toggle_favorite)
285 add_action_slug_id = slug_id_decorator(add_action)
286 flattr_episode_slug_id = slug_id_decorator(flattr_episode)
287 episode_history_slug_id = slug_id_decorator(history)
289 show_oldid = oldid_decorator(episode)
290 toggle_favorite_oldid = oldid_decorator(toggle_favorite)
291 add_action_oldid = oldid_decorator(add_action)
292 flattr_episode_oldid = oldid_decorator(flattr_episode)
293 episode_history_oldid = oldid_decorator(history)