Browse Source

implemented favorites page and fixed some bugs

sleepytaco 3 years ago
parent
commit
b6924e7a68

+ 3 - 5
apps/main/models.py

@@ -432,6 +432,7 @@ class PlaylistManager(models.Manager):
         playlist.playlist_duration = getHumanizedTimeString(playlist_duration_in_seconds)
 
         playlist.is_in_db = True
+        playlist.last_accessed_on = datetime.datetime.now(pytz.utc)
 
         playlist.save()
 
@@ -453,7 +454,6 @@ class PlaylistManager(models.Manager):
         # basically checks all the playlist video for any updates
         if playlist.last_full_scan_at + datetime.timedelta(minutes=1) < datetime.datetime.now(pytz.utc):
             print("DOING A FULL SCAN")
-            current_video_ids = [playlist_item.video_id for playlist_item in playlist.playlist_items.all()]
             current_playlist_item_ids = [playlist_item.playlist_item_id for playlist_item in
                                          playlist.playlist_items.all()]
 
@@ -983,7 +983,8 @@ class PlaylistManager(models.Manager):
         if command == "duplicate":
             playlist_items = playlist.playlist_items.filter(is_duplicate=True)
         elif command == "unavailable":
-            playlist_items = playlist.playlist_items.filter(Q(video__is_unavailable_on_yt=True) & Q(video__was_deleted_on_yt=False))
+            playlist_items = playlist.playlist_items.filter(
+                Q(video__is_unavailable_on_yt=True) & Q(video__was_deleted_on_yt=False))
 
         playlist_item_ids = []
         for playlist_item in playlist_items:
@@ -1079,7 +1080,6 @@ class PlaylistManager(models.Manager):
         return [0]
 
 
-
 class Tag(models.Model):
     name = models.CharField(max_length=69)
     created_by = models.ForeignKey(User, related_name="playlist_tags", on_delete=models.CASCADE, null=True)
@@ -1154,7 +1154,6 @@ class Video(models.Model):
         default=False)  # True if the video was unavailable (private/deleted) when the API call was first made
     was_deleted_on_yt = models.BooleanField(default=False)  # True if video became unavailable on a subsequent API call
 
-    is_pinned = models.BooleanField(default=False)
     is_marked_as_watched = models.BooleanField(default=False)  # mark video as watched
     is_favorite = models.BooleanField(default=False, blank=True)  # mark video as favorite
     num_of_accesses = models.IntegerField(default=0)  # tracks num of times this video was clicked on by user
@@ -1201,7 +1200,6 @@ class Playlist(models.Model):
     last_watched = models.DateTimeField(auto_now_add=True, null=True)
 
     # manage playlist
-    is_pinned = models.BooleanField(default=False)
     user_notes = models.CharField(max_length=420, default="")  # user can take notes on the playlist and save them
     user_label = models.CharField(max_length=100, default="")  # custom user given name for this playlist
     marked_as = models.CharField(default="none",

+ 182 - 0
apps/main/templates/favorites.html

@@ -0,0 +1,182 @@
+{% extends 'base.html' %}
+{% load humanize %}
+{% block content %}
+
+        <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
+            <h1 class="h2">Favorite Playlists
+                <span class="badge bg-primary rounded-pill">{{ playlists.count|default:"0" }}</span>
+                {% if playlists %}
+                <a href="{% url 'all_playlists' 'favorites' %}" style="text-decoration: none; color: black"><i class="fas fa-search"></i></a>
+                {% endif %}
+            </h1>
+        </div>
+
+        <div>
+            <div class="row row-cols-1 row-cols-md-3 g-4">
+                    {% if playlists %}
+                    {% for playlist in playlists %}
+                    <div class="col">
+                        <div class="card" style="background-color: #515355;">
+                            <div style="background-color: #1A4464;" class="list-group-item list-group-item-action" aria-current="true">
+
+                                <div class="card-body">
+
+                                    <h5 class="card-title text-white">
+                                        <a style="text-decoration: none; color: white" href="{% url 'playlist' playlist.playlist_id %}">{{ playlist.name }}</a>
+                                    </h5>
+
+                                    <p class="card-text text-uppercase">
+                                        {% if playlist.is_user_owned %}<span class="badge bg-light text-black-50">OWNED</span>{% else %}<span class="badge bg-light text-black-50">IMPORTED</span>{% endif %}
+                                        {% if playlist.is_private_on_yt %}<span class="badge bg-secondary text-white">Private</span> {% endif %}
+                                        <span class="badge bg-warning text-black-50">{{ playlist.video_count }} videos</span>
+                                        <span class="badge bg-dark text-white-50">{{ playlist.playlist_duration }} </span>
+                                        {% if playlist.is_from_yt %}<span class="badge bg-danger text-black-50">YT</span> {% endif %}
+                                        {% if playlist.marked_as == "watching" %}<span class="badge bg-primary text-white">WATCHING</span>{% endif %}
+
+                                    </p>
+
+                                    {% if playlist.tags.all %}
+                                        <p class="card-text">
+                                        <span class="d-flex justify-content-start flex-wrap">
+                                        <small>
+                                        <span style="color: #eed868;" class="me-lg-1 mb-lg-1">
+                                            <i class="fas fa-tags"></i>
+                                        </span>
+                                        </small>
+                                        {% for tag in playlist.tags.all %}
+                                            <span class="badge rounded-pill bg-info mb-lg-1 me-lg-1 text-black-50">
+                                                {{ tag.name }}
+                                            </span>
+                                        {% endfor %}
+                                        </span>
+                                        </p>
+                                    {% endif %}
+
+                                    <span class="d-flex justify-content-center">
+        <a href="https://www.youtube.com/playlist?list={{ playlist.playlist_id }}" class="btn btn-info me-1" target="_blank" id="share_link" style=""><i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
+
+                                    <button class="btn btn-dark" type="button" hx-get="{% url 'mark_playlist_as' playlist.playlist_id 'favorite' %}" hx-target="#playlist-{{ forloop.counter }}-fav">
+                                    <div id="playlist-{{ forloop.counter }}-fav">
+                                        {% if playlist.is_favorite %}
+                                            <i class="fas fa-star" style="color: #fafa06"></i>
+                                        {% else %}
+                                            <i class="far fa-star"></i>
+                                        {% endif %}
+                                    </div>
+                                </button>
+    </span>
+
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    {% endfor %}
+
+                    {% else %}
+                    <h5 class="text-dark align-content-center">No playlists marked favorite :(</h5>
+                    {% endif %}
+                </div>
+        </div>
+
+        <br>
+        <br>
+
+        <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
+            <h1 class="h2">Favorite Videos
+                <span class="badge bg-primary rounded-pill">{{ videos.count }}</span>
+                {% if videos %}
+                <a href="{% url 'all_playlists' 'favorites' %}" style="text-decoration: none; color: black"><i class="fas fa-search"></i></a>
+                {% endif %}
+            </h1>
+        </div>
+
+                <div>
+            <div class="row row-cols-1 row-cols-md-3 g-4">
+                {% if videos %}
+                {% for video in videos %}
+                <div class="col">
+
+                    <div class="card" style="max-width: 540px; background-color: #1A4464;">
+                          <div class="row g-0">
+                            <div class="col-md-4">
+                              <img src="{{ video.thumbnail_url }}" class="img-fluid rounded-3" style="    width: 100%;     height: 15vw;     object-fit: cover;">
+                            </div>
+                            <div class="col-md-8">
+                              <div class="card-body">
+                                <h5 class="card-title"><a href="{% url 'video' video.video_id %}" style="text-decoration: none; color: white"> {{ video.name|truncatewords:"15" }}</a></h5>
+                                <h5 class="card-text">
+                                    <small>
+                                        <span class="badge bg-dark text-white-50">{{ video.duration }}</span>
+                                        {% if video.is_unavailable_on_yt %}<span class="badge bg-light text-dark">Private</span>{% endif %}
+                                        {% if video.has_cc %}<span class="badge bg-danger text-dark">CC</span>{% endif %}
+                                        <span class="badge bg-info text-black-50"><i class="fas fa-eye"></i> {% if video.view_count == -1 %}HIDDEN{% else %}{{ video.view_count|intword|intcomma }}{% endif %}</span>
+                                        <span class="badge bg-warning text-black-50"><i class="fas fa-thumbs-up"></i> {% if video.like_count == -1 %}HIDDEN{% else %}{{ video.like_count|intword|intcomma }}{% endif %}</span>
+
+                                    </small>
+                                </h5>
+
+                                <span class="card-text d-flex justify-content-start">
+                                <a href="https://www.youtube.com/watch?v={{ video.video_id }}" class="btn btn-info me-1" target="_blank" id="share_link" style=""><i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
+                                <input class="form-control me-1 visually-hidden" id="video-{{ video.video_id }}" value="https://www.youtube.com/watch?v={{ video.video_id }}">
+                                <button class="copy-btn btn btn-success  me-1" data-clipboard-target="#video-{{ video.video_id }}">
+                                <i class="far fa-copy" aria-hidden="true"></i>
+                                </button>
+                                <button class="btn btn-dark" type="button" hx-get="{% url 'mark_video_favorite' video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                                <div id="video-{{ forloop.counter }}-fav">
+                                {% if video.is_favorite %}
+                                <i class="fas fa-heart" style="color: #fafa06"></i>
+                                {% else %}
+                                <i class="far fa-heart"></i>
+                                {% endif %}
+                                </div>
+                                </button>
+                                </span>
+
+                              </div>
+                            </div>
+                          </div>
+                        </div>
+                <!--
+                    <div class="card" style="background-color: #1A4464;">
+
+                    <div class="card-body">
+                        <h5 class="card-title text-light">
+                            <a href="{% url 'video' video.video_id %}" style="text-decoration: none; color: white"> {{ video.name|truncatewords:"15" }}</a><br>
+
+                            <small>
+
+                                <span class="badge bg-dark text-white-50">{{ video.duration }}</span>
+                            </small>
+                            {% if video.is_unavailable_on_yt %}<small><span class="badge bg-light text-dark">Private</span></small> {% endif %}
+                            {% if video.has_cc %}<small><span class="badge bg-danger text-dark">CC</span></small> {% endif %}
+                        </h5>
+                                                        <br>
+
+                        <span class="d-flex justify-content-center">
+                            <a href="https://www.youtube.com/watch?v={{ video.video_id }}" class="btn btn-info me-1" target="_blank" id="share_link" style=""><i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
+                            <input class="form-control me-1 visually-hidden" id="video-{{ video.video_id }}" value="https://www.youtube.com/watch?v={{ video.video_id }}">
+                            <button class="copy-btn btn btn-success  me-1" data-clipboard-target="#video-{{ video.video_id }}">
+                                <i class="far fa-copy" aria-hidden="true"></i>
+                            </button>
+                            <button class="btn btn-dark" type="button" hx-get="{% url 'mark_video_favorite' video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                                <div id="video-{{ forloop.counter }}-fav">
+                                    {% if video.is_favorite %}
+                                        <i class="fas fa-heart" style="color: #fafa06"></i>
+                                    {% else %}
+                                        <i class="far fa-heart"></i>
+                                    {% endif %}
+                                </div>
+                            </button>
+                        </span>
+                    </div>
+                    </div>
+                    -->
+                </div>
+                {% endfor %}
+
+                {% else %}
+                <h5 class="text-dark align-content-center">Nothing favorites :(</h5>
+                {% endif %}
+            </div>
+        </div>
+{% endblock %}

+ 10 - 10
apps/main/templates/home.html

@@ -48,7 +48,7 @@
             <div class="col-6 mb-4">
                 <div class="card bg-transparent text-dark">
                     <div class="card-body">
-                        <h6 class="d-flex align-items-center mb-3"><span class="text-primary me-2">{{ user.playlists.count }}</span>Playlists Statistics{% if user.playlists.count == 0 %}: You have no playlists in your UnTube!{% endif %}</h6>
+                        <h6 class="d-flex align-items-center mb-3"><span class="text-warning me-2">{{ user.playlists.count }}</span>Playlists Statistics{% if user.playlists.count == 0 %}: You have no playlists in your UnTube!{% endif %}</h6>
                         <div class="d-flex align-items-center mb-3">
 
                             <canvas id="overall-playlists-distribution" data-url="{% url 'overall_playlists_distribution' %}">
@@ -62,7 +62,7 @@
             <div class="col-6 mb-4">
                 <div class="card bg-transparent text-dark">
                     <div class="card-body">
-                        <h6 class="d-flex align-items-center mb-3"><span class="text-primary me-2">{{ watching.count }}</span>
+                        <h6 class="d-flex align-items-center mb-3"><span class="text-warning me-2">{{ watching.count }}</span>
                             {% if watching.count > 0 %}
                                 Playlist{% if watching.count > 1 %}s{% endif %} Watching: Percent Complete Chart
                                 <small> <a class="btn btn-sm btn-success ms-2" href="#continue-watching">View</a></small>
@@ -87,7 +87,7 @@
 
         <div class="row" ><!--data-masonry='{"percentPosition": true }'-->
                 <div class="col-sm-6 col-lg-4 mb-4">
-                    <div class="card card-cover h-100 overflow-hidden text-white gradient-bg-3 rounded-5 shadow-lg" style="">
+                    <div class="card card-cover h-100 overflow-hidden text-white {% if not user.profile.enable_gradient_bg  %}gradient-bg-3{% else %}bg-dark{% endif %} rounded-5 shadow-lg" style="">
                         <div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1">
                             <h2 class="pt-5 mt-5 mb-4 display-6 lh-1 fw-bold">
                                 <a href="{% url 'all_playlists' 'all' %}" class="stretched-link" style="text-decoration: none; color: #fafafa">
@@ -96,7 +96,7 @@
                             <ul class="d-flex list-unstyled mt-auto">
                                 <li class="me-auto">
                                     <h3>
-                                        <i class="fas fa-burn fa-lg" style="color: #b300ff"></i>
+                                        <i class="fas fa-mountain fa-lg" style="color: #e26f94"></i>
                                     </h3>
                                 </li>
                             </ul>
@@ -105,7 +105,7 @@
                 </div>
 
                 <div class="col-sm-6 col-lg-4 mb-4">
-                <div class="card card-cover h-100 overflow-hidden text-white gradient-bg-3 rounded-5 shadow-lg" style="">
+                <div class="card card-cover h-100 overflow-hidden text-white {% if not user.profile.enable_gradient_bg  %}gradient-bg-3{% else %}bg-dark{% endif %} rounded-5 shadow-lg" style="">
                 <div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1">
                 <h2 class="pt-5 mt-5 mb-4 display-6 lh-1 fw-bold">
                 <a href="{% url 'playlist' 'LL' %}" class="stretched-link" style="text-decoration: none; color: #fafafa">
@@ -124,17 +124,17 @@
                 </div>
 
                 <div class="col-sm-6 col-lg-4 mb-4">
-                <div class="card card-cover h-100 overflow-hidden text-white gradient-bg-3 rounded-5 shadow-lg" style="">
+                <div class="card card-cover h-100 overflow-hidden text-white {% if not user.profile.enable_gradient_bg  %}gradient-bg-3{% else %}bg-dark{% endif %} rounded-5 shadow-lg" style="">
                 <div class="d-flex flex-column h-100 p-5 pb-3 text-white text-shadow-1">
                 <h2 class="pt-5 mt-5 mb-4 display-6 lh-1 fw-bold">
-                <a href="{% url 'playlist' 'LL' %}" class="stretched-link" style="text-decoration: none; color: #fafafa">
-                    Your Pins
+                <a href="{% url 'favorites' %}" class="stretched-link" style="text-decoration: none; color: #fafafa">
+                    Your Favorites
                 </a>
                 </h2>
                 <ul class="d-flex list-unstyled mt-auto">
                 <li class="me-auto">
                     <h3>
-                        <i class="fas fa-thumbtack fa-lg" style="color: #db4747"></i>
+                        <i class="fas fa-star fa-lg" style="color: #dbcc47"></i>
                     </h3>
                 </li>
                 </ul>
@@ -158,7 +158,7 @@
             <div class="col">
                 <div class="card bg-transparent text-dark">
                     <div class="card-body">
-                        <h6 class="d-flex align-items-center mb-3">A total of <span class="text-primary me-1 ms-1" id="num-channels">{{ channels.count }} channels</span> and <span class="text-primary ms-1 me-1" id="num-channels"> {{ videos.count }} videos</span> found in your UnTube collection</h6>
+                        <h6 class="d-flex align-items-center mb-3">A total of <span class="text-warning me-1 ms-1" id="num-channels">{{ channels.count|intword|intcomma }} channels</span> and <span class="text-warning ms-1 me-1" id="num-channels"> {{ videos.count|intword|intcomma }} videos</span> found in your UnTube collection</h6>
                         <div class="d-flex align-items-center mb-3">
 
                             <canvas id="overall-channels-distribution" data-url="{% url 'overall_channels_distribution' %}">

+ 2 - 6
apps/main/templates/intercooler/playlist_updates.html

@@ -2,19 +2,15 @@
 {% load humanize %}
 {% load static %}
     <div class="alert alert-success alert-dismissible fade show" role="alert">
-      <h4 class="alert-heading">Updates</h4>
+      <h4 class="alert-heading">Successfully refreshed playlist!</h4>
       <p>{{ playlist_changed_text|linebreaksbr }}</p>
       <hr>
       <p class="mb-0">Tip: Sort By Updates to see what changed in this playlist. Deleted videos will not show up there. Updates can be seen for 12 hrs.</p>
+
         <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
         <div class="d-flex justify-content-center">
         <a class="btn btn-primary mt-2" href="{% url 'playlist' playlist_id %}">OK</a>
     </div>
     </div>
 
-<!--
-    <div class="d-flex justify-content-center">
-        <a class="btn btn-primary" href="{% url 'playlist' playlist_id %}">Back</a>
-    </div>
-    -->
       <meta http-equiv="refresh" content="100" />

+ 1 - 1
apps/main/templates/intercooler/playlists.html

@@ -84,7 +84,7 @@
         {% endfor %}
 
         {% else %}
-        <h5 class="text-dark align-content-center">Nothing playlists found :(</h5>
+        <h5 class="text-dark align-content-center">No playlists found :(</h5>
         {% endif %}
     </div>
 {% endif %}

+ 2 - 2
apps/main/templates/intercooler/search_untube_results.html

@@ -36,11 +36,11 @@
 
                 <span class="d-flex justify-content-center">
                     <a href="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}" class="btn btn-info me-1" target="_blank" id="share_link" style=""><i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
-                    <input class="form-control me-1 visually-hidden" id="video-{{ video.video_id }}" value="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}">
+                    <input class="form-control me-1 visually-hidden" id="video-{{ playlist_item.video.video_id }}" value="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}">
                     <button class="copy-btn btn btn-success  me-1" data-clipboard-target="#video-{{ playlist_item.video.video_id }}">
                         <i class="far fa-copy" aria-hidden="true"></i>
                     </button>
-                    <button class="btn btn-dark" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                    <button class="btn btn-dark" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
                         <div id="video-{{ forloop.counter }}-fav">
                             {% if playlist_item.video.is_favorite %}
                                 <i class="fas fa-heart" style="color: #fafa06"></i>

+ 0 - 22
apps/main/templates/intercooler/video_details.html

@@ -1,22 +0,0 @@
-    {% load humanize %}
-
-
-    <div class="offcanvas-header text-dark">
-    <h5 class="offcanvas-title" id="offcanvasWithBackdropLabel">
-        {{ video.name}}
-            <span class="badge bg-secondary">{{ video.duration }}</span>
-    {% if video.has_cc %}<span class="badge bg-secondary">CC</span>{% endif %}
-    {% if video.published_at %}<span class="badge bg-secondary">{{ video.published_at }}</span>{% endif %}
-  {% if video.view_count %}<span class="badge bg-info">{{ video.view_count|intword|intcomma }} views</span>{% endif %}
-
-    {% if video.is_duplicate %}<span class="badge bg-primary">duplicate</span>{% endif %}<br>
-    </h5>
-    <button type="button" class="btn-close" data-bs-dismiss="offcanvas" aria-label="Close"></button>  </div>
-  <div class="offcanvas-body text-dark">
-      <form>
-                <p>{{ video.description|linebreaksbr|urlize }}</p>
-    </form>
-      <br style="background-color: cadetblue">
-  </div>
-
-

+ 0 - 19
apps/main/templates/intercooler/video_notes.html

@@ -1,19 +0,0 @@
-
-                <div class="offcanvas-body text-white-50">
-                    <div id="video-notes-status-div" class="text-white">
-
-                    </div>
-                    <div id="video-notes-form">
-                          <div class="form-group text-white">
-                            <h1>Your Notes</h1>
-                                                  <br>
-
-                            <textarea name="video-notes-text-area" class="form-control bg-dark text-white-50" placeholder="Enter here..." id="video-notes-text-area" rows="17">{{ video.user_notes }}</textarea>
-                          </div>
-                    </div>
-                    <br>
-                    <button type="button" hx-post="{% url 'video_notes' playlist_id video.video_id %}" hx-include="[id='video-notes-form']" hx-target="#video-notes-status-div" class="btn btn-success">Save</button>
-
-                    <br>
-                </div>
-    

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

@@ -79,8 +79,8 @@
                 <div class="ms-5">
                 {% if playlist_item.video.is_unavailable_on_yt or playlist_item.video.was_deleted_on_yt %}
                 <a class="btn btn-sm  btn-primary mb-1" href="{% url 'video' playlist_item.video.video_id %}"><i class="fas fa-info"></i></a>
-                <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
-                    <div id="video-{{ forloop.counter }}-fav">
+                <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.video.video_id %}" hx-target="#video-{{ page }}-{{ forloop.counter }}-fav">
+                    <div id="video-{{ page }}-{{ forloop.counter }}-fav">
                         {% if playlist_item.video.is_favorite %}
                             <i class="fas fa-heart" style="color: #fafa06"></i>
                         {% else %}
@@ -91,13 +91,13 @@
                 {% else %}
 
                 <a class="btn btn-sm btn-info mb-1" type="button" href="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}&list={{ playlist.playlist_id }}" class="btn btn-info me-1" target="_blank"><i class="fas fa-external-link-alt" aria-hidden="true"></i></a>
-                <input class="form-control me-1 visually-hidden" id="video-{{ playlist_item.video.video_id }}" value="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}">
-                <button class="copy-btn btn btn-sm  btn-success mb-1" data-clipboard-target="#video-{{ playlist_item.video.video_id }}">
+                <input class="form-control me-1 visually-hidden" id="video-{{ page }}-{{ playlist_item.video.video_id }}" value="https://www.youtube.com/watch?v={{ playlist_item.video.video_id }}">
+                <button class="copy-btn btn btn-sm  btn-success mb-1" data-clipboard-target="#video-{{ page }}-{{ playlist_item.video.video_id }}">
                     <i class="far fa-copy" aria-hidden="true"></i>
                 </button>
                 <a class="btn btn-sm  btn-primary mb-1" href="{% url 'video' playlist_item.video.video_id %}"><i class="fas fa-info"></i></a>
-                <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
-                    <div id="video-{{ forloop.counter }}-fav">
+                <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.video.video_id %}" hx-target="#video-{{ page }}-{{ forloop.counter }}-fav">
+                    <div id="video-{{ page }}-{{ forloop.counter }}-fav">
                         {% if playlist_item.video.is_favorite %}
                             <i class="fas fa-heart" style="color: #fafa06"></i>
                         {% else %}
@@ -106,8 +106,8 @@
                     </div>
                 </button>
                     {% if playlist.marked_as == "watching" and not playlist_item.is_duplicate %}
-                    <button class="btn btn-sm btn-light mb-1" type="button" hx-get="{% url 'mark_video_watched' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-watched">
-                        <div id="video-{{ forloop.counter }}-watched">
+                    <button class="btn btn-sm btn-light mb-1" type="button" hx-get="{% url 'mark_video_watched' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ page }}-{{ forloop.counter }}-watched">
+                        <div id="video-{{ page }}-{{ forloop.counter }}-watched">
                             {% if playlist_item.video.is_marked_as_watched %}
                                 <i class="fas fa-check-circle"></i>
                             {% else %}

+ 20 - 8
apps/main/templates/view_playlist.html

@@ -27,8 +27,8 @@
         {% if playlist.has_playlist_changed %}
             <div hx-get="{% url 'update_playlist' playlist.playlist_id 'auto' %}" hx-trigger="load" hx-swap="outerHTML">
                 <div class="d-flex justify-content-center mt-4 mb-3" id="loading-sign">
-                    <img src="{% static 'svg-loaders/circles.svg' %}" width="40" height="40" style="color: black">
-                    <h5 class="mt-2 ms-2">Updating playlist '{{ playlist.name }}', please wait!</h5>
+                    <img src="{% static 'svg-loaders/circles.svg' %}" width="40" height="40" style="filter: invert(0%) sepia(18%) saturate(7468%) hue-rotate(241deg) brightness(84%) contrast(101%);">
+                    <h5 class="mt-2 ms-2">Refreshing playlist '{{ playlist.name }}', please wait!</h5>
                 </div>
             </div>
         {% else %}
@@ -73,9 +73,11 @@
                                         {% endif %}
                                     </span>
                                     <span id="">
+                                        <!--
                                         <span class="badge bg-dark">
                                             <i class="fas fa-map-pin"></i>
                                         </span>
+                                        -->
                                     </span>
                                 </h4>
                             </div>
@@ -277,10 +279,10 @@
 
                             <div class="btn-group me-2 mb-2">
 
-                                <button class="btn btn-warning" type="button" hx-get="{% url 'mark_playlist_as' playlist.playlist_id 'favorite' %}" hx-target="#playlist-fav">
+                                <button class="btn btn-dark" type="button" hx-get="{% url 'mark_playlist_as' playlist.playlist_id 'favorite' %}" hx-target="#playlist-fav">
                                     <div id="playlist-fav">
                                         {% if playlist.is_favorite %}
-                                            <i class="fas fa-star"></i>
+                                            <i class="fas fa-star" style="color: #fafa06"></i>
                                         {% else %}
                                             <i class="far fa-star"></i>
                                         {% endif %}
@@ -352,7 +354,12 @@
                         <div class="btn-toolbar mb-2 mb-md-0">
                             <div class="btn-group me-2 mb-2">
                                 <button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#deleteItemsCollapse" aria-expanded="false" aria-controls="deleteItemsCollapse">
-                                    <i class="fa fa-history"></i> History
+                                    <i class="fa fa-plus"></i> Add New Videos
+                                </button>
+                            </div>
+                            <div class="btn-group me-2 mb-2">
+                                <button class="btn btn-dark" data-bs-toggle="collapse" data-bs-target="#deleteItemsCollapse" aria-expanded="false" aria-controls="deleteItemsCollapse">
+                                    <i class="far fa-plus-square"></i> Create a New Playlist
                                 </button>
                             </div>
                             <div class="btn-group me-2 mb-2">
@@ -542,7 +549,12 @@
                                                 <span class="badge bg-warning text-black-50"><i class="fas fa-thumbs-up"></i> {% if playlist_item.video.like_count == -1 %}HIDDEN{% else %}{{ playlist_item.video.like_count|intword|intcomma }}{% endif %}</span>
 
                                                 {% if playlist_item.is_duplicate %}<span class="badge bg-primary">duplicate</span>{% endif %}
-                                                {% if playlist_item.video.playlists.count|add:"-1" != 0 %}<span class="badge bg-dark"><a href="{% url 'video' playlist_item.video.video_id %}#found-in" style="text-decoration: none; color: white">found in {{ playlist_item.video.playlists.count|add:"-1" }} other playlist{% if playlist_item.video.playlists.count|add:"-1" > 1 %}s{% endif %}</a></span>{% endif %}
+                                                {% if playlist_item.video.playlists.count|add:"-1" != 0 %}
+                                                    <span class="badge bg-dark">
+                                                        <a href="{% url 'video' playlist_item.video.video_id %}#found-in" style="text-decoration: none; color: white">
+                                                            found in {{ playlist_item.video.playlists.count|add:"-1" }} other playlist{% if playlist_item.video.playlists.count|add:"-1" > 1 %}s{% endif %}
+                                                        </a></span>
+                                                {% endif %}
                                                 {% if playlist_item.video.video_details_modified %}<span class="badge bg-danger">{% if playlist_item.video.was_deleted_on_yt %}went private/deleted{% else %}added{% endif %} {{ playlist_item.created_at|naturaltime }}</span>{% endif %}<br>
                                                 <br>
                                             {% endif %}
@@ -553,7 +565,7 @@
                                     <div class="ms-5">
                                         {% if playlist_item.video.is_unavailable_on_yt or playlist_item.video.was_deleted_on_yt %}
                                             <a class="btn btn-sm  btn-primary mb-1" href="{% url 'video' playlist_item.video.video_id %}"><i class="fas fa-info"></i></a>
-                                            <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                                            <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
                                                 <div id="video-{{ forloop.counter }}-fav">
                                                     {% if playlist_item.video.is_favorite %}
                                                         <i class="fas fa-heart" style="color: #fafa06"></i>
@@ -570,7 +582,7 @@
                                                 <i class="far fa-copy" aria-hidden="true"></i>
                                             </button>
                                             <a class="btn btn-sm  btn-primary mb-1" href="{% url 'video' playlist_item.video.video_id %}"><i class="fas fa-info"></i></a>
-                                            <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist.playlist_id playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                                            <button class="btn btn-sm btn-dark mb-1" type="button" hx-get="{% url 'mark_video_favorite' playlist_item.video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
                                                 <div id="video-{{ forloop.counter }}-fav">
                                                     {% if playlist_item.video.is_favorite %}
                                                         <i class="fas fa-heart" style="color: #fafa06"></i>

+ 7 - 9
apps/main/urls.py

@@ -3,23 +3,21 @@ from django.urls import path
 from . import views
 
 urlpatterns = [
+
+    ### STUFF RELATED TO WHOLE SITE
     path("home/", views.home, name='home'),
-    # path("", views.index, name='index'),
-    # path("login/", views.log_in, name='log_in'),
+    path("search", views.search, name="search"),
+    path("search/UnTube/", views.search_UnTube, name="search_UnTube"),
+    path("favorites", views.favorites, name="favorites"),
 
     ### STUFF RELATED TO INDIVIDUAL VIDEOS
     path("video/<slug:video_id>", views.view_video, name='video'),
+    path("video/<slug:video_id>/video-details/favorite", views.mark_video_favortie, name='mark_video_favorite'),
     path("video/<slug:video_id>/notes", views.video_notes, name='video_notes'),
-    path("video/<slug:video_id>/get-video-completion-times", views.video_completion_times,
-         name="video_completion_times"),
-
-    ### STUFF RELATED TO WHOLE SITE
-    path("search", views.search, name="search"),
-    path("search/UnTube/", views.search_UnTube, name="search_UnTube"),
+    path("video/<slug:video_id>/get-video-completion-times", views.video_completion_times, name="video_completion_times"),
 
     ### STUFF RELATED TO VIDEO(S) INSIDE PLAYLISTS
     path("<slug:playlist_id>/<slug:video_id>/video-details", views.view_video, name='video_details'),
-    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("videos/<slug:videos_type>", views.all_videos, name='all_videos'),
 

+ 40 - 20
apps/main/views.py

@@ -9,7 +9,7 @@ from django.shortcuts import render, redirect, get_object_or_404
 from django.utils.html import strip_tags
 
 import apps
-from apps.main.models import Playlist, Tag
+from apps.main.models import Playlist, Tag, Video
 from django.contrib.auth.decorators import login_required  # redirects user to settings.LOGIN_URL
 from allauth.socialaccount.models import SocialToken
 from django.views.decorators.http import require_POST
@@ -103,6 +103,14 @@ def home(request):
                                          "channels": channels})
 
 
+@login_required
+def favorites(request):
+    favorite_playlists = request.user.playlists.filter(Q(is_favorite=True) & Q(is_in_db=True)).order_by('-last_accessed_on')
+    favorite_videos = request.user.videos.filter(is_favorite=True).order_by('updated_at')
+
+    return render(request, 'favorites.html', {"playlists": favorite_playlists,
+                                              "videos": favorite_videos})
+
 @login_required
 def view_video(request, video_id):
     if request.user.videos.filter(video_id=video_id).exists():
@@ -150,12 +158,17 @@ def view_playlist(request, playlist_id):
     # specific playlist requested
     if user_profile.playlists.filter(Q(playlist_id=playlist_id) & Q(is_in_db=True)).exists():
         playlist = user_profile.playlists.get(playlist_id__exact=playlist_id)
-        # playlist.num_of_accesses += 1
+
+        # if its been 30 days since the last playlist visit, force refresh the playlist
+        if datetime.datetime.now(pytz.utc) - playlist.last_accessed_on > datetime.timedelta(days=15):
+            playlist.has_playlist_changed = True
+
         # only note down that the playlist as been viewed when 5mins has passed since the last access
-        if playlist.last_accessed_on + datetime.timedelta(minutes=5) < datetime.datetime.now(pytz.utc):
+        if playlist.last_accessed_on + datetime.timedelta(minutes=1) < datetime.datetime.now(pytz.utc):
             playlist.num_of_accesses += 1
             playlist.last_accessed_on = datetime.datetime.now(pytz.utc)
-        playlist.save()
+
+        playlist.save(update_fields=['num_of_accesses', 'last_accessed_on', 'has_playlist_changed'])
     else:
         if playlist_id == "LL":  # liked videos playlist hasnt been imported yet
             return render(request, 'view_playlist.html', {"not_imported_LL": True})
@@ -433,7 +446,7 @@ def mark_playlist_as(request, playlist_id, mark_as):
         else:
             playlist.is_favorite = True
             playlist.save()
-            return HttpResponse('<i class="fas fa-star"></i>')
+            return HttpResponse('<i class="fas fa-star" style="color: #fafa06"></i>')
     else:
         return redirect('home')
 
@@ -457,7 +470,8 @@ def delete_videos(request, playlist_id, command):
             all = True
             num_vids = request.user.playlists.get(playlist_id=playlist_id).playlist_items.all().count()
             if command == "start":
-                playlist_item_ids = [playlist_item.playlist_item_id for playlist_item in request.user.playlists.get(playlist_id=playlist_id).playlist_items.all()]
+                playlist_item_ids = [playlist_item.playlist_item_id for playlist_item in
+                                     request.user.playlists.get(playlist_id=playlist_id).playlist_items.all()]
     else:
         playlist_item_ids = request.POST.getlist("video-id", default=[])
         num_vids = len(playlist_item_ids)
@@ -545,6 +559,7 @@ def delete_specific_videos(request, playlist_id, command):
         <hr>
         """)
 
+
 @login_required
 @require_POST
 def search_tagged_playlists(request, tag):
@@ -602,7 +617,7 @@ def search_playlists(request, playlist_type):
 
 #### MANAGE VIDEOS #####
 @login_required
-def mark_video_favortie(request, playlist_id, video_id):
+def mark_video_favortie(request, video_id):
     video = request.user.videos.get(video_id=video_id)
 
     if video.is_favorite:
@@ -963,7 +978,7 @@ def update_playlist(request, playlist_id, command):
         return HttpResponse(
             f"""<div hx-get="/playlist/{playlist_id}/update/auto" hx-trigger="load" hx-swap="outerHTML">
                     <div class="d-flex justify-content-center mt-4 mb-3" id="loading-sign">
-                        <img src="/static/svg-loaders/circles.svg" width="40" height="40">
+                        <img src="/static/svg-loaders/circles.svg" width="40" height="40" style="filter: invert(0%) sepia(18%) saturate(7468%) hue-rotate(241deg) brightness(84%) contrast(101%);">
                         <h5 class="mt-2 ms-2">Refreshing playlist '{playlist.name}', please wait!</h5>
                     </div>
                 </div>""")
@@ -1020,7 +1035,8 @@ def update_playlist(request, playlist_id, command):
                 playlist.videos.remove(video)
 
     if len(playlist_changed_text) == 0:
-        playlist_changed_text = ["Successfully refreshed playlist! No new changes found!"]
+        playlist_changed_text = [
+            "Updated playlist and video details to their latest. No new changes found in terms of modifications made to this playlist!"]
 
     # return HttpResponse
     return HttpResponse(loader.get_template("intercooler/playlist_updates.html")
@@ -1219,7 +1235,8 @@ def playlist_move_copy_videos(request, playlist_id, action):
                                                              action="move")
         if status[0] == -1:
             if status[1] == 404:
-                return HttpResponse("<span class='text-danger'>You cannot copy/move unavailable videos! De-select them and try again.</span>")
+                return HttpResponse(
+                    "<span class='text-danger'>You cannot copy/move unavailable videos! De-select them and try again.</span>")
             return HttpResponse("Error moving!")
     else:  # copy
         status = Playlist.objects.moveCopyVideosFromPlaylist(request.user,
@@ -1228,11 +1245,13 @@ def playlist_move_copy_videos(request, playlist_id, action):
                                                              playlist_item_ids=playlist_item_ids)
         if status[0] == -1:
             if status[1] == 404:
-                return HttpResponse("<span class='text-danger'>You cannot copy/move unavailable videos! De-select them and try again.</span>")
+                return HttpResponse(
+                    "<span class='text-danger'>You cannot copy/move unavailable videos! De-select them and try again.</span>")
             return HttpResponse("Error copying!")
 
     return HttpResponse(success_message)
 
+
 @login_required
 def playlist_open_random_video(request, playlist_id):
     playlist = request.user.playlists.get(playlist_id=playlist_id)
@@ -1242,27 +1261,28 @@ def playlist_open_random_video(request, playlist_id):
 
     return redirect(f'/video/{random_video.video_id}')
 
+
 @login_required
 def playlist_completion_times(request, playlist_id):
     playlist_duration = request.user.playlists.get(playlist_id=playlist_id).playlist_duration_in_seconds
 
     return HttpResponse(f"""
         <h5 class="text-warning">Playlist completion times:</h5>
-        <h6>At 1.25x speed: {getHumanizedTimeString(playlist_duration/1.25)}</h6>
-        <h6>At 1.5x speed: {getHumanizedTimeString(playlist_duration/1.5)}</h6>
-        <h6>At 1.75x speed: {getHumanizedTimeString(playlist_duration/1.75)}</h6>
-        <h6>At 2x speed: {getHumanizedTimeString(playlist_duration/2)}</h6>
+        <h6>At 1.25x speed: {getHumanizedTimeString(playlist_duration / 1.25)}</h6>
+        <h6>At 1.5x speed: {getHumanizedTimeString(playlist_duration / 1.5)}</h6>
+        <h6>At 1.75x speed: {getHumanizedTimeString(playlist_duration / 1.75)}</h6>
+        <h6>At 2x speed: {getHumanizedTimeString(playlist_duration / 2)}</h6>
     """)
 
+
 @login_required
 def video_completion_times(request, video_id):
     video_duration = request.user.videos.get(video_id=video_id).duration_in_seconds
 
     return HttpResponse(f"""
         <h5 class="text-warning">Video completion times:</h5>
-        <h6>At 1.25x speed: {getHumanizedTimeString(video_duration/1.25)}</h6>
-        <h6>At 1.5x speed: {getHumanizedTimeString(video_duration/1.5)}</h6>
-        <h6>At 1.75x speed: {getHumanizedTimeString(video_duration/1.75)}</h6>
-        <h6>At 2x speed: {getHumanizedTimeString(video_duration/2)}</h6>
+        <h6>At 1.25x speed: {getHumanizedTimeString(video_duration / 1.25)}</h6>
+        <h6>At 1.5x speed: {getHumanizedTimeString(video_duration / 1.5)}</h6>
+        <h6>At 1.75x speed: {getHumanizedTimeString(video_duration / 1.75)}</h6>
+        <h6>At 2x speed: {getHumanizedTimeString(video_duration / 2)}</h6>
     """)
-

+ 1 - 0
apps/users/models.py

@@ -31,6 +31,7 @@ class Profile(models.Model):
 
     # preferences
     open_search_new_tab = models.BooleanField(default=True)  # open search page in new tab by default
+    enable_gradient_bg = models.BooleanField(default=False)
 
     # global playlist preferences (this will make all playlists)
     hide_unavailable_videos = models.BooleanField(default=False)

+ 12 - 5
apps/users/templates/settings.html

@@ -116,21 +116,28 @@
                         <label class="form-check-label" for="openSearchCheckDefault">Open Search in new tab</label>
                         </div>
 
+
+                        <div class="mb-3 form-check form-switch">
+                        <input class="form-check-input" name="enable gradient bg" type="checkbox" id="gradient-bg" {% if user.profile.enable_gradient_bg  %}checked{% endif %}>
+                        <label class="form-check-label" for="gradient-bg">Use a gradient background</label>
+                        </div>
+
                         <div class="mb-3 form-check form-switch">
                         <input class="form-check-input" name="auto refresh playlists" type="checkbox" id="flexSwitchCheckDefault">
                         <label class="form-check-label" for="flexSwitchCheckDefault">Automatically check for playlist updates on visit</label>
                         </div>
 
                         <div class="mb-3 form-check form-switch">
-                        <input class="form-check-input" name="hide videos" type="checkbox" id="flexSwitchCheckChecked" {% if profile.hide_unavailable_videos %}checked{% endif %}>
-                        <label class="form-check-label" for="flexSwitchCheckChecked">Hide deleted/private videos</label>
+                        <input class="form-check-input" name="hide videos" type="checkbox" id="hide-videos" {% if profile.hide_unavailable_videos %}checked{% endif %}>
+                        <label class="form-check-label" for="hide-videos">Hide deleted/private videos</label>
                         </div>
 
 
                         <div class="mb-3 form-check form-switch">
-                        <input class="form-check-input" name="confirm before deleting" type="checkbox" id="flexSwitchCheckChecked" {% if profile.confirm_before_deleting  %}checked{% endif %}>
-                        <label class="form-check-label" for="flexSwitchCheckChecked">Confirm before deleting</label>
+                        <input class="form-check-input" name="confirm before deleting" type="checkbox" id="confirm-before-delete" {% if profile.confirm_before_deleting  %}checked{% endif %}>
+                        <label class="form-check-label" for="confirm-before-delete">Confirm before deleting</label>
                         </div>
+
                     </div>
                 </div>
                 <hr>
@@ -140,7 +147,7 @@
               <div class="row">
                 <div class="col-sm-12">
                     <a href="#navbar" hx-post="{% url 'update_settings' %}" hx-include="[id='settings-form']" hx-target="#settings-status-div" class="btn btn-success">Save</a>
-                    <a class="btn btn-outline-danger" href="{% url 'delete_account' %}">Delete account</a>
+                    <a class="btn btn-outline-danger" href="{% url 'delete_account' %}">Delete UnTube account</a>
 
                 </div>
               </div>

+ 6 - 1
apps/users/views.py

@@ -84,6 +84,11 @@ def update_settings(request):
     else:
         user.profile.open_search_new_tab = False
 
+    if 'enable gradient bg' in request.POST:
+        user.profile.enable_gradient_bg = True
+    else:
+        user.profile.enable_gradient_bg = False
+
     user.save()
 
     return HttpResponse(loader.get_template("intercooler/messages.html").render(
@@ -204,7 +209,7 @@ def continue_import(request):
     if request.user.profile.import_in_progress is False:
         return redirect('home')
 
-    num_of_playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(playlist_id="LL").count()
+    num_of_playlists = request.user.playlists.filter(Q(is_user_owned=True)).exclude(playlist_id="LL").count()
 
     try:
         remaining_playlists = request.user.playlists.filter(Q(is_user_owned=True) & Q(is_in_db=False)).exclude(playlist_id="LL")

+ 7 - 4
templates/base.html

@@ -19,12 +19,15 @@
 
             body {
                 margin: 0;
-                //background: linear-gradient(-45deg, #e2b968, #68af5b, #8a97bc, #d69ab2);
+                {% if user.profile.enable_gradient_bg %}
+                background: linear-gradient(-45deg, #e2b968, #68af5b, #8a97bc, #d69ab2);
+                    background-size: 400% 400%;
+                animation: gradient 10s ease infinite;
+                {% endif %}
                 //background: linear-gradient(-45deg, #B2A3FF, #84bcf3, #AE876B, #B0E7AE);
                 //background: linear-gradient(-45deg, #0645a4, #2480cd, #84bcf3, #b7d6f7);
                 //background: linear-gradient(-45deg, #AE876B, #ABA27B, #A7BC8A, #A3D69A);
-                //background-size: 400% 400%;
-                //animation: gradient 10s ease infinite;
+
                 }
 
             .gradient-bg {
@@ -122,7 +125,7 @@
         <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.min.js' %}" type="application/javascript"></script>
             </head>
-    <body class="text-dark" style="font-family: 'Fredoka One', monospace; background-color: #FDF4DC;">
+    <body class="text-dark" style="font-family: 'Fredoka One', monospace; {% if not user.profile.enable_gradient_bg %}background-color: #FDF4DC;{% endif %}">
 
 
         {% if user.profile.show_import_page %}