Selaa lähdekoodia

watch message semantic bugs fixed

sleepytaco 3 vuotta sitten
vanhempi
commit
9a18f15358

+ 25 - 0
apps/main/migrations/0017_auto_20210711_0240.py

@@ -0,0 +1,25 @@
+# Generated by Django 3.2.3 on 2021-07-11 07:40
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0016_playlist_percent_complete'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='playlist',
+            name='num_videos_watched',
+        ),
+        migrations.RemoveField(
+            model_name='playlist',
+            name='percent_complete',
+        ),
+        migrations.RemoveField(
+            model_name='playlist',
+            name='watch_time_left',
+        ),
+    ]

+ 56 - 77
apps/main/models.py

@@ -1,74 +1,19 @@
 import datetime
-import time
-
 import googleapiclient.errors
 import humanize
 from django.contrib.auth.models import User
 from django.db import models
-from django.db.models import Q
 from google.oauth2.credentials import Credentials
 from allauth.socialaccount.models import SocialAccount, SocialApp, SocialToken
 from google.auth.transport.requests import Request
 
 from apps.users.models import Profile
-import re
 from datetime import timedelta
 from googleapiclient.discovery import build
 from UnTube.secrets import SECRETS
-
+from .util import *
 import pytz
 
-# Create your models here.
-
-input = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
-
-
-def getVideoIdsStrings(video_ids):
-    output = []
-
-    i = 0
-    while i < len(video_ids):
-        output.append(",".join(video_ids[i:i + 50]))
-        i += 50
-
-    return output
-
-
-def calculateDuration(vid_durations):
-    hours_pattern = re.compile(r'(\d+)H')
-    minutes_pattern = re.compile(r'(\d+)M')
-    seconds_pattern = re.compile(r'(\d+)S')
-
-    total_seconds = 0
-    for duration in vid_durations:
-        hours = hours_pattern.search(duration)  # returns matches in the form "24H"
-        mins = minutes_pattern.search(duration)  # "24M"
-        secs = seconds_pattern.search(duration)  # "24S"
-
-        hours = int(hours.group(1)) if hours else 0  # returns 24
-        mins = int(mins.group(1)) if mins else 0
-        secs = int(secs.group(1)) if secs else 0
-
-        video_seconds = timedelta(
-            hours=hours,
-            minutes=mins,
-            seconds=secs
-        ).total_seconds()
-
-        total_seconds += video_seconds
-
-    return total_seconds
-
-
-def getThumbnailURL(thumbnails):
-    priority = ("maxres", "standard", "high", "medium", "default")
-
-    for quality in priority:
-        if quality in thumbnails:
-            return thumbnails[quality]["url"]
-
-    return ''
-
 
 class PlaylistManager(models.Manager):
 
@@ -318,7 +263,6 @@ class PlaylistManager(models.Manager):
                 playlist = current_user.playlists.get(playlist_id__exact=playlist_id)
                 print(f"PLAYLIST {playlist.name} ALREADY EXISTS IN DB")
 
-
                 # POSSIBLE CASES:
                 # 1. PLAYLIST HAS DUPLICATE VIDEOS, DELETED VIDS, UNAVAILABLE VIDS
 
@@ -515,7 +459,7 @@ class PlaylistManager(models.Manager):
                 playlist_duration_in_seconds = calculateDuration(vid_durations)
 
                 playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
-                playlist.playlist_duration = humanize.precisedelta(timedelta(seconds=playlist_duration_in_seconds)).upper()
+                playlist.playlist_duration = getHumanizedTimeString(playlist_duration_in_seconds)
 
                 if len(video_ids) != len(vid_durations):  # that means some videos in the playlist are deleted
                     playlist.has_unavailable_videos = True
@@ -598,7 +542,7 @@ class PlaylistManager(models.Manager):
                 # 1. PLAYLIST HAS DUPLICATE VIDEOS, DELETED VIDS, UNAVAILABLE VIDS
 
                 # check if playlist count changed on youtube
-                #if playlist.video_count != item['contentDetails']['itemCount']:
+                # if playlist.video_count != item['contentDetails']['itemCount']:
                 #    playlist.has_playlist_changed = True
                 #    playlist.save()
             else:  # no such playlist in database
@@ -785,7 +729,7 @@ class PlaylistManager(models.Manager):
         playlist_duration_in_seconds = calculateDuration(vid_durations)
 
         playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
-        playlist.playlist_duration = humanize.precisedelta(timedelta(seconds=playlist_duration_in_seconds)).upper()
+        playlist.playlist_duration = getHumanizedTimeString(playlist_duration_in_seconds)
 
         if len(video_ids) != len(vid_durations):  # that means some videos in the playlist are deleted
             playlist.has_unavailable_videos = True
@@ -999,7 +943,7 @@ class PlaylistManager(models.Manager):
         playlist_duration_in_seconds = calculateDuration(vid_durations)
 
         playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
-        playlist.playlist_duration = humanize.precisedelta(timedelta(seconds=playlist_duration_in_seconds)).upper()
+        playlist.playlist_duration = getHumanizedTimeString(playlist_duration_in_seconds)
 
         if len(video_ids) != len(vid_durations) or len(
                 unavailable_videos) != 0:  # that means some videos in the playlist became private/deleted
@@ -1021,8 +965,8 @@ class PlaylistManager(models.Manager):
         credentials = self.getCredentials(user)
         playlist = Playlist.objects.get(playlist_id=playlist_id)
 
-        #new_playlist_duration_in_seconds = playlist.playlist_duration_in_seconds
-        #new_playlist_video_count = playlist.video_count
+        # new_playlist_duration_in_seconds = playlist.playlist_duration_in_seconds
+        # new_playlist_video_count = playlist.video_count
         with build('youtube', 'v3', credentials=credentials) as youtube:
             for playlist_item_id in playlist_item_ids:
                 pl_request = youtube.playlistItems().delete(
@@ -1036,19 +980,19 @@ class PlaylistManager(models.Manager):
                     # playlistItemsNotAccessible (403)
                     # playlistItemNotFound (404)
                     # playlistOperationUnsupported (400)
-                    pass
+                    continue
 
                 # playlistItem was successfully deleted if no HttpError, so delete it from db
-                #video = playlist.videos.get(playlist_item_id=playlist_item_id)
-                #new_playlist_video_count -= 1
-                #new_playlist_duration_in_seconds -= video.duration_in_seconds
-                #video.delete()
+                # video = playlist.videos.get(playlist_item_id=playlist_item_id)
+                # new_playlist_video_count -= 1
+                # new_playlist_duration_in_seconds -= video.duration_in_seconds
+                # video.delete()
 
-        #playlist.video_count = new_playlist_video_count
-        #playlist.playlist_duration_in_seconds = new_playlist_duration_in_seconds
-        #playlist.playlist_duration = humanize.precisedelta(timedelta(seconds=new_playlist_duration_in_seconds)).upper()
-        #playlist.save(update_fields=['video_count', 'playlist_duration', 'playlist_duration_in_seconds'])
-        #time.sleep(2)
+        # playlist.video_count = new_playlist_video_count
+        # playlist.playlist_duration_in_seconds = new_playlist_duration_in_seconds
+        # playlist.playlist_duration = getHumanizedTimeString(new_playlist_duration_in_seconds)
+        # playlist.save(update_fields=['video_count', 'playlist_duration', 'playlist_duration_in_seconds'])
+        # time.sleep(2)
 
     def updatePlaylistDetails(self, user, playlist_id, details):
         """
@@ -1132,9 +1076,7 @@ class Playlist(models.Model):
     user_label = models.CharField(max_length=100, default="")  # custom user given name for this playlist
 
     # watch playlist details
-    num_videos_watched = models.IntegerField(default=0)
-    percent_complete = models.FloatField(default=0)
-    watch_time_left = models.CharField(max_length=150, default="")
+    # watch_time_left = models.CharField(max_length=150, default="")
     started_on = models.DateTimeField(auto_now_add=True, null=True)
     last_watched = models.DateTimeField(auto_now_add=True, null=True)
 
@@ -1167,6 +1109,44 @@ class Playlist(models.Model):
     def __str__(self):
         return str(self.playlist_id)
 
+    # return count of watchable videos, i.e # videos that are not private or deleted in the playlist
+    def get_watchable_videos_count(self):
+        return self.videos.filter(Q(is_unavailable_on_yt=False) & Q(was_deleted_on_yt=False)).count()
+
+    def get_watched_videos_count(self):
+        return self.videos.filter(Q(is_marked_as_watched=True) & Q(is_unavailable_on_yt=False) & Q(was_deleted_on_yt=False)).count()
+
+    # diff of time from when playlist was first marked as watched and playlist reached 100% completion
+    def get_finish_time(self):
+        return self.last_watched - self.started_on
+
+    def get_watch_time_left(self):
+        watched_videos = self.videos.filter(
+            Q(is_marked_as_watched=True) & Q(is_unavailable_on_yt=False) & Q(was_deleted_on_yt=False))
+
+        watched_seconds = 0
+        for video in watched_videos:
+            watched_seconds += video.duration_in_seconds
+
+        watch_time_left = getHumanizedTimeString(self.playlist_duration_in_seconds - watched_seconds)
+        return watch_time_left
+
+    # return 0 if playlist empty or all videos in playlist are unavailable
+    def get_percent_complete(self):
+        total_playlist_video_count = self.get_watchable_videos_count()
+        watched_videos = self.videos.filter(
+            Q(is_marked_as_watched=True) & Q(is_unavailable_on_yt=False) & Q(was_deleted_on_yt=False))
+        num_videos_watched = watched_videos.count()
+        percent_complete = round((num_videos_watched / total_playlist_video_count) * 100,
+                                 1) if total_playlist_video_count != 0 else 0
+        return percent_complete
+
+    def all_videos_unavailable(self):
+        all_vids_unavailable = False
+        if self.videos.filter(
+                Q(is_unavailable_on_yt=True) | Q(was_deleted_on_yt=True)).count() == self.video_count:
+            all_vids_unavailable = True
+        return all_vids_unavailable
 
 class Video(models.Model):
     playlist_item_id = models.CharField(max_length=100)  # the item id of the playlist this video beo
@@ -1249,4 +1229,3 @@ class PlaylistItem(models.Model):
 
     created_at = models.DateTimeField(auto_now_add=True)
     updated_at = models.DateTimeField(auto_now=True)
-

+ 1 - 1
apps/main/templates/all_playlists.html

@@ -110,7 +110,7 @@
                         {% if user.profile.imported_yt_playlists %}
                             Looks like you have no playlists on YouTube.
                         {% else %}
-                            You can always head over to your <a href="{% url 'profile' %}" class="btn btn-sm btn-primary p-2">Profile</a> to import all of your public/private YouTube playlists.
+                            You can always head over to your <a href="{% url 'profile' %}" class="btn btn-sm btn-primary ms-2 me-2">Profile</a> to import all of your public/private YouTube playlists.
                         {% endif %}
                     {% else %}
                         {{ playlist_type }}

+ 2 - 3
apps/main/templates/home.html

@@ -79,10 +79,9 @@
                             {% if playlist.is_from_yt %}<small><span class="badge bg-danger text-dark">YT</span></small> {% endif %}
                         </h5>
                         <p class="card-text">
-                            <span class="badge bg-{% if playlist.watch_time_left == "0secs." %}success{% else %}info{% endif %} text-white">{{ playlist.num_videos_watched }}/{{ playlist.video_count }} viewed</span>
-
-                            {% if playlist.watch_time_left != "0secs." %}<span class="badge bg-dark text-white">{{ playlist.watch_time_left }} left</span>{% endif %}
+                            <span class="badge bg-{% if playlist.get_watch_time_left == "0secs." %}success{% else %}info{% endif %} text-white">{{ playlist.get_watched_videos_count }}/{{ playlist.get_watchable_videos_count }} viewed</span>
 
+                            {% if playlist.get_watch_time_left != "0secs." %}<span class="badge bg-dark text-white">{{ playlist.get_watch_time_left }} left</span>{% endif %}
                         </p>
                             {% if playlist.tags.all %}
                         <small>

+ 34 - 16
apps/main/templates/intercooler/playlist_watch_message.html

@@ -1,34 +1,52 @@
+{% load humanize %}
+{% with all_videos_unavailable=playlist.all_videos_unavailable percent_complete=playlist.get_percent_complete %}
 <div class="py-2 bg-light rounded-3">
   <div class="d-flex justify-content-center">
-      <span class="h3 {% if watching_message.percent_complete == 0 %}w-75{% endif %}" style="color: #70777E">
-          {% if watching_message.percent_complete == 0 %}
-              <i class="fas fa-sun fa-spin" style="color: #d0bc0b"></i> Yay, you marked a playlist as watching! This message will keep updating as you mark videos as watched. Good luck! <i class="fas fa-sun fa-spin" style="color: #d0bc0b"></i>
+      <span class="h3 {% if percent_complete == 0 or all_videos_unavailable %}w-75{% endif %}" style="color: #70777E">
+          {% if percent_complete == 0 or all_videos_unavailable %}
+              <i class="fas fa-sun fa-spin" style="color: {% if not all_videos_unavailable %}#d0bc0b{% else %}red{% endif %};"></i>
+              Yay, you marked a playlist as watching! {% if all_videos_unavailable %}However, there's no videos in this playlist you can mark as 'watched' :({% else %}This message will keep updating as you mark videos as watched. Good luck!{% endif %}
+              <i class="fas fa-sun fa-spin" style="color: {% if not all_videos_unavailable %}#d0bc0b{% else %}red{% endif %}"></i>
           {% else %}
-            {% if watching_message.percent_complete == 100 %}
+            {% if percent_complete == 100 %}
                 <i class="fas fa-flag-checkered" style="color: #2c9c6b"></i>
             {% endif %}
-          <span style="color: #2c9c2c">{{ watching_message.watched_videos }}</span>
+          <span style="color: #2c9c2c">{{ playlist.get_watched_videos_count }}</span>
               /
-              <span {% if watching_message.percent_complete == 100 %}style="color: #2c9c2c"{% endif %}>{{ watching_message.total_videos }}</span> <span {% if watching_message.percent_complete == 100 %}style="color: #2c9c2c"{% endif %}> watched!</span>
-          {% if watching_message.percent_complete != 100 %}<span class="text-dark" style="border-bottom: 3px #e760f1 dashed; {% if watching_message.percent_complete >= 70 and watching_message.percent_complete != 100 %}background: linear-gradient(-45deg, #AE876B, #ABA27B, #A7BC8A, #A3D69A);
-                        background-size: 400% 400%; animation: gradient 7s ease infinite;{% endif %}">{{ watching_message.watch_time_left }}</span> left to go!{% endif %}
+              <span {% if percent_complete == 100 %}style="color: #2c9c2c"{% endif %}>{{ playlist.get_watchable_videos_count }}</span> <span {% if percent_complete == 100 %}style="color: #2c9c2c"{% endif %}> watched!</span>
+          {% if percent_complete != 100 %}<span class="text-dark" style="border-bottom: 3px #e760f1 dashed; {% if percent_complete >= 70 and percent_complete != 100 %}background: linear-gradient(-45deg, #AE876B, #ABA27B, #A7BC8A, #A3D69A);
+                        background-size: 400% 400%; animation: gradient 7s ease infinite;{% endif %}">{{ playlist.get_watch_time_left }}</span> left to go!{% endif %}
 
-            {% if watching_message.percent_complete == 100 %}
+            {% if percent_complete == 100 %}
                 <i class="fas fa-flag-checkered" style="color: #2c9c6b"></i>
-            {% elif watching_message.percent_complete >= 90  %}
+            {% elif percent_complete >= 90  %}
               <i class="fas fa-glass-cheers" style="color: #9C2C42FF"></i>
-            {% elif watching_message.percent_complete >= 75  %}
+            {% elif percent_complete >= 75  %}
               <i class="fas fa-cocktail" style="color: #9C2C42FF"></i>
-            {% elif watching_message.percent_complete >= 60 %}
+            {% elif percent_complete >= 60 %}
               <i class="fas fa-glass-martini" style="color: #9C2C42FF"></i>
-            {% elif watching_message.percent_complete >= 40 %}
+            {% elif percent_complete >= 40 %}
                 <i class="fas fa-glass-martini-alt" style="color: #9C2C42FF"></i>
-            {% elif watching_message.percent_complete >= 20 %}
+            {% elif percent_complete >= 20 %}
               <i class="fas fa-mug-hot" style="color: #9C2C42FF"></i>
-            {% elif watching_message.percent_complete >= 0 %}
+            {% elif percent_complete >= 0 %}
               <i class="fas fa-coffee" style="color: #9C2C42FF"></i>
             {% endif %}
           {% endif %}
       </span>
   </div>
-</div>
+    {% if percent_complete == 100 %}
+        <!--
+      <div class="d-flex justify-content-center h5">
+        Started {{ playlist.started_on|naturaltime }}
+      </div>
+        <div class="d-flex justify-content-center h5">
+            Finished {{ playlist.last_watched|naturaltime }}
+      </div>
+      -->
+  <div class="d-flex justify-content-center mt-2">
+    <a class="btn btn-danger" href="{% url 'reset_watched' playlist.playlist_id %}">Reset</a>
+  </div>
+    {% endif %}
+</div>
+{% endwith %}

+ 1 - 0
apps/main/urls.py

@@ -36,6 +36,7 @@ urlpatterns = [
     path("playlist/<slug:playlist_id>/get-unused-tags", views.get_unused_playlist_tags, name="get_unused_playlist_tags"),
     path("playlist/<slug:playlist_id>/get-watch-message", views.get_watch_message, name="get_watch_message"),
     path("playlist/<slug:playlist_id>/delete", views.delete_playlist, name="delete_playlist"),
+    path("playlist/<slug:playlist_id>/reset-watched", views.reset_watched, name="reset_watched"),
 
     ### STUFF RELATED TO PLAYLISTS IN BULK
     path("search/playlists/<slug:playlist_type>", views.search_playlists, name="search_playlists"),

+ 77 - 0
apps/main/util.py

@@ -0,0 +1,77 @@
+import datetime
+import humanize
+import re
+
+# given amount of seconds makes it into this 54 MINUTES AND 53 SECONDS (from humanize) then
+# perform a bunch of replace operations to make it look like 54mins. 53secs.
+from django.db.models import Q
+
+
+def getHumanizedTimeString(seconds):
+    return humanize.precisedelta(
+        datetime.timedelta(seconds=seconds)).upper(). \
+        replace(" month,".upper(), "m.").replace(" months,".upper(), "m.").replace(" days,".upper(), "d.").replace(
+        " day,".upper(), "d.").replace(" hours,".upper(), "hrs.").replace(" hour,".upper(), "hr.").replace(
+        " minutes".upper(), "mins.").replace(
+        "and".upper(), "").replace(" seconds".upper(), "secs.").replace(" second".upper(), "sec.")
+
+
+# input => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", ..., "100"]  <- array of 100 video ids
+# output => ["1,2,3,4,...,50", "51,52,53,...,100"]  <- array of 2 video id strings, each with upto 50 comma sperated video ids
+def getVideoIdsStrings(video_ids):
+    output = []
+
+    i = 0
+    while i < len(video_ids):
+        output.append(",".join(video_ids[i:i + 50]))
+        i += 50
+
+    return output
+
+
+# input: array of youtube video duration strings => ["24M23S", "2H2M2S", ...]
+# output:integer => seconds
+def calculateDuration(vid_durations):
+    hours_pattern = re.compile(r'(\d+)H')
+    minutes_pattern = re.compile(r'(\d+)M')
+    seconds_pattern = re.compile(r'(\d+)S')
+
+    total_seconds = 0
+    for duration in vid_durations:
+        hours = hours_pattern.search(duration)  # returns matches in the form "24H"
+        mins = minutes_pattern.search(duration)  # "24M"
+        secs = seconds_pattern.search(duration)  # "24S"
+
+        hours = int(hours.group(1)) if hours else 0  # returns 24
+        mins = int(mins.group(1)) if mins else 0
+        secs = int(secs.group(1)) if secs else 0
+
+        video_seconds = datetime.timedelta(
+            hours=hours,
+            minutes=mins,
+            seconds=secs
+        ).total_seconds()
+
+        total_seconds += video_seconds
+
+    return total_seconds
+
+
+def getThumbnailURL(thumbnails):
+    priority = ("maxres", "standard", "high", "medium", "default")
+
+    for quality in priority:
+        if quality in thumbnails:
+            return thumbnails[quality]["url"]
+
+    return ''
+
+
+# generates a message in the form of "1 / 19 watched! 31mins. 15secs. left to go!"
+def generateWatchingMessage(playlist):
+    """
+    This is the message that will be seen when a playlist is set to watching.
+    Takes in the playlist object and calculates the watch time left by looping over unwatched video
+    and using their durations
+    """
+    pass

+ 22 - 44
apps/main/views.py

@@ -14,47 +14,7 @@ from allauth.socialaccount.models import SocialToken
 from django.views.decorators.http import require_POST
 from django.contrib import messages
 from django.template import loader
-
-
-def generateWatchingMessage(playlist):
-    """
-    This is the message that will be seen when a playlist is set to watching.
-    """
-    total_playlist_video_count = playlist.video_count
-    num_videos_watched = playlist.videos.filter(is_marked_as_watched=True).count()
-    percent_complete = round((num_videos_watched / total_playlist_video_count) * 100,
-                             1) if total_playlist_video_count != 0 else 100
-
-    print(total_playlist_video_count, num_videos_watched)
-    if num_videos_watched == 0:  # hasnt started watching any videos yet
-        watch_time_left = playlist.playlist_duration.replace(" month,".upper(), "m.").replace(" days,".upper(),
-                                                                                              "d.").replace(
-            " hours,".upper(), "hr.").replace(" minutes".upper(), "mins.").replace(
-            "and".upper(), "").replace(" seconds".upper(), "sec.")
-    elif total_playlist_video_count == num_videos_watched:  # finished watching all videos in the playlist
-        watch_time_left = "0secs."
-    else:
-        watch_time_left = playlist.watch_time_left
-        watched_seconds = 0
-        for video in playlist.videos.filter(is_marked_as_watched=True):
-            watched_seconds += video.duration_in_seconds
-
-        watch_time_left = humanize.precisedelta(
-            datetime.timedelta(seconds=playlist.playlist_duration_in_seconds - watched_seconds)).upper(). \
-            replace(" month,".upper(), "m.").replace(" months,".upper(), "m.").replace(" days,".upper(), "d.").replace(
-            " day,".upper(), "d.").replace(" hours,".upper(), "hrs.").replace(" hour,".upper(), "hr.").replace(
-            " minutes".upper(), "mins.").replace(
-            "and".upper(), "").replace(" seconds".upper(), "secs.").replace(" second".upper(), "sec.")
-
-    playlist.watch_time_left = watch_time_left
-    playlist.num_videos_watched = num_videos_watched
-    playlist.percent_complete = percent_complete
-    playlist.save(update_fields=['watch_time_left', 'num_videos_watched', 'percent_complete'])
-
-    return {"total_videos": total_playlist_video_count,
-            "watched_videos": num_videos_watched,
-            "percent_complete": percent_complete,
-            "watch_time_left": watch_time_left}
+from .util import *
 
 
 # Create your views here.
@@ -184,12 +144,17 @@ def view_playlist(request, playlist_id):
 
     unused_tags = user_created_tags.difference(playlist_tags)
 
+    all_videos_unavailable = False
+    if playlist.videos.filter(Q(is_unavailable_on_yt=True) | Q(was_deleted_on_yt=True)).count() == playlist.videos.all().count():
+        all_videos_unavailable = True
+
     return render(request, 'view_playlist.html', {"playlist": playlist,
                                                   "playlist_tags": playlist_tags,
                                                   "unused_tags": unused_tags,
                                                   "videos": videos,
                                                   "user_owned_playlists": user_owned_playlists,
-                                                  "watching_message": generateWatchingMessage(playlist)})
+                                                  "watching_message": generateWatchingMessage(playlist),
+                                                  })
 
 
 @login_required
@@ -446,7 +411,7 @@ def delete_videos(request, playlist_id, command):
         # playlist.has_playlist_changed = True
         # playlist.save(update_fields=['has_playlist_changed'])
         return HttpResponse(f"""
-        <div hx-get="/playlist/{playlist_id}/update/checkforupdates" hx-trigger="load delay:1s" hx-target="#checkforupdates" class="sticky-top" style="top: 0.5rem;">
+        <div hx-get="/playlist/{playlist_id}/update/checkforupdates" hx-trigger="load delay:3s" hx-target="#checkforupdates" class="sticky-top" style="top: 0.5rem;">
             Done! Playlist on UnTube will update in soon...
         </div>
         """)
@@ -935,7 +900,7 @@ def get_watch_message(request, playlist_id):
 
     return HttpResponse(loader.get_template("intercooler/playlist_watch_message.html")
         .render(
-        {"watching_message": generateWatchingMessage(playlist)}))
+        {"playlist": playlist}))
 
 
 @login_required
@@ -1027,3 +992,16 @@ def delete_playlist(request, playlist_id):
     messages.success(request, "Successfully deleted playlist from UnTube.")
 
     return redirect('home')
+
+
+@login_required
+def reset_watched(request, playlist_id):
+    playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
+
+    for video in playlist.videos.filter(Q(is_unavailable_on_yt=False) & Q(was_deleted_on_yt=False)):
+        video.is_marked_as_watched = False
+        video.save(update_fields=['is_marked_as_watched'])
+
+    # messages.success(request, "Successfully marked all videos unwatched.")
+
+    return redirect(f'/playlist/{playlist.playlist_id}')

+ 2 - 2
apps/users/templates/profile.html

@@ -119,9 +119,9 @@
                   <h6 class="d-flex align-items-center mb-3"><span class="text-info me-2">{{ watching.count }}</span>Watching</h6>
                   {% if watching %}
                         {% for pl in watching|slice:"0:5" %}
-                      <small><a href="{% url 'playlist' pl.playlist_id %}" style="text-decoration: none; color: white;">{{ pl.name|truncatechars:"40" }}</a> <span class="text-warning ms-1">{{ pl.num_videos_watched }}/{{ pl.video_count }} viewed</span></small>
+                      <small><a href="{% url 'playlist' pl.playlist_id %}" style="text-decoration: none; color: white;">{{ pl.name|truncatechars:"40" }}</a> <span class="text-warning ms-1">{{ pl.get_watched_videos_count }}/{{ pl.get_watchable_videos_count }} viewed</span></small>
                       <div class="progress mb-3" style="height: 5px">
-                        <div class="progress-bar bg-{% if pl.percent_complete == 100 %}success{% else %}primary{% endif %}" role="progressbar" style="width: {{ pl.percent_complete }}%" aria-valuenow="{{ pl.num_videos_watched }}" aria-valuemin="0" aria-valuemax="{{ pl.video_count }}"></div>
+                        <div class="progress-bar bg-{% if pl.get_percent_complete == 100 %}success{% else %}primary{% endif %}" role="progressbar" style="width: {{ pl.get_percent_complete }}%" aria-valuenow="{{ pl.get_watched_videos_count }}" aria-valuemin="0" aria-valuemax="{{ pl.get_watchable_videos_count }}"></div>
                       </div>
                             {% if forloop.last and watching.count > 5 %}
                                 <a href="{% url 'all_playlists' 'watching' %}" style="color: white; text-decoration: none">+ {{ watching.count|add:"-5" }} more</a>

+ 2 - 2
templates/base.html

@@ -73,7 +73,7 @@
                             <a class="nav-link dropdown-toggle" href="#" id="libraryDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                                 Library
                             </a>
-                            <ul class="dropdown-menu" aria-labelledby="libraryDropdown">
+                            <ul class="dropdown-menu" aria-labelledby="libraryDropdown" style="z-index: 1021;">
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'home' %}">Playlists</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_videos' 'home' %}">Videos</a></li>
                             </ul>
@@ -82,7 +82,7 @@
                             <a class="nav-link dropdown-toggle" href="#" id="quickViewDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                                 Quick View
                             </a>
-                            <ul class="dropdown-menu" aria-labelledby="quickViewDropdown">
+                            <ul class="dropdown-menu" aria-labelledby="quickViewDropdown" style="z-index: 1021;">
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'favorites' %}">Favorites</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'watching' %}">Watching</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'plan-to-watch' %}">Plan to Watch</a></li>