aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Ángel Moreno <mail@migalmoreno.com>2024-12-03 11:15:42 +0100
committerMiguel Ángel Moreno <mail@migalmoreno.com>2024-12-03 11:15:42 +0100
commiteddc9acc1d92b1326ef269d3743400f62f027fee (patch)
tree3d0ce6a9a40c068180ec2569c9e0629f1c919c6d
parenta5f7e628570477c7f4b2b69fc56e10dd9deb9a3b (diff)
feat(backend): add channel tabs support and adapt NPE changes
-rw-r--r--src/backend/tubo/api.clj208
-rw-r--r--src/backend/tubo/handler.clj4
-rw-r--r--src/backend/tubo/routes.clj5
3 files changed, 134 insertions, 83 deletions
diff --git a/src/backend/tubo/api.clj b/src/backend/tubo/api.clj
index d0d33e2..8fc3aa0 100644
--- a/src/backend/tubo/api.clj
+++ b/src/backend/tubo/api.clj
@@ -4,13 +4,20 @@
[ring.util.codec :refer [url-decode]])
(:import
org.schabi.newpipe.extractor.channel.ChannelInfo
+ org.schabi.newpipe.extractor.channel.tabs.ChannelTabInfo
+ org.schabi.newpipe.extractor.channel.tabs.ChannelTabs
org.schabi.newpipe.extractor.comments.CommentsInfo
org.schabi.newpipe.extractor.kiosk.KioskInfo
org.schabi.newpipe.extractor.playlist.PlaylistInfo
org.schabi.newpipe.extractor.search.SearchInfo
org.schabi.newpipe.extractor.stream.StreamInfo
org.schabi.newpipe.extractor.NewPipe
- org.schabi.newpipe.extractor.Page))
+ org.schabi.newpipe.extractor.Page
+ org.schabi.newpipe.extractor.linkhandler.ReadyChannelTabListLinkHandler))
+
+(defn non-negative
+ [val]
+ (when-not (= val -1) val))
(defn get-stream-item
[stream]
@@ -18,15 +25,14 @@
:service-id (.getServiceId stream)
:url (.getUrl stream)
:name (.getName stream)
- :thumbnail-url (.getThumbnailUrl stream)
+ :thumbnails (j/from-java (.getThumbnails stream))
:uploader-name (.getUploaderName stream)
:uploader-url (.getUploaderUrl stream)
- :uploader-avatar (.getUploaderAvatarUrl stream)
+ :uploader-avatars (j/from-java (.getUploaderAvatars stream))
:upload-date (.getTextualUploadDate stream)
:short-description (.getShortDescription stream)
:duration (.getDuration stream)
- :view-count (when-not (= (.getViewCount stream) -1)
- (.getViewCount stream))
+ :view-count (non-negative (.getViewCount stream))
:uploaded (when (.getUploadDate stream)
(.. stream
(getUploadDate)
@@ -41,12 +47,10 @@
:service-id (.getServiceId channel)
:url (.getUrl channel)
:name (.getName channel)
- :thumbnail-url (.getThumbnailUrl channel)
+ :thumbnails (j/from-java (.getThumbnails channel))
:description (.getDescription channel)
- :subscriber-count (when-not (= (.getSubscriberCount channel) -1)
- (.getSubscriberCount channel))
- :stream-count (when-not (= (.getStreamCount channel) -1)
- (.getStreamCount channel))
+ :subscriber-count (non-negative (.getSubscriberCount channel))
+ :stream-count (non-negative (.getStreamCount channel))
:verified? (.isVerified channel)})
(defn get-playlist-item
@@ -55,10 +59,9 @@
:service-id (.getServiceId playlist)
:url (.getUrl playlist)
:name (.getName playlist)
- :thumbnail-url (.getThumbnailUrl playlist)
+ :thumbnails (j/from-java (.getThumbnails playlist))
:uploader-name (.getUploaderName playlist)
- :stream-count (when-not (= (.getStreamCount playlist) -1)
- (.getStreamCount playlist))})
+ :stream-count (non-negative (.getStreamCount playlist))})
(defn get-items
[items]
@@ -68,82 +71,126 @@
"PLAYLIST" (get-playlist-item %))
items))
-(defn get-common-info
- [info]
- {:name (.getName info)
- :service-id (.getServiceId info)
- :related-streams (get-items (.getRelatedItems info))})
-
(defn get-channel
([url]
- (let [info (ChannelInfo/getInfo (url-decode url))]
- (merge (get-common-info info)
- {:id (.getId info)
- :verified? (.isVerified info)
- :banner (.getBannerUrl info)
- :description (.getDescription info)
- :avatar (.getAvatarUrl info)
- :subscriber-count (when-not (= (.getSubscriberCount info) -1)
- (.getSubscriberCount info))
- :donation-links (.getDonationLinks info)
- :next-page (j/from-java (.getNextPage info))})))
+ (let [info (ChannelInfo/getInfo (url-decode url))
+ service (NewPipe/getServiceByUrl (url-decode url))
+ preloaded-videos-tab (->> (.getTabs info)
+ (filter #(instance?
+ ReadyChannelTabListLinkHandler
+ %))
+ (filter #(some #{ChannelTabs/VIDEOS}
+ (.getContentFilters %)))
+ first)
+ tab-info (ChannelTabInfo/getInfo
+ service
+ (or preloaded-videos-tab
+ (first (.getTabs info))))]
+ {:name (.getName info)
+ :service-id (.getServiceId info)
+ :id (.getId info)
+ :tabs (j/from-java (.getTabs info))
+ :verified? (.isVerified info)
+ :banners (j/from-java (.getBanners info))
+ :description (.getDescription info)
+ :avatars (j/from-java (.getAvatars info))
+ :subscriber-count (non-negative (.getSubscriberCount info))
+ :donation-links (.getDonationLinks info)
+ :next-page (j/from-java (.getNextPage tab-info))
+ :related-streams (when tab-info
+ (get-items (.getRelatedItems tab-info)))}))
([url page-url]
- (let [service (NewPipe/getServiceByUrl (url-decode url))
- info (ChannelInfo/getMoreItems service
- (url-decode url)
- (Page. (url-decode page-url)))]
+ (let [channel-info (ChannelInfo/getInfo (url-decode url))
+ service (NewPipe/getServiceByUrl (url-decode url))
+ preloaded-videos-tab (->> (.getTabs channel-info)
+ (filter #(instance?
+ ReadyChannelTabListLinkHandler
+ %))
+ (filter #(some #{ChannelTabs/VIDEOS}
+ (.getContentFilters %)))
+ first)
+ tab-info (ChannelTabInfo/getMoreItems service
+ preloaded-videos-tab
+ (Page. (url-decode
+ page-url)))]
+ {:related-streams (get-items (.getRelatedItems tab-info))
+ :next-page (j/from-java (.getNextPage tab-info))})))
+
+(defn get-channel-tab
+ ([url tabId]
+ (let [service (NewPipe/getServiceByUrl (url-decode url))
+ channel-info (ChannelInfo/getInfo (url-decode url))
+ tab (if (= tabId "default")
+ (first (.getTabs channel-info))
+ (->> (.getTabs channel-info)
+ (filter #(some #{tabId} (.getContentFilters %)))
+ first))
+ info (ChannelTabInfo/getInfo service tab)]
+ {:related-streams (get-items (.getRelatedItems info))
+ :next-page (j/from-java (.getNextPage info))}))
+ ([url tabId page-url]
+ (let [service (NewPipe/getServiceByUrl (url-decode url))
+ channel-info (ChannelInfo/getInfo (url-decode url))
+ tab (if (= tabId "default")
+ (first (.getTabs channel-info))
+ (->> (.getTabs channel-info)
+ (filter #(some #{tabId} (.getContentFilters %)))
+ first))
+ info (ChannelTabInfo/getMoreItems service
+ tab
+ (Page. (url-decode
+ page-url)))]
{:related-streams (get-items (.getItems info))
:next-page (j/from-java (.getNextPage info))})))
(defn get-stream
[url]
(let [info (StreamInfo/getInfo (url-decode url))]
- (merge (get-common-info info)
- {:url (.getUrl info)
- :thumbnail-url (.getThumbnailUrl info)
- :description (.. info (getDescription) (getContent))
- :duration (.getDuration info)
- :upload-date (.getTextualUploadDate info)
- :uploader-name (.getUploaderName info)
- :uploader-url (.getUploaderUrl info)
- :uploader-avatar (.getUploaderAvatarUrl info)
- :uploader-verified? (.isUploaderVerified info)
- :tags (.getTags info)
- :category (.getCategory info)
- :view-count (when-not (= (.getViewCount info) -1)
- (.getViewCount info))
- :like-count (when-not (= (.getLikeCount info) -1)
- (.getLikeCount info))
- :dislike-count (when-not (= (.getDislikeCount info) -1)
- (.getDislikeCount info))
- :subscriber-count (when-not (= (.getUploaderSubscriberCount info)
- -1)
- (.getUploaderSubscriberCount info))
- :audio-streams (j/from-java (.getAudioStreams info))
- :video-streams (j/from-java (.getVideoStreams info))
- :hls-url (.getHlsUrl info)
- :dash-mpd-url (.getDashMpdUrl info)
- :preview-frames (.getPreviewFrames info)
- :stream-segments (.getStreamSegments info)
- :support-info (.getSupportInfo info)
- :short? (.isShortFormContent info)
- :license (.getLicence info)
- :subtitles (.getSubtitles info)})))
+ {:name (.getName info)
+ :service-id (.getServiceId info)
+ :related-streams (get-items (.getRelatedItems info))
+ :url (.getUrl info)
+ :thumbnails (j/from-java (.getThumbnails info))
+ :description (.. info (getDescription) (getContent))
+ :duration (.getDuration info)
+ :upload-date (.getTextualUploadDate info)
+ :uploader-name (.getUploaderName info)
+ :uploader-url (.getUploaderUrl info)
+ :uploader-avatars (j/from-java (.getUploaderAvatars info))
+ :uploader-verified? (.isUploaderVerified info)
+ :tags (.getTags info)
+ :category (.getCategory info)
+ :view-count (non-negative (.getViewCount info))
+ :like-count (non-negative (.getLikeCount info))
+ :dislike-count (non-negative (.getDislikeCount info))
+ :subscriber-count (non-negative (.getUploaderSubscriberCount info))
+ :audio-streams (j/from-java (.getAudioStreams info))
+ :video-streams (j/from-java (.getVideoStreams info))
+ :hls-url (.getHlsUrl info)
+ :dash-mpd-url (.getDashMpdUrl info)
+ :preview-frames (j/from-java (.getPreviewFrames info))
+ :stream-segments (.getStreamSegments info)
+ :support-info (.getSupportInfo info)
+ :short? (.isShortFormContent info)
+ :license (.getLicence info)
+ :subtitles (j/from-java (.getSubtitles info))}))
(defn get-playlist
([url]
(let [service (NewPipe/getServiceByUrl (url-decode url))
info (PlaylistInfo/getInfo service (url-decode url))]
- (merge (get-common-info info)
- {:id (.getId info)
- :playlist-type (j/from-java (.getPlaylistType info))
- :thumbnail-url (.getThumbnailUrl info)
- :banner-url (.getBannerUrl info)
- :uploader-name (.getUploaderName info)
- :uploader-url (.getUploaderUrl info)
- :uploader-avatar (.getUploaderAvatarUrl info)
- :stream-count (.getStreamCount info)
- :next-page (j/from-java (.getNextPage info))})))
+ {:name (.getName info)
+ :service-id (.getServiceId info)
+ :related-streams (get-items (.getRelatedItems info))
+ :id (.getId info)
+ :playlist-type (j/from-java (.getPlaylistType info))
+ :thumbnails (j/from-java (.getThumbnails info))
+ :banners (j/from-java (.getBanners info))
+ :uploader-name (.getUploaderName info)
+ :uploader-url (.getUploaderUrl info)
+ :uploader-avatars (j/from-java (.getUploaderAvatars info))
+ :stream-count (.getStreamCount info)
+ :next-page (j/from-java (.getNextPage info))}))
([url page-url]
(let [service (NewPipe/getServiceByUrl (url-decode url))
info
@@ -158,16 +205,13 @@
:upload-date (.getTextualUploadDate item)
:uploader-name (.getUploaderName item)
:uploader-url (.getUploaderUrl item)
- :uploader-avatar (.getUploaderAvatarUrl item)
+ :uploader-avatars (j/from-java (.getUploaderAvatars item))
:uploader-verified? (.isUploaderVerified item)
- :like-count (when-not (= (.getLikeCount item) -1)
- (.getLikeCount item))
- :reply-count (when-not (= (.getReplyCount item) -1)
- (.getReplyCount item))
+ :like-count (non-negative (.getLikeCount item))
+ :reply-count (non-negative (.getReplyCount item))
:hearted-by-uploader? (.isHeartedByUploader item)
:pinned? (.isPinned item)
- :stream-position (when-not (= (.getStreamPosition item) -1)
- (.getStreamPosition item))
+ :stream-position (non-negative (.getStreamPosition item))
:replies (when (.getReplies item)
(if extractor
(let [comments-page (.getPage extractor
diff --git a/src/backend/tubo/handler.clj b/src/backend/tubo/handler.clj
index 96ce49c..bff4326 100644
--- a/src/backend/tubo/handler.clj
+++ b/src/backend/tubo/handler.clj
@@ -37,6 +37,10 @@
[{{:keys [url]} :path-params {:strs [nextPage]} :query-params}]
(response (apply api/get-channel url (if nextPage [nextPage] []))))
+(defn channel-tabs
+ [{{:keys [url tab-id]} :path-params {:strs [nextPage]} :query-params}]
+ (response (apply api/get-channel-tab url tab-id (if nextPage [nextPage] []))))
+
(defn playlist
[{{:keys [url]} :path-params {:strs [nextPage]} :query-params}]
(response (apply api/get-playlist url (if nextPage [nextPage] []))))
diff --git a/src/backend/tubo/routes.clj b/src/backend/tubo/routes.clj
index 7a3a456..e8dd536 100644
--- a/src/backend/tubo/routes.clj
+++ b/src/backend/tubo/routes.clj
@@ -42,7 +42,10 @@
:parameters {:path {:service-id int? :kiosk-id string?}}
:handler handler/kiosk}}]]]]
["/streams/:url" {:get handler/stream}]
- ["/channels/:url" {:get handler/channel}]
+ ["/channels"
+ ["/:url"
+ ["" {:get handler/channel}]
+ ["/tabs/:tab-id" {:get handler/channel-tabs}]]]
["/playlists/:url" {:get handler/playlist}]
["/comments/:url" {:get handler/comments}]]]
{:data {:middleware [rrc/coerce-request-middleware