[videos] Add youtube scrobbling
This commit is contained in:
@ -1186,7 +1186,7 @@ class Scrobble(TimeStampedModel):
|
|||||||
profile = UserProfile.objects.filter(
|
profile = UserProfile.objects.filter(
|
||||||
user_id=scrobble_data["user_id"]
|
user_id=scrobble_data["user_id"]
|
||||||
).first()
|
).first()
|
||||||
if profile.ntfy_enabled and profile.ntfy_url:
|
if profile and profile.ntfy_enabled and profile.ntfy_url:
|
||||||
# TODO allow prority and tags to be configured in the profile
|
# TODO allow prority and tags to be configured in the profile
|
||||||
notify_str = f"{scrobble.media_obj}"
|
notify_str = f"{scrobble.media_obj}"
|
||||||
if scrobble.log and scrobble.log.get("description"):
|
if scrobble.log and scrobble.log.get("description"):
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
import pendulum
|
import pendulum
|
||||||
import pytz
|
import pytz
|
||||||
from beers.models import Beer
|
from beers.models import Beer
|
||||||
@ -133,6 +134,49 @@ def jellyfin_scrobble_media(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def web_scrobbler_scrobble_media(
|
||||||
|
post_data: dict, user_id: int
|
||||||
|
) -> Optional[Scrobble]:
|
||||||
|
media_type = Scrobble.MediaType.VIDEO
|
||||||
|
|
||||||
|
event_name = post_data.get("eventName")
|
||||||
|
parsed = post_data.get("data").get("song").get("parsed")
|
||||||
|
processed = post_data.get("data").get("song").get("processed")
|
||||||
|
video, created = Video.objects.get_or_create(
|
||||||
|
video_type=Video.VideoType.YOUTUBE,
|
||||||
|
youtube_url=parsed.get("originUrl"),
|
||||||
|
)
|
||||||
|
timestamp = datetime.utcfromtimestamp(
|
||||||
|
post_data.get("time", 0)/1000
|
||||||
|
).replace(tzinfo=pytz.utc)
|
||||||
|
|
||||||
|
if created or event_name == "nowplaying":
|
||||||
|
video.run_time_seconds = 1800
|
||||||
|
processed = post_data.get("data").get("song").get("processed")
|
||||||
|
# TODO maybe artist could be the series?
|
||||||
|
video.title = " - ".join([processed.get("artist"), processed.get("track")])
|
||||||
|
video.save()
|
||||||
|
return video.scrobble_for_user(
|
||||||
|
user_id,
|
||||||
|
source="YouTube",
|
||||||
|
playback_position_seconds=0,
|
||||||
|
status=event_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
scrobble = Scrobble.objects.filter(user_id=user_id, video=video, in_progress=True).first()
|
||||||
|
if not scrobble:
|
||||||
|
return video.scrobble_for_user(
|
||||||
|
user_id,
|
||||||
|
source="YouTube",
|
||||||
|
playback_position_seconds=0,
|
||||||
|
status=event_name,
|
||||||
|
)
|
||||||
|
if event_name == "paused":
|
||||||
|
scrobble.pause()
|
||||||
|
if event_name == "resumedplaying":
|
||||||
|
scrobble.resume()
|
||||||
|
return scrobble
|
||||||
|
|
||||||
def manual_scrobble_video(imdb_id: str, user_id: int):
|
def manual_scrobble_video(imdb_id: str, user_id: int):
|
||||||
if "tt" not in imdb_id:
|
if "tt" not in imdb_id:
|
||||||
imdb_id = "tt" + imdb_id
|
imdb_id = "tt" + imdb_id
|
||||||
|
|||||||
@ -31,6 +31,11 @@ urlpatterns = [
|
|||||||
views.lastfm_import,
|
views.lastfm_import,
|
||||||
name="lastfm-import",
|
name="lastfm-import",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"webhook/web-scrobbler/",
|
||||||
|
views.web_scrobbler_webhook,
|
||||||
|
name="web-scrobbler-webhook",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"webhook/gps/",
|
"webhook/gps/",
|
||||||
views.gps_webhook,
|
views.gps_webhook,
|
||||||
|
|||||||
@ -314,6 +314,29 @@ def lastfm_import(request):
|
|||||||
|
|
||||||
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
@api_view(["POST"])
|
||||||
|
def web_scrobbler_webhook(request):
|
||||||
|
post_data = request.data
|
||||||
|
logger.info(
|
||||||
|
"[web_scrobbler_webhook] called",
|
||||||
|
extra={
|
||||||
|
"post_data": post_data,
|
||||||
|
"user_id": request.user.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
scrobble = web_scrobbler_scrobble_media(post_data, request.user.id)
|
||||||
|
|
||||||
|
if not scrobble:
|
||||||
|
return Response({}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
"[jellyfin_webhook] finished",
|
||||||
|
extra={"scrobble_id": scrobble.id},
|
||||||
|
)
|
||||||
|
return Response({"scrobble_id": scrobble.id}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 4.2.16 on 2024-11-06 03:07
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("videos", "0017_alter_video_video_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="video",
|
||||||
|
name="youtube_url",
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="video",
|
||||||
|
name="video_type",
|
||||||
|
field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("U", "Unknown"),
|
||||||
|
("E", "TV Episode"),
|
||||||
|
("M", "Movie"),
|
||||||
|
("S", "Skate Video"),
|
||||||
|
("Y", "YouTube Video"),
|
||||||
|
],
|
||||||
|
default="U",
|
||||||
|
max_length=1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -128,6 +128,7 @@ class Video(ScrobblableMixin):
|
|||||||
TV_EPISODE = "E", _("TV Episode")
|
TV_EPISODE = "E", _("TV Episode")
|
||||||
MOVIE = "M", _("Movie")
|
MOVIE = "M", _("Movie")
|
||||||
SKATE_VIDEO = "S", _("Skate Video")
|
SKATE_VIDEO = "S", _("Skate Video")
|
||||||
|
YOUTUBE = "Y", _("YouTube Video")
|
||||||
|
|
||||||
video_type = models.CharField(
|
video_type = models.CharField(
|
||||||
max_length=1,
|
max_length=1,
|
||||||
@ -161,6 +162,7 @@ class Video(ScrobblableMixin):
|
|||||||
tvrage_id = models.CharField(max_length=20, **BNULL)
|
tvrage_id = models.CharField(max_length=20, **BNULL)
|
||||||
tvdb_id = models.CharField(max_length=20, **BNULL)
|
tvdb_id = models.CharField(max_length=20, **BNULL)
|
||||||
tmdb_id = models.CharField(max_length=20, **BNULL)
|
tmdb_id = models.CharField(max_length=20, **BNULL)
|
||||||
|
youtube_url = models.CharField(max_length=255, **BNULL)
|
||||||
plot = models.TextField(**BNULL)
|
plot = models.TextField(**BNULL)
|
||||||
year = models.IntegerField(**BNULL)
|
year = models.IntegerField(**BNULL)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user