mirror of
https://github.com/kiranshila/Doplarr.git
synced 2026-04-05 08:53:59 -04:00
Overseerr is searching now
This commit is contained in:
16
src/doplarr/backends/overseerr.clj
Normal file
16
src/doplarr/backends/overseerr.clj
Normal file
@@ -0,0 +1,16 @@
|
||||
(ns doplarr.backends.overseerr
|
||||
(:require
|
||||
[doplarr.backends.overseerr.impl :as impl]
|
||||
[doplarr.utils :as utils]))
|
||||
|
||||
(defn search [term media-type]
|
||||
(utils/request-and-process-body
|
||||
impl/GET
|
||||
(partial impl/process-search-result (name media-type))
|
||||
(str "/search?query=" term)))
|
||||
|
||||
(defn additional-options [result])
|
||||
|
||||
(defn request-embed [] ["" "4K"])
|
||||
|
||||
(defn request [payload])
|
||||
130
src/doplarr/backends/overseerr/impl.clj
Normal file
130
src/doplarr/backends/overseerr/impl.clj
Normal file
@@ -0,0 +1,130 @@
|
||||
(ns doplarr.backends.overseerr.impl
|
||||
(:require
|
||||
[taoensso.timbre :refer [fatal]]
|
||||
[com.rpl.specter :as s]
|
||||
[clojure.core.async :as a]
|
||||
[config.core :refer [env]]
|
||||
[fmnoise.flow :as flow :refer [then else]]
|
||||
[doplarr.utils :as utils]))
|
||||
|
||||
(def base-url (delay (str (:overseerr/url env) "/api/v1")))
|
||||
(def api-key (delay (:overseerr/api env)))
|
||||
|
||||
(def poster-path "https://image.tmdb.org/t/p/w500")
|
||||
|
||||
(def status [:unknown :pending :processing :partially-available :available])
|
||||
|
||||
(defn GET [endpoint & [params]]
|
||||
(utils/http-request :get (str @base-url endpoint) @api-key params))
|
||||
|
||||
(defn POST [endpoint & [params]]
|
||||
(utils/http-request :post (str @base-url endpoint) @api-key params))
|
||||
|
||||
(defn backend-4k? [media-type]
|
||||
(a/go
|
||||
(->> (a/<! (GET (str "/settings/" (if (= media-type "tv") "sonarr" "radarr"))))
|
||||
(then #(->> (:body %)
|
||||
(map :is4k)
|
||||
(some identity)))
|
||||
(else #(fatal % "Exception on checking Overseeerr 4K backend support")))))
|
||||
|
||||
(defn parse-year [result]
|
||||
(.getYear (java.time.LocalDate/parse
|
||||
(if (empty? (or (:first-air-date result)
|
||||
(:release-date result)))
|
||||
"0000-01-01"
|
||||
(or (:first-air-date result)
|
||||
(:release-date result))))))
|
||||
|
||||
(defn process-search-result [media-type-str body]
|
||||
(->> (utils/from-camel body)
|
||||
(s/select [:results
|
||||
s/ALL
|
||||
(s/selected? :media-type (s/pred= media-type-str))
|
||||
(s/view #(assoc % :year (parse-year %)))
|
||||
(s/submap [:title :id :year])])))
|
||||
|
||||
(defn num-users []
|
||||
(a/go
|
||||
(->> (a/<! (GET "/user" {:query-params {:take 1}}))
|
||||
(then #(s/select-one [:body :pageInfo :results] %))
|
||||
(else #(fatal % "Exception on querying Overseerr users")))))
|
||||
|
||||
(defn all-users []
|
||||
(a/go
|
||||
(->> (a/<! (GET "/user" {:query-params {:take (a/<! (num-users))}}))
|
||||
(then #(->> (s/select-one [:body :results] %)
|
||||
(map :id)
|
||||
(into [])))
|
||||
(else #(fatal % "Exception on querying Overseerr users")))))
|
||||
|
||||
(defn discord-id [ovsr-id]
|
||||
(a/go
|
||||
(->> (a/<! (GET (str "/user/" ovsr-id)))
|
||||
(then #(s/select-one [:body :settings :discordId] %))
|
||||
(else #(fatal % "Exception on querying Overseerr discord id")))))
|
||||
|
||||
(defn discord-users []
|
||||
(a/go-loop [ids (a/<! (all-users))
|
||||
users {}]
|
||||
(if (empty? ids)
|
||||
users
|
||||
(let [id (first ids)]
|
||||
(recur (rest ids) (assoc users (a/<! (discord-id id)) id))))))
|
||||
|
||||
(defn details
|
||||
([selection] (details (:id selection) (:mediaType selection)))
|
||||
([id media-type]
|
||||
(a/go
|
||||
(->> (a/<! (GET (str "/" media-type "/" id)))
|
||||
(then :body)
|
||||
(else #(fatal % "Error requesting details on selection from Overseerr"))))))
|
||||
|
||||
(defn series-status [selection & {:keys [is4k]}]
|
||||
(when-let [info (:mediaInfo selection)]
|
||||
(status (dec ((if is4k :status4k :status) info)))))
|
||||
|
||||
(defn season-status [selection & {:keys [season is4k]}]
|
||||
(when-let [ss (series-status selection :is4k is4k)]
|
||||
(if (= ss :partially-available)
|
||||
(when-let [seasons (seq (:seasons (:mediaInfo selection)))]
|
||||
(status (dec ((if is4k :status4k :status) (nth seasons (dec season))))))
|
||||
ss)))
|
||||
|
||||
(defn movie-status [selection & {:keys [is4k]}]
|
||||
(when-let [info (:mediaInfo selection)]
|
||||
(status (dec ((if is4k :status4k :status) info)))))
|
||||
|
||||
(defn selection-to-request [selection & {:keys [season is4k]}]
|
||||
(cond-> {:mediaType (:mediaType selection)
|
||||
:mediaId (:id selection)
|
||||
:is4k is4k}
|
||||
(= "tv" (:mediaType selection)) (assoc :seasons (if (= -1 season)
|
||||
(into [] (range 1 (inc (:seasonCount selection))))
|
||||
[season]))))
|
||||
|
||||
(defn selection-to-embedable [selection]
|
||||
(as-> selection s
|
||||
(assoc s :seasonCount (:numberOfSeasons s))
|
||||
(assoc s :description (:overview s))
|
||||
(assoc s :remotePoster (str poster-path (:posterPath s)))))
|
||||
|
||||
(defn post-process-selection [selection]
|
||||
(a/go
|
||||
(let [details (a/<! (details selection))
|
||||
fourK-backend? (a/<! (backend-4k? (:mediaType selection)))]
|
||||
(selection-to-embedable (merge details selection {:backend-4k fourK-backend?})))))
|
||||
|
||||
(defn request [body & {:keys [ovsr-id]}]
|
||||
(a/go
|
||||
(->> (a/<! (POST "/request" {:form-params body
|
||||
:content-type :json
|
||||
:headers {"X-API-User" (str ovsr-id)}}))
|
||||
(then (constantly nil)))))
|
||||
|
||||
(defn partial-seasons? []
|
||||
(a/go
|
||||
(->> (a/<! (GET "/settings/main"))
|
||||
(then #(->> (:body %)
|
||||
:partialRequestsEnabled))
|
||||
(else #(fatal % "Exception testing for partial seasons")))))
|
||||
@@ -7,7 +7,7 @@
|
||||
[doplarr.backends.radarr.impl :as impl]
|
||||
[clojure.core.async :as a]))
|
||||
|
||||
(defn search [term]
|
||||
(defn search [term _]
|
||||
(utils/request-and-process-body
|
||||
impl/GET
|
||||
#(map utils/process-search-result %)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[doplarr.backends.sonarr.impl :as impl]
|
||||
[clojure.core.async :as a]))
|
||||
|
||||
(defn search [term]
|
||||
(defn search [term _]
|
||||
(utils/request-and-process-body
|
||||
impl/GET
|
||||
#(mapv utils/process-search-result %)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
(cond-> #{}
|
||||
(:radarr/url env) (conj :radarr)
|
||||
(:sonarr/url env) (conj :sonarr)
|
||||
(:overserr/url env) (conj :overseerr)
|
||||
(:overseerr/url env) (conj :overseerr)
|
||||
(:readarr/url env) (conj :readarr)
|
||||
(:lidarr/url env) (conj :lidarr)))
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[discljord.messaging :as m]
|
||||
[clojure.string :as str]
|
||||
[doplarr.state :as state]
|
||||
[doplarr.utils :as utils]))
|
||||
[doplarr.utils :as utils :refer [log-on-error]]))
|
||||
|
||||
(def channel-timeout 600000)
|
||||
|
||||
@@ -27,9 +27,11 @@
|
||||
(else #(fatal % "Error in interaction ack")))
|
||||
; Search for results
|
||||
(info "Performing search for" (name media-type) query)
|
||||
(let [results (->> (a/<! ((utils/media-fn media-type "search") query))
|
||||
(take (:max-results env 10))
|
||||
(into []))]
|
||||
(let [results (->> (log-on-error
|
||||
(a/<! ((utils/media-fn media-type "search") query media-type))
|
||||
"Exception from search")
|
||||
(then #(->> (take (:max-results env 10) %)
|
||||
(into []))))]
|
||||
; Setup ttl cache entry
|
||||
(swap! state/cache assoc uuid {:results results
|
||||
:media-type media-type
|
||||
@@ -44,7 +46,9 @@
|
||||
(let [{:keys [messaging bot-id]} @state/discord
|
||||
{:keys [media-type token payload]} (get @state/cache uuid)]
|
||||
(if (empty? pending-opts)
|
||||
(let [embed (a/<! ((utils/media-fn media-type "request-embed") payload))]
|
||||
(let [embed (log-on-error
|
||||
(a/<! ((utils/media-fn media-type "request-embed") payload))
|
||||
"Exception from request-embed")]
|
||||
(->> @(m/edit-original-interaction-response! messaging bot-id token (discord/request embed uuid))
|
||||
(else #(fatal % "Error in sending request embed"))))
|
||||
(let [[op options] (first pending-opts)]
|
||||
@@ -57,7 +61,9 @@
|
||||
(a/go
|
||||
(let [{:keys [results media-type]} (get @state/cache uuid)
|
||||
result (nth results (discord/dropdown-result interaction))
|
||||
add-opts (a/<! ((utils/media-fn media-type "additional-options") result))
|
||||
add-opts (log-on-error
|
||||
(a/<! ((utils/media-fn media-type "additional-options") result))
|
||||
"Exception thrown from additional-options")
|
||||
pending-opts (->> add-opts
|
||||
(filter #(seq? (second %)))
|
||||
(into {}))
|
||||
@@ -80,8 +86,10 @@
|
||||
{:keys [payload media-type token]} (get @state/cache uuid)]
|
||||
(letfn [(msg-resp [msg] (->> @(m/edit-original-interaction-response! messaging bot-id token (discord/content-response msg))
|
||||
(else #(fatal % "Error in message response"))))]
|
||||
(->> (a/<!! ((utils/media-fn media-type "request")
|
||||
(assoc payload :format (keyword format))))
|
||||
(->> (log-on-error
|
||||
(a/<!! ((utils/media-fn media-type "request")
|
||||
(assoc payload :format (keyword format))))
|
||||
"Exception from request")
|
||||
(then (fn [status]
|
||||
(case status
|
||||
:unauthorized (msg-resp "You do not have an associated account in the request backend")
|
||||
|
||||
@@ -77,3 +77,10 @@
|
||||
(symbol (str "doplarr.backends." (name (config/available-backed-for-media
|
||||
media)))
|
||||
f)))
|
||||
|
||||
(defmacro log-on-error [expr msg]
|
||||
`(try
|
||||
~expr
|
||||
(catch Exception e#
|
||||
(fatal e# ~msg)
|
||||
(throw e#))))
|
||||
|
||||
Reference in New Issue
Block a user