diff --git a/tubearchivist/api/views.py b/tubearchivist/api/views.py index 5a71522..cfa1875 100644 --- a/tubearchivist/api/views.py +++ b/tubearchivist/api/views.py @@ -123,7 +123,7 @@ class VideoProgressView(ApiBaseView): """set progress position in redis""" position = request.data.get("position", 0) key = f"{request.user.id}:progress:{video_id}" - message = {"position": position} + message = {"position": position, "youtube_id": video_id} RedisArchivist().set_message(key, message, expire=False) self.response = request.data diff --git a/tubearchivist/home/src/download/subscriptions.py b/tubearchivist/home/src/download/subscriptions.py index 2e4a29f..84627b6 100644 --- a/tubearchivist/home/src/download/subscriptions.py +++ b/tubearchivist/home/src/download/subscriptions.py @@ -48,7 +48,13 @@ class ChannelSubscription: } if limit: obs["playlistend"] = self.channel_size - chan = yt_dlp.YoutubeDL(obs).extract_info(url, download=False) + + try: + chan = yt_dlp.YoutubeDL(obs).extract_info(url, download=False) + except yt_dlp.utils.DownloadError: + print(f"{channel_id}: failed to extract videos, skipping.") + return False + last_videos = [(i["id"], i["title"]) for i in chan["entries"]] return last_videos @@ -66,9 +72,11 @@ class ChannelSubscription: for idx, channel in enumerate(all_channels): channel_id = channel["channel_id"] last_videos = self.get_last_youtube_videos(channel_id) - for video in last_videos: - if video[0] not in to_ignore: - missing_videos.append(video[0]) + + if last_videos: + for video in last_videos: + if video[0] not in to_ignore: + missing_videos.append(video[0]) # notify message = { "status": "message:rescan", diff --git a/tubearchivist/home/src/ta/ta_redis.py b/tubearchivist/home/src/ta/ta_redis.py index d131c96..6a9efea 100644 --- a/tubearchivist/home/src/ta/ta_redis.py +++ b/tubearchivist/home/src/ta/ta_redis.py @@ -59,6 +59,19 @@ class RedisArchivist: return json_str + def list_items(self, query): + """list all matches""" + reply = self.redis_connection.execute_command( + "KEYS", self.NAME_SPACE + query + "*" + ) + all_matches = [i.decode().lstrip(self.NAME_SPACE) for i in reply] + all_results = [] + for match in all_matches: + json_str = self.get_message(match) + all_results.append(json_str) + + return all_results + def del_message(self, key): """delete key from redis""" response = self.redis_connection.execute_command( diff --git a/tubearchivist/home/templates/home/base.html b/tubearchivist/home/templates/home/base.html index 9824e3f..5516282 100644 --- a/tubearchivist/home/templates/home/base.html +++ b/tubearchivist/home/templates/home/base.html @@ -132,7 +132,7 @@ diff --git a/tubearchivist/home/templates/home/channel_id.html b/tubearchivist/home/templates/home/channel_id.html index 6a42c10..41ee11e 100644 --- a/tubearchivist/home/templates/home/channel_id.html +++ b/tubearchivist/home/templates/home/channel_id.html @@ -110,6 +110,11 @@
video-thumb + {% if video.source.player.progress %} +
+ {% else %} +
+ {% endif %}
play-icon diff --git a/tubearchivist/home/templates/home/home.html b/tubearchivist/home/templates/home/home.html index 62d2e9a..d24263a 100644 --- a/tubearchivist/home/templates/home/home.html +++ b/tubearchivist/home/templates/home/home.html @@ -49,6 +49,11 @@
video-thumb + {% if video.source.player.progress %} +
+ {% else %} +
+ {% endif %}
play-icon diff --git a/tubearchivist/home/templates/home/playlist_id.html b/tubearchivist/home/templates/home/playlist_id.html index 41d8268..687df98 100644 --- a/tubearchivist/home/templates/home/playlist_id.html +++ b/tubearchivist/home/templates/home/playlist_id.html @@ -91,6 +91,11 @@
video-thumb + {% if video.source.player.progress %} +
+ {% else %} +
+ {% endif %}
play-icon diff --git a/tubearchivist/home/templates/home/video.html b/tubearchivist/home/templates/home/video.html index 90e986d..7a8b522 100644 --- a/tubearchivist/home/templates/home/video.html +++ b/tubearchivist/home/templates/home/video.html @@ -113,6 +113,8 @@ {% endif %}
{% endblock content %} diff --git a/tubearchivist/home/views.py b/tubearchivist/home/views.py index 660ec73..dd87ac0 100644 --- a/tubearchivist/home/views.py +++ b/tubearchivist/home/views.py @@ -169,6 +169,20 @@ class ArchivistResultsView(ArchivistViewConfig): } self.data = data + def match_progress(self): + """add video progress to result context""" + results = RedisArchivist().list_items(f"{self.user_id}:progress:") + if not results or not self.context["results"]: + return + + progress = {i["youtube_id"]: i["position"] for i in results} + for hit in self.context["results"]: + video = hit["source"] + if video["youtube_id"] in progress: + played_sec = progress.get(video["youtube_id"]) + total = video["player"]["duration"] + video["player"]["progress"] = 100 * (played_sec / total) + def single_lookup(self, es_path): """retrieve a single item from url""" search = SearchHandler(es_path, config=self.default_conf) @@ -212,6 +226,7 @@ class HomeView(ArchivistResultsView): self.initiate_vars(request) self._update_view_data() self.find_results() + self.match_progress() return render(request, "home/home.html", self.context) @@ -355,6 +370,7 @@ class ChannelIdView(ArchivistResultsView): self.initiate_vars(request) self._update_view_data(channel_id) self.find_results() + self.match_progress() if self.context["results"]: channel_info = self.context["results"][0]["source"]["channel"] @@ -456,6 +472,7 @@ class PlaylistIdView(ArchivistResultsView): playlist_name = playlist_info["playlist_name"] self._update_view_data(playlist_id, playlist_info) self.find_results() + self.match_progress() self.context.update( { "title": "Playlist: " + playlist_name, diff --git a/tubearchivist/static/cast-videos.js b/tubearchivist/static/cast-videos.js index 0a30af4..867093e 100644 --- a/tubearchivist/static/cast-videos.js +++ b/tubearchivist/static/cast-videos.js @@ -43,6 +43,7 @@ function castVideoProgress(player) { var duration = player.duration; if ((currentTime % 10) <= 1.0 && currentTime != 0 && duration != 0) { // Check progress every 10 seconds or else progress is checked a few times a second postVideoProgress(videoId, currentTime); + setProgressBar(videoId, currentTime, duration); if (!getVideoPlayerWatchStatus()) { // Check if video is already marked as watched if (watchedThreshold(currentTime, duration)) { isWatched(videoId); diff --git a/tubearchivist/static/css/style.css b/tubearchivist/static/css/style.css index 53a8882..22e1b2e 100644 --- a/tubearchivist/static/css/style.css +++ b/tubearchivist/static/css/style.css @@ -391,8 +391,17 @@ button:hover { grid-template-columns: 25% auto; } +.video-progress-bar { + position: absolute; + background-color: var(--accent-font-dark); + height: 7px; + left: 0; + bottom: 3px; +} + .video-thumb img { width: 100%; + position: relative; } .video-play img { diff --git a/tubearchivist/static/script.js b/tubearchivist/static/script.js index 162e5ab..8df9ffd 100644 --- a/tubearchivist/static/script.js +++ b/tubearchivist/static/script.js @@ -10,6 +10,7 @@ function sortChange(sortValue) { function isWatched(youtube_id) { postVideoProgress(youtube_id, 0); // Reset video progress on watched; + removeProgressBar(youtube_id); var payload = JSON.stringify({'watched': youtube_id}); sendPost(payload); var seenIcon = document.createElement('img'); @@ -22,6 +23,11 @@ function isWatched(youtube_id) { document.getElementById(youtube_id).replaceWith(seenIcon); } +// Removes the progress bar when passed a video id +function removeProgressBar(videoId) { + setProgressBar(videoId, 0, 1); +} + function isWatchedButton(button) { youtube_id = button.getAttribute("data-id"); var payload = JSON.stringify({'watched': youtube_id}); @@ -298,9 +304,10 @@ function cancelDelete() { function createPlayer(button) { var videoId = button.getAttribute('data-id'); var videoData = getVideoData(videoId); + var videoProgress = getVideoProgress(videoId).position; var videoName = videoData.data.title; - var videoTag = createVideoTag(videoId); + var videoTag = createVideoTag(videoData, videoProgress); var playlist = ''; var videoPlaylists = videoData.data.playlist; // Array of playlists the video is in @@ -369,19 +376,17 @@ function createPlayer(button) { } // Add video tag to video page when passed a video id, function loaded on page load `video.html (115-117)` -function insertVideoTag(videoId) { - var videoTag = createVideoTag(videoId); +function insertVideoTag(videoData, videoProgress) { + var videoTag = createVideoTag(videoData, videoProgress); var videoMain = document.getElementsByClassName("video-main"); videoMain[0].innerHTML = videoTag; } -// Generates a video tag with subtitles when passed a video id. -function createVideoTag(videoId) { - var videoData = getVideoData(videoId); - var videoProgress = getVideoProgress(videoId).position; +// Generates a video tag with subtitles when passed videoData and videoProgress. +function createVideoTag(videoData, videoProgress) { + var videoId = videoData.data.youtube_id; var videoUrl = videoData.data.media_url; var videoThumbUrl = videoData.data.vid_thumb_url; - var subtitles = ''; var videoSubtitles = videoData.data.subtitles; // Array of subtitles if (typeof(videoSubtitles) != 'undefined' && videoData.config.downloads.subtitle) { @@ -391,7 +396,7 @@ function createVideoTag(videoId) { } var videoTag = ` -