sleepytaco 3 lat temu
rodzic
commit
1d3fbf0536

+ 18 - 0
apps/main/migrations/0022_playlist_last_accessed_on.py

@@ -0,0 +1,18 @@
+# Generated by Django 3.2.3 on 2021-07-12 20:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0021_alter_playlist_confirm_before_deleting'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='playlist',
+            name='last_accessed_on',
+            field=models.DateTimeField(null=True),
+        ),
+    ]

+ 19 - 0
apps/main/migrations/0023_alter_playlist_last_accessed_on.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.2.3 on 2021-07-12 20:13
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0022_playlist_last_accessed_on'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='playlist',
+            name='last_accessed_on',
+            field=models.DateTimeField(default=datetime.datetime.now, null=True),
+        ),
+    ]

+ 17 - 0
apps/main/migrations/0024_remove_playlist_last_accessed_on.py

@@ -0,0 +1,17 @@
+# Generated by Django 3.2.3 on 2021-07-12 20:14
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0023_alter_playlist_last_accessed_on'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='playlist',
+            name='last_accessed_on',
+        ),
+    ]

+ 19 - 0
apps/main/migrations/0025_playlist_last_accessed_on.py

@@ -0,0 +1,19 @@
+# Generated by Django 3.2.3 on 2021-07-12 20:14
+
+import datetime
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('main', '0024_remove_playlist_last_accessed_on'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='playlist',
+            name='last_accessed_on',
+            field=models.DateTimeField(default=datetime.datetime.now),
+        ),
+    ]

+ 8 - 0
apps/main/models.py

@@ -634,6 +634,7 @@ class PlaylistManager(models.Manager):
 
                     video.is_duplicate = True
                     playlist.has_duplicate_videos = True
+                    video_ids.append(video_id)
                     video.save()
 
             while True:
@@ -689,6 +690,9 @@ class PlaylistManager(models.Manager):
 
                             video.is_duplicate = True
                             playlist.has_duplicate_videos = True
+
+                            video_ids.append(video_id)
+
                             video.save()
                 except AttributeError:
                     break
@@ -815,6 +819,7 @@ class PlaylistManager(models.Manager):
                         video_ids.append(video_id)
                         current_video_ids.remove(video_id)
                     else:
+                        video_ids.append(video_id)
                         video.is_duplicate = True
                         playlist.has_duplicate_videos = True
 
@@ -887,7 +892,9 @@ class PlaylistManager(models.Manager):
                             if video_id in current_video_ids:
                                 video.is_duplicate = False
                                 current_video_ids.remove(video_id)
+                                video_ids.append(video_id)
                             else:
+                                video_ids.append(video_id)
                                 video.is_duplicate = True
                                 playlist.has_duplicate_videos = True
 
@@ -1088,6 +1095,7 @@ class Playlist(models.Model):
                                  max_length=100)  # can be set to "none", "watching", "on-hold", "plan-to-watch"
     is_favorite = models.BooleanField(default=False, blank=True)  # to mark playlist as fav
     num_of_accesses = models.IntegerField(default="0")  # tracks num of times this playlist was opened by user
+    last_accessed_on = models.DateTimeField(default=datetime.datetime.now)
     is_private_on_yt = models.BooleanField(default=False)
     is_user_owned = models.BooleanField(default=True)  # represents YouTube playlist owned by user
     has_duplicate_videos = models.BooleanField(default=False)  # duplicate videos will not be shown on site

+ 69 - 90
apps/main/templates/home.html

@@ -52,62 +52,6 @@
             </div>
             {% endif %}
 
-            <div class="row row-cols-1 row-cols-md-4 g-4 text-dark d-flex justify-content-center">
-                <div class="col">
-                    <div class="card">
-                        <a style="background: linear-gradient(-45deg, #ae6ba3, #ab7b91, #bc8a9a, #d69aa1); background-size: 400% 400%; animation: gradient 7s ease infinite;" href="#" class="list-group-item list-group-item-action" aria-current="true">
-                            <div class="card-body">
-                                <div class="d-flex justify-content-center h1">
-                                    <i class="fas fa-map-pin"></i>
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    YOUR
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    PINS
-                                </div>
-                            </div>
-                        </a>
-                    </div>
-                </div>
-                <div class="col">
-                    <div class="card">
-                        <a style="background: linear-gradient(-45deg, #0645a4, #2480cd, #84bcf3, #b7d6f7); background-size: 400% 400%; animation: gradient 7s ease infinite;" href="#" class="list-group-item list-group-item-action" aria-current="true">
-                            <div class="card-body">
-                                <div class="d-flex justify-content-center h1">
-                                    <i class="fas fa-thumbs-up"></i>
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    LIKED
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    VIDEOS
-                                </div>
-                            </div>
-                        </a>
-                    </div>
-                </div>
-                <div class="col">
-                    <div class="card">
-                        <a style="background: linear-gradient(-45deg, #e2b968, #68af5b, #8a97bc, #d69ab2); background-size: 400% 400%; animation: gradient 7s ease infinite;" href="#" class="list-group-item list-group-item-action" aria-current="true">
-                            <div class="card-body">
-                                <div class="d-flex justify-content-center h1">
-                                    <i class="fas fa-history"></i>
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    YOUR
-                                </div>
-                                <div class="d-flex justify-content-center h1">
-                                    ACTIVITY
-                                </div>
-                            </div>
-                        </a>
-                    </div>
-                </div>
-
-            </div>
-            <br>
-
             {% if watching %}
                 <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>
@@ -167,38 +111,73 @@
 
 
 
-           <div class="row text-dark mt-0 d-flex justify-content-evenly">
+           <div class="row text-dark mt-0 align-items-center">
                 <div class="col">
-                  <div class="card bg-dark text-white">
-                        <div class="card-body">
-                          <h6 class="d-flex align-items-center mb-3"><span class="text-info me-2">{{ user.profile.playlists.count }}</span>Playlists Statistics</h6>
-
-                          <small>Public <span class="text-warning ms-1">{{ statistics.public_x }}%</span></small>
-                          <div class="progress mb-3" style="height: 5px">
-                            <div class="progress-bar bg-primary" role="progressbar" style="width: {{ statistics.public_x }}%" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></div>
-                          </div>
-
-                          <small>Private <span class="text-warning ms-1">{{ statistics.private_x }}%</span></small>
-                          <div class="progress mb-3" style="height: 5px">
-                            <div class="progress-bar bg-primary" role="progressbar" style="width: {{ statistics.private_x }}%" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100"></div>
-                          </div>
-                          <small>Favorites <span class="text-warning ms-1">{{ statistics.favorites_x }}%</span></small>
-                          <div class="progress mb-3" style="height: 5px">
-                            <div class="progress-bar bg-primary" role="progressbar" style="width: {{ statistics.favorites_x }}%" aria-valuenow="89" aria-valuemin="0" aria-valuemax="100"></div>
-                          </div>
-                          <small>Watching <span class="text-warning ms-1">{{ statistics.watching_x }}%</span></small>
-                          <div class="progress mb-3" style="height: 5px">
-                            <div class="progress-bar bg-primary" role="progressbar" style="width: {{ statistics.watching_x }}%" aria-valuenow="55" aria-valuemin="0" aria-valuemax="100"></div>
-                          </div>
-                          <small>Imported <span class="text-warning ms-1">{{ statistics.imported_x }}%</span></small>
-                          <div class="progress mb-3" style="height: 5px">
-                            <div class="progress-bar bg-primary" role="progressbar" style="width: {{ statistics.imported_x }}%" aria-valuenow="66" aria-valuemin="0" aria-valuemax="100"></div>
-                          </div>
+                    <div class="row">
+                        <div class="col">
+                            <div class="card" style="background: linear-gradient(-45deg, #aaae6b, #7b8eab, #bc8a9a, #d69aa1); background-size: 400% 400%; animation: gradient 7s ease infinite;">
+                                <a href="#" class="list-group-item bg-transparent list-group-item-action" aria-current="true">
+                                    <div class="card-body">
+                                        <div class="d-flex justify-content-center h1">
+                                            <i class="fas fa-map-pin" style="color:#c22929"></i>
+                                        </div>
+                                        <div class="d-flex justify-content-center h1">
+                                            YOUR
+                                        </div>
+                                        <div class="d-flex justify-content-center h1">
+                                            PINS
+                                        </div>
+                                    </div>
+                                </a>
+                            </div>
                         </div>
-                      </div>
+                        <div class="col">
+                            <div class="card" style="background: linear-gradient(-45deg, #0645a4, #2480cd, #84bcf3, #b7d6f7); background-size: 400% 400%; animation: gradient 7s ease infinite;">
+                                <a href="#" class="list-group-item list-group-item-action bg-transparent" aria-current="true">
+                                    <div class="card-body">
+                                        <div class="d-flex justify-content-center h1">
+                                            <i class="fas fa-heart" style="color: indianred"></i>
+                                        </div>
+                                        <div class="d-flex justify-content-center h1">
+                                            LIKED
+                                        </div>
+                                        <div class="d-flex justify-content-center h1">
+                                            VIDEOS
+                                        </div>
+                                    </div>
+                                </a>
+                            </div>
+                        </div>
+                    </div>
+                    <!-- Implement later
+                    <div class="row mt-3">
+                        <div class="col">
 
+                        </div>
+                        <div class="col-6">
+                        <div class="card">
+                            <a style="background: linear-gradient(-45deg, #e2b968, #68af5b, #8a97bc, #d69ab2); background-size: 400% 400%; animation: gradient 7s ease infinite;" href="#" class="list-group-item list-group-item-action" aria-current="true">
+                                <div class="card-body">
+                                    <div class="d-flex justify-content-center h1">
+                                        <i class="fas fa-history"></i>
+                                    </div>
+                                    <div class="d-flex justify-content-center h1">
+                                        YOUR
+                                    </div>
+                                    <div class="d-flex justify-content-center h1">
+                                        ACTIVITY
+                                    </div>
+                                </div>
+                            </a>
+                        </div>
+                        </div>
+                        <div class="col">
+
+                        </div>
+                    </div>
+                    -->
                 </div>
-               <div class="col">
+               <div class="col-8">
                   <div class="card bg-transparent text-black border border-5 rounded-3 border-success p-3">
                         <div class="card-body">
                               <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>
@@ -206,8 +185,8 @@
                                 <div class="row row-cols-1 row-cols-md-3 g-4 text-dark mt-0">
                                     {% for playlist in user_playlists|slice:"0:3" %}
                                     <div class="col">
-                                        <div class="card">
-                                            <a style="background-color: #4790c7;" href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item list-group-item-action" aria-current="true">
+                                        <div class="card overflow-auto" style="background-color: #4790c7;">
+                                            <a href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item bg-transparent list-group-item-action" aria-current="true">
                                                 <div class="card-body">
                                             <h5 class="card-title">
                                                #{{ forloop.counter }} <br><br>{{ playlist.name }}
@@ -249,8 +228,8 @@
                     <div class="row row-cols-1 row-cols-md-3 g-4 text-dark mt-0">
                         {% for playlist in recently_added_playlists %}
                         <div class="col">
-                            <div class="card">
-                                <a style="background-color: #958a44;" href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item list-group-item-action" aria-current="true">
+                            <div class="card overflow-auto" style="background-color: #958a44;">
+                                <a href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item bg-transparent list-group-item-action" aria-current="true">
                                     <div class="card-body">
                                 <h5 class="card-title">
                                    {{ playlist.name }}
@@ -282,8 +261,8 @@
                     <div class="row row-cols-1 row-cols-md-3 g-4 text-dark mt-0">
                         {% for playlist in recently_accessed_playlists %}
                         <div class="col">
-                            <div class="card">
-                                <a style="background-color: #357779;" href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item list-group-item-action" aria-current="true">
+                            <div class="card overflow-auto" style="background-color: #357779;">
+                                <a  href="{% url 'playlist' playlist.playlist_id %}" class="list-group-item bg-transparent list-group-item-action" aria-current="true">
                                     <div class="card-body">
                                 <h5 class="card-title">
                                    {{ playlist.name }}

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

@@ -8,7 +8,7 @@
       <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 %}">Refresh</a>
+        <a class="btn btn-primary mt-2" href="{% url 'playlist' playlist_id %}">OK</a>
     </div>
     </div>
 

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

@@ -2,7 +2,7 @@
 {% with all_videos_unavailable=playlist.all_videos_unavailable percent_complete=playlist.get_percent_complete %}
 <div class="py-2 bg-light rounded-3">
   <div class="d-flex justify-content-center">
-      <span class="h3 {% if percent_complete == 0 or all_videos_unavailable %}w-75{% endif %}" style="color: #70777E">
+      <span class="h3 ms-3 {% if percent_complete == 0 or all_videos_unavailable %}w-75{% endif %}" style="color: #70777E">
           {% if percent_complete == 0 or all_videos_unavailable %}
               <i class="fas fa-sun fa-spin" style="color: {% if not all_videos_unavailable %}#d0bc0b{% else %}red{% endif %};"></i>
               Yay, you marked a playlist as watching! {% if all_videos_unavailable %}However, there's no videos in this playlist you can mark as 'watched' :({% else %}This message will keep updating as you mark videos as watched. Good luck!{% endif %}

+ 17 - 0
apps/main/templates/search_untube_page.html

@@ -82,4 +82,21 @@
         {% endif %}
     </div>
 
+    <button class="scrollToTopBtn sticky-top">
+<i class="fa fa-angle-double-up fa-lg"></i></button>
+
+    <script type="application/javascript">
+
+        function triggerSubmit() {
+            var startsWithCB = document.getElementById("starts-with-cb");
+            var containsCB = document.getElementById("contains-cb");
+
+            if (startsWithCB.checked) {
+                startsWithCB.click();
+            } else {
+                containsCB.click();
+            }
+        }
+    </script>
+
 {% endblock %}

+ 158 - 25
apps/main/templates/view_playlist.html

@@ -40,7 +40,7 @@
                     </small> </h2>
                 <h6>by {{ playlist.channel_name }}</h6>
             </span>
-            <h4>
+                        <h4>
                 <span id="notice-div">
                     {% if playlist.marked_as != "none" %}
                     <span class="badge bg-success text-white" >
@@ -59,6 +59,7 @@
                     </span>
                 </span>
             </h4>
+
         </div>
         <p class="mb-1">
             {% if playlist.description %}
@@ -70,7 +71,7 @@
             <h5>No description</h5>
             {% endif %}
         </p>
-        <h6 class="h6 text-uppercase">
+        <h6 class="h6 text-uppercase overflow-auto">
             <span class="badge bg-light text-black-50">{% if playlist.is_user_owned %}OWNED{% else %}IMPORTED{% endif %}</span>
 
             <span class="badge bg-light text-black-50">{{ playlist.video_count }} VIDEOS</span>
@@ -93,13 +94,14 @@
         <h6 class="h6 pt-1">
 
             Tags:
-            <span class="text-uppercase">
+            <span class="text-uppercase" >
                 <span id="playlist-tags">
                 {% for tag in playlist_tags %}
-                    <span id="tag-{{ tag.name|slugify }}">
-                    <span class="badge rounded-pill bg-info mb-lg-1">
+                    <span id="tag-{{ tag.name|slugify }}" class="mb-2">
+                    <span class="badge rounded-pill bg-info mb-lg-2">
                         <a href="{% url 'tagged_playlists' tag.name %}" style="text-decoration: none; color: white">{{ tag.name }} </a>
-                        <a class="ms-1" hx-post="{% url 'remove_playlist_tag' playlist.playlist_id tag.name %}" hx-trigger="click" hx-target="#tag-{{ tag.name|slugify }}"><i class="fas fa-times-circle"></i></a></span>
+                        <a class="ms-1" hx-post="{% url 'remove_playlist_tag' playlist.playlist_id tag.name %}" hx-trigger="click" hx-target="#tag-{{ tag.name|slugify }}"><i class="fas fa-times-circle"></i></a>
+                    </span>
                     </span>
                 {% endfor %}
                 </span>
@@ -158,7 +160,7 @@
                 </div>
                 -->
                 {% if videos.count != 0 %}
-                <div class="btn-group me-2">
+                <div class="btn-group me-2 mb-2">
                     <button type="button" class="btn btn-success dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
                         Sort By
                     </button>
@@ -179,7 +181,7 @@
                 {% endif %}
 
 
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <button type="button" class="btn btn-warning dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
                             Mark As
                         </button>
@@ -193,15 +195,15 @@
         </div>
 
         <div class="bd-highlight">
-            <div class="btn-toolbar mb-2 mb-md-0">
+            <div class="btn-toolbar mb-2 mb-md-0 overflow-auto">
 
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <a hx-get="{% url 'update_playlist' playlist.playlist_id 'manual' %}" hx-target="#view_playlist" class="btn btn-secondary">
                             <i class="fas fa-sync"></i>
                         </a>
                     </div>
 
-                    <div class="btn-group me-2">
+                    <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">
                             <div id="playlist-fav">
@@ -215,14 +217,14 @@
                     </div>
 
 
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <a href="{% url 'view_playlist_settings' playlist.playlist_id %}" class="btn btn-primary">
                             <i class="fas fa-cog"></i>
                         </a>
                     </div>
 
                     {% if videos.count != 0 %}
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <button type="button" class="btn btn-light" onclick="row1_hide()">Manage</button>
                     </div>
                     {% endif %}
@@ -235,17 +237,17 @@
             <div class="me-auto bd-highlight">
                 <div class="btn-toolbar mb-2 mb-md-0">
                     <div id="select-all-btn">
-                        <div class="btn-group me-2">
+                        <div class="btn-group me-2 mb-2">
                         <button type="button" class="btn btn-info" id="select-all-btn">Select All</button>
                     </div>
                     </div>
                     <div id="deselect-all-btn" style="display: none">
-                        <div class="btn-group me-2">
+                        <div class="btn-group me-2 mb-2">
                         <button type="button" class="btn btn-info" id="select-all-btn">De-select All</button>
                         </div>
                     </div>
 
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <!-- <button type="submit" form="my-form" class="btn btn-outline-success" data-bs-toggle="dropdown" aria-expanded="false">
                             Move
                         </button> -->
@@ -263,7 +265,7 @@
                     </div>
                     -->
                     {% if playlist.is_user_owned %}
-                    <div class="btn-group me-2">
+                    <div class="btn-group me-2 mb-2">
                         <button class="btn btn-danger" data-bs-toggle="collapse" data-bs-target="#deleteItemsCollapse" aria-expanded="false" aria-controls="deleteItemsCollapse">
                             Delete Videos
                         </button>
@@ -276,12 +278,16 @@
 
             <div class="bd-highlight">
             <div class="btn-toolbar mb-2 mb-md-0">
-
-                    <div class="btn-group me-2">
-                        <button type="button" class="btn btn-light" onclick="row1_show()" id="manageBtn">Manage</button>
-                    </div>
-
+                <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
+                    </button>
                 </div>
+                <div class="btn-group me-2 mb-2">
+                    <button type="button" class="btn btn-light" onclick="row1_show()" id="manageBtn">Manage</button>
+                </div>
+
+            </div>
         </div>
 
         </div>
@@ -295,7 +301,7 @@
       <div class="d-flex justify-content-start">
 
 
-          <div class="col-md-7 text-dark">
+          <div class="col-md-8 text-dark">
                 <select class="visually-hidden" onchange="triggerSubmit()"
                      id="choices-multiple-remove-button" name="playlist-tags" placeholder="Select Playlists" multiple>
                     {% for pl in user_owned_playlists %}
@@ -371,6 +377,7 @@
 
 
     <div class="table-responsive" id="videos-div">
+
         <br>
         {% if videos %}
         <div class="list-group" id="video-checkboxes">
@@ -378,7 +385,7 @@
 
             <li id="{{ video.playlist_item_id }}" onclick="selectVideo(this);" {% 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">
+    hx-swap="afterend" hx-indicator="#load-more-videos-spinner" {% endif %} class="list-group-item d-flex justify-content-between align-items-center bg-transparent videos" style="background-color: #40B3A2">
 
         {% if video.is_unavailable_on_yt and not video.was_deleted_on_yt %}
 
@@ -511,7 +518,133 @@
 
     </div>
 
-
+    <button class="scrollToTopBtn sticky-top">
+    <i class="fa fa-angle-double-up fa-lg"></i></button>
+
+
+    <script type="application/javascript">
+
+            // when a video list item is clicked on, it gets checked and its bg turns red
+            function selectVideo(video) {
+                if ($('#row2').is(':visible')) {
+                    var videoCB = document.getElementById("video-" + video.id);
+
+                    if (videoCB.checked) {
+                        video.classList.remove("bg-danger");
+                        video.classList.add("bg-transparent");
+                        videoCB.checked = false;
+                    } else {
+                        video.classList.remove("bg-transparent");
+                        video.classList.add("bg-danger");
+                        videoCB.checked = true;
+                    }
+                }
+            }
+
+
+            var moveCopyBtn = document.getElementById('move-copy-vids-btn');
+            var moveCopyCollapse = document.getElementById('moveItemsToCollapse');
+            var bsMoveCopyCollapse = new bootstrap.Collapse(moveCopyCollapse, {
+                toggle: false
+            });
+            var deleteBtn = document.getElementById('delete-vids-btn');
+            var deleteCollapse = document.getElementById('deleteItemsCollapse');
+            var bsDeleteCollapse = new bootstrap.Collapse(deleteCollapse, {
+                toggle: false
+            });
+
+
+            document.getElementById('manageBtn').addEventListener('click', function () {                document.getElementById("delete-videos-confirm-box").innerHTML = "";
+                document.getElementById("delete-videos-confirm-box").innerHTML = "";
+                bsMoveCopyCollapse.hide();
+                bsDeleteCollapse.hide();
+            });
+            moveCopyCollapse.addEventListener('show.bs.collapse', function () {
+                    bsDeleteCollapse.hide();
+            });
+            deleteCollapse.addEventListener('show.bs.collapse', function () {
+                    bsMoveCopyCollapse.hide();
+            });
+
+            <!-- end -->
+
+
+            document.getElementById('select-all-btn').onclick = function() {
+                document.getElementById('select-all-btn').style.display = "none";
+                document.getElementById('deselect-all-btn').style.display = "block";
+
+                var checkboxes = document.getElementsByClassName('video-checkboxes');
+                for (var checkbox of checkboxes) {
+                    checkbox.checked = true;
+                }
+
+                var listItems = document.getElementsByClassName('videos'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
+                 for(var i=0, len=listItems.length; i<len; i++)
+                {
+                    if(listItems[i].classList.contains("bg-transparent")){
+                        listItems[i].classList.remove("bg-transparent");
+                        listItems[i].classList.add("bg-danger");
+                    }
+                }
+            }
+            document.getElementById('deselect-all-btn').onclick = function() {
+                document.getElementById('deselect-all-btn').style.display = "none";
+                document.getElementById('select-all-btn').style.display = "block";
+
+                var checkboxes = document.getElementsByClassName('video-checkboxes');
+                for (var checkbox of checkboxes) {
+                    checkbox.checked = false;
+                }
+
+                var listItems = document.getElementsByClassName('videos'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
+                 for(var i=0, len=listItems.length; i<len; i++)
+                {
+                    if(listItems[i].classList.contains("bg-danger")){
+                        listItems[i].classList.remove("bg-danger");
+                        listItems[i].classList.add("bg-transparent");
+                    }
+                }
+            }
+
+             function row1_hide() {
+                 document.getElementById("row1").style.display = "none";
+                 var checkboxes = document.getElementsByClassName('video-checkboxes'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
+                 for(var i=0, len=checkboxes.length; i<len; i++)
+                {
+                    //checkboxes[i].style.display = "block";
+                    checkboxes[i].checked = false;
+                }
+                 document.getElementById("row2").style.display = "block";
+             }
+
+             function row1_show() {
+                 document.getElementById("row1").style.display = "block";
+                 var checkboxes = document.getElementsByClassName('video-checkboxes'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
+                 for(var i=0, len=checkboxes.length; i<len; i++)
+                {
+                    checkboxes[i].style.display = "none";
+                    checkboxes[i].checked = false;
+                }
+                document.getElementById("row2").style.display = "none";
+                 var listItems = document.getElementsByClassName('videos'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
+                 for(var i=0, len=listItems.length; i<len; i++)
+                {
+                    if(listItems[i].classList.contains("bg-danger")){
+                        listItems[i].classList.remove("bg-danger");
+                        listItems[i].classList.add("bg-transparent");
+                    }
+                }
+             }
+
+             function select_all_checkboxes(source) {
+              checkboxes = document.getElementsByClassName('big-checkbox');
+              for(var i=0, n=checkboxes.length;i<n;i++) {
+                checkboxes[i].checked = source.checked;
+              }
+            }
+
+
+    </script>
     <!--
       <script src="{% static 'youtube-audio-player/iframe-api.js' %}"></script>
   <script src="{% static 'youtube-audio-player/yt.min.js' %}"></script>

+ 3 - 3
apps/main/util.py

@@ -10,10 +10,10 @@ 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(
+        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.")
+        "and".upper(), "").replace(" seconds".upper(), "secs.").replace(" second".upper(), "sec.").replace(",", "")
 
 
 # input => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", ..., "100"]  <- array of 100 video ids

+ 5 - 1
apps/main/views.py

@@ -138,7 +138,11 @@ def view_playlist(request, playlist_id):
     # specific playlist requested
     if user_profile.playlists.filter(Q(playlist_id=playlist_id) & Q(is_in_db=True)).count() != 0:
         playlist = user_profile.playlists.get(playlist_id__exact=playlist_id)
-        playlist.num_of_accesses += 1
+        # playlist.num_of_accesses += 1
+        # 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):
+            playlist.num_of_accesses += 1
+            playlist.last_accessed_on = datetime.datetime.now(pytz.utc)
         playlist.save()
     else:
         messages.error(request, "No such playlist found!")

+ 106 - 138
templates/base.html

@@ -13,7 +13,11 @@
         <title>UnTube - A Youtube Playlist Manager</title>
 
         <style type="text/css">
+            html {
+                scroll-behavior: smooth;
+            }
             body {
+                margin: 0;
                 background: linear-gradient(-45deg, #e2b968, #68af5b, #8a97bc, #d69ab2);
                 //background: linear-gradient(-45deg, #B2A3FF, #84bcf3, #AE876B, #B0E7AE);
                 //background: linear-gradient(-45deg, #0645a4, #2480cd, #84bcf3, #b7d6f7);
@@ -35,11 +39,55 @@
             }
 
 
-            #btn-back-to-top {
-              position: fixed;
+            .scrollToTopBtn {
+              background-color: black;
+              border: none;
+              border-radius: 50%;
+              color: white;
+              cursor: pointer;
+              font-size: 16px;
+              line-height: 58px;
+              width: 58px;
+
+              /* place it at the bottom right corner */
+              position: relative;
               bottom: 20px;
-              right: 20px;
-              display: none;
+                right: 0px;
+              left: 30px;
+
+              /* hide with opacity */
+              opacity: 0;
+              /* also add a translate effect */
+              transform: translateY(100px);
+              /* and a transition */
+              transition: all .5s ease
+            }
+
+            .showBtn {
+              opacity: 1;
+              transform: translateY(0)
+            }
+
+
+
+             #btn-back-to-top {
+              display: none; /* Hidden by default */
+              position: fixed; /* Fixed/sticky position */
+              bottom: 20px; /* Place the button at the bottom of the page */
+              right: 30px; /* Place the button 30px from the right */
+              z-index: 99999; /* Make sure it does not overlap */
+              border: none; /* Remove borders */
+              outline: none; /* Remove outline */
+              background-color: red; /* Set a background color */
+              color: white; /* Text color */
+              cursor: pointer; /* Add a mouse pointer on hover */
+              padding: 15px; /* Some padding */
+              border-radius: 10px; /* Rounded corners */
+              font-size: 18px; /* Increase font size */
+            }
+
+            #btn-back-to-top:hover {
+              background-color: #555; /* Add a dark-grey background on hover */
             }
 
             .big-checkbox {width: 30px; height: 30px;}
@@ -52,7 +100,12 @@
         <link href="{% static 'bootstrap5.0.1/css/bootstrap.min.css' %}" rel="stylesheet">
         <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/bbbootstrap/libraries@main/choices.min.css">
 
-
+        <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.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>
     </head>
     <body class="text-dark" style="font-family: 'Fredoka One', monospace; filter: contrast(80%);">
 
@@ -112,18 +165,26 @@
                         <!--
                         <input class="form-control border border-secondary bg-dark text-white-50 me-lg-2 bg-dark mb-1" id="unTubeSearchBar" type="text" placeholder="Search UnTube">
                         -->
-                        <a class="nav-link btn-lg me-1 mt-1" href="{% url 'search' %}" {% if user.profile.open_search_new_tab %}target="_blank"{% endif %}>
+                        <a class="nav-link btn-lg me-1 mt-2" href="{% url 'search' %}" {% if user.profile.open_search_new_tab %}target="_blank"{% endif %}>
                             <i class="fas fa-search"></i>
                         </a>
 
-                        <a href="{% url 'profile' %}" class="mt-1">
-                        <img src="https://robohash.org/{{ user.username }}.png?set=set{{ user.profile.robohash_set }}" alt="{{ user.username }}" class="border border-3 border-primary rounded-circle me-2" width="42" height="42">
-                        </a>
+                        <li class="nav-item dropdown">
+                            <a class="nav-link dropdown-toggle" href="#" id="quickViewDropdown" role="button" data-bs-toggle="dropdown" data-bs-display="static" aria-expanded="false">
+                                <img src="https://robohash.org/{{ user.username }}.png?set=set{{ user.profile.robohash_set }}" alt="{{ user.username }}" class="border border-3 border-primary rounded-circle" width="42" height="42">
+                            </a>
+                            <ul class="dropdown-menu dropdown-menu-lg-end" aria-labelledby="quickViewDropdown" style="z-index: 1021;">
+                                <li class="dropdown-item overflow-auto">Welcome, <span style="border-bottom: 3px #020000 dashed;">{{ user.username }}</span></li>
+                                <li><a class="dropdown-item" href="{% url 'profile' %}">Profile</a></li>
+                                <li><a class="dropdown-item" href="{% url 'settings' %}">Settings</a></li>
+
+                                <li><hr class="dropdown-divider"></li>
+                                <li><a class="dropdown-item" href="{% url 'log_out' %}">Log out</a></li>
+                            </ul>
+                        </li>
+
 
                     </div>
-                    <a class="btn btn-outline-danger" href="{% url 'log_out' %}">
-                      Log out
-                    </a>
                 </div>
             </div>
         </nav>
@@ -144,160 +205,67 @@
                 {% endif %}
                 {% block content %}
                 {% endblock %}
+
                 </main>
             </div>
+
         </div>
 
         <br>
 
 
-        <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.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> -->
-
 
         <script type="application/javascript">
+
             $(document).ready(function(){
 
+                // multiple choices select search box
                 var multipleCancelButton = new Choices('#choices-multiple-remove-button', {
-                removeItemButton: true,
+                    removeItemButton: true,
                 });
 
             });
 
-            function selectVideo(video) {
-                console.log(video.id);
-                console.log("video-" + video.id);
-                var videoCB = document.getElementById("video-" + video.id);
-                videoCB.checked = !videoCB.checked;  // uncheck video if check and vice versa
-            }
-
-            function triggerSubmit() {
-                var startsWithCB = document.getElementById("starts-with-cb");
-                var containsCB = document.getElementById("contains-cb");
-
-                if (startsWithCB.checked) {
-                    startsWithCB.click();
-                } else {
-                    containsCB.click();
-                }
-            }
-
+            // copy functionality
+            var clipboard = new ClipboardJS('.copy-btn');
 
+            // send csrf_token when htmx does a post request
             document.body.addEventListener('htmx:configRequest', (event) => {
-            event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
-          })
-
-            // window.onbeforeunload = function() {
-        //return "Dude, are you sure you want to leave? Think of the kittens!";
-            //}
-
-                 var moveCopyBtn = document.getElementById('move-copy-vids-btn');
-            var moveCopyCollapse = document.getElementById('moveItemsToCollapse');
-            var bsMoveCopyCollapse = new bootstrap.Collapse(moveCopyCollapse, {
-                toggle: false
-            });
-            var deleteBtn = document.getElementById('delete-vids-btn');
-            var deleteCollapse = document.getElementById('deleteItemsCollapse');
-            var bsDeleteCollapse = new bootstrap.Collapse(deleteCollapse, {
-                toggle: false
-            });
+                event.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
+              })
 
 
-            document.getElementById('manageBtn').addEventListener('click', function () {                document.getElementById("delete-videos-confirm-box").innerHTML = "";
-                document.getElementById("delete-videos-confirm-box").innerHTML = "";
-                bsMoveCopyCollapse.hide();
-                bsDeleteCollapse.hide();
-            });
-            moveCopyCollapse.addEventListener('show.bs.collapse', function () {
-                    bsDeleteCollapse.hide();
-            });
-            deleteCollapse.addEventListener('show.bs.collapse', function () {
-                    bsMoveCopyCollapse.hide();
-            });
-
-            <!-- end -->
-
-
-            document.getElementById('select-all-btn').onclick = function() {
-                document.getElementById('select-all-btn').style.display = "none";
-                document.getElementById('deselect-all-btn').style.display = "block";
-
-                var checkboxes = document.getElementsByClassName('video-checkboxes');
-                for (var checkbox of checkboxes) {
-                    checkbox.checked = true;
-                }
-            }
-            document.getElementById('deselect-all-btn').onclick = function() {
-                document.getElementById('deselect-all-btn').style.display = "none";
-                document.getElementById('select-all-btn').style.display = "block";
+            var scrollToTopBtn = document.querySelector(".scrollToTopBtn")
+            var rootElement = document.documentElement
 
-                var checkboxes = document.getElementsByClassName('video-checkboxes');
-                for (var checkbox of checkboxes) {
-                    checkbox.checked = false;
-                }
-            }
-
-             function row1_hide() {
-                 document.getElementById("row1").style.display = "none";
-                 var checkboxes = document.getElementsByClassName('video-checkboxes'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
-                 for(var i=0, len=checkboxes.length; i<len; i++)
-                {
-                    checkboxes[i].style.display = "block";
-                    checkboxes[i].checked = false;
-                }
-                 document.getElementById("row2").style.display = "block";
-             }
-
-             function row1_show() {
-                 document.getElementById("row1").style.display = "block";
-                 var checkboxes = document.getElementsByClassName('video-checkboxes'); //Cache the collection here, so that even a new element added with the same class later we can avoid querying this again by using the cached collection.
-                 for(var i=0, len=checkboxes.length; i<len; i++)
-                {
-                    checkboxes[i].style.display = "none";
-                }
-                document.getElementById("row2").style.display = "none";
-             }
-
-             function select_all_checkboxes(source) {
-              checkboxes = document.getElementsByClassName('big-checkbox');
-              for(var i=0, n=checkboxes.length;i<n;i++) {
-                checkboxes[i].checked = source.checked;
+            function handleScroll() {
+              // Do something on scroll
+              var scrollTotal = rootElement.scrollHeight - rootElement.clientHeight
+              if ((rootElement.scrollTop / scrollTotal ) > 0.10) {
+                // Show button
+                scrollToTopBtn.classList.add("showBtn")
+              } else {
+                // Hide button
+                scrollToTopBtn.classList.remove("showBtn");
               }
             }
 
+            function scrollToTop() {
+              // Scroll to top logic
+              rootElement.scrollTo({
+                top: 0,
+                behavior: "smooth"
+              })
+            }
+            scrollToTopBtn.addEventListener("click", scrollToTop)
+            document.addEventListener("scroll", handleScroll)
 
-            var clipboard = new ClipboardJS('.copy-btn');
-
-
-            //Get the button
-            let mybutton = document.getElementById("btn-back-to-top");
 
-            // When the user scrolls down 20px from the top of the document, show the button
-            window.onscroll = function () {
-              scrollFunction();
-            };
 
-            function scrollFunction() {
-              if (
-                document.body.scrollTop > 150 ||
-                document.documentElement.scrollTop > 150
-              ) {
-                mybutton.style.display = "block";
-              } else {
-                mybutton.style.display = "none";
-              }
-            }
-            // When the user clicks on the button, scroll to the top of the document
-            mybutton.addEventListener("click", backToTop);
 
-            function backToTop() {
-              document.body.scrollTop = 0;
-              document.documentElement.scrollTop = 0;
-            }
+            // window.onbeforeunload = function() {
+        //return "Dude, are you sure you want to leave? Think of the kittens!";
+            //}
 
         </script>