diff --git a/README.md b/README.md index 9d4b259..1b80457 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ We have come far, nonetheless we are not short of ideas on how to improve and ex - [ ] Implement [Apprise](https://github.com/caronc/apprise) for notifications ([#97](https://github.com/bbilly1/tubearchivist/issues/97)) - [ ] Add [SponsorBlock](https://sponsor.ajay.app/) integration - [ ] Add passing browser cookies to yt-dlp ([#199](https://github.com/bbilly1/tubearchivist/issues/199)) -- [ ] User created playlists ([#108](https://github.com/bbilly1/tubearchivist/issues/108)) +- [ ] User created playlists, random and repeat controls ([#108](https://github.com/bbilly1/tubearchivist/issues/108), [#220](https://github.com/bbilly1/tubearchivist/issues/220)) - [ ] Auto play or play next link - [ ] Show similar videos on video page - [ ] Multi language support diff --git a/tubearchivist/api/README.md b/tubearchivist/api/README.md index 3c019cd..65cab1f 100644 --- a/tubearchivist/api/README.md +++ b/tubearchivist/api/README.md @@ -123,6 +123,9 @@ POST /api/channel/ ## Channel Item View /api/channel/\/ +## Playlist List View +/api/playlist/ + ## Playlists Item View /api/playlist/\/ diff --git a/tubearchivist/api/src/search_processor.py b/tubearchivist/api/src/search_processor.py index dc0a5db..0fc672b 100644 --- a/tubearchivist/api/src/search_processor.py +++ b/tubearchivist/api/src/search_processor.py @@ -40,6 +40,8 @@ class SearchProcess: processed = self._process_video(result["_source"]) if index == "ta_channel": processed = self._process_channel(result["_source"]) + if index == "ta_playlist": + processed = self._process_playlist(result["_source"]) return processed @@ -80,3 +82,19 @@ class SearchProcess: ) return dict(sorted(video_dict.items())) + + @staticmethod + def _process_playlist(playlist_dict): + """run on single playlist dict""" + playlist_id = playlist_dict["playlist_id"] + playlist_last_refresh = date_praser( + playlist_dict["playlist_last_refresh"] + ) + playlist_dict.update( + { + "playlist_thumbnail": f"/cache/playlists/{playlist_id}.jpg", + "playlist_last_refresh": playlist_last_refresh, + } + ) + + return dict(sorted(playlist_dict.items())) diff --git a/tubearchivist/api/urls.py b/tubearchivist/api/urls.py index 0cdf11a..cb70e4c 100644 --- a/tubearchivist/api/urls.py +++ b/tubearchivist/api/urls.py @@ -7,6 +7,7 @@ from api.views import ( DownloadApiView, LoginApiView, PingView, + PlaylistApiListView, PlaylistApiView, VideoApiListView, VideoApiView, @@ -53,6 +54,11 @@ urlpatterns = [ PlaylistApiView.as_view(), name="api-playlist", ), + path( + "playlist/", + PlaylistApiListView.as_view(), + name="api-playlist-list", + ), path( "download/", DownloadApiListView.as_view(), diff --git a/tubearchivist/api/views.py b/tubearchivist/api/views.py index 4ad216e..afe1c0b 100644 --- a/tubearchivist/api/views.py +++ b/tubearchivist/api/views.py @@ -258,6 +258,22 @@ class PlaylistApiView(ApiBaseView): return Response(self.response, status=self.status_code) +class PlaylistApiListView(ApiBaseView): + """resolves to /api/playlist/ + GET: returns list of indexed playlists + """ + + search_base = "ta_playlist/_search/" + + def get(self, request): + # pylint: disable=unused-argument + """handle get request""" + data = {"query": {"match_all": {}}} + self.get_document_list(data) + self.get_paginate() + return Response(self.response) + + class DownloadApiView(ApiBaseView): """resolves to /api/download// GET: returns metadata dict of an item in the download queue diff --git a/tubearchivist/home/src/frontend/forms.py b/tubearchivist/home/src/frontend/forms.py index 337a805..8709ff1 100644 --- a/tubearchivist/home/src/frontend/forms.py +++ b/tubearchivist/home/src/frontend/forms.py @@ -200,7 +200,7 @@ class ChannelOverwriteForm(forms.Form): SP_CHOICES = [ ("", "-- change sponsorblock integrations"), - ("0", "disable sponsorblock integration"), + ("disable", "disable sponsorblock integration"), ("1", "enable sponsorblock integration"), ] diff --git a/tubearchivist/home/src/index/channel.py b/tubearchivist/home/src/index/channel.py index 75824d8..06d0086 100644 --- a/tubearchivist/home/src/index/channel.py +++ b/tubearchivist/home/src/index/channel.py @@ -351,6 +351,9 @@ class YoutubeChannel(YouTubeItem): for key, value in overwrites.items(): if key not in valid_keys: raise ValueError(f"invalid overwrite key: {key}") + if value == "disable": + to_write[key] = False + continue if value in [0, "0"]: del to_write[key] continue diff --git a/tubearchivist/home/src/index/video.py b/tubearchivist/home/src/index/video.py index a5d8a42..8866118 100644 --- a/tubearchivist/home/src/index/video.py +++ b/tubearchivist/home/src/index/video.py @@ -412,16 +412,18 @@ class YoutubeVideo(YouTubeItem, YoutubeSubtitle): def _check_get_sb(self): """check if need to run sponsor block""" + integrate = False if self.config["downloads"]["integrate_sponsorblock"]: - return True - try: - single_overwrite = self.video_overwrites[self.youtube_id] - _ = single_overwrite["integrate_sponsorblock"] - return True - except KeyError: - return False + integrate = True - return False + if self.video_overwrites: + single_overwrite = self.video_overwrites.get(self.youtube_id) + if not single_overwrite: + return integrate + + integrate = single_overwrite.get("integrate_sponsorblock", False) + + return integrate def _process_youtube_meta(self): """extract relevant fields from youtube""" diff --git a/tubearchivist/home/templates/home/settings.html b/tubearchivist/home/templates/home/settings.html index 4fba379..58e69cb 100644 --- a/tubearchivist/home/templates/home/settings.html +++ b/tubearchivist/home/templates/home/settings.html @@ -48,7 +48,7 @@

Current download speed limit in KB/s: {{ config.downloads.limit_speed }}

- Limit download speed. 0 (zero) to deactivate, e.g. 1000 (1MB/s). Speeds are in KB/s.
+ Limit download speed. 0 (zero) to deactivate, e.g. 1000 (1MB/s). Speeds are in KB/s. Setting takes effect on new download jobs or application restart.
{{ app_form.downloads_limit_speed }}
diff --git a/tubearchivist/home/templates/home/video.html b/tubearchivist/home/templates/home/video.html index d1ff3a7..3848f16 100644 --- a/tubearchivist/home/templates/home/video.html +++ b/tubearchivist/home/templates/home/video.html @@ -5,31 +5,11 @@
- {% if video.channel.channel_overwrites.integrate_sponsorblock %} - {% if video.channel.channel_overwrites.integrate_sponsorblock == True %} - {% if not video.sponsorblock %} -

This video doesn't have any sponsor segments added. To add a segment go to this video on YouTube and add a segment using the SponsorBlock extension.

- {% endif %} - {% if video.sponsorblock %} - {% for segment in video.sponsorblock %} - {% if segment.locked != 1 %} -

This video has unlocked sponsor segments. Go to this video on YouTube and vote on the segments using the SponsorBlock extension.

- {{ break }} - {% endif %} - {% endfor %} - {% endif %} - {% endif %} - {% elif config.downloads.integrate_sponsorblock %} - {% if not video.sponsorblock %} + {% if video.sponsorblock.is_enabled %} + {% if video.sponsorblock.segments|length == 0 %}

This video doesn't have any sponsor segments added. To add a segment go to this video on YouTube and add a segment using the SponsorBlock extension.

- {% endif %} - {% if video.sponsorblock %} - {% for segment in video.sponsorblock %} - {% if segment.locked != 1 %} -

This video has unlocked sponsor segments. Go to this video on YouTube and vote on the segments using the SponsorBlock extension.

- {{ break }} - {% endif %} - {% endfor %} + {% elif video.sponsorblock.has_unlocked %} +

This video has unlocked sponsor segments. Go to this video on YouTube and vote on the segments using the SponsorBlock extension.

{% endif %} {% endif %}
diff --git a/tubearchivist/static/script.js b/tubearchivist/static/script.js index ee95b6f..4031ec0 100644 --- a/tubearchivist/static/script.js +++ b/tubearchivist/static/script.js @@ -333,24 +333,21 @@ function createPlayer(button) { var videoData = getVideoData(videoId); var sponsorBlockElements = ''; - if (videoData.config.downloads.integrate_sponsorblock && (typeof(videoData.data.channel.channel_overwrites) == "undefined" || typeof(videoData.data.channel.channel_overwrites.integrate_sponsorblock) == "undefined" || videoData.data.channel.channel_overwrites.integrate_sponsorblock == true)) { + if (videoData.data.sponsorblock.is_enabled) { sponsorBlock = videoData.data.sponsorblock; - if (!sponsorBlock) { + if (sponsorBlock.segments.length == 0) { sponsorBlockElements = `

This video doesn't have any sponsor segments added. To add a segment go to this video on Youtube and add a segment using the SponsorBlock extension.

`; } else { - for(let i in sponsorBlock) { - if(sponsorBlock[i].locked != 1) { - sponsorBlockElements = ` -
-

This video has unlocked sponsor segments. Go to this video on YouTube and vote on the segments using the SponsorBlock extension.

-
- `; - break; - } + if (sponsorBlock.has_unlocked) { + sponsorBlockElements = ` +
+

This video has unlocked sponsor segments. Go to this video on YouTube and vote on the segments using the SponsorBlock extension.

+
+ `; } } } @@ -425,53 +422,6 @@ function createPlayer(button) { divPlayer.innerHTML = markup; } -// function sendSponsorBlockVote(uuid, vote) { -// var videoId = getVideoPlayerVideoId(); -// postSponsorSegmentVote(videoId, uuid, vote); -// } - -// var sponsorBlockTimestamps = []; -// function sendSponsorBlockSegment() { -// var videoId = getVideoPlayerVideoId(); -// var currentTime = getVideoPlayerCurrentTime(); -// var sponsorBlockElement = document.getElementById("sponsorblock"); -// if (sponsorBlockTimestamps[1]) { -// if (sponsorBlockTimestamps[1] > sponsorBlockTimestamps[0]) { -// postSponsorSegment(videoId, sponsorBlockTimestamps[0], sponsorBlockTimestamps[1]); -// sponsorBlockElement.innerHTML = ` -//

Timestamps sent! (Not really)

-// `; -// setTimeout(function(){ -// sponsorBlockElement.innerHTML = ` -// -// `; -// }, 3000); -// } else { -// sponsorBlockElement.innerHTML = ` -// Invalid Timestamps! -// `; -// setTimeout(function(){ -// sponsorBlockElement.innerHTML = ` -// -// `; -// }, 3000); -// } -// sponsorBlockTimestamps = []; -// } else if (sponsorBlockTimestamps[0]) { -// sponsorBlockTimestamps.push(currentTime); -// sponsorBlockElement.innerHTML = ` -//

${sponsorBlockTimestamps[0].toFixed(1)} s |

-//

${sponsorBlockTimestamps[1].toFixed(1)} s |

-// -// `; -// } else { -// sponsorBlockTimestamps.push(currentTime); -// sponsorBlockElement.innerHTML = ` -// -// `; -// } -// } - // Add video tag to video page when passed a video id, function loaded on page load `video.html (115-117)` function insertVideoTag(videoData, videoProgress) { var videoTag = createVideoTag(videoData, videoProgress); @@ -563,23 +513,14 @@ function onVideoProgress() { var videoElement = getVideoPlayer(); // var sponsorBlockElement = document.getElementById("sponsorblock"); var notificationsElement = document.getElementById("notifications"); - if (sponsorBlock) { - for(let i in sponsorBlock) { - if(sponsorBlock[i].segment[0] <= currentTime + 0.3 && sponsorBlock[i].segment[0] >= currentTime) { - videoElement.currentTime = sponsorBlock[i].segment[1]; - notificationsElement.innerHTML += `

Skipped sponsor segment from ${formatTime(sponsorBlock[i].segment[0])} to ${formatTime(sponsorBlock[i].segment[1])}.

`; + if (sponsorBlock.segments.length > 0) { + for(let i in sponsorBlock.segments) { + if(sponsorBlock.segments[i].segment[0] <= currentTime + 0.3 && sponsorBlock.segments[i].segment[0] >= currentTime) { + videoElement.currentTime = sponsorBlock.segments[i].segment[1]; + notificationsElement.innerHTML += `

Skipped sponsor segment from ${formatTime(sponsorBlock.segments[i].segment[0])} to ${formatTime(sponsorBlock.segments[i].segment[1])}.

`; } - // if(currentTime >= sponsorBlock[i].segment[1] && currentTime <= sponsorBlock[i].segment[1] + 0.2) { - // if(sponsorBlock[i].locked != 1) { - // sponsorBlockElement.innerHTML += ` - //
- // - // - //
`; - // } - // } - if(currentTime > sponsorBlock[i].segment[1] + 10) { - var notificationsElementUUID = document.getElementById("notification-" + sponsorBlock[i].UUID); + if(currentTime > sponsorBlock.segments[i].segment[1] + 10) { + var notificationsElementUUID = document.getElementById("notification-" + sponsorBlock.segments[i].UUID); if(notificationsElementUUID) { notificationsElementUUID.outerHTML = ''; } @@ -602,6 +543,12 @@ function onVideoEnded() { if (!getVideoPlayerWatchStatus()) { // Check if video is already marked as watched updateVideoWatchStatus(videoId, "unwatched"); } + for(let i in sponsorBlock.segments) { + var notificationsElementUUID = document.getElementById("notification-" + sponsorBlock.segments[i].UUID); + if(notificationsElementUUID) { + notificationsElementUUID.outerHTML = ''; + } + } } function watchedThreshold(currentTime, duration) {