views.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. import json
  2. import logging
  3. from django.conf import settings
  4. from django.db.models.fields import timezone
  5. from django.http import HttpResponseRedirect
  6. from django.urls import reverse
  7. from django.utils import timezone
  8. from django.views.decorators.csrf import csrf_exempt
  9. from django.views.generic import FormView
  10. from django.views.generic.list import ListView
  11. import pytz
  12. from rest_framework import status
  13. from rest_framework.decorators import api_view
  14. from rest_framework.response import Response
  15. from scrobbles.constants import (
  16. JELLYFIN_AUDIO_ITEM_TYPES,
  17. JELLYFIN_VIDEO_ITEM_TYPES,
  18. )
  19. from scrobbles.forms import ScrobbleForm
  20. from scrobbles.imdb import lookup_video_from_imdb
  21. from scrobbles.models import Scrobble
  22. from scrobbles.scrobblers import (
  23. jellyfin_scrobble_track,
  24. jellyfin_scrobble_video,
  25. manual_scrobble_event,
  26. manual_scrobble_video,
  27. mopidy_scrobble_podcast,
  28. mopidy_scrobble_track,
  29. )
  30. from scrobbles.serializers import ScrobbleSerializer
  31. from scrobbles.thesportsdb import lookup_event_from_thesportsdb
  32. from vrobbler.apps.music.aggregators import (
  33. scrobble_counts,
  34. top_artists,
  35. top_tracks,
  36. week_of_scrobbles,
  37. )
  38. logger = logging.getLogger(__name__)
  39. class RecentScrobbleList(ListView):
  40. model = Scrobble
  41. def get_context_data(self, **kwargs):
  42. data = super().get_context_data(**kwargs)
  43. user = self.request.user
  44. now = timezone.now()
  45. if self.request.user.is_authenticated:
  46. if user.profile:
  47. timezone.activate(pytz.timezone(user.profile.timezone))
  48. now = timezone.localtime(timezone.now())
  49. data['now_playing_list'] = Scrobble.objects.filter(
  50. in_progress=True,
  51. is_paused=False,
  52. timestamp__lte=now,
  53. user=user,
  54. )
  55. completed_for_user = Scrobble.objects.filter(
  56. played_to_completion=True, user=user
  57. )
  58. data['video_scrobble_list'] = completed_for_user.filter(
  59. video__isnull=False
  60. ).order_by('-timestamp')[:15]
  61. data['podcast_scrobble_list'] = completed_for_user.filter(
  62. podcast_episode__isnull=False
  63. ).order_by('-timestamp')[:15]
  64. data['sport_scrobble_list'] = completed_for_user.filter(
  65. sport_event__isnull=False
  66. ).order_by('-timestamp')[:15]
  67. # data['top_daily_tracks'] = top_tracks()
  68. data['top_weekly_tracks'] = top_tracks(user, filter='week')
  69. data['top_monthly_tracks'] = top_tracks(user, filter='month')
  70. # data['top_daily_artists'] = top_artists()
  71. data['top_weekly_artists'] = top_artists(user, filter='week')
  72. data['top_monthly_artists'] = top_artists(user, filter='month')
  73. data["weekly_data"] = week_of_scrobbles(user=user)
  74. data['counts'] = scrobble_counts(user)
  75. data['imdb_form'] = ScrobbleForm
  76. return data
  77. def get_queryset(self):
  78. return Scrobble.objects.filter(
  79. track__isnull=False, in_progress=False
  80. ).order_by('-timestamp')[:15]
  81. class ManualScrobbleView(FormView):
  82. form_class = ScrobbleForm
  83. template_name = 'scrobbles/manual_form.html'
  84. def form_valid(self, form):
  85. item_id = form.cleaned_data.get('item_id')
  86. data_dict = None
  87. if 'tt' in item_id:
  88. data_dict = lookup_video_from_imdb(
  89. form.cleaned_data.get('item_id')
  90. )
  91. if data_dict:
  92. manual_scrobble_video(data_dict, self.request.user.id)
  93. if not data_dict:
  94. logger.debug(f"Looking for sport event with ID {item_id}")
  95. data_dict = lookup_event_from_thesportsdb(
  96. form.cleaned_data.get('item_id')
  97. )
  98. if data_dict:
  99. manual_scrobble_event(data_dict, self.request.user.id)
  100. return HttpResponseRedirect(reverse("home"))
  101. @csrf_exempt
  102. @api_view(['GET'])
  103. def scrobble_endpoint(request):
  104. """List all Scrobbles, or create a new Scrobble"""
  105. scrobble = Scrobble.objects.all()
  106. serializer = ScrobbleSerializer(scrobble, many=True)
  107. return Response(serializer.data)
  108. @csrf_exempt
  109. @api_view(['POST'])
  110. def jellyfin_websocket(request):
  111. data_dict = request.data
  112. # For making things easier to build new input processors
  113. if getattr(settings, "DUMP_REQUEST_DATA", False):
  114. json_data = json.dumps(data_dict, indent=4)
  115. logger.debug(f"{json_data}")
  116. scrobble = None
  117. media_type = data_dict.get("ItemType", "")
  118. if media_type in JELLYFIN_AUDIO_ITEM_TYPES:
  119. scrobble = jellyfin_scrobble_track(data_dict, request.user.id)
  120. if media_type in JELLYFIN_VIDEO_ITEM_TYPES:
  121. scrobble = jellyfin_scrobble_video(data_dict, request.user.id)
  122. if not scrobble:
  123. return Response({}, status=status.HTTP_400_BAD_REQUEST)
  124. return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)
  125. @csrf_exempt
  126. @api_view(['POST'])
  127. def mopidy_websocket(request):
  128. try:
  129. data_dict = json.loads(request.data)
  130. except TypeError:
  131. logger.warning('Received Mopidy data as dict, rather than a string')
  132. data_dict = request.data
  133. # For making things easier to build new input processors
  134. if getattr(settings, "DUMP_REQUEST_DATA", False):
  135. json_data = json.dumps(data_dict, indent=4)
  136. logger.debug(f"{json_data}")
  137. if 'podcast' in data_dict.get('mopidy_uri'):
  138. scrobble = mopidy_scrobble_podcast(data_dict, request.user.id)
  139. else:
  140. scrobble = mopidy_scrobble_track(data_dict, request.user.id)
  141. if not scrobble:
  142. return Response({}, status=status.HTTP_400_BAD_REQUEST)
  143. return Response({'scrobble_id': scrobble.id}, status=status.HTTP_200_OK)
  144. @csrf_exempt
  145. @api_view(['GET'])
  146. def scrobble_finish(request, uuid):
  147. user = request.user
  148. if not user.is_authenticated:
  149. return Response({}, status=status.HTTP_403_FORBIDDEN)
  150. scrobble = Scrobble.objects.filter(user=user, uuid=uuid).first()
  151. if not scrobble:
  152. return Response({}, status=status.HTTP_404_NOT_FOUND)
  153. scrobble.stop(force_finish=True)
  154. return Response(
  155. {'id': scrobble.id, 'status': scrobble.status},
  156. status=status.HTTP_200_OK,
  157. )
  158. @csrf_exempt
  159. @api_view(['GET'])
  160. def scrobble_cancel(request, uuid):
  161. user = request.user
  162. if not user.is_authenticated:
  163. return Response({}, status=status.HTTP_403_FORBIDDEN)
  164. scrobble = Scrobble.objects.filter(user=user, uuid=uuid).first()
  165. if not scrobble:
  166. return Response({}, status=status.HTTP_404_NOT_FOUND)
  167. scrobble.cancel()
  168. return Response(
  169. {'id': scrobble.id, 'status': 'cancelled'}, status=status.HTTP_200_OK
  170. )