forked from rc/aircox
		
	
		
			
				
	
	
		
			192 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from django.http import Http404
 | 
						|
from django.shortcuts import get_object_or_404
 | 
						|
from django.utils import timezone as tz
 | 
						|
 | 
						|
from rest_framework import viewsets
 | 
						|
from rest_framework.decorators import action
 | 
						|
from rest_framework.exceptions import ValidationError
 | 
						|
from rest_framework.permissions import IsAdminUser
 | 
						|
from rest_framework.response import Response
 | 
						|
 | 
						|
from aircox.models import Sound, Station
 | 
						|
from aircox.serializers import SoundSerializer
 | 
						|
from . import controllers
 | 
						|
from .serializers import *
 | 
						|
 | 
						|
 | 
						|
__all__ = ['Streamers', 'BaseControllerAPIView',
 | 
						|
           'RequestViewSet', 'StreamerViewSet', 'SourceViewSet',
 | 
						|
           'PlaylistSourceViewSet', 'QueueSourceViewSet']
 | 
						|
 | 
						|
 | 
						|
class Streamers:
 | 
						|
    date = None
 | 
						|
    """ next update datetime """
 | 
						|
    streamers = None
 | 
						|
    """ stations by station id """
 | 
						|
    timeout = None
 | 
						|
    """ timedelta to next update """
 | 
						|
 | 
						|
    def __init__(self, timeout=None):
 | 
						|
        self.timeout = timeout or tz.timedelta(seconds=2)
 | 
						|
 | 
						|
    def load(self, force=False):
 | 
						|
        # FIXME: cf. TODO in aircox.controllers about model updates
 | 
						|
        stations = Station.objects.active()
 | 
						|
        if self.streamers is None or force:
 | 
						|
            self.streamers = {station.pk: controllers.Streamer(station)
 | 
						|
                              for station in stations}
 | 
						|
            return
 | 
						|
 | 
						|
        streamers = self.streamers
 | 
						|
        self.streamers = {station.pk: controllers.Streamer(station)
 | 
						|
                          if station.pk in streamers else streamers[station.pk]
 | 
						|
                          for station in stations}
 | 
						|
 | 
						|
    def fetch(self):
 | 
						|
        if self.streamers is None:
 | 
						|
            self.load()
 | 
						|
 | 
						|
        now = tz.now()
 | 
						|
        if self.date is not None and now < self.date:
 | 
						|
            return
 | 
						|
 | 
						|
        for streamer in self.streamers.values():
 | 
						|
            streamer.fetch()
 | 
						|
        self.date = now + self.timeout
 | 
						|
 | 
						|
    def get(self, key, default=None):
 | 
						|
        return self.streamers.get(key, default)
 | 
						|
 | 
						|
    def values(self):
 | 
						|
        return self.streamers.values()
 | 
						|
 | 
						|
    def __getitem__(self, key):
 | 
						|
        return self.streamers[key]
 | 
						|
 | 
						|
    def __contains__(self, key):
 | 
						|
        return key in self.streamers
 | 
						|
 | 
						|
 | 
						|
streamers = Streamers()
 | 
						|
 | 
						|
 | 
						|
class BaseControllerAPIView(viewsets.ViewSet):
 | 
						|
    permission_classes = (IsAdminUser,)
 | 
						|
    serializer_class = None
 | 
						|
    streamer = None
 | 
						|
    object = None
 | 
						|
 | 
						|
    def get_streamer(self, request, station_pk=None, **kwargs):
 | 
						|
        streamers.fetch()
 | 
						|
        id = int(request.station.pk if station_pk is None else station_pk)
 | 
						|
        if id not in streamers:
 | 
						|
            raise Http404('station not found')
 | 
						|
        return streamers[id]
 | 
						|
 | 
						|
    def get_serializer(self, **kwargs):
 | 
						|
        return self.serializer_class(self.object, **kwargs)
 | 
						|
 | 
						|
    def serialize(self, obj, **kwargs):
 | 
						|
        self.object = obj
 | 
						|
        serializer = self.get_serializer(**kwargs)
 | 
						|
        return serializer.data
 | 
						|
 | 
						|
    def dispatch(self, request, *args, station_pk=None, **kwargs):
 | 
						|
        self.streamer = self.get_streamer(request, station_pk, **kwargs)
 | 
						|
        return super().dispatch(request, *args, **kwargs)
 | 
						|
 | 
						|
 | 
						|
class RequestViewSet(BaseControllerAPIView):
 | 
						|
    serializer_class = RequestSerializer
 | 
						|
 | 
						|
 | 
						|
class StreamerViewSet(BaseControllerAPIView):
 | 
						|
    serializer_class = StreamerSerializer
 | 
						|
 | 
						|
    def retrieve(self, request, pk=None):
 | 
						|
        return Response(self.serialize(self.streamer))
 | 
						|
 | 
						|
    def list(self, request, pk=None):
 | 
						|
        return Response({
 | 
						|
            'results': self.serialize(streamers.values(), many=True)
 | 
						|
        })
 | 
						|
 | 
						|
    def dispatch(self, request, *args, pk=None, **kwargs):
 | 
						|
        if pk is not None:
 | 
						|
            kwargs.setdefault('station_pk', pk)
 | 
						|
        self.streamer = self.get_streamer(request, **kwargs)
 | 
						|
        self.object = self.streamer
 | 
						|
        return super().dispatch(request, *args, **kwargs)
 | 
						|
 | 
						|
 | 
						|
class SourceViewSet(BaseControllerAPIView):
 | 
						|
    serializer_class = SourceSerializer
 | 
						|
    model = controllers.Source
 | 
						|
 | 
						|
    def get_sources(self):
 | 
						|
        return (s for s in self.streamer.sources if isinstance(s, self.model))
 | 
						|
 | 
						|
    def get_source(self, pk):
 | 
						|
        source = next((source for source in self.get_sources()
 | 
						|
                      if source.id == pk), None)
 | 
						|
        if source is None:
 | 
						|
            raise Http404('source `%s` not found' % pk)
 | 
						|
        return source
 | 
						|
 | 
						|
    def retrieve(self, request, pk=None):
 | 
						|
        self.object = self.get_source(pk)
 | 
						|
        return Response(self.serialize())
 | 
						|
 | 
						|
    def list(self, request):
 | 
						|
        return Response({
 | 
						|
            'results': self.serialize(self.get_sources(), many=True)
 | 
						|
        })
 | 
						|
 | 
						|
    def _run(self, pk, action):
 | 
						|
        source = self.object = self.get_source(pk)
 | 
						|
        action(source)
 | 
						|
        source.fetch()
 | 
						|
        return Response(self.serialize(source))
 | 
						|
 | 
						|
    @action(detail=True, methods=['POST'])
 | 
						|
    def sync(self, request, pk):
 | 
						|
        return self._run(pk, lambda s: s.sync())
 | 
						|
 | 
						|
    @action(detail=True, methods=['POST'])
 | 
						|
    def skip(self, request, pk):
 | 
						|
        return self._run(pk, lambda s: s.skip())
 | 
						|
 | 
						|
    @action(detail=True, methods=['POST'])
 | 
						|
    def restart(self, request, pk):
 | 
						|
        return self._run(pk, lambda s: s.restart())
 | 
						|
 | 
						|
    @action(detail=True, methods=['POST'])
 | 
						|
    def seek(self, request, pk):
 | 
						|
        count = request.POST['seek']
 | 
						|
        return self._run(pk, lambda s: s.seek(count))
 | 
						|
 | 
						|
 | 
						|
class PlaylistSourceViewSet(SourceViewSet):
 | 
						|
    serializer_class = PlaylistSerializer
 | 
						|
    model = controllers.PlaylistSource
 | 
						|
 | 
						|
 | 
						|
class QueueSourceViewSet(SourceViewSet):
 | 
						|
    serializer_class = QueueSourceSerializer
 | 
						|
    model = controllers.QueueSource
 | 
						|
 | 
						|
    def get_sound_queryset(self):
 | 
						|
        return Sound.objects.station(self.request.station).archive()
 | 
						|
 | 
						|
    @action(detail=True, methods=['POST'])
 | 
						|
    def push(self, request, pk):
 | 
						|
        if not request.data.get('sound_id'):
 | 
						|
            raise ValidationError('missing "sound_id" POST data')
 | 
						|
 | 
						|
        sound = get_object_or_404(self.get_sound_queryset(),
 | 
						|
                                  pk=request.data['sound_id'])
 | 
						|
        return self._run(
 | 
						|
            pk, lambda s: s.push(sound.path) if sound.path else None)
 | 
						|
 |