Allow starting next in series
This commit is contained in:
@ -229,7 +229,7 @@ class ManualScrobbleView(FormView):
|
|||||||
if key == "-i":
|
if key == "-i":
|
||||||
manual_scrobble_video(item_id, self.request.user.id)
|
manual_scrobble_video(item_id, self.request.user.id)
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse("vrobbler-home"))
|
return HttpResponseRedirect(self.request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
|
||||||
class JsonableResponseMixin:
|
class JsonableResponseMixin:
|
||||||
@ -272,7 +272,7 @@ class AudioScrobblerImportCreateView(
|
|||||||
self.object.user = self.request.user
|
self.object.user = self.request.user
|
||||||
self.object.save()
|
self.object.save()
|
||||||
process_tsv_import.delay(self.object.id)
|
process_tsv_import.delay(self.object.id)
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
|
||||||
class KoReaderImportCreateView(
|
class KoReaderImportCreateView(
|
||||||
@ -288,7 +288,7 @@ class KoReaderImportCreateView(
|
|||||||
self.object.user = self.request.user
|
self.object.user = self.request.user
|
||||||
self.object.save()
|
self.object.save()
|
||||||
process_koreader_import.delay(self.object.id)
|
process_koreader_import.delay(self.object.id)
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
return HttpResponseRedirect(self.request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
@ -300,8 +300,7 @@ def lastfm_import(request):
|
|||||||
|
|
||||||
process_lastfm_import.delay(lfm_import.id)
|
process_lastfm_import.delay(lfm_import.id)
|
||||||
|
|
||||||
success_url = reverse_lazy("vrobbler-home")
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||||
return HttpResponseRedirect(success_url)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@ -394,6 +393,7 @@ def scrobble_start(request, uuid):
|
|||||||
success_url = request.META.get("HTTP_REFERER")
|
success_url = request.META.get("HTTP_REFERER")
|
||||||
|
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
|
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(success_url)
|
||||||
|
|
||||||
media_obj = None
|
media_obj = None
|
||||||
@ -506,7 +506,7 @@ def scrobble_cancel(request, uuid):
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
messages.add_message(request, messages.ERROR, "Scrobble not found.")
|
messages.add_message(request, messages.ERROR, "Scrobble not found.")
|
||||||
return HttpResponseRedirect(success_url)
|
return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
|
||||||
|
|
||||||
|
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
|
|||||||
21
vrobbler/apps/videos/migrations/0013_video_next_imdb_id.py
Normal file
21
vrobbler/apps/videos/migrations/0013_video_next_imdb_id.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 4.1.5 on 2023-03-15 17:52
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
(
|
||||||
|
"videos",
|
||||||
|
"0012_remove_series_tags_remove_video_tags_series_genre_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="video",
|
||||||
|
name="next_imdb_id",
|
||||||
|
field=models.CharField(blank=True, max_length=20, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Dict
|
from typing import Dict, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
@ -40,13 +40,33 @@ class Series(TimeStampedModel):
|
|||||||
def imdb_link(self):
|
def imdb_link(self):
|
||||||
return f"https://www.imdb.com/title/tt{self.imdb_id}"
|
return f"https://www.imdb.com/title/tt{self.imdb_id}"
|
||||||
|
|
||||||
def scrobbles_for_user(self, user_id: int):
|
def scrobbles_for_user(self, user_id: int, include_playing=False):
|
||||||
from scrobbles.models import Scrobble
|
from scrobbles.models import Scrobble
|
||||||
|
|
||||||
|
played_query = models.Q(played_to_completion=True)
|
||||||
|
if include_playing:
|
||||||
|
played_query = models.Q()
|
||||||
return Scrobble.objects.filter(
|
return Scrobble.objects.filter(
|
||||||
video__tv_series=self, user=user_id, played_to_completion=True
|
played_query,
|
||||||
|
video__tv_series=self,
|
||||||
|
user=user_id,
|
||||||
).order_by("-timestamp")
|
).order_by("-timestamp")
|
||||||
|
|
||||||
|
def last_scrobbled_episode(self, user_id: int) -> Optional["Video"]:
|
||||||
|
episode = None
|
||||||
|
last_scrobble = self.scrobbles_for_user(
|
||||||
|
user_id, include_playing=True
|
||||||
|
).first()
|
||||||
|
if last_scrobble:
|
||||||
|
episode = last_scrobble.media_obj
|
||||||
|
return episode
|
||||||
|
|
||||||
|
def is_episode_playing(self, user_id: int) -> bool:
|
||||||
|
last_scrobble = self.scrobbles_for_user(
|
||||||
|
user_id, include_playing=True
|
||||||
|
).first()
|
||||||
|
return not last_scrobble.played_to_completion
|
||||||
|
|
||||||
def fix_metadata(self, force_update=False):
|
def fix_metadata(self, force_update=False):
|
||||||
name_or_id = self.name
|
name_or_id = self.name
|
||||||
if self.imdb_id:
|
if self.imdb_id:
|
||||||
@ -95,6 +115,7 @@ class Video(ScrobblableMixin):
|
|||||||
tv_series = models.ForeignKey(Series, on_delete=models.DO_NOTHING, **BNULL)
|
tv_series = models.ForeignKey(Series, on_delete=models.DO_NOTHING, **BNULL)
|
||||||
season_number = models.IntegerField(**BNULL)
|
season_number = models.IntegerField(**BNULL)
|
||||||
episode_number = models.IntegerField(**BNULL)
|
episode_number = models.IntegerField(**BNULL)
|
||||||
|
next_imdb_id = models.CharField(max_length=20, **BNULL)
|
||||||
imdb_id = models.CharField(max_length=20, **BNULL)
|
imdb_id = models.CharField(max_length=20, **BNULL)
|
||||||
imdb_rating = models.FloatField(**BNULL)
|
imdb_rating = models.FloatField(**BNULL)
|
||||||
cover_image = models.ImageField(upload_to="videos/video/", **BNULL)
|
cover_image = models.ImageField(upload_to="videos/video/", **BNULL)
|
||||||
|
|||||||
@ -37,6 +37,7 @@ def get_or_create_video(name_or_id: str, force_update=False):
|
|||||||
"run_time_seconds": run_time_seconds,
|
"run_time_seconds": run_time_seconds,
|
||||||
"episode_number": imdb_dict.get("episode", None),
|
"episode_number": imdb_dict.get("episode", None),
|
||||||
"season_number": imdb_dict.get("season", None),
|
"season_number": imdb_dict.get("season", None),
|
||||||
|
"next_imdb_id": imdb_dict.get("next episode", None),
|
||||||
"tv_series_id": series.id if series else None,
|
"tv_series_id": series.id if series else None,
|
||||||
}
|
}
|
||||||
Video.objects.filter(pk=video.id).update(**video_dict)
|
Video.objects.filter(pk=video.id).update(**video_dict)
|
||||||
|
|||||||
@ -21,11 +21,16 @@ class SeriesDetailView(generic.DetailView):
|
|||||||
slug_field = "uuid"
|
slug_field = "uuid"
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
user_id = self.request.user.id
|
||||||
context_data = super().get_context_data(**kwargs)
|
context_data = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
context_data["scrobbles"] = self.object.scrobbles_for_user(
|
context_data["scrobbles"] = self.object.scrobbles_for_user(user_id)
|
||||||
self.request.user.id
|
next_episode_id = self.object.last_scrobbled_episode(
|
||||||
)
|
user_id
|
||||||
|
).next_imdb_id
|
||||||
|
if self.object.is_episode_playing(user_id):
|
||||||
|
next_episode_id = None
|
||||||
|
context_data["next_episode_id"] = next_episode_id
|
||||||
return context_data
|
return context_data
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,13 @@
|
|||||||
<div class="cover image-wrapper">
|
<div class="cover image-wrapper">
|
||||||
{% if object.imdb_rating %}<div class="caption">{{object.imdb_rating}}</div>{% endif %}
|
{% if object.imdb_rating %}<div class="caption">{{object.imdb_rating}}</div>{% endif %}
|
||||||
<img src="{% if object.cover_image %}{{object.cover_image.url}}{% else %}{% static 'images/no-video-cover.jpg' %}{% endif %}" width="400px" />
|
<img src="{% if object.cover_image %}{{object.cover_image.url}}{% else %}{% static 'images/no-video-cover.jpg' %}{% endif %}" width="400px" />
|
||||||
|
{% if next_episode_id %}
|
||||||
|
<form id="scrobble-form" action="{% url 'scrobbles:lookup-manual-scrobble' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="item_id" id="id_item_id" value="-i {{next_episode_id}}">
|
||||||
|
<button type="submit" class="btn btn-primary">Start next episode</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="summary">
|
<div class="summary">
|
||||||
{% if object.plot%}<p>{{object.plot|safe|linebreaks|truncatewords:160}}</p>{% endif %}
|
{% if object.plot%}<p>{{object.plot|safe|linebreaks|truncatewords:160}}</p>{% endif %}
|
||||||
|
|||||||
Reference in New Issue
Block a user