Browse Source

watch message semantic bugs fixed

sleepytaco 3 years ago
parent
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 datetime
-import time
-
 import googleapiclient.errors
 import googleapiclient.errors
 import humanize
 import humanize
 from django.contrib.auth.models import User
 from django.contrib.auth.models import User
 from django.db import models
 from django.db import models
-from django.db.models import Q
 from google.oauth2.credentials import Credentials
 from google.oauth2.credentials import Credentials
 from allauth.socialaccount.models import SocialAccount, SocialApp, SocialToken
 from allauth.socialaccount.models import SocialAccount, SocialApp, SocialToken
 from google.auth.transport.requests import Request
 from google.auth.transport.requests import Request
 
 
 from apps.users.models import Profile
 from apps.users.models import Profile
-import re
 from datetime import timedelta
 from datetime import timedelta
 from googleapiclient.discovery import build
 from googleapiclient.discovery import build
 from UnTube.secrets import SECRETS
 from UnTube.secrets import SECRETS
-
+from .util import *
 import pytz
 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):
 class PlaylistManager(models.Manager):
 
 
@@ -318,7 +263,6 @@ class PlaylistManager(models.Manager):
                 playlist = current_user.playlists.get(playlist_id__exact=playlist_id)
                 playlist = current_user.playlists.get(playlist_id__exact=playlist_id)
                 print(f"PLAYLIST {playlist.name} ALREADY EXISTS IN DB")
                 print(f"PLAYLIST {playlist.name} ALREADY EXISTS IN DB")
 
 
-
                 # POSSIBLE CASES:
                 # POSSIBLE CASES:
                 # 1. PLAYLIST HAS DUPLICATE VIDEOS, DELETED VIDS, UNAVAILABLE VIDS
                 # 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_duration_in_seconds = calculateDuration(vid_durations)
 
 
                 playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
                 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
                 if len(video_ids) != len(vid_durations):  # that means some videos in the playlist are deleted
                     playlist.has_unavailable_videos = True
                     playlist.has_unavailable_videos = True
@@ -598,7 +542,7 @@ class PlaylistManager(models.Manager):
                 # 1. PLAYLIST HAS DUPLICATE VIDEOS, DELETED VIDS, UNAVAILABLE VIDS
                 # 1. PLAYLIST HAS DUPLICATE VIDEOS, DELETED VIDS, UNAVAILABLE VIDS
 
 
                 # check if playlist count changed on youtube
                 # 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.has_playlist_changed = True
                 #    playlist.save()
                 #    playlist.save()
             else:  # no such playlist in database
             else:  # no such playlist in database
@@ -785,7 +729,7 @@ class PlaylistManager(models.Manager):
         playlist_duration_in_seconds = calculateDuration(vid_durations)
         playlist_duration_in_seconds = calculateDuration(vid_durations)
 
 
         playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
         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
         if len(video_ids) != len(vid_durations):  # that means some videos in the playlist are deleted
             playlist.has_unavailable_videos = True
             playlist.has_unavailable_videos = True
@@ -999,7 +943,7 @@ class PlaylistManager(models.Manager):
         playlist_duration_in_seconds = calculateDuration(vid_durations)
         playlist_duration_in_seconds = calculateDuration(vid_durations)
 
 
         playlist.playlist_duration_in_seconds = playlist_duration_in_seconds
         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(
         if len(video_ids) != len(vid_durations) or len(
                 unavailable_videos) != 0:  # that means some videos in the playlist became private/deleted
                 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)
         credentials = self.getCredentials(user)
         playlist = Playlist.objects.get(playlist_id=playlist_id)
         playlist = Playlist.objects.get(playlist_id=playlist_id)
 
 
-        #new_playlist_duration_in_seconds = playlist.playlist_duration_in_seconds
+        # new_playlist_duration_in_seconds = playlist.playlist_duration_in_seconds
-        #new_playlist_video_count = playlist.video_count
+        # new_playlist_video_count = playlist.video_count
         with build('youtube', 'v3', credentials=credentials) as youtube:
         with build('youtube', 'v3', credentials=credentials) as youtube:
             for playlist_item_id in playlist_item_ids:
             for playlist_item_id in playlist_item_ids:
                 pl_request = youtube.playlistItems().delete(
                 pl_request = youtube.playlistItems().delete(
@@ -1036,19 +980,19 @@ class PlaylistManager(models.Manager):
                     # playlistItemsNotAccessible (403)
                     # playlistItemsNotAccessible (403)
                     # playlistItemNotFound (404)
                     # playlistItemNotFound (404)
                     # playlistOperationUnsupported (400)
                     # playlistOperationUnsupported (400)
-                    pass
+                    continue
 
 
                 # playlistItem was successfully deleted if no HttpError, so delete it from db
                 # playlistItem was successfully deleted if no HttpError, so delete it from db
-                #video = playlist.videos.get(playlist_item_id=playlist_item_id)
+                # video = playlist.videos.get(playlist_item_id=playlist_item_id)
-                #new_playlist_video_count -= 1
+                # new_playlist_video_count -= 1
-                #new_playlist_duration_in_seconds -= video.duration_in_seconds
+                # new_playlist_duration_in_seconds -= video.duration_in_seconds
-                #video.delete()
+                # video.delete()
 
 
-        #playlist.video_count = new_playlist_video_count
+        # playlist.video_count = new_playlist_video_count
-        #playlist.playlist_duration_in_seconds = new_playlist_duration_in_seconds
+        # playlist.playlist_duration_in_seconds = new_playlist_duration_in_seconds
-        #playlist.playlist_duration = humanize.precisedelta(timedelta(seconds=new_playlist_duration_in_seconds)).upper()
+        # playlist.playlist_duration = getHumanizedTimeString(new_playlist_duration_in_seconds)
-        #playlist.save(update_fields=['video_count', 'playlist_duration', 'playlist_duration_in_seconds'])
+        # playlist.save(update_fields=['video_count', 'playlist_duration', 'playlist_duration_in_seconds'])
-        #time.sleep(2)
+        # time.sleep(2)
 
 
     def updatePlaylistDetails(self, user, playlist_id, details):
     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
     user_label = models.CharField(max_length=100, default="")  # custom user given name for this playlist
 
 
     # watch playlist details
     # watch playlist details
-    num_videos_watched = models.IntegerField(default=0)
+    # watch_time_left = models.CharField(max_length=150, default="")
-    percent_complete = models.FloatField(default=0)
-    watch_time_left = models.CharField(max_length=150, default="")
     started_on = models.DateTimeField(auto_now_add=True, null=True)
     started_on = models.DateTimeField(auto_now_add=True, null=True)
     last_watched = 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):
     def __str__(self):
         return str(self.playlist_id)
         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):
 class Video(models.Model):
     playlist_item_id = models.CharField(max_length=100)  # the item id of the playlist this video beo
     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)
     created_at = models.DateTimeField(auto_now_add=True)
     updated_at = models.DateTimeField(auto_now=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 %}
                         {% if user.profile.imported_yt_playlists %}
                             Looks like you have no playlists on YouTube.
                             Looks like you have no playlists on YouTube.
                         {% else %}
                         {% 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 %}
                         {% endif %}
                     {% else %}
                     {% else %}
                         {{ playlist_type }}
                         {{ 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 %}
                             {% if playlist.is_from_yt %}<small><span class="badge bg-danger text-dark">YT</span></small> {% endif %}
                         </h5>
                         </h5>
                         <p class="card-text">
                         <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>
+                            <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.watch_time_left != "0secs." %}<span class="badge bg-dark text-white">{{ playlist.watch_time_left }} left</span>{% endif %}
 
 
+                            {% if playlist.get_watch_time_left != "0secs." %}<span class="badge bg-dark text-white">{{ playlist.get_watch_time_left }} left</span>{% endif %}
                         </p>
                         </p>
                             {% if playlist.tags.all %}
                             {% if playlist.tags.all %}
                         <small>
                         <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="py-2 bg-light rounded-3">
   <div class="d-flex justify-content-center">
   <div class="d-flex justify-content-center">
-      <span class="h3 {% if watching_message.percent_complete == 0 %}w-75{% endif %}" style="color: #70777E">
+      <span class="h3 {% if percent_complete == 0 or all_videos_unavailable %}w-75{% endif %}" style="color: #70777E">
-          {% if watching_message.percent_complete == 0 %}
+          {% if percent_complete == 0 or all_videos_unavailable %}
-              <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>
+              <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 %}
           {% else %}
-            {% if watching_message.percent_complete == 100 %}
+            {% if percent_complete == 100 %}
                 <i class="fas fa-flag-checkered" style="color: #2c9c6b"></i>
                 <i class="fas fa-flag-checkered" style="color: #2c9c6b"></i>
             {% endif %}
             {% 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>
+              <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 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);
+          {% 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 %}">{{ watching_message.watch_time_left }}</span> left to go!{% endif %}
+                        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>
                 <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>
               <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>
               <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>
               <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>
                 <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>
               <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>
               <i class="fas fa-coffee" style="color: #9C2C42FF"></i>
             {% endif %}
             {% endif %}
           {% endif %}
           {% endif %}
       </span>
       </span>
   </div>
   </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-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>/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>/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
     ### STUFF RELATED TO PLAYLISTS IN BULK
     path("search/playlists/<slug:playlist_type>", views.search_playlists, name="search_playlists"),
     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.views.decorators.http import require_POST
 from django.contrib import messages
 from django.contrib import messages
 from django.template import loader
 from django.template import loader
-
+from .util import *
-
-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}
 
 
 
 
 # Create your views here.
 # Create your views here.
@@ -184,12 +144,17 @@ def view_playlist(request, playlist_id):
 
 
     unused_tags = user_created_tags.difference(playlist_tags)
     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,
     return render(request, 'view_playlist.html', {"playlist": playlist,
                                                   "playlist_tags": playlist_tags,
                                                   "playlist_tags": playlist_tags,
                                                   "unused_tags": unused_tags,
                                                   "unused_tags": unused_tags,
                                                   "videos": videos,
                                                   "videos": videos,
                                                   "user_owned_playlists": user_owned_playlists,
                                                   "user_owned_playlists": user_owned_playlists,
-                                                  "watching_message": generateWatchingMessage(playlist)})
+                                                  "watching_message": generateWatchingMessage(playlist),
+                                                  })
 
 
 
 
 @login_required
 @login_required
@@ -446,7 +411,7 @@ def delete_videos(request, playlist_id, command):
         # playlist.has_playlist_changed = True
         # playlist.has_playlist_changed = True
         # playlist.save(update_fields=['has_playlist_changed'])
         # playlist.save(update_fields=['has_playlist_changed'])
         return HttpResponse(f"""
         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...
             Done! Playlist on UnTube will update in soon...
         </div>
         </div>
         """)
         """)
@@ -935,7 +900,7 @@ def get_watch_message(request, playlist_id):
 
 
     return HttpResponse(loader.get_template("intercooler/playlist_watch_message.html")
     return HttpResponse(loader.get_template("intercooler/playlist_watch_message.html")
         .render(
         .render(
-        {"watching_message": generateWatchingMessage(playlist)}))
+        {"playlist": playlist}))
 
 
 
 
 @login_required
 @login_required
@@ -1027,3 +992,16 @@ def delete_playlist(request, playlist_id):
     messages.success(request, "Successfully deleted playlist from UnTube.")
     messages.success(request, "Successfully deleted playlist from UnTube.")
 
 
     return redirect('home')
     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>
                   <h6 class="d-flex align-items-center mb-3"><span class="text-info me-2">{{ watching.count }}</span>Watching</h6>
                   {% if watching %}
                   {% if watching %}
                         {% for pl in watching|slice:"0:5" %}
                         {% 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 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>
                       </div>
                             {% if forloop.last and watching.count > 5 %}
                             {% 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>
                                 <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">
                             <a class="nav-link dropdown-toggle" href="#" id="libraryDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                                 Library
                                 Library
                             </a>
                             </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_playlists' 'home' %}">Playlists</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_videos' 'home' %}">Videos</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_videos' 'home' %}">Videos</a></li>
                             </ul>
                             </ul>
@@ -82,7 +82,7 @@
                             <a class="nav-link dropdown-toggle" href="#" id="quickViewDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                             <a class="nav-link dropdown-toggle" href="#" id="quickViewDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                                 Quick View
                                 Quick View
                             </a>
                             </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' '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' 'watching' %}">Watching</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'plan-to-watch' %}">Plan to Watch</a></li>
                                 <li><a class="dropdown-item" href="{% url 'all_playlists' 'plan-to-watch' %}">Plan to Watch</a></li>