aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--middleware/authentication.py10
-rw-r--r--static/css/main.css4
-rw-r--r--templates/watch/watch.html41
-rw-r--r--user_profile/migrations/0004_remove_userhistory_timestamp_and_more.py21
-rw-r--r--user_profile/models.py2
-rw-r--r--watch/urls.py1
-rw-r--r--watch/utils.py12
-rw-r--r--watch/views.py39
8 files changed, 120 insertions, 10 deletions
diff --git a/middleware/authentication.py b/middleware/authentication.py
index 5845036..7bd8a93 100644
--- a/middleware/authentication.py
+++ b/middleware/authentication.py
@@ -43,7 +43,7 @@ class AuthMiddleware:
)
if timezone.now() > verified_at + timedelta(hours=24):
# Verification expired, need to re-check
- raise ValueError("Verification expired")
+ pass
except (json.JSONDecodeError, ValueError):
# Cookie is invalid or expired, need to re-check
pass
@@ -57,6 +57,14 @@ class AuthMiddleware:
token_type=request.user.discord_token_type,
)
+ # update user object
+ request.user.usrname = user["username"]
+ request.user.discord_global_name = user["global_name"]
+ request.user.discord_guild_name = user["guild_name"]
+ request.user.save()
+
+ print(user, "user")
+
if not user["is_authorized"]:
logout(request)
request.session["next"] = request.get_full_path()
diff --git a/static/css/main.css b/static/css/main.css
index c50c0fc..d235d15 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -1366,6 +1366,10 @@ video {
--tw-bg-opacity: 0.4;
}
+.bg-opacity-30 {
+ --tw-bg-opacity: 0.3;
+}
+
.bg-cover {
background-size: cover;
}
diff --git a/templates/watch/watch.html b/templates/watch/watch.html
index 309566f..2574a96 100644
--- a/templates/watch/watch.html
+++ b/templates/watch/watch.html
@@ -45,7 +45,7 @@
</a>
{% else %}
- <a href="{% url "watch:watch_episode" anime_id episode.number %}" class="flex flex-row justify-between w-full gap-4 bg-white bg-opacity-10 p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30">
+ <a href="{% url "watch:watch_episode" anime_id episode.number %}" class="flex flex-row justify-between w-full gap-4 {% if episode.number in watched_episodes %}bg-{{ user.preferences.accent_colour }}-600 bg-opacity-20{% else %}bg-white bg-opacity-10{% endif %} p-2 rounded hover:bg-{{ user.preferences.accent_colour }}-600 hover:bg-opacity-30">
<span class="truncate max-w-full overflow-hidden text-ellipsis whitespace-nowrap">{{ episode.number }}. {{ episode.title }}</span>
<span class="flex flex-row item-center gap-2">
{% if anime_selected.episodes.sub >= episode.number %}
@@ -88,10 +88,10 @@
const introOverlay = document.getElementById('intro-overlay');
const outroOverlay = document.getElementById('outro-overlay');
const autoSkipIntro = {% if user.preferences.auto_skip_intro %}true{% else %}false{% endif %};
- console.log(introStart, introEnd, outroStart, outroEnd);
-
+ const currentWatchTime = {{ current_watched_time }};
+
import { VidstackPlayer, VidstackPlayerLayout, TextTrack } from 'https://cdn.vidstack.io/player';
-
+
const layout = new VidstackPlayerLayout({
{% for track in episode_data.tracks %}
{% if track.kind == 'thumbnails' %}
@@ -161,12 +161,15 @@
if (chapter.startTime === chapter.endTime || chapter.endTime === 0) {
continue;
}
- console.log(`Adding chapter at ${chapter.startTime} for ${chapter.title}`);
chaptersTrack.addCue(new VTTCue(chapter.startTime, chapter.endTime, chapter.title));
}
player.textTracks.add(chaptersTrack);
chaptersLoaded = true;
+
+ if (currentWatchTime > 0) {
+ player.currentTime = currentWatchTime;
+ }
});
player.addEventListener('time-update', (event) => {
@@ -233,5 +236,33 @@
});
{% endif %}
+ let lastTime = 0;
+ setInterval(() => {
+ const {
+ paused,
+ playing,
+ waiting,
+ currentTime,
+ } = player.state;
+ if (paused || waiting) {
+ lastTime = currentTime;
+ }
+ if (paused || waiting || currentTime === lastTime) {
+ return;
+ }
+ fetch('{% url "watch:update_watch_history" %}', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-CSRFToken': '{{ csrf_token }}'
+ },
+ body: JSON.stringify({
+ 'anime_id': {{ anime_id }},
+ 'episode': {{ current_episode }},
+ 'time_watched': parseInt(player.currentTime)
+ })
+ });
+ }, 30000);
+
</script>
{% endblock scripts %}
diff --git a/user_profile/migrations/0004_remove_userhistory_timestamp_and_more.py b/user_profile/migrations/0004_remove_userhistory_timestamp_and_more.py
new file mode 100644
index 0000000..e5455be
--- /dev/null
+++ b/user_profile/migrations/0004_remove_userhistory_timestamp_and_more.py
@@ -0,0 +1,21 @@
+# Generated by Django 5.1 on 2024-08-30 23:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("user_profile", "0003_userpreferences_accent_colour"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="userhistory",
+ name="timestamp",
+ ),
+ migrations.AddField(
+ model_name="userhistory",
+ name="time_watched",
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/user_profile/models.py b/user_profile/models.py
index 205a46c..554ac49 100644
--- a/user_profile/models.py
+++ b/user_profile/models.py
@@ -28,7 +28,7 @@ class UserHistory(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
anime_id = models.IntegerField()
episode = models.IntegerField()
- timestamp = models.DateTimeField(auto_now=True)
+ time_watched = models.IntegerField(default=0)
def __str__(self):
return f"{self.user.username} watched episode {self.episode} of anime {self.anime_id}"
diff --git a/watch/urls.py b/watch/urls.py
index ab7d14e..4e0f66d 100644
--- a/watch/urls.py
+++ b/watch/urls.py
@@ -6,4 +6,5 @@ app_name = "watch"
urlpatterns = [
path('<int:anime_id>/', views.watch, name='watch'),
path('<int:anime_id>/<int:episode>/', views.watch, name='watch_episode'),
+ path('update_watch_history/', views.update_episode_watch_time, name='update_watch_history'),
]
diff --git a/watch/utils.py b/watch/utils.py
index 83888f6..857601a 100644
--- a/watch/utils.py
+++ b/watch/utils.py
@@ -1,6 +1,7 @@
import redis
import os
import dotenv
+from user_profile.models import UserHistory
dotenv.load_dotenv()
@@ -11,6 +12,17 @@ r = redis.Redis(
ssl=True,
)
+def update_anime_user_history(user, anime_id, episode, time_watched):
+ # per episode history
+ history, created = UserHistory.objects.get_or_create(user=user, anime_id=anime_id, episode=episode)
+ history.time_watched = time_watched
+ history.save()
+
+def get_anime_user_history(user, anime_id):
+ history = UserHistory.objects.filter(user=user, anime_id=anime_id).order_by("-episode")
+ return history
+
+
def store_in_redis_cache(anime_id, data):
r.set(anime_id, data, ex=60*60*24*30) # 30 days
print("data cached", anime_id)
diff --git a/watch/views.py b/watch/views.py
index 570082c..7ebd472 100644
--- a/watch/views.py
+++ b/watch/views.py
@@ -2,7 +2,9 @@ import os
from django.http import JsonResponse
import dotenv
from django.shortcuts import render, redirect
+from watch.utils import update_anime_user_history, get_anime_user_history
import requests
+import json
dotenv.load_dotenv()
@@ -50,9 +52,23 @@ def watch(request, anime_id, episode=None):
response = requests.get(base_url)
episode_data = response.json()
- # search for the current episode name in anime_episodes.episodes where episode.number == episode
current_episode_name = [e["title"] for e in anime_episodes["episodes"] if e["number"] == episode][0]
+ # store anime history
+ if request.user.is_authenticated:
+ anime_history = get_anime_user_history(request.user, anime_id)
+
+ # if current episode is not in history, add it
+ if not any(h.episode == episode for h in anime_history):
+ update_anime_user_history(request.user, anime_id, episode, 0)
+
+ watched_episodes = [h.episode for h in anime_history]
+ current_watched_time = [h.time_watched for h in anime_history if h.episode == episode][0]
+ else:
+ anime_history = None
+ watched_episodes = []
+ current_watched_time = 0
+
context = {
"anime_data": anime_data,
"anime_selected": anime_selected,
@@ -62,10 +78,27 @@ def watch(request, anime_id, episode=None):
"stream_url": episode_data["sources"][0]["url"],
"anime_id": anime_id,
"current_episode_name": current_episode_name,
+ "anime_history": anime_history,
+ "watched_episodes": watched_episodes,
+ "current_watched_time": current_watched_time,
"mode": mode,
}
- # print(anime_search_result)
- # print(anime_data)
+ print(context)
return render(request, "watch/watch.html", context)
+
+def update_episode_watch_time(request):
+ if request.method != "POST":
+ return JsonResponse({"status": "error", "message": "Invalid request"})
+
+ data = json.loads(request.body)
+ anime_id =data.get("anime_id")
+ episode =data.get("episode")
+ time_watched =data.get("time_watched")
+
+ if request.user.is_authenticated:
+ update_anime_user_history(request.user, anime_id, episode, time_watched)
+ return JsonResponse({"status": "success"})
+ else:
+ return JsonResponse({"status": "error", "message": "User not authenticated"})