Procházet zdrojové kódy

Added auto-update user YouTube playlists feature

sleepytaco před 3 roky
rodič
revize
f6f45fad95

+ 41 - 20
apps/main/models.py

@@ -25,8 +25,8 @@ def getVideoIdsStrings(video_ids):
 
     i = 0
     while i < len(video_ids):
-        output.append(",".join(video_ids[i:i + 10]))
-        i += 10
+        output.append(",".join(video_ids[i:i + 50]))
+        i += 50
 
     return output
 
@@ -286,6 +286,8 @@ class PlaylistManager(models.Manager):
                 print("YouTube playlist not found if id=playlist_id")
                 return -1
 
+            print("Playlist", pl_response)
+
             if pl_response["pageInfo"]["totalResults"] == 0:
                 print("No playlists created yet on youtube.")
                 return -2
@@ -330,20 +332,12 @@ class PlaylistManager(models.Manager):
                     description=item['snippet']['description'],
                     published_at=item['snippet']['publishedAt'],
                     thumbnail_url=getThumbnailURL(item['snippet']['thumbnails']),
-                    channel_id=item['snippet']['channelId'] if 'channelId' in
-                                                               item['snippet'] else '',
-                    channel_name=item['snippet']['channelTitle'] if 'channelTitle' in
-                                                                    item[
-                                                                        'snippet'] else '',
                     video_count=item['contentDetails']['itemCount'],
                     is_private_on_yt=True if item['status']['privacyStatus'] == 'private' else False,
                     playlist_yt_player_HTML=item['player']['embedHtml'],
                     user=current_user
                 )
 
-                if user.profile.yt_channel_id.strip() != playlist.channel_id.strip():
-                    playlist.is_user_owned = False
-
                 playlist.save()
 
                 playlist = current_user.playlists.get(playlist_id__exact=playlist_id)
@@ -360,9 +354,20 @@ class PlaylistManager(models.Manager):
                     # execute the above request, and store the response
                     pl_response = pl_request.execute()
 
+                    print("Playlist Items", pl_response)
+
                     for item in pl_response['items']:
                         video_id = item['contentDetails']['videoId']
 
+                        if playlist.channel_id == "":
+                            playlist.channel_id = item['snippet']['channelId']
+                            playlist.channel_name = item['snippet']['channelTitle']
+
+                            if user.profile.yt_channel_id.strip() != item['snippet']['channelId']:
+                                playlist.is_user_owned = False
+
+                            playlist.save()
+
                         if playlist.videos.filter(video_id=video_id).count() == 0:  # video DNE
                             if (item['snippet']['title'] == "Deleted video" and
                                 item['snippet']['description'] == "This video is unavailable.") or (
@@ -386,8 +391,8 @@ class PlaylistManager(models.Manager):
                                                                                                    'contentDetails'] else None,
                                     name=item['snippet']['title'],
                                     thumbnail_url=getThumbnailURL(item['snippet']['thumbnails']),
-                                    channel_id=item['snippet']['channelId'],
-                                    channel_name=item['snippet']['channelTitle'],
+                                    channel_id=item['snippet']['videoOwnerChannelId'],
+                                    channel_name=item['snippet']['videoOwnerChannelTitle'],
                                     description=item['snippet']['description'],
                                     video_position=item['snippet']['position'] + 1,
                                     playlist=playlist
@@ -442,8 +447,8 @@ class PlaylistManager(models.Manager):
                                                 'contentDetails'] else None,
                                             name=item['snippet']['title'],
                                             thumbnail_url=getThumbnailURL(item['snippet']['thumbnails']),
-                                            channel_id=item['snippet']['channelId'],
-                                            channel_name=item['snippet']['channelTitle'],
+                                            channel_id=item['snippet']['videoOwnerChannelId'],
+                                            channel_name=item['snippet']['videoOwnerChannelTitle'],
                                             video_position=item['snippet']['position'] + 1,
                                             playlist=playlist
                                         )
@@ -468,6 +473,9 @@ class PlaylistManager(models.Manager):
                     # API expects the video ids to be a string of comma seperated values, not a python list
                     video_ids_strings = getVideoIdsStrings(video_ids)
 
+                    print(video_ids)
+                    print(video_ids_strings)
+
                     # store duration of all the videos in the playlist
                     vid_durations = []
 
@@ -481,6 +489,8 @@ class PlaylistManager(models.Manager):
 
                         vid_response = vid_request.execute()
 
+                        print("Videos()", pl_response)
+
                         for item in vid_response['items']:
                             duration = item['contentDetails']['duration']
                             vid = playlist.videos.get(video_id=item['id'])
@@ -656,8 +666,8 @@ class PlaylistManager(models.Manager):
                                                                                            'contentDetails'] else None,
                             name=item['snippet']['title'],
                             thumbnail_url=getThumbnailURL(item['snippet']['thumbnails']),
-                            channel_id=item['snippet']['channelId'],
-                            channel_name=item['snippet']['channelTitle'],
+                            channel_id=item['snippet']['videoOwnerChannelId'],
+                            channel_name=item['snippet']['videoOwnerChannelTitle'],
                             description=item['snippet']['description'],
                             video_position=item['snippet']['position'] + 1,
                             playlist=playlist
@@ -712,8 +722,8 @@ class PlaylistManager(models.Manager):
                                         'contentDetails'] else None,
                                     name=item['snippet']['title'],
                                     thumbnail_url=getThumbnailURL(item['snippet']['thumbnails']),
-                                    channel_id=item['snippet']['channelId'],
-                                    channel_name=item['snippet']['channelTitle'],
+                                    channel_id=item['snippet']['videoOwnerChannelId'],
+                                    channel_name=item['snippet']['videoOwnerChannelTitle'],
                                     video_position=item['snippet']['position'] + 1,
                                     playlist=playlist
                                 )
@@ -866,9 +876,11 @@ class PlaylistManager(models.Manager):
                             item['snippet']['description'] == "This video is unavailable.") or (
                                 item['snippet']['title'] == "Private video" and item['snippet'][
                             'description'] == "This video is private."):
+                            video.is_unavailable_on_yt = True
                             video.was_deleted_on_yt = True  # video went private on YouTube
                             video.video_details_modified = True
                             video.video_details_modified_at = datetime.datetime.now(tz=pytz.utc)
+
                             unavailable_videos.append(video)
 
                     video.save()
@@ -937,6 +949,7 @@ class PlaylistManager(models.Manager):
                                     item['snippet']['description'] == "This video is unavailable.") or (
                                         item['snippet']['title'] == "Private video" and item['snippet'][
                                     'description'] == "This video is private."):
+                                    video.is_unavailable_on_yt = True
                                     video.was_deleted_on_yt = True
                                     video.video_details_modified = True
                                     video.video_details_modified_at = datetime.datetime.now(tz=pytz.utc)
@@ -1017,8 +1030,8 @@ class Playlist(models.Model):
     has_unavailable_videos = models.BooleanField(default=False)  # if videos in playlist are private/deleted
 
     # playlist is made by this channel
-    channel_id = models.CharField(max_length=420, blank=True)
-    channel_name = models.CharField(max_length=420, blank=True)
+    channel_id = models.CharField(max_length=420, default="")
+    channel_name = models.CharField(max_length=420, default="")
 
     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
@@ -1087,9 +1100,17 @@ class Video(models.Model):
 
     # manage video
     is_duplicate = models.BooleanField(default=False)  # True if the same video exists more than once in the playlist
+
+    # NOTE: For a video in db:
+    # 1.) if both is_unavailable_on_yt and was_deleted_on_yt are true,
+    # that means the video was originally fine, but then went unavailable when updatePlaylist happened
+    # 2.) if only is_unavailable_on_yt is true and was_deleted_on_yt is false,
+    # then that means the video was an unavaiable video when initPlaylist was happening
+    # 3.) if both is_unavailable_on_yt and was_deleted_on_yt are false, the video is fine, ie up on Youtube
     is_unavailable_on_yt = models.BooleanField(
         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_marked_as_watched = models.BooleanField(default=False, blank=True)  # mark video as watched
     is_favorite = models.BooleanField(default=False, blank=True)  # mark video as favorite
     num_of_accesses = models.CharField(max_length=69,

+ 3 - 0
apps/main/static/youtube-audio-player/iframe-api.js

@@ -0,0 +1,3 @@
+var scriptUrl = 'https:\/\/www.youtube.com\/s\/player\/997fe684\/www-widgetapi.vflset\/www-widgetapi.js';try{var ttPolicy=window.trustedTypes.createPolicy("youtube-widget-api",{createScriptURL:function(x){return x}});scriptUrl=ttPolicy.createScriptURL(scriptUrl)}catch(e){}if(!window["YT"])var YT={loading:0,loaded:0};if(!window["YTConfig"])var YTConfig={"host":"https://www.youtube.com"};
+if(!YT.loading){YT.loading=1;(function(){var l=[];YT.ready=function(f){if(YT.loaded)f();else l.push(f)};window.onYTReady=function(){YT.loaded=1;for(var i=0;i<l.length;i++)try{l[i]()}catch(e$0){}};YT.setConfig=function(c){for(var k in c)if(c.hasOwnProperty(k))YTConfig[k]=c[k]};var a=document.createElement("script");a.type="text/javascript";a.id="www-widgetapi-script";a.src=scriptUrl;a.async=true;var c=document.currentScript;if(c){var n=c.nonce||c.getAttribute("nonce");if(n)a.setAttribute("nonce",n)}var b=
+document.getElementsByTagName("script")[0];b.parentNode.insertBefore(a,b)})()};

+ 6 - 0
apps/main/static/youtube-audio-player/yt.min.js

@@ -0,0 +1,6 @@
+/*
+ YouTube Audio Embed
+ Author: Amit Agarwal
+ Web: http://www.labnol.org/?p=26740
+*/
+function onYouTubeIframeAPIReady(){var e=document.getElementById("youtube-audio"),t=document.createElement("img");t.setAttribute("id","youtube-icon"),t.style.cssText="cursor:pointer;cursor:hand;width:40px;height:40px",e.appendChild(t);var a=document.createElement("div");a.setAttribute("id","youtube-player"),e.appendChild(a);var o=function(e){var a=e?"IDzX9gL.png":"quyUPXN.png";t.setAttribute("src","https://i.imgur.com/"+a)};e.onclick=function(){r.getPlayerState()===YT.PlayerState.PLAYING||r.getPlayerState()===YT.PlayerState.BUFFERING?(r.pauseVideo(),o(!1)):(r.playVideo(),o(!0))};var r=new YT.Player("youtube-player",{height:"0",width:"0",videoId:e.dataset.video,playerVars:{autoplay:e.dataset.autoplay,loop:e.dataset.loop},events:{onReady:function(e){r.setPlaybackQuality("small"),o(r.getPlayerState()!==YT.PlayerState.CUED)},onStateChange:function(e){e.data===YT.PlayerState.ENDED&&o(!1)}}})}

+ 46 - 35
apps/main/templates/intercooler/search_untube.html

@@ -1,21 +1,23 @@
 {% load humanize %}
 
-        <div class="bg-dark">
-                <div class="d-flex justify-content-center">
-                   <h1> Search all of UnTube                     <h6>Press <code>Esc</code> to close.</h6>
-</h1>
-                </div>
+        <div>
+            <div class="d-flex justify-content-center">
+               <h1> Search all of UnTube
+                   <h6>Press <code>Esc</code> to close.</h6>
+                </h1>
+            </div>
 
             <input class="form-control me-lg-2 bg-dark text-white" type="text"
-       name="search" placeholder="Search UnTube"
-                   value="{{ search_query }}"
-       hx-post="{% url 'search_UnTube' %}"
-       hx-trigger="keyup changed delay:1s"
-       hx-target="#untube-searchbar-results"
-                   hx-include="[id='searchbar-radio-form']"
-       hx-indicator=".htmx-indicator" autofocus onfocus="this.setSelectionRange(this.value.length,this.value.length);">
+                name="search" placeholder="Search UnTube"
+                value="{{ search_query }}"
+                hx-post="{% url 'search_UnTube' %}"
+                hx-trigger="keyup changed delay:750ms"
+                hx-target="#untube-searchbar-results"
+                hx-include="[id='searchbar-radio-form']"
+                hx-indicator=".htmx-indicator" autofocus onfocus="this.setSelectionRange(this.value.length,this.value.length);">
             <br>
-                          <div id="searchbar-radio-form">
+
+              <div id="searchbar-radio-form">
 
                   <div class="d-flex justify-content-center">
 
@@ -89,28 +91,37 @@
                 <div class="col">
                     <div class="card" style="background-color: #1A4464;">
 
-                            <div class="card-body">
-                                <h5 class="card-title text-light">
-                                    {{ video.name|truncatewords:"15" }}<br>
-
-                                    <small>
-                                        <a class="badge bg-white text-black-50" href="{% url 'playlist' video.playlist.playlist_id %}">{{ video.playlist.name }}</a>
-
-                                        <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" data-clipboard-target="#video-{{ video.video_id }}">
-                                        <i class="far fa-copy" aria-hidden="true"></i>
-                                    </button>
-                                </span>
-                            </div>
+                    <div class="card-body">
+                        <h5 class="card-title text-light">
+                            {{ video.name|truncatewords:"15" }}<br>
+
+                            <small>
+                                <a class="badge bg-white text-black-50" href="{% url 'playlist' video.playlist.playlist_id %}">{{ video.playlist.name }}</a>
+
+                                <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.playlist.playlist_id video.video_id %}" hx-target="#video-{{ forloop.counter }}-fav">
+                                <div id="video-{{ forloop.counter }}-fav">
+                                    {% if video.is_favorite %}
+                                        <i class="fas fa-heart"></i>
+                                    {% else %}
+                                        <i class="far fa-heart"></i>
+                                    {% endif %}
+                                </div>
+                            </button>
+                        </span>
+                    </div>
                     </div>
                 </div>
                 {% endfor %}

+ 13 - 5
apps/main/templates/intercooler/videos.html

@@ -1,12 +1,15 @@
 {% load humanize %}
-
-
+<br>
+{% if videos_details and videos.count != 0 %}
+<h5>{{ videos_details }}, Results: {{ videos.count }}</h5>
+<br>
+{% endif %}
 <div class="list-group" id="video-checkboxes">
     {% if videos %}
       {% for video in videos %}
                 <li class="list-group-item d-flex justify-content-between align-items-center" style="background-color: #40B3A2">
 
-            {% if video.is_unavailable_on_yt %}
+            {% if video.is_unavailable_on_yt and not video.was_deleted_on_yt %}
 
                 <div class="d-flex justify-content-between align-items-center">
                     <div>
@@ -32,13 +35,18 @@
                           <img src="{% if video.thumbnail_url %}{{ video.thumbnail_url }}{% else %}https://i.ytimg.com/vi/9219YrnwDXE/maxresdefault.jpg{% endif %}" class="img-fluid" alt="">
                       </div>
                         <div class="ms-4">
-                            <a class="link-dark" href="https://www.youtube.com/watch?v={{ video.video_id }}&list={{ video.playlist.playlist_id }}" target="_blank">{{ video.video_position }}. {{ video.name }}</a> <br>
+                            {{ video.video_position }}.
+                            <a class="link-dark" href="https://www.youtube.com/watch?v={{ video.videoid }}&list={{ video.playlist.playlist_id }}" target="_blank">
+                                {{ video.name|truncatewords:"16" }}
+                            </a> by {{ video.channel_name }}<br>
                             <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.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 %}
+                            {% if video.is_unavailable_on_yt and video.was_deleted_on_yt %}<span class="badge bg-dark">UNAVAILABLE</span>{% endif %}
+
                             {% if video.video_details_modified %}
                                 <span class="badge bg-danger">UPDATED - {% if video.was_deleted_on_yt %}WENT PRIVATE/DELETED{% else %}NEWLY ADDED{% endif %}</span>
                             {% endif %}<br>

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

@@ -0,0 +1,54 @@
+{% extends 'base.html' %}
+{% load humanize %}
+{% load static %}
+
+{% block content %}
+    <br>
+    <script>
+    window.onkeydown = function( event ) {
+        if ( event.keyCode === 27 ) {
+            window.close(); // closes current tab
+        }
+    };
+    </script>
+<div id="untube-searchbar-results">
+
+    <div class="d-flex justify-content-center">
+        <h1> Search all of UnTube
+            <h6>Press <code>Esc</code> to close.</h6>
+        </h1>
+    </div>
+<input class="form-control me-lg-2 bg-dark text-white" type="text"
+       id="untubeSearchBar"
+        name="search" placeholder="Search UnTube"
+        hx-post="{% url 'search_UnTube' %}"
+        hx-trigger="keyup changed delay:700ms"
+        hx-target="#untube-searchbar-results"
+        hx-include="[id='searchbar-radio-form']"
+        hx-indicator=".htmx-indicator" autofocus>
+
+<br>
+
+<div id="searchbar-radio-form">
+    <div class="d-flex justify-content-center">
+        <div class="form-check me-5">
+            <input class="form-check-input" type="radio" name="search-settings" value="starts-with" id="starts-with-cb" checked>
+            <label class="form-check-label" for="starts-with-cb">
+                    Starts with
+            </label>
+        </div>
+         <div class="form-check">
+            <input class="form-check-input" type="radio" name="search-settings" value="contains" id="contains-cb">
+                  <label class="form-check-label" for="contains-cb">
+                    Contains
+                  </label>
+        </div>
+    </div>
+</div>
+</div>
+<div id="spinner" class="htmx-indicator d-flex justify-content-center">
+                <div class="spinner-border text-light" role="status">
+                </div>
+                </div>
+
+{% endblock %}

+ 19 - 5
apps/main/templates/view_playlist.html

@@ -97,6 +97,8 @@
                             <li><button class="dropdown-item" hx-get="{% url 'order_playlist_by' playlist.playlist_id 'duration' %}" hx-trigger="click" hx-target="#videos-div">Duration</button></li>
                             <li><hr class="dropdown-divider"></li>
                             <li><button class="dropdown-item" hx-get="{% url 'order_playlist_by' playlist.playlist_id 'new-updates' %}" hx-trigger="click" hx-target="#videos-div">Updates</button></li>
+                            <li><button class="dropdown-item" hx-get="{% url 'order_playlist_by' playlist.playlist_id 'unavailable-videos' %}" hx-trigger="click" hx-target="#videos-div">Unavailable</button></li>
+
                         </ul>
                     </div>
 
@@ -262,14 +264,14 @@
             </div>
         </div>
 
-    <br>
     <div class="table-responsive" id="videos-div">
+        <br>
         <div class="list-group" id="video-checkboxes">
           {% for video in videos %}
 
             <li class="list-group-item d-flex justify-content-between align-items-center" style="background-color: #40B3A2">
 
-        {% if video.is_unavailable_on_yt %}
+        {% if video.is_unavailable_on_yt and not video.was_deleted_on_yt %}
 
             <div class="d-flex justify-content-between align-items-center">
                 <div>
@@ -292,18 +294,22 @@
                     <input class="video-checkboxes" style="display: none" type="checkbox" value="{{ video.playlist_item_id }}" name="video-id">
                 </div>
                   <div class="ms-4" style="max-width: 115px; max-height: 100px;">
+
+
                       <img src="{% if video.thumbnail_url %}{{ video.thumbnail_url }}{% else %}https://i.ytimg.com/vi/9219YrnwDXE/maxresdefault.jpg{% endif %}" class="img-fluid" alt="">
-                  </div>
+                   </div>
                     <div class="ms-4">
+                        {{ video.video_position }}.
                         <a class="link-dark" href="https://www.youtube.com/watch?v={{ video.videoid }}&list={{ video.playlist.playlist_id }}" target="_blank">
-                            {{ video.video_position }}. {{ video.name|truncatewords:"16" }}
-                        </a> <br>
+                            {{ video.name|truncatewords:"16" }}
+                        </a> by {{ video.channel_name }} <br>
                         <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 %}
+                        {% if video.is_unavailable_on_yt and video.was_deleted_on_yt %}<span class="badge bg-dark">UNAVAILABLE</span>{% endif %}
                         {% if video.video_details_modified %}<span class="badge bg-danger">UPDATED - {% if video.was_deleted_on_yt %}WENT PRIVATE/DELETED{% else %}NEWLY ADDED{% endif %}</span>{% endif %}<br>
                         <br>
                     </div>
@@ -342,5 +348,13 @@
     {% endif %}
         </div>
     </div>
+
+
+
     </div>
+
+    <!--
+      <script src="{% static 'youtube-audio-player/iframe-api.js' %}"></script>
+  <script src="{% static 'youtube-audio-player/yt.min.js' %}"></script>
+        -->
 {% endblock %}

+ 1 - 0
apps/main/urls.py

@@ -8,6 +8,7 @@ urlpatterns = [
     # path("login/", views.log_in, name='log_in'),
 
     ### STUFF RELATED TO WHOLE SITE
+    path("search", views.search, name="search"),
     path("search/UnTube/", views.search_UnTube, name="search_UnTube"),
 
     ### STUFF RELATED TO VIDEO(S)

+ 21 - 1
apps/main/views.py

@@ -151,25 +151,33 @@ def order_playlist_by(request, playlist_id, order_by):
     playlist = request.user.profile.playlists.get(Q(playlist_id=playlist_id) & Q(is_in_db=True))
 
     display_text = "Nothing in this playlist! Add something!"  # what to display when requested order/filter has no videws
+    videos_details = ""
 
     if order_by == "all":
         videos = playlist.videos.order_by("video_position")
     elif order_by == "favorites":
         videos = playlist.videos.filter(is_favorite=True).order_by("video_position")
+        videos_details = "Sorted by Favorites"
         display_text = "No favorites yet!"
     elif order_by == "popularity":
+        videos_details = "Sorted by Popularity"
         videos = playlist.videos.order_by("-like_count")
     elif order_by == "date-published":
+        videos_details = "Sorted by Date Published"
         videos = playlist.videos.order_by("-published_at")
     elif order_by == "views":
+        videos_details = "Sorted by View Count"
         videos = playlist.videos.order_by("-view_count")
     elif order_by == "has-cc":
+        videos_details = "Filtered by Has CC"
         videos = playlist.videos.filter(has_cc=True).order_by("video_position")
         display_text = "No videos in this playlist have CC :("
     elif order_by == "duration":
+        videos_details = "Sorted by Video Duration"
         videos = playlist.videos.order_by("-duration_in_seconds")
     elif order_by == 'new-updates':
         videos = []
+        videos_details = "Sorted by New Updates"
         display_text = "No new updates! Note that deleted videos will not show up here."
         if playlist.has_new_updates:
             recently_updated_videos = playlist.videos.filter(video_details_modified=True)
@@ -185,11 +193,16 @@ def order_playlist_by(request, playlist_id, order_by):
                 playlist.save()
             else:
                 videos = recently_updated_videos.order_by("video_position")
+    elif order_by == 'unavailable-videos':
+        videos = playlist.videos.filter(Q(is_unavailable_on_yt=True) & Q(was_deleted_on_yt=True))
+        videos_details = "Sorted by Unavailable Videos"
+        display_text = "None of the videos in this playlist have gone unavailable... yet."
     else:
         return redirect('home')
 
     return HttpResponse(loader.get_template("intercooler/videos.html").render({"playlist": playlist,
                                                                                "videos": videos,
+                                                                               "videos_details": videos_details,
                                                                                "display_text": display_text}))
 
 
@@ -343,6 +356,13 @@ def mark_video_favortie(request, playlist_id, video_id):
 
 
 ###########
+@login_required
+def search(request):
+    if request.method == "GET":
+        return render(request, 'search_untube_page.html')
+    else:
+        return render('home')
+
 
 @login_required
 @require_POST
@@ -373,7 +393,7 @@ def search_UnTube(request):
 
         if search_query != "":
             for playlist in all_playlists:
-                pl_videos = playlist.videos.filter(Q(name__contains=search_query) & Q(is_in_db=True))
+                pl_videos = playlist.videos.filter(name__contains=search_query)
 
                 if pl_videos.count() != 0:
                     for v in pl_videos.all():

+ 6 - 7
apps/users/views.py

@@ -66,9 +66,6 @@ def start_import(request):
     '''
     user_profile = request.user.profile
 
-    if user_profile.yt_channel_id == "":
-        Playlist.objects.getUserYTChannelID(request.user)
-
     if user_profile.access_token.strip() == "" or user_profile.refresh_token.strip() == "":
         user_social_token = SocialToken.objects.get(account__user=request.user)
         user_profile.access_token = user_social_token.token
@@ -92,6 +89,9 @@ def start_import(request):
 
         print("User has no playlists on YT")
 
+        if request.user.profile.yt_channel_id == "":
+            Playlist.objects.getUserYTChannelID(request.user)
+
         return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
             {"total_playlists": 0,
              "playlists_imported": 0,
@@ -99,6 +99,9 @@ def start_import(request):
              "progress": 100,
              "channel_found": channel_found}))
     else:
+        if request.user.profile.yt_channel_id == "":
+            Playlist.objects.getUserYTChannelID(request.user)
+
         return HttpResponse(loader.get_template('intercooler/progress_bar.html').render(
             {"total_playlists": result["num_of_playlists"],
              "playlist_name": result["first_playlist_name"],
@@ -110,10 +113,6 @@ def start_import(request):
 
 @login_required
 def settings(request):
-
-    if request.user.profile.yt_channel_id == "":
-        Playlist.objects.getUserYTChannelID(request.user)
-
     return render(request, 'settings.html')
 
 

+ 19 - 78
templates/base.html

@@ -22,13 +22,6 @@
 
         </style>
 
-          <!--
-          <script src="https://unpkg.com/htmx.org@1.4.1"></script>
-          <script src="https://unpkg.com/htmx.org/dist/ext/class-tools.js"></script>
-          <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.8/dist/clipboard.min.js"></script>
-
-          -->
-
         <link href="https://fonts.googleapis.com/css2?family=Fredoka+One&family=Open+Sans&display=swap" rel="stylesheet">
 
         <link href="{% static 'fontawesome-free-5.15.3-web/css/all.min.css' %}" rel="stylesheet">
@@ -80,7 +73,7 @@
                         <!--
                         <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 id="unTubeSearchBtn" class="nav-link btn-lg me-2" data-bs-toggle="modal" data-bs-target="#unTubeSearchBarResultsModal">
+                        <a class="nav-link btn-lg me-2" href="{% url 'search' %}" target="_blank">
                             <i class="fas fa-search"></i>
                         </a>
 
@@ -99,10 +92,13 @@
     {% endblock %}
 
 
+
         </main>
 
+
       </div>
 
+
     </div>
 
 
@@ -116,74 +112,25 @@
 
         <br>
 
-                    <!-- Button trigger modal -->
-
-        <!-- Modal -->
-        <div class="modal fade bg-dark" id="unTubeSearchBarResultsModal" tabindex="-1" aria-labelledby="unTubeSearchBarResultsModalLabel" aria-hidden="true">
-          <div class="modal-dialog modal-fullscreen modal-dialog-scrollable bg-dark">
-            <div class="modal-content bg-dark">
-              <div class="modal-body bg-dark">
-              <div id="untube-searchbar-results">
-
-                <div class="d-flex justify-content-center">
-                   <h1> Search all of UnTube                     <h6>Press <code>Esc</code> to close.</h6>
-</h1>
-                </div>
-                <input class="form-control me-lg-2 bg-dark text-white" id="unTubeSearchBarModal" type="text"
-                       name="search" placeholder="Search UnTube"
-                       hx-post="{% url 'search_UnTube' %}"
-                       hx-trigger="keyup changed delay:1s"
-                       hx-target="#untube-searchbar-results"
-                       hx-include="[id='searchbar-radio-form']"
-                       hx-indicator=".htmx-indicator">
-                  <br>
-                  <div id="searchbar-radio-form">
-                    <div class="d-flex justify-content-center">
-                     <div class="form-check me-5">
-                      <input class="form-check-input" type="radio" name="search-settings" value="starts-with" id="starts-with-cb" checked>
-                      <label class="form-check-label" for="starts-with-cb">
-                        Starts with
-                      </label>
-                    </div>
-                     <div class="form-check">
-                      <input class="form-check-input" type="radio" name="search-settings" value="contains" id="contains-cb">
-                      <label class="form-check-label" for="contains-cb">
-                        Contains
-                      </label>
-                    </div>
-                  </div>
-                  </div>
-                </div>
-                <div id="spinner" class="htmx-indicator d-flex justify-content-center">
-                <div class="spinner-border text-light" role="status">
-                </div>
-                </div>
-              </div>
-
-            </div>
-          </div>
-        </div>
 
+        <script src="{% static 'htmx/htmx.min.js' %}" type="application/javascript"></script>
+        <script src="{% static 'clipboard.js/clipboard.min.js' %}" type="application/javascript"></script>
+        <script src="{% static 'jquery3.6.0/js/jquery-3.6.0.min.js' %}" type="application/javascript"></script>
+        <script src="{% static 'bootstrap5.0.1/js/bootstrap.bundle.js' %}" type="application/javascript"></script>
+        <!-- <script src="{% static 'htmx/extensions/class-tools.js' %}" type="application/javascript"></script> -->
 
-        <script src="{% static 'htmx/htmx.min.js' %}"></script>
-        <script src="{% static 'clipboard.js/clipboard.min.js' %}"></script>
-        <script src="{% static 'jquery3.6.0/js/jquery-3.6.0.min.js' %}"></script>
-        <script src="{% static 'bootstrap5.0.1/js/bootstrap.bundle.js' %}"></script>
-        <script src="{% static 'htmx/extensions/class-tools.js' %}"></script>
 
-
-        <script>
+        <script type="application/javascript">
 
             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 clipboard = new ClipboardJS('.copy-btn');
-
-            <!-- for view_playlist.html -->
-            var moveCopyBtn = document.getElementById('move-copy-vids-btn');
+                 var moveCopyBtn = document.getElementById('move-copy-vids-btn');
             var moveCopyCollapse = document.getElementById('moveItemsToCollapse');
             var bsMoveCopyCollapse = new bootstrap.Collapse(moveCopyCollapse, {
                 toggle: false
@@ -218,17 +165,6 @@
             <!-- end -->
 
 
-             // Get the input field
-            var input = document.getElementById("unTubeSearchBar");
-
-            // Execute a function when the user releases a key on the keyboard
-            input.addEventListener("click", function(event) {
-
-                document.getElementById("unTubeSearchBtn").click();
-                document.getElementById("unTubeSearchBarModal").focus();
-
-            });
-
             document.getElementById('select-all-btn').onclick = function() {
                 document.getElementById('select-all-btn').style.display = "none";
                 document.getElementById('deselect-all-btn').style.display = "block";
@@ -275,6 +211,10 @@
               }
             }
 
+
+            var clipboard = new ClipboardJS('.copy-btn');
+
+
             //Get the button
             let mybutton = document.getElementById("btn-back-to-top");
 
@@ -303,6 +243,7 @@
 
         </script>
 
+
         <!--
         <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-gtEjrD/SeCtmISkJkNUaaKMoLD0//ElJ19smozuHV6z3Iehds+3Ulb9Bn9Plx0x4" crossorigin="anonymous"></script>
         <script src="https://cdn.jsdelivr.net/npm/feather-icons@4.28.0/dist/feather.min.js" integrity="sha384-uO3SXW5IuS1ZpFPKugNNWqTZRRglnUJK6UAZ/gxOX80nxEkN9NcGZTftn6RzhGWE" crossorigin="anonymous"></script>