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