;;; web.lisp (in-package :seanut) (eval-when (:compile-toplevel) (declaim (inline json-request format-url generate-authorization download-media format-hostname))) (defun generate-authorization (&optional token) "generates a properly formatted authorization header" (let ((hostname (str:replace-first ".local" "" (uiop:hostname)))) (apply #'format `(nil ,*authorization-format* ,@(mapcar #'url-encode (list (str:concat "Seanut " (seanut-version)) hostname (string-downcase (md5-string hostname)) (seanut-version) (or token ""))))))) (defun format-url (domain slug &rest args) "formats DOMAIN into a url, ensures we include the url scheme SLUG is a format-coded string that represents the path for the query ARGS are the arguments for the SLUG format string" (format nil "~:[https://~;~]~A/~A" (uiop:string-prefix-p "https://" domain) domain (apply #'format `(nil ,slug ,@args)))) (defun json-request (url &key auth (method :get) extra-headers content) "makes a request to URL, using AUTH as the X-Emby-Authorization header and METHOD as the http method (defaults to get) and parses the returned value with jzon:parse if EXTRA-HEADERS is non-nil, includes them in the headers alongside the X-Emby-Authorization one if CONTENT is non-nil, passes that along to the request" (parse (dex:request url :method method :content content :headers `(,(when auth `("Authorization" . ,auth)) ,@extra-headers)))) (defun run-search-query (domain auth type name) "runs the search query to get the initial list of items can probably be removed and the request can be made in-line" (gethash "Items" (json-request (format-url domain "Items?fields=Path&includeItemTypes=~A&recursive=true&searchTerm=~A" type name) :auth auth))) (defun download-media (path url auth) "downloads the media at URL, using HEADER as the authorization header. if DESTINATION is non-nil, dumps media into that directory, otherwise it uses CWD" (dex:fetch url path :if-exists nil :headers `(("Authorization" . ,auth))))