fixed error fetching results after authentication

updated authorization format string
ensured we url encode our authorization values
removed ".local" from hostnames if it's present
added str package as dependency
fixed using deprecated authorization header
main
a. fox 2 years ago
parent 51b659725f
commit 96f6b24227

@ -21,9 +21,9 @@
(in-package :seanut)
(defvar *authorization-format*
"MediaBrowser Client=\"Seanut v~A\", Device=\"~A\", DeviceId=\"~A\", Version=\"~A\", Token=\"~A\"")
(defparameter *authorization-format*
"MediaBrowser Client=\"~A\", Device=\"~A\", DeviceId=\"~A\", Version=\"~A\", Token=\"~A\"")
(defvar *valid-media-types*
(defparameter *valid-media-types*
'("Book" "BoxSet" "Movie" "MusicAlbum" "MusicArtist"
"MusicGenre" "Playlist" "Season" "Series"))

@ -8,7 +8,7 @@
:serial t
:depends-on (#:dexador #:with-user-abort #:unix-opts
#:com.inuoe.jzon #:babel #:ironclad #:quri
#:alexandria)
#:alexandria #:str)
:components ((:file "package")
(:file "util")
(:file "web")

@ -2,67 +2,93 @@
(in-package #:seanut)
(defun prompt-and-download (domain auth item opts &optional assume-yes)
(defun prompt-and-download (domain auth root opts &optional assume-yes)
"prompt the user with y-or-n-p and download ITEM"
;; debug print statements lol
(format t "~A~%" auth)
(y-or-n-p)
(labels ((fetch-user-id ()
(gethash "Id" (json-request (format-url domain "Users/Me")
:auth auth)))
(generate-filename (item)
(make-pathname :name
(str:replace-all "/" "-"
(format nil "~A.~A"
(gethash "Name" item)
(if (string= (gethash "Type" item) "Season")
(subseq (gethash "Container" item) 0
(search "," (gethash "Container" item)))
(pathname-type (gethash "Path" item)))))))
(labels ((generate-root-name (&optional add-trailing)
(generate-root-name (&optional add-trailing)
(format nil "~A (~A)~@[ Season ~A~]~@[/~]"
(gethash "Name" item)
(gethash "ProductionYear" item)
(gethash "Name" root)
(gethash "ProductionYear" root)
(getf opts :season-number)
add-trailing))
(ensure-download-dir (&optional new-path)
(ensure-directories-exist
(merge-pathnames (or new-path "")
(merge-pathnames (generate-root-name 'add-slash)
(getf opts :output #P"./")))))
(create-directory (dir)
(ensure-directories-exist (uiop:ensure-directory-pathname (make-pathname :name dir))))
(download-item-or-children (item &optional parents)
(download-item-or-children (item)
;; PARENTS is a list of all parent names with FIRST being
;; the oldest grandparent (for building complete download path)
(let ((children (if (string= (gethash "Type" item) "Season")
(json-request (format-url domain "Shows/~A/Episodes?fields=Path~@[&season=~A~]"
(let ((children (gethash "Items"
(if (string= (gethash "Type" item) "Season")
(json-request (format-url domain "Shows/~A/Episodes?fields=Path&seasonId=~A~@[&season=~A~]"
(gethash "SeriesId" item)
(gethash "Id" item)
(getf opts :season-number))
:auth auth)
(json-request (format-url domain "Items?fields=Path&parentId=~A"
(json-request (format-url domain "Items?userId=~A&fields=Path,ChildCount&parentId=~A"
(fetch-user-id)
(gethash "Id" item))
:auth auth))))
(if (zerop (length children))
:auth auth)))))
;; DELETE: debug prints lmao
(loop :for child :across children
:do (format t "Name: ~A, Id: ~A~%"
(gethash "Name" child)
(gethash "Id" child)))
(loop :for child :across children
:if (zerop (gethash "ChildCount" child 0))
:do
;; download single file
;; to get the extension type i think we may need to include
;; "Path" field in the api request, then use that to get the extension
(download-media (format nil "~A~A~{~A~^/~}~A.~A"
(getf opts :output #P"./")
(generate-root-name 'trailing)
parents (gethash "Name" item)
(pathname-type (gethash "Path" item)))
(download-media (generate-filename child)
(format-url domain "Items/~A/Download"
(gethash "Id" item))
(gethash "Id" child))
auth)
:else
:do
;; if the item has children we need to download them.
;; to accomplish this we get the list of children
;; and loop over them, recursing for each one
;; ensure that a directory exists for Parent
;; then recurse with children
(progn
(ensure-download-dir (format nil "~A~A~{~A/~}~A"
(getf opts :output #P"./")
(generate-root-name 'trailing)
parents (gethash "Name" item)))
(loop :for child :in children
:do (apply #'download-item-or-children
`(,child (,@parents ,(gethash "Name" item))))))))))
(format t "Creating child directory in ~A and recursing: ~A~%"
(uiop:getcwd) (gethash "Name" child))
(uiop:with-current-directory ((create-directory (gethash "Name" child)))
(download-item-or-children child))))))
(format t "Name: ~A, Id: ~A~%"
(gethash "Name" root)
(gethash "Id" root))
(when (or assume-yes
(y-or-n-p "Download ~A" (generate-root-name)))
(ensure-download-dir)
(download-item-or-children item))))
;; CD into our output directory
(uiop:with-current-directory ((getf opts :output #P"./"))
(let ((grandest-parent (format nil "~A (~A)~@[ Season ~A~]/"
(gethash "Name" root)
(gethash "ProductionYear" root)
(getf opts :season-number))))
;; create our folder for our current download
(ensure-directories-exist grandest-parent)
;; CD into it, then download the files
(uiop:with-current-directory (grandest-parent)
(download-item-or-children root)))))))
(defun main ()
@ -94,7 +120,7 @@
(quit-with-message 1 "domain and/or media name not provided"))
(destructuring-bind (domain search-term) args
(let* ((authorization (or (getf opts :token)
(let* ((authorization (or (and (getf opts :token) (generate-authorization (getf opts :token)))
(generate-authorization (get-access-token domain opts))))
(results (run-search-query domain authorization
(getf opts :media-type)

@ -3,13 +3,21 @@
(in-package :seanut)
(eval-when (:compile-toplevel)
(declaim (inline json-request format-url generate-authorization download-media)))
(declaim (inline json-request format-url generate-authorization download-media
format-hostname)))
(defun format-hostname ()
(if (uiop:string-suffix-p (uiop:hostname) ".local")
(subseq (uiop:hostname) 0 (- (length (uiop:hostname)) 6))
(uiop:hostname)))
(defun generate-authorization (&optional token)
"generates a properly formatted authorization header"
(format nil *authorization-format*
(seanut-version) (uiop:hostname) (md5-string (uiop:hostname))
(seanut-version) (or token "")))
(apply #'format `(nil ,*authorization-format*
,@(mapcar #'url-encode
(list (concatenate 'string "Seanut " (seanut-version))
(format-hostname) (string-downcase (md5-string (format-hostname)))
(seanut-version) (or token ""))))))
(defun format-url (domain slug &rest args)
"formats DOMAIN into a url, ensures we include the url scheme
@ -46,4 +54,4 @@ can probably be removed and the request can be made in-line"
if DESTINATION is non-nil, dumps media into that directory, otherwise it uses CWD"
(dex:fetch url path
:if-exists nil
:headers `(("X-Emby-Authorization" . ,auth))))
:headers `(("Authorization" . ,auth))))

Loading…
Cancel
Save