def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) logger.info('Subscription Upload @{username}/{device_uid}'.format( username=request.user.username, device_uid=device_uid)) d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) actions = self.parsed_body(request) add = list(filter(None, actions.get('add', []))) rem = list(filter(None, actions.get('remove', []))) logger.info('Subscription Upload @{username}/{device_uid}: add ' '{num_add}, remove {num_remove}'.format( username=request.user.username, device_uid=device_uid, num_add=len(add), num_remove=len(rem))) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({ 'timestamp': now, 'update_urls': update_urls, })
def device(request, username, device_uid): d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) try: data = parse_request_body(request) except (JSONDecodeError, UnicodeDecodeError, ValueError) as e: msg = ('Could not decode device update POST data for ' + 'user %s: %s') % (username, request.body.decode('ascii', errors='replace')) logger.warn(msg, exc_info=True) return HttpResponseBadRequest(msg) if 'caption' in data: if not data['caption']: return HttpResponseBadRequest('caption must not be empty') d.name = data['caption'] if 'type' in data: if not valid_devicetype(data['type']): return HttpResponseBadRequest('invalid device type %s' % data['type']) d.type = data['type'] set_device(request.user, d) return HttpResponse()
def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) logger.info( "Subscription Upload @{username}/{device_uid}".format( username=request.user.username, device_uid=device_uid ) ) d = get_device( request.user, device_uid, request.META.get("HTTP_USER_AGENT", "") ) actions = self.parsed_body(request) add = list(filter(None, actions.get("add", []))) rem = list(filter(None, actions.get("remove", []))) logger.info( "Subscription Upload @{username}/{device_uid}: add " "{num_add}, remove {num_remove}".format( username=request.user.username, device_uid=device_uid, num_add=len(add), num_remove=len(rem), ) ) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({"timestamp": now, "update_urls": update_urls})
def parse_new_chapters(user, chapters): for c in chapters: if not 'start' in c: raise ParameterMissing('start parameter missing') start = parse_time(c['start']) if not 'end' in c: raise ParameterMissing('end parameter missing') end = parse_time(c['end']) label = c.get('label', '') adv = c.get('advertisement', False) device_uid = c.get('device', None) if device_uid: device_id = get_device(user, device_uid, request.META.get('HTTP_USER_AGENT', ''), undelete=True).id else: device_id = None chapter = Chapter() chapter.device = device_id chapter.created = timestamp chapter.start = start chapter.end = end chapter.label = label chapter.advertisement = adv yield chapter
def parse_episode_action(action, user, update_urls, now, ua_string): action_str = action.get('action', None) if not valid_episodeaction(action_str): raise Exception('invalid action %s' % action_str) new_action = EpisodeAction() new_action.action = action['action'] if action.get('device', False): device = get_device(user, action['device'], ua_string) new_action.device = device.id if action.get('timestamp', False): new_action.timestamp = dateutil.parser.parse(action['timestamp']) else: new_action.timestamp = now new_action.timestamp = new_action.timestamp.replace(microsecond=0) new_action.upload_timestamp = get_timestamp(now) new_action.started = action.get('started', None) new_action.playmark = action.get('position', None) new_action.total = action.get('total', None) return new_action
def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) logger.info( 'Subscription Upload @{username}/{device_uid}'.format( username=request.user.username, device_uid=device_uid ) ) d = get_device( request.user, device_uid, request.META.get('HTTP_USER_AGENT', '') ) actions = self.parsed_body(request) add = list(filter(None, actions.get('add', []))) rem = list(filter(None, actions.get('remove', []))) logger.info( 'Subscription Upload @{username}/{device_uid}: add ' '{num_add}, remove {num_remove}'.format( username=request.user.username, device_uid=device_uid, num_add=len(add), num_remove=len(rem), ) ) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({'timestamp': now, 'update_urls': update_urls})
def set_subscriptions(urls, user, device_uid, user_agent): device = get_device(user, device_uid, user_agent, undelete=True) subscriptions = dict( (p.url, p) for p in device.get_subscribed_podcasts()) new = [p for p in urls if p not in subscriptions.keys()] rem = [p for p in subscriptions.keys() if p not in urls] subscriber = BulkSubscribe(user, device, podcasts=subscriptions) for r in rem: subscriber.add_action(r, 'unsubscribe') for n in new: subscriber.add_action(n, 'subscribe') try: errors = subscriber.execute() except BulkException as be: for err in be.errors: logger.warn('Simple API: %(username)s: Updating subscription for ' '%(podcast_url)s on %(device_uid)s failed: ' '%(error)s (%(reason)s)'.format(username=user.username, podcast_url=err.doc, device_uid=device.uid, error=err.error, reason=err.reason) ) # Only an empty response is a successful response return HttpResponse('', mimetype='text/plain')
def device(request, username, device_uid, version=None): d = get_device(request.user, device_uid, request.META.get("HTTP_USER_AGENT", "")) try: data = parse_request_body(request) except (UnicodeDecodeError, ValueError) as e: msg = ("Could not decode device update POST data for " + "user %s: %s") % ( username, request.body.decode("ascii", errors="replace"), ) logger.warning(msg, exc_info=True) return HttpResponseBadRequest(msg) if "caption" in data: if not data["caption"]: return HttpResponseBadRequest("caption must not be empty") d.name = data["caption"] if "type" in data: if not valid_devicetype(data["type"]): return HttpResponseBadRequest("invalid device type %s" % data["type"]) d.type = data["type"] d.save() return HttpResponse()
def device(request, username, device_uid, version=None): d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) try: data = parse_request_body(request) except (UnicodeDecodeError, ValueError) as e: msg = ('Could not decode device update POST data for ' + 'user %s: %s') % ( username, request.body.decode('ascii', errors='replace'), ) logger.warn(msg, exc_info=True) return HttpResponseBadRequest(msg) if 'caption' in data: if not data['caption']: return HttpResponseBadRequest('caption must not be empty') d.name = data['caption'] if 'type' in data: if not valid_devicetype(data['type']): return HttpResponseBadRequest('invalid device type %s' % data['type']) d.type = data['type'] d.save() return HttpResponse()
def subscriptions(request, username, device_uid): now = datetime.now() now_ = get_timestamp(now) if request.method == 'GET': try: device = request.user.get_device_by_uid(device_uid) except DeviceDoesNotExist as e: return HttpResponseNotFound(str(e)) since_ = request.GET.get('since', None) if since_ is None: return HttpResponseBadRequest('parameter since missing') try: since = datetime.fromtimestamp(float(since_)) except ValueError: return HttpResponseBadRequest('since-value is not a valid timestamp') changes = get_subscription_changes(request.user, device, since, now) return JsonResponse(changes) elif request.method == 'POST': d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) if not request.body: return HttpResponseBadRequest('POST data must not be empty') try: actions = parse_request_body(request) except (JSONDecodeError, UnicodeDecodeError, ValueError) as e: msg = (u'Could not decode subscription update POST data for ' + 'user %s: %s') % (username, request.body.decode('ascii', errors='replace')) logger.warn(msg, exc_info=True) return HttpResponseBadRequest(msg) add = actions['add'] if 'add' in actions else [] rem = actions['remove'] if 'remove' in actions else [] add = filter(None, add) rem = filter(None, rem) try: update_urls = update_subscriptions(request.user, d, add, rem) except ValueError, e: return HttpResponseBadRequest(e) return JsonResponse({ 'timestamp': now_, 'update_urls': update_urls, })
def setUp(self): self.podcast1 = Podcast.objects.get_or_create_for_url('http://example.com/feed.rss') self.podcast2 = Podcast.objects.get_or_create_for_url(self.P2_URL) User = get_user_model() self.user = User(username='******') self.user.email = '*****@*****.**' self.user.set_password('secret!') self.user.save() self.device = get_device(self.user, 'dev', '')
def setUp(self): self.podcast1 = Podcast(urls=['http://example.com/feed.rss']) self.podcast2 = Podcast(urls=[self.P2_URL]) self.podcast1.save() self.podcast2.save() self.user = User(username='******') self.user.email = '*****@*****.**' self.user.set_password('secret!') self.user.save() self.device = get_device(self.user, 'dev', '')
def upload(request): try: emailaddr = request.POST['username'] password = request.POST['password'] action = request.POST['action'] protocol = request.POST['protocol'] opml = request.FILES['opml'].read() except MultiValueDictKeyError: return HttpResponse("@PROTOERROR", mimetype='text/plain') user = auth(emailaddr, password) if (not user): return HttpResponse('@AUTHFAIL', mimetype='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', '')) existing_urls = [x.url for x in dev.get_subscribed_podcasts()] i = Importer(opml) podcast_urls = [p['url'] for p in i.items] podcast_urls = map(normalize_feed_url, podcast_urls) podcast_urls = filter(None, podcast_urls) new = [u for u in podcast_urls if u not in existing_urls] rem = [u for e in existing_urls if u not in podcast_urls] #remove duplicates new = list(set(new)) rem = list(set(rem)) for n in new: p = podcast_for_url(n, create=True) try: p.subscribe(user, dev) except SubscriptionException as e: logger.exception('Legacy API: %(username)s: could not subscribe to podcast %(podcast_url)s on device %(device_id)s' % {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id}) for r in rem: p = podcast_for_url(r, create=True) try: p.unsubscribe(user, dev) except SubscriptionException as e: logger.exception('Legacy API: %(username): could not unsubscribe from podcast %(podcast_url) on device %(device_id)' % {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id}) return HttpResponse('@SUCCESS', mimetype='text/plain')
def setUp(self): self.podcast1 = Podcast.objects.get_or_create_for_url( "http://example.com/feed.rss").object self.podcast2 = Podcast.objects.get_or_create_for_url( self.P2_URL).object User = get_user_model() self.user = User(username="******") self.user.email = "*****@*****.**" self.user.set_password("secret!") self.user.save() self.device = get_device(self.user, "dev", "")
def subscriptions(request, username, device_uid): now = datetime.now() now_ = get_timestamp(now) if request.method == 'GET': try: device = request.user.get_device_by_uid(device_uid) except DeviceDoesNotExist as e: return HttpResponseNotFound(str(e)) since_ = request.GET.get('since', None) if since_ == None: return HttpResponseBadRequest('parameter since missing') try: since = datetime.fromtimestamp(float(since_)) except ValueError: return HttpResponseBadRequest('since-value is not a valid timestamp') changes = get_subscription_changes(request.user, device, since, now) return JsonResponse(changes) elif request.method == 'POST': d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) if not request.raw_post_data: return HttpResponseBadRequest('POST data must not be empty') actions = json.loads(request.raw_post_data) add = actions['add'] if 'add' in actions else [] rem = actions['remove'] if 'remove' in actions else [] add = filter(None, add) rem = filter(None, rem) try: update_urls = update_subscriptions(request.user, d, add, rem) except IntegrityError, e: return HttpResponseBadRequest(e) return JsonResponse({ 'timestamp': now_, 'update_urls': update_urls, })
def set_subscriptions(urls, user, device_uid, user_agent): device = get_device(user, device_uid, user_agent, undelete=True) subscriptions = dict( (p.url, p) for p in device.get_subscribed_podcasts()) new = [p for p in urls if p not in subscriptions.keys()] rem = [p for p in subscriptions.keys() if p not in urls] remove_podcasts = Podcast.objects.filter(urls__url__in=rem) for podcast in remove_podcasts: unsubscribe(podcast, user, device) for url in new: podcast = Podcast.objects.get_or_create_for_url(url) subscribe(podcast, user, device, url) # Only an empty response is a successful response return HttpResponse('', content_type='text/plain')
def post(self, request, version, username, device_uid): """ Client sends subscription updates """ now = get_timestamp(datetime.utcnow()) d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) actions = self.parsed_body(request) add = filter(None, actions.get('add', [])) rem = filter(None, actions.get('remove', [])) update_urls = self.update_subscriptions(request.user, d, add, rem) return JsonResponse({ 'timestamp': now, 'update_urls': update_urls, })
def upload(request): try: emailaddr = request.POST['username'] password = request.POST['password'] action = request.POST['action'] protocol = request.POST['protocol'] opml = request.FILES['opml'].read() except MultiValueDictKeyError: return HttpResponse("@PROTOERROR", mimetype='text/plain') user = auth(emailaddr, password) if (not user): return HttpResponse('@AUTHFAIL', mimetype='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', '')) existing_urls = [x.url for x in dev.get_subscribed_podcasts()] i = Importer(opml) podcast_urls = [p['url'] for p in i.items] podcast_urls = sanitize_urls(podcast_urls) podcast_urls = filter(lambda x: x, podcast_urls) new = [u for u in podcast_urls if u not in existing_urls] rem = [u for e in existing_urls if u not in podcast_urls] #remove duplicates new = list(set(new)) rem = list(set(rem)) for n in new: try: p = Podcast.for_url(n, create=True) except IntegrityError, e: log('/upload: Error trying to get podcast object: %s (error: %s)' % (n, e)) continue try: p.subscribe(user, dev) except Exception as e: log('Legacy API: %(username)s: could not subscribe to podcast %(podcast_url)s on device %(device_id)s: %(exception)s' % {'username': user.username, 'podcast_url': p.url, 'device_id': dev.id, 'exception': e})
def getlist(request): emailaddr = request.GET.get('username', None) password = request.GET.get('password', None) user = auth(emailaddr, password) if user is None: return HttpResponse('@AUTHFAIL', mimetype='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', ''), undelete=True) podcasts = dev.get_subscribed_podcasts() title = "{username}'s subscriptions".format(username=user.username) exporter = Exporter(title) opml = exporter.generate(podcasts) return HttpResponse(opml, mimetype='text/xml')
def getlist(request): emailaddr = request.GET.get('username', None) password = request.GET.get('password', None) user = auth(emailaddr, password) if user is None: return HttpResponse('@AUTHFAIL', content_type='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', ''), undelete=True) podcasts = dev.get_subscribed_podcasts() title = "{username}'s subscriptions".format(username=user.username) exporter = Exporter(title) opml = exporter.generate(podcasts) return HttpResponse(opml, content_type='text/xml')
def update_chapters(req, user): podcast_url = sanitize_url(req['podcast']) episode_url = sanitize_url(req['episode'], 'episode') episode = models.Episode.for_podcast_url(podcast_url, episode_url, create=True) e_state = episode.get_user_state(request.user) device = None if 'device' in req: device = get_device(request.user, req['device'], request.META.get('HTTP_USER_AGENT', ''), undelete=True) timestamp = dateutil.parser.parse(req['timestamp']) if 'timestamp' in req else datetime.utcnow() new_chapters = parse_new_chapters(request.user, req.get('chapters_add', [])) rem_chapters = parse_rem_chapters(req.get('chapters_remove', [])) e_state.update_chapters(new_chapters, rem_chapters)
def getlist(request): emailaddr = request.GET.get('username', None) password = request.GET.get('password', None) user = auth(emailaddr, password) if user is None: return HttpResponse('@AUTHFAIL', mimetype='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', ''), undelete=True) podcasts = dev.get_subscribed_podcasts() # FIXME: Get username and set a proper title (e.g. "thp's subscription list") title = 'Your subscription list' exporter = Exporter(title) opml = exporter.generate(podcasts) return HttpResponse(opml, mimetype='text/xml')
def device(request, username, device_uid): d = get_device(request.user, device_uid, request.META.get('HTTP_USER_AGENT', '')) data = json.loads(request.raw_post_data) if 'caption' in data: if not data['caption']: return HttpResponseBadRequest('caption must not be empty') d.name = data['caption'] if 'type' in data: if not valid_devicetype(data['type']): return HttpResponseBadRequest('invalid device type %s' % data['type']) d.type = data['type'] request.user.update_device(d) return HttpResponse()
def upload(request): try: emailaddr = request.POST['username'] password = request.POST['password'] action = request.POST['action'] protocol = request.POST['protocol'] opml = request.FILES['opml'].read() except MultiValueDictKeyError: return HttpResponse("@PROTOERROR", content_type='text/plain') user = auth(emailaddr, password) if (not user): return HttpResponse('@AUTHFAIL', content_type='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', '')) existing_urls = [x.url for x in dev.get_subscribed_podcasts()] i = Importer(opml) podcast_urls = [p['url'] for p in i.items] podcast_urls = map(normalize_feed_url, podcast_urls) podcast_urls = list(filter(None, podcast_urls)) new = [u for u in podcast_urls if u not in existing_urls] rem = [u for u in existing_urls if u not in podcast_urls] #remove duplicates new = list(set(new)) rem = list(set(rem)) for n in new: p = Podcast.objects.get_or_create_for_url(n) subscribe(p, user, dev) for r in rem: p = Podcast.objects.get_or_create_for_url(r) unsubscribe(p, user, dev) return HttpResponse('@SUCCESS', content_type='text/plain')
def upload(request): try: emailaddr = request.POST['username'] password = request.POST['password'] action = request.POST['action'] protocol = request.POST['protocol'] opml = request.FILES['opml'].read() except MultiValueDictKeyError: return HttpResponse("@PROTOERROR", content_type='text/plain') user = auth(emailaddr, password) if not user: return HttpResponse('@AUTHFAIL', content_type='text/plain') dev = get_device(user, LEGACY_DEVICE_UID, request.META.get('HTTP_USER_AGENT', '')) existing_urls = [x.url for x in dev.get_subscribed_podcasts()] i = Importer(opml) podcast_urls = [p['url'] for p in i.items] podcast_urls = map(normalize_feed_url, podcast_urls) podcast_urls = list(filter(None, podcast_urls)) new = [u for u in podcast_urls if u not in existing_urls] rem = [u for u in existing_urls if u not in podcast_urls] # remove duplicates new = list(set(new)) rem = list(set(rem)) for n in new: p = Podcast.objects.get_or_create_for_url(n).object subscribe(p.pk, user.pk, dev.uid) for r in rem: p = Podcast.objects.get_or_create_for_url(r).object unsubscribe(p.pk, user.pk, dev.uid) return HttpResponse('@SUCCESS', content_type='text/plain')
def set_subscriptions(urls, user, device_uid, user_agent): # remove empty urls urls = list(filter(None, (u.strip() for u in urls))) device = get_device(user, device_uid, user_agent, undelete=True) subscriptions = dict((p.url, p) for p in device.get_subscribed_podcasts()) new = [p for p in urls if p not in subscriptions.keys()] rem = [p for p in subscriptions.keys() if p not in urls] remove_podcasts = Podcast.objects.filter(urls__url__in=rem) for podcast in remove_podcasts: unsubscribe(podcast.pk, user.pk, device.uid) for url in new: podcast = Podcast.objects.get_or_create_for_url(url).object subscribe(podcast.pk, user.pk, device.uid, url) # Only an empty response is a successful response return HttpResponse("", content_type="text/plain")
def parse_episode_action(action, user, update_urls, now, ua_string): action_str = action.get('action', None) if not valid_episodeaction(action_str): raise Exception('invalid action %s' % action_str) history = EpisodeHistoryEntry() history.action = action['action'] if action.get('device', False): client = get_device(user, action['device'], ua_string) history.client = client if action.get('timestamp', False): history.timestamp = dateutil.parser.parse(action['timestamp']) else: history.timestamp = now history.started = action.get('started', None) history.stopped = action.get('position', None) history.total = action.get('total', None) return history
def parse_episode_action(action, user, update_urls, now, ua_string): action_str = action.get("action", None) if not valid_episodeaction(action_str): raise Exception("invalid action %s" % action_str) history = EpisodeHistoryEntry() history.action = action["action"] if action.get("device", False): client = get_device(user, action["device"], ua_string) history.client = client if action.get("timestamp", False): history.timestamp = dateutil.parser.parse(action["timestamp"]) else: history.timestamp = now history.started = action.get("started", None) history.stopped = action.get("position", None) history.total = action.get("total", None) return history
def get_subscriptions(user, device_uid, user_agent=None): device = get_device(user, device_uid, user_agent) return device.get_subscribed_podcasts()