浏览代码

added videos library and randomize playlist button

sleepytaco 3 年之前
父节点
当前提交
879a159cf6

+ 2 - 1
apps/main/models.py

@@ -1133,6 +1133,7 @@ class Playlist(models.Model):
 
     # 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="")
     started_on = models.DateTimeField(auto_now_add=True, null=True)
     last_watched = models.DateTimeField(auto_now_add=True, null=True)
@@ -1164,7 +1165,7 @@ class Playlist(models.Model):
     has_new_updates = models.BooleanField(default=False)  # meant to keep track of newly added/unavailable videos
 
     def __str__(self):
-        return "Playlist Len " + str(self.video_count)
+        return str(self.playlist_id)
 
 
 class Video(models.Model):

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

@@ -65,7 +65,6 @@
 
 
             {% if watching %}
-                <br>
                 <div class="border border-5 rounded-3 border-primary p-3">
                     <h3><span style="border-bottom: 3px #ffffff dashed;">Continue Watching</span><i class="fas fa-fire-alt ms-2" style="color: #d24646"></i></h3>
            <div class="row row-cols-1 row-cols-md-4 g-4 text-dark mt-0">
@@ -118,9 +117,9 @@
                 {% endfor %}
             </div>
             </div>
+                <br>
                 {% endif %}
 
-            <br>
             <h3><span style="border-bottom: 3px #ffffff dashed;">Most viewed playlists</span> <a href="{% url 'all_playlists' 'all' %}" class="pt-1"><i class="fas fa-binoculars"></i> </a></h3>
             {% if user_playlists %}
             <div class="row row-cols-1 row-cols-md-3 g-4 text-dark mt-0">

+ 12 - 8
apps/main/templates/intercooler/videos.html

@@ -1,11 +1,11 @@
 {% load humanize %}
-{% if videos_details and videos.count != 0 %}
+{% if videos_details and videos %}
     <h4 class="mt-3 mb-3"><span class="badge bg-dark text-white">{{ videos_details }}, Results: {{ videos.count }}</span></h4>
 {% endif %}
 <div class="list-group" id="video-checkboxes">
     {% if videos %}
       {% for video in videos|slice:"0:50" %}
-                <li {% if forloop.last and videos.count > 50 %}hx-get="{% url 'load_more_videos' playlist.playlist_id page|default:"1" %}"
+                <li {% if forloop.last and videos.count > 50 %}hx-get="{% url 'load_more_videos' playlist.playlist_id order_by|default:"all" page|default:"1" %}"
     hx-trigger="revealed"
     hx-swap="afterend" hx-indicator="#load-more-videos-spinner"{% endif %} class="list-group-item d-flex justify-content-between align-items-center bg-transparent" style="background-color: #40B3A2">
 
@@ -95,11 +95,15 @@
                     </div>
                 </button>
                     {% if playlist.marked_as == "watching" %}
-                                        <button class="btn btn-sm btn-light mb-1" type="button">
-
-                        <i class="far fa-check-circle"></i>
-                                        </button>
-
+                    <button class="btn btn-sm btn-light mb-1" type="button" hx-get="{% url 'mark_video_watched' playlist.playlist_id video.video_id %}" hx-target="#video-{{ forloop.counter }}-watched">
+                        <div id="video-{{ forloop.counter }}-watched">
+                            {% if video.is_marked_as_watched %}
+                                <i class="fas fa-check-circle"></i>
+                            {% else %}
+                                <i class="far fa-check-circle"></i>
+                            {% endif %}
+                        </div>
+                    </button>
                     {% endif %}
                 {% endif %}
             </div>
@@ -108,6 +112,6 @@
                 </li>
       {% endfor %}
     {% else %}
-        <h3>{{ display_text }}</h3>
+        <h3 class="mt-3">{{ display_text }}</h3>
     {% endif %}
 </div>

+ 11 - 1
apps/main/templates/manage_playlists.html

@@ -43,7 +43,17 @@
         <div class="card" style="background-color: #a75e27;">
             <div class="card-body">
                 <h4 class="card-title">Add Videos to Existing Playlists</h4>
-                <p class="card-text">Search for YouTube videos in your UnTube collection or just input YouTube video links to add new videos to your existing YouTube playlists.</p>
+                <p class="card-text">Add videos to existing YouTube playlists by searching for YouTube videos in your UnTube collection or by inputting YouTube video links.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div id="manage-delete-playlists-btn" class="col">
+        <a  hx-get="{% url 'manage_view_page' 'nuke-playlists' %}" hx-trigger="click" hx-target="#manage-pl-div" class="text-decoration-none text-white">
+        <div class="card" style="background-color: #286593;">
+            <div class="card-body">
+                <h4 class="card-title">Merge Playlists</h4>
+                <p class="card-text">Merge two or more playlists into a single one.</p>
             </div>
         </div>
         </a>

+ 10 - 8
apps/main/templates/playlists_home.html

@@ -92,14 +92,16 @@
             <div class="card-body">
                 <h5 class="card-title">Open a Random Playlist</h5>
                 <p class="card-text mt-4">
-                    <button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
-                              From
-                    </button>
-                  <ul class="dropdown-menu">
-                    <li><a class="dropdown-item" href="#">All</a></li>
-                    <li><a class="dropdown-item" href="#">Watching</a></li>
-                    <li><a class="dropdown-item" href="#">Favorites</a></li>
-                  </ul>
+                    <form action="{% url 'all_playlists' 'random' %}" method="post">
+                        {% csrf_token %}
+                        <select onchange="this.form.submit()" class="form-select w-50 bg-success text-white" name="playlistsType">
+                              <option value="From" selected>From</option>
+                                <option value="All">All</option>
+                              <option value="Favorites">Favorites</option>
+                            <option value="Watching">Watching</option>
+                            <option value="Plan to Watch">Plan to Watch</option>
+                        </select>
+                     </form>
                 </p>
             </div>
         </div>

+ 100 - 0
apps/main/templates/videos_home.html

@@ -0,0 +1,100 @@
+{% extends 'base.html' %}
+{% block content %}
+    <br>
+
+<div class="row row-cols-1 row-cols-md-3 g-4" >
+    <div class="col">
+        <a href="{% url 'all_videos' 'all' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #64381a;">
+            <div class="card-body">
+                <h4 class="card-title">All Videos</h4>
+                    <p class="card-text">Everything. From videos in your YouTube playlists to videos in the playlists you Imported.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div class="col">
+        <a href="{% url 'all_videos' 'user-owned' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #1A4464;">
+            <div class="card-body">
+                <h4 class="card-title">Your YouTube Playlists Videos</h4>
+                    <p class="card-text">View a list of all the videos you that are in your YouTube playlists.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+
+    <div class="col">
+        <a href="{% url 'all_videos' 'imported' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #1a645e;">
+            <div class="card-body">
+                <h4 class="card-title">Imported YouTube Playlists Videos</h4>
+                <p class="card-text">View a list of all the videos from external playlists you imported from YouTube.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div class="col">
+        <a href="{% url 'all_videos' 'favorites' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #aa8c2e;">
+            <div class="card-body">
+                <h4 class="card-title">Favorite Videos</h4>
+                <p class="card-text">All the videos you've marked favorite.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div class="col">
+        <a href="{% url 'all_videos' 'watched' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #541a64;">
+            <div class="card-body">
+                <h4 class="card-title">Watched Videos</h4>
+                <p class="card-text">All the videos you've marked watched.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div class="col">
+        <a href="{% url 'all_videos' 'hidden-videos' %}" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #641a29;">
+            <div class="card-body">
+                <h4 class="card-title">Hidden Videos</h4>
+                <p class="card-text">All the videos you've marked hidden inside playlists.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+    <div class="col">
+        <a href="#" class="text-decoration-none text-white">
+        <div class="card h-100" style="background-color: #bd39a7;">
+            <div class="card-body">
+                <h5 class="card-title">Unavailable YouTube Videos</h5>
+                <p class="card-text">List of videos in your collection that went unavailable on YouTube. Note that all details except the video title will have been deleted once a video goes unavailable on YouTube.</p>
+            </div>
+        </div>
+        </a>
+    </div>
+
+    <div class="col">
+        <a href="#" class="text-decoration-none text-white">
+
+        <div class="card h-100" style="background-color: #d04623;">
+            <div class="card-body">
+                <h5 class="card-title">Open a Random Video</h5>
+                <p class="card-text mt-4">
+                    <button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
+                              From
+                    </button>
+                  <ul class="dropdown-menu">
+                    <li><a class="dropdown-item" href="#">All</a></li>
+                    <li><a class="dropdown-item" href="#">Watched</a></li>
+                    <li><a class="dropdown-item" href="#">Favorites</a></li>
+                  </ul>
+                </p>
+            </div>
+        </div>
+        </a>
+    </div>
+</div>
+<br>
+{% endblock %}

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

@@ -345,7 +345,7 @@
         <div class="list-group" id="video-checkboxes">
           {% for video in videos|slice:"0:50" %}
 
-            <li {% if forloop.last and videos.count > 50 %}hx-get="{% url 'load_more_videos' playlist.playlist_id page|default:"1" %}"
+            <li {% if forloop.last and videos.count > 50 %}hx-get="{% url 'load_more_videos' playlist.playlist_id order_by|default:"all" page|default:"1" %}"
     hx-trigger="revealed"
     hx-swap="afterend" hx-indicator="#load-more-videos-spinner" {% endif %} class="list-group-item d-flex justify-content-between align-items-center bg-transparent" style="background-color: #40B3A2">
 

+ 3 - 3
apps/main/templates/view_playlist_settings.html

@@ -26,7 +26,7 @@
                     </div>
                     <div class="col-sm-9 text-white-50">
                         <input type="text" class="form-control" name="playlistTitle" value="{{ playlist.name }}" aria-describedby="plTitleHelp">
-                        <div id="plTitleHelp" class="form-text">Make sure the title does not contain '>' or '<'. They will be replaced by 'greater than' or 'less than' if found.</div>
+                        <div id="plTitleHelp" class="form-text">Maximum of 150 characters. Make sure the title does not contain '>' or '<'. They will be replaced by 'greater than' or 'less than' if found.</div>
                     </div>
                   </div>
                     <hr>
@@ -47,7 +47,7 @@
 
                             <div class="col-sm-9 text-white-50">
                                 <textarea class="form-control form-text" name="playlistDesc" rows="6" placeholder="Enter a playlist description here!" aria-describedby="plDescHelp">{{ playlist.description }}</textarea>
-                                <div id="plDescHelp" class="form-text">Make sure the description does not contain '>' or '<'. They will be replaced by 'greater than' or 'less than' if found.</div>
+                                <div id="plDescHelp" class="form-text">Maximum of 5000 characters. Make sure the description does not contain '>' or '<'. They will be replaced by 'greater than' or 'less than' if found.</div>
                             </div>
                         </div>
                       <hr>
@@ -169,7 +169,7 @@
                           <h6 class="mb-0">Danger Zone</h6>
                         </div>
                         <div class="col-sm-9 text-white-50">
-                            <button type="button" class="btn btn-outline-danger">Remove Playlist From UnTube</button>
+                            <a href="{% url 'delete_playlist' playlist.playlist_id %}" class="btn btn-outline-danger">Remove Playlist From UnTube</a>
                         </div>
                       </div>
 

+ 3 - 2
apps/main/urls.py

@@ -17,6 +17,7 @@ urlpatterns = [
     path("<slug:playlist_id>/<slug:video_id>/video-details/favorite", views.mark_video_favortie, name='mark_video_favorite'),
     path('<slug:playlist_id>/<slug:video_id>/video-details/watched', views.mark_video_watched, name='mark_video_watched'),
     path("from/<slug:playlist_id>/delete-videos/<slug:command>", views.delete_videos, name='delete_videos'),
+    path("videos/<slug:videos_type>", views.all_videos, name='all_videos'),
 
     ### STUFF RELATED TO ONE PLAYLIST
     path("playlist/<slug:playlist_id>", views.view_playlist, name='playlist'),
@@ -27,13 +28,14 @@ urlpatterns = [
          name='mark_playlist_as'),
     path("playlist/<slug:playlist_id>/update/<slug:type>", views.update_playlist, name="update_playlist"),
     path("playlist/<slug:playlist_id>/update-settings", views.update_playlist_settings, name="update_playlist_settings"),
-    path("playlist/<slug:playlist_id>/load-more-videos/<int:page>", views.load_more_videos, name="load_more_videos"),
+    path("playlist/<slug:playlist_id>/<slug:order_by>/load-more-videos/<int:page>", views.load_more_videos, name="load_more_videos"),
     path("playlist/<slug:playlist_id>/create-tag", views.create_playlist_tag, name="create_playlist_tag"),
     path("playlist/<slug:playlist_id>/add-tag", views.add_playlist_tag, name="add_playlist_tag"),
     path("playlist/<slug:playlist_id>/remove-tag/<str:tag_name>", views.remove_playlist_tag, name="remove_playlist_tag"),
     path("playlist/<slug:playlist_id>/get-tags", views.get_playlist_tags, name="get_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>/delete", views.delete_playlist, name="delete_playlist"),
 
     ### STUFF RELATED TO PLAYLISTS IN BULK
     path("search/playlists/<slug:playlist_type>", views.search_playlists, name="search_playlists"),
@@ -48,5 +50,4 @@ urlpatterns = [
     path("manage/view/<slug:page>", views.manage_view_page, name='manage_view_page'),  # views the import pl, create pl, create untube pl pages
     path("manage/import", views.manage_import_playlists, name="manage_import_playlists"),
     path("manage/create", views.manage_create_playlist, name="manage_create_playlist")
-
 ]

+ 135 - 11
apps/main/views.py

@@ -1,10 +1,13 @@
 import datetime
+import random
 
 import humanize
 import pytz
 from django.db.models import Q
 from django.http import HttpResponse, HttpResponseRedirect
 from django.shortcuts import render, redirect, get_object_or_404
+
+import apps
 from apps.main.models import Playlist, Tag
 from django.contrib.auth.decorators import login_required  # redirects user to settings.LOGIN_URL
 from allauth.socialaccount.models import SocialToken
@@ -19,7 +22,8 @@ def generateWatchingMessage(playlist):
     """
     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
+    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
@@ -35,14 +39,17 @@ def generateWatchingMessage(playlist):
         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(
+        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.save(update_fields=['watch_time_left', '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,
@@ -57,7 +64,8 @@ def home(request):
     user_playlists = user_profile.playlists.filter(Q(is_in_db=True) & Q(num_of_accesses__gt=0)).order_by(
         "-num_of_accesses")
     watching = user_profile.playlists.filter(Q(marked_as="watching") & Q(is_in_db=True)).order_by("-num_of_accesses")
-    recently_accessed_playlists = user_profile.playlists.filter(is_in_db=True).filter(updated_at__gt=user_profile.updated_at).order_by("-updated_at")[:6]
+    recently_accessed_playlists = user_profile.playlists.filter(is_in_db=True).filter(
+        updated_at__gt=user_profile.updated_at).order_by("-updated_at")[:6]
     recently_added_playlists = user_profile.playlists.filter(is_in_db=True).order_by("-created_at")[:6]
 
     #### FOR NEWLY JOINED USERS ######
@@ -217,6 +225,26 @@ def all_playlists(request, playlist_type):
         playlist_type_display = playlist_type.lower().replace("-", " ")
     elif playlist_type.lower() == "home":  # displays cards of all playlist types
         return render(request, 'playlists_home.html')
+    elif playlist_type.lower() == "random":  # randomize playlist
+        if request.method == "POST":
+            playlists_type = request.POST["playlistsType"]
+            if playlists_type == "All":
+                playlists = request.user.profile.playlists.all().filter(is_in_db=True)
+            elif playlists_type == "Favorites":
+                playlists = request.user.profile.playlists.all().filter(Q(is_favorite=True) & Q(is_in_db=True))
+            elif playlists_type == "Watching":
+                playlists = request.user.profile.playlists.filter(Q(marked_as="watching") & Q(is_in_db=True))
+            elif playlists_type == "Plan to Watch":
+                playlists = request.user.profile.playlists.filter(Q(marked_as="plan-to-watch") & Q(is_in_db=True))
+            else:
+                return redirect('/playlists/home')
+
+            if playlists.count() == 0:
+                messages.warning(request, f"No playlists in {playlists_type}")
+                return redirect('home')
+            random_playlist = random.choice(playlists)
+            return redirect(f'/playlist/{random_playlist.playlist_id}')
+        return render(request, 'playlists_home.html')
     else:
         return redirect('home')
 
@@ -225,6 +253,45 @@ def all_playlists(request, playlist_type):
                                                   "playlist_type_display": playlist_type_display})
 
 
+@login_required
+def all_videos(request, videos_type):
+    """
+    To implement this need to redesign the database
+    Currently videos -> playlist -> user.profile
+
+    Need to do
+    user.profile <- videos <- playlistItem -> playlist
+    many ways actually
+    """
+    videos_type = videos_type.lower()
+
+    if videos_type == "" or videos_type == "all":
+        playlists = request.user.profile.playlists.all().filter(is_in_db=True)
+        videos_type_display = "All Videos"
+    elif videos_type == "user-owned":  # YT playlists owned by user
+        playlists = request.user.profile.playlists.all().filter(Q(is_user_owned=True) & Q(is_in_db=True))
+        videos_type_display = "All Videos in your YouTube Playlists"
+    elif videos_type == "imported":  # YT playlists (public) owned by others
+        playlists = request.user.profile.playlists.all().filter(Q(is_user_owned=False) & Q(is_in_db=True))
+        videos_type_display = "Imported YouTube Playlists Videos"
+    elif videos_type == "favorites":  # YT playlists (public) owned by others
+        playlists = request.user.profile.playlists.all().filter(Q(is_favorite=True) & Q(is_in_db=True))
+        videos_type_display = "Favorite Videos"
+    elif videos_type == "watched":  # YT playlists (public) owned by others
+        playlists = request.user.profile.playlists.all().filter(Q(is_favorite=True) & Q(is_in_db=True))
+        videos_type_display = "Watched Videos"
+    elif videos_type == 'hidden-videos':  # YT playlists (public) owned by others
+        playlists = request.user.profile.playlists.all().filter(Q(is_favorite=True) & Q(is_in_db=True))
+        videos_type_display = "Hidden Videos"
+    elif videos_type.lower() == "home":  # displays cards of all playlist types
+        return render(request, 'videos_home.html')
+    else:
+        return redirect('home')
+
+    return render(request, 'all_playlists.html', {"playlists": playlists,
+                                                  "videos_type": videos_type,
+                                                  "videos_type_display": videos_type_display})
+
 @login_required
 def order_playlist_by(request, playlist_id, order_by):
     playlist = request.user.profile.playlists.get(Q(playlist_id=playlist_id) & Q(is_in_db=True))
@@ -282,7 +349,8 @@ def order_playlist_by(request, playlist_id, order_by):
     return HttpResponse(loader.get_template("intercooler/videos.html").render({"playlist": playlist,
                                                                                "videos": videos,
                                                                                "videos_details": videos_details,
-                                                                               "display_text": display_text}))
+                                                                               "display_text": display_text,
+                                                                               "order_by": order_by}))
 
 
 @login_required
@@ -443,6 +511,7 @@ def search_playlists(request, playlist_type):
 
 
 #### MANAGE VIDEOS #####
+@login_required
 def mark_video_favortie(request, playlist_id, video_id):
     video = request.user.profile.playlists.get(playlist_id=playlist_id).videos.get(video_id=video_id)
 
@@ -456,6 +525,7 @@ def mark_video_favortie(request, playlist_id, video_id):
         return HttpResponse('<i class="fas fa-heart" style="color: #fafa06"></i>')
 
 
+@login_required
 def mark_video_watched(request, playlist_id, video_id):
     playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
     video = playlist.videos.get(video_id=video_id)
@@ -473,6 +543,8 @@ def mark_video_watched(request, playlist_id, video_id):
             f'<i class="fas fa-check-circle" hx-get="/playlist/{playlist_id}/get-watch-message" hx-trigger="load" hx-target="#playlist-watch-message"></i>')
 
     generateWatchingMessage(playlist)
+
+
 ###########
 @login_required
 def search(request):
@@ -609,16 +681,49 @@ def manage_create_playlist(request):
 
 
 @login_required
-def load_more_videos(request, playlist_id, page):
+def load_more_videos(request, playlist_id, order_by, page):
     playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
-    videos = playlist.videos.order_by("video_position")[50 * page:]
+
+    if order_by == "all":
+        videos = playlist.videos.order_by("video_position")
+    elif order_by == "favorites":
+        videos = playlist.videos.filter(is_favorite=True).order_by("video_position")
+    elif order_by == "popularity":
+        videos = playlist.videos.order_by("-like_count")
+    elif order_by == "date-published":
+        videos = playlist.videos.order_by("-published_at")
+    elif order_by == "views":
+        videos = playlist.videos.order_by("-view_count")
+    elif order_by == "has-cc":
+        videos = playlist.videos.filter(has_cc=True).order_by("video_position")
+    elif order_by == "duration":
+        videos = playlist.videos.order_by("-duration_in_seconds")
+    elif order_by == 'new-updates':
+        videos = []
+        if playlist.has_new_updates:
+            recently_updated_videos = playlist.videos.filter(video_details_modified=True)
+
+            for video in recently_updated_videos:
+                if video.video_details_modified_at + datetime.timedelta(hours=12) < datetime.datetime.now(
+                        pytz.utc):  # expired
+                    video.video_details_modified = False
+                    video.save()
+
+            if recently_updated_videos.count() == 0:
+                playlist.has_new_updates = False
+                playlist.save()
+            else:
+                videos = recently_updated_videos.order_by("video_position")
+    elif order_by == 'unavailable-videos':
+        videos = playlist.videos.filter(Q(is_unavailable_on_yt=True) & Q(was_deleted_on_yt=True))
 
     return HttpResponse(loader.get_template("intercooler/videos.html")
         .render(
         {
             "playlist": playlist,
-            "videos": videos,
-            "page": page + 1}))
+            "videos": videos[50 * page:],  # only send 50 results per page
+            "page": page + 1,
+            "order_by": order_by}))
 
 
 @login_required
@@ -790,7 +895,11 @@ def update_playlist(request, playlist_id, type):
 
 @login_required
 def view_playlist_settings(request, playlist_id):
-    playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
+    try:
+        playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
+    except apps.main.models.Playlist.DoesNotExist:
+        messages.error(request, "No such playlist found!")
+        return redirect('home')
 
     return render(request, 'view_playlist_settings.html', {"playlist": playlist})
 
@@ -903,3 +1012,18 @@ def remove_playlist_tag(request, playlist_id, tag_name):
         return HttpResponse("Whoops >w<")
 
     return HttpResponse("")
+
+
+@login_required
+def delete_playlist(request, playlist_id):
+    playlist = request.user.profile.playlists.get(playlist_id=playlist_id)
+
+    if not playlist.is_user_owned:  # if playlist trying to delete isn't user owned
+        playlist.delete()  # just delete it from untrue
+    else:
+        # delete it from YouTube first then from UnTube
+        pass
+
+    messages.success(request, "Successfully deleted playlist from UnTube.")
+
+    return redirect('home')

+ 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>{{ pl.name|truncatechars:"40" }} <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.num_videos_watched }}/{{ pl.video_count }} viewed</span></small>
                       <div class="progress mb-3" style="height: 5px">
-                        <div class="progress-bar bg-primary" role="progressbar" style="width: 80%" aria-valuenow="{{ pl.num_videos_watched }}" aria-valuemin="0" aria-valuemax="{{ pl.video_count }}"></div>
+                        <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>
                             {% 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>

+ 12 - 6
templates/base.html

@@ -4,7 +4,7 @@
 <html lang="en">
     <head>
         <meta charset="utf-8">
-        <meta name="keywords" content="youtube, playlists, playlist panager, youtube playlists, untube, google">
+        <meta name="keywords" content="youtube, playlists, videos, delete videos, delete playlists, delete multiple videos, move multiple videos, merge playlists, video manager, playlist manager, youtube playlists, untube, google">
         <meta name="viewport" content="width=device-width, initial-scale=1">
         <meta name="description" content="UnTube is a simple Youtube playlist manager. Modify and keep track of your YouTube playlists with ease.">
         <meta name="author" content="Mohammed Abu Bakar Khan">
@@ -69,14 +69,20 @@
                             <a class="nav-link" aria-current="page" href="{% url 'home' %}">Dashboard</a>
                         </li>
 
-                        <li class="nav-item">
-                            <a class="nav-link" href="{% url 'all_playlists' 'home' %}">Library</a>
+                        <li class="nav-item dropdown">
+                            <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">
+                                <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>
                         </li>
                         <li class="nav-item dropdown">
-                            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" 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
                             </a>
-                            <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
+                            <ul class="dropdown-menu" aria-labelledby="quickViewDropdown">
                                 <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>
@@ -137,7 +143,7 @@
         <script src="{% static 'htmx/htmx.min.js' %}" type="application/javascript"></script>
         <script src="{% static 'clipboard.js/clipboard.min.js' %}" type="application/javascript"></script>
         <script src="{% static 'jquery3.6.0/js/jquery-3.6.0.min.js' %}" type="application/javascript"></script>
-        <script src="{% static 'bootstrap5.0.1/js/bootstrap.bundle.js' %}" type="application/javascript"></script>
+        <script src="{% static 'bootstrap5.0.1/js/bootstrap.bundle.min.js' %}" type="application/javascript"></script>
         <script src="https://cdn.jsdelivr.net/gh/bbbootstrap/libraries@main/choices.min.js"></script>
         <!-- <script src="{% static 'htmx/extensions/class-tools.js' %}" type="application/javascript"></script> -->