Archived
Refactor alot
- add django-redis-cache - remove CurrentTrack db model
This commit is contained in:
1 parent
531488d4a0
commit
d4f862cb4a
9 files changed
+120
-141
No files matched your search
+3
-44
@@ -1,55 +1,14 @@
|
||||
from django.utils import timezone
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from dateutil import parser
|
||||
|
||||
from oliver_screen import lastfm, utils
|
||||
from oliver_screen.lastfm import get_track_data, get_last_track_data
|
||||
|
||||
|
||||
class LastTrackView(APIView):
|
||||
def _get_playback_date(self, playback_date_string):
|
||||
playback_date = parser.parse(playback_date_string)
|
||||
return timezone.make_aware(playback_date, timezone.UTC()) # Last.fm API is in UTC
|
||||
|
||||
def _time_since(self, playback_date_string):
|
||||
playback_date = self._get_playback_date(playback_date_string)
|
||||
formatted = naturaltime(playback_date)
|
||||
formatted = formatted.replace(u'\xa0', u' ') # remove non-breaking space (in latin1)
|
||||
return formatted
|
||||
|
||||
def get(self, request, format=None):
|
||||
user = lastfm.get_user()
|
||||
playedtracks = user.get_recent_tracks(limit=2)
|
||||
|
||||
track_data = {
|
||||
'artist': playedtracks[0].track.get_artist().get_name(),
|
||||
'title': playedtracks[0].track.get_title(),
|
||||
'playback_date': self._get_playback_date(playedtracks[0].playback_date),
|
||||
'time_since': self._time_since(playedtracks[0].playback_date)
|
||||
}
|
||||
return Response({'track': track_data})
|
||||
return Response({'track': get_last_track_data()})
|
||||
|
||||
|
||||
class CurrentTrackView(APIView):
|
||||
def get(self, request, format=None):
|
||||
user = lastfm.get_user()
|
||||
track = user.get_now_playing()
|
||||
|
||||
if track is None:
|
||||
# No response from LastFM?
|
||||
return Response({'track': utils.get_current_from_cache()})
|
||||
|
||||
artist = track.get_artist()
|
||||
video = utils.get_youtubevideo(artist.get_name(), track.get_title())
|
||||
image = artist.get_cover_image(size=4)
|
||||
|
||||
now_playing = {
|
||||
'artist': artist.get_name(),
|
||||
'title': track.get_title(),
|
||||
'image': image,
|
||||
'video': video
|
||||
}
|
||||
utils.cache_current(now_playing)
|
||||
|
||||
return Response({'track': now_playing})
|
||||
return Response({'track': get_track_data()})
|
||||
@@ -1,9 +1,7 @@
|
||||
from channels import Group
|
||||
from channels.sessions import channel_session
|
||||
from django.conf import settings
|
||||
import json
|
||||
|
||||
from oliver_screen.models import ScreenConsumer
|
||||
from oliver_screen.tasks import update_screens_task
|
||||
|
||||
|
||||
@@ -17,21 +15,11 @@ def ws_connect(message):
|
||||
g = Group(settings.OLIVER_WS_GROUP, channel_layer=message.channel_layer)
|
||||
g.add(message.reply_channel)
|
||||
|
||||
active_consumers = ScreenConsumer.objects.filter(is_active=True)
|
||||
# At last one
|
||||
if active_consumers.exists():
|
||||
screen_consumer = active_consumers.first()
|
||||
else:
|
||||
screen_consumer = ScreenConsumer.objects.create()
|
||||
update_screens_task() # async
|
||||
|
||||
g.send({'text': json.dumps({'screen_consumer': {'uuid': str(screen_consumer.uuid)}})})
|
||||
update_screens_task() # async
|
||||
|
||||
|
||||
@channel_session
|
||||
def ws_disconnect(message):
|
||||
Group(settings.OLIVER_WS_GROUP, channel_layer=message.channel_layer).discard(message.reply_channel)
|
||||
|
||||
# FIXME: only set specific uuid inactive and only revoke is no more active screens
|
||||
ScreenConsumer.objects.filter(is_active=True).update(is_active=False)
|
||||
update_screens_task.revoke()
|
||||
# update_screens_task.revoke()
|
||||
+57
-5
@@ -1,13 +1,65 @@
|
||||
import pylast
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.contrib.humanize.templatetags.humanize import naturaltime
|
||||
from dateutil import parser
|
||||
|
||||
from oliver_screen import utils
|
||||
from oliver_screen.models import LastFMUser
|
||||
|
||||
|
||||
def get_user():
|
||||
network = pylast.LastFMNetwork(api_key=settings.LASTFM_API_KEY, api_secret=settings.LASTFM_API_SECRET)
|
||||
lastfmusers = LastFMUser.objects.filter(active=True)
|
||||
if len(lastfmusers) != 1:
|
||||
# default
|
||||
return network.get_user(settings.LASTFM_DEFAULT_USERNAME)
|
||||
|
||||
return network.get_user(lastfmusers[0].name)
|
||||
lastfmusers = LastFMUser.objects.filter(active=True)
|
||||
lastfm_username = lastfmusers[0].name if len(lastfmusers) == 1 else settings.LASTFM_DEFAULT_USERNAME
|
||||
|
||||
return network.get_user(lastfm_username)
|
||||
|
||||
|
||||
def _get_playback_date(playback_date_string):
|
||||
playback_date = parser.parse(playback_date_string)
|
||||
|
||||
return timezone.make_aware(playback_date, timezone.UTC()) # Last.fm API is in UTC
|
||||
|
||||
|
||||
def _time_since(playback_date_string):
|
||||
playback_date = _get_playback_date(playback_date_string)
|
||||
formatted = naturaltime(playback_date)
|
||||
formatted = formatted.replace(u'\xa0', u' ') # remove non-breaking space (in latin1)
|
||||
|
||||
return formatted
|
||||
|
||||
|
||||
def get_last_track_data():
|
||||
user = get_user()
|
||||
playedtracks = user.get_recent_tracks(limit=2)
|
||||
|
||||
track_data = {
|
||||
'artist': playedtracks[0].track.get_artist().get_name(),
|
||||
'title': playedtracks[0].track.get_title(),
|
||||
'playback_date': _get_playback_date(playedtracks[0].playback_date),
|
||||
'time_since': _time_since(playedtracks[0].playback_date)
|
||||
}
|
||||
return track_data
|
||||
|
||||
|
||||
def get_track_data():
|
||||
user = get_user()
|
||||
track = user.get_now_playing()
|
||||
|
||||
if track is None:
|
||||
return utils.get_current_from_cache()
|
||||
|
||||
artist = track.get_artist()
|
||||
image = artist.get_cover_image(size=4)
|
||||
|
||||
track_data = {
|
||||
'artist': artist.get_name(),
|
||||
'title': track.get_title(),
|
||||
'image': image,
|
||||
}
|
||||
|
||||
utils.cache_current(track_data)
|
||||
|
||||
return track_data
|
||||
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oliver_screen', '0003_auto_20160403_1343'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='CurrentTrack',
|
||||
),
|
||||
]
|
||||
@@ -12,16 +12,6 @@ class ScreenConsumer(models.Model):
|
||||
return str(self.uuid)
|
||||
|
||||
|
||||
class CurrentTrack(models.Model):
|
||||
title = models.CharField(max_length=200)
|
||||
artist = models.CharField(max_length=200)
|
||||
image = models.CharField(max_length=200)
|
||||
retries = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return self.title + " - " + self.artist
|
||||
|
||||
|
||||
class LastFMUser(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
+26
-27
@@ -2,45 +2,44 @@ import json
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
from huey.contrib.djhuey import db_task
|
||||
from channels import Group
|
||||
|
||||
from oliver_screen import utils, lastfm
|
||||
from oliver_screen.models import ScreenConsumer
|
||||
from oliver_screen import lastfm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LOCK_EXPIRE = 60 * 5 # Lock expires in 5 minutes
|
||||
|
||||
|
||||
def acquire_lock(lock_id):
|
||||
# cache.add fails if the key already exists
|
||||
return cache.add(lock_id, 'true', LOCK_EXPIRE)
|
||||
|
||||
|
||||
def release_lock(lock_id):
|
||||
# memcache delete is very slow, but we have to use it to take advantage of using add() for atomic locking
|
||||
return cache.delete(lock_id)
|
||||
|
||||
|
||||
@db_task()
|
||||
def update_screens_task():
|
||||
screen_channel_group = Group(settings.OLIVER_WS_GROUP)
|
||||
lock_id = '{0}-lock'.format(settings.OLIVER_WS_GROUP)
|
||||
|
||||
logger.debug('Updating screens')
|
||||
if acquire_lock(lock_id):
|
||||
try:
|
||||
track_data = lastfm.get_track_data()
|
||||
finally:
|
||||
release_lock(lock_id)
|
||||
|
||||
user = lastfm.get_user()
|
||||
track = user.get_now_playing()
|
||||
if track is None:
|
||||
track_data = utils.get_current_from_cache()
|
||||
logger.debug("sending ", track_data, "to", screen_channel_group.name)
|
||||
screen_channel_group.send({'track': json.dumps({'track': track_data})})
|
||||
return
|
||||
screen_channel_group.send({'text': json.dumps({'track': track_data})})
|
||||
|
||||
artist = track.get_artist()
|
||||
image = artist.get_cover_image(size=4)
|
||||
|
||||
track_data = {
|
||||
'artist': artist.get_name(),
|
||||
'title': track.get_title(),
|
||||
'image': image,
|
||||
}
|
||||
utils.cache_current(track_data)
|
||||
|
||||
logger.debug("sending ", track_data, "to", screen_channel_group.name)
|
||||
screen_channel_group.send({'text': json.dumps({'track': track_data})})
|
||||
|
||||
# At least one active screen
|
||||
if ScreenConsumer.objects.filter(is_active=True).exists():
|
||||
update_screens_task.schedule(delay=settings.OLIVER_LASTFM_POLL_INTERVAL)
|
||||
else:
|
||||
logger.debug('No active screens, bailing')
|
||||
|
||||
return track_data
|
||||
return track_data
|
||||
|
||||
logger.debug('Last.FM is already being polled, doing nothing.')
|
||||
|
||||
+6
-38
@@ -1,4 +1,5 @@
|
||||
from oliver_screen.models import CurrentTrack, YouTubeVideo
|
||||
from django.core.cache import cache
|
||||
from oliver_screen.models import YouTubeVideo
|
||||
|
||||
|
||||
def get_youtubevideo(artist, title):
|
||||
@@ -9,45 +10,12 @@ def get_youtubevideo(artist, title):
|
||||
|
||||
return videos[0]
|
||||
|
||||
CACHE_KEY = 'oliver-track'
|
||||
|
||||
def cache_current(now_playing):
|
||||
# Fetch the one CurrentTrack entry in the db and update it with the current track.
|
||||
try:
|
||||
current = CurrentTrack.objects.get(pk=1)
|
||||
except CurrentTrack.DoesNotExist:
|
||||
# update
|
||||
current = CurrentTrack(artist="", title="", image="", retries=0)
|
||||
|
||||
current.artist = now_playing['artist']
|
||||
current.title = now_playing['title']
|
||||
current.image = now_playing['image']
|
||||
current.retries = 0
|
||||
current.save()
|
||||
def cache_current(track):
|
||||
return cache.set(CACHE_KEY, track, 20) # in seconds
|
||||
|
||||
|
||||
def get_current_from_cache():
|
||||
try:
|
||||
current = CurrentTrack.objects.get(pk=1)
|
||||
except CurrentTrack.DoesNotExist:
|
||||
current = CurrentTrack(artist="", title="", image="", retries=0)
|
||||
current.save()
|
||||
|
||||
now_playing = {}
|
||||
|
||||
# try 3 times before clearing the cache
|
||||
if current.artist != "" and current.retries < 3:
|
||||
now_playing = {
|
||||
'artist': current.artist,
|
||||
'title': current.title,
|
||||
'image': current.image
|
||||
}
|
||||
|
||||
current.retries += 1
|
||||
current.save()
|
||||
else:
|
||||
current.artist = ""
|
||||
current.title = ""
|
||||
current.image = ""
|
||||
current.save()
|
||||
|
||||
return now_playing
|
||||
return cache.get(CACHE_KEY)
|
||||
@@ -1,14 +1,12 @@
|
||||
from __future__ import unicode_literals
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
|
||||
from huey import RedisHuey
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = '#r65fsg)wsk&cs8m^i)@##7-wg=#z=*b_)a82#5z#4%&!j_9=6'
|
||||
@@ -102,6 +100,13 @@ OLIVER_LASTFM_POLL_INTERVAL = 10 # seconds
|
||||
|
||||
HUEY = RedisHuey('oliverscreen', result_store=True)
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'redis_cache.RedisCache',
|
||||
'LOCATION': 'localhost:6379',
|
||||
}
|
||||
}
|
||||
|
||||
# Local settings
|
||||
try:
|
||||
from .local_settings import *
|
||||
|
||||
@@ -3,6 +3,7 @@ channels==0.10.3
|
||||
Django>=1.8,<1.9
|
||||
django-extensions==1.6.1
|
||||
django-flat-theme==1.1.3
|
||||
django-redis-cache==1.6.5
|
||||
djangorestframework==3.3.3
|
||||
gevent==1.1.0
|
||||
gunicorn==19.4.5
|
||||
|
||||
Reference in new issue
Block a user