From 8a8d9a7a61ceae766d410bf917eb8537fce94726 Mon Sep 17 00:00:00 2001 From: Brook Gagnon Date: Mon, 19 Aug 2024 16:22:31 +0000 Subject: [PATCH] create proper download endpoint (migrate away from custom download.php file to using api properly) --- VERSION | 2 +- controllers/downloads.php | 149 +++++++++++++++++++++ js/api.js | 5 + js/media/download.js | 6 +- routes.json | 274 ++++++++++++++++++++------------------ 5 files changed, 307 insertions(+), 129 deletions(-) create mode 100644 controllers/downloads.php diff --git a/VERSION b/VERSION index f6a5a862..f7b330f7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.3.3-20240818 +5.3.3-20240819 diff --git a/controllers/downloads.php b/controllers/downloads.php new file mode 100644 index 00000000..7158557a --- /dev/null +++ b/controllers/downloads.php @@ -0,0 +1,149 @@ +. +*/ + +/** + * Endpoints where the output is raw/binary. + * + * @package Controller + */ +class Downloads extends OBFController +{ + private $io; + + public function __construct() + { + parent::__construct(); + $this->io = OBFIO::get_instance(); + } + + /** + * Download media item. + * + * @param id Media ID + * + * @route GET /v2/downloads/media/(:id:) + */ + public function media() + { + $id = $this->data('id'); + $media = $this->models->media('get_by_id', ['id' => $id]); + + if (!$media) { + $this->error(OB_ERROR_NOTFOUND); + } + + // check permissions + if ($media['status'] != 'public') { + $this->user->require_authenticated(); + $is_media_owner = $media['owner_id'] == $this->user->param('id'); + + // download requires download_media if this is not the media owner + if (!$is_media_owner) { + $user->require_permission('download_media'); + } + + // private media requires manage_media if this is not the media owner + if ($media['status'] == 'private' && !$is_media_owner) { + $this->user->require_permission('manage_media'); + } + } + + if ($media['is_archived'] == 1) { + $filedir = OB_MEDIA_ARCHIVE; + } elseif ($media['is_approved'] == 0) { + $filedir = OB_MEDIA_UPLOADS; + } else { + $filedir = OB_MEDIA; + } + + $filedir .= '/' . $media['file_location'][0] . '/' . $media['file_location'][1]; + + $fullpath = $filedir . '/' . $media['filename']; + + if (!file_exists($fullpath)) { + $this->error(OB_ERROR_NOTFOUND); + } + + header("Access-Control-Allow-Origin: *"); + header('Content-Description: File Transfer'); + header('Content-Type: application/octet-stream'); + header("Content-Transfer-Encoding: binary"); + header("Content-Length: " . filesize($fullpath)); + header('Content-Disposition: attachment; filename="' . $media['filename'] . '"'); + + readfile($fullpath); + } + + /** + * Get all restrictions. + * + * @param id Media ID + * + * @route GET /v2/downloads/stream/(:id:) + */ + public function stream() + { + } + + /** + * Get all restrictions. + * + * @param id Media ID + * + * @route GET /v2/downloads/thumbnail/(:id:) + */ + public function thumbnail() + { + $id = $this->data('id'); + $media = $this->models->media('get_by_id', ['id' => $id]); + + if (!$media) { + $this->error(OB_ERROR_NOTFOUND); + } + + // check permissions + if ($media['status'] != 'public') { + $this->user->require_authenticated(); + $is_media_owner = $media['owner_id'] == $this->user->param('id'); + if ($media['status'] == 'private' && !$is_media_owner) { + $this->user->require_permission('manage_media'); + } + } + + // get thumbnail + $file = $this->models->media('thumbnail_file', ['media' => $id]); + + if (!$file || !file_exists($file)) { + $this->error(OB_ERROR_NOTFOUND); + } else { + $mime = mime_content_type($file); + $contents = file_get_contents($file); + header('Content-Type: ' . $mime); + echo $contents; + } + } + + private function error($code) + { + $this->io->error($code); + die(); + } +} diff --git a/js/api.js b/js/api.js index da04a8e9..411bc5e0 100644 --- a/js/api.js +++ b/js/api.js @@ -262,6 +262,11 @@ OB.API.callbackAppend = function (controller, action, callback) { // download with auth credentials OB.API.download = function (url) { + // strip leading "/" from url + if (url.charAt(0) == "/") url = url.substr(1); + + url = "/api/v2/" + url; + // get a nonce OB.API.post("account", "nonce", {}, function (data) { if (data.error) { diff --git a/js/media/download.js b/js/media/download.js index 3b268842..65453717 100644 --- a/js/media/download.js +++ b/js/media/download.js @@ -18,5 +18,9 @@ */ OB.Media.download = function (id, version) { - window.open("/preview.php?id=" + id + "&dl=1" + (version ? "&v=" + version : ""), "Download"); + if (version) { + window.open("/preview.php?id=" + id + "&dl=1" + (version ? "&v=" + version : ""), "Download"); + } else { + OB.API.download("downloads/media/" + id); + } }; diff --git a/routes.json b/routes.json index b49b50f7..4e96a4b5 100644 --- a/routes.json +++ b/routes.json @@ -1,137 +1,20 @@ { - "POST": [ - [ - "/api/v2/clientsettings/login-message", - "clientsettings", - "set_login_message" - ], - [ - "/api/v2/clientsettings/welcome-page", - "clientsettings", - "set_welcome_page" - ], - [ - "/api/v2/users", - "users", - "user_manage_addedit" - ], - [ - "/api/v2/users/appkeys", - "users", - "user_manage_key_new" - ], - [ - "/api/v2/users/permissions", - "users", - "permissions_manage_addedit" - ], - [ - "/api/v2/media/formats", - "media", - "formats_save" - ], - [ - "/api/v2/media/searches", - "media", - "media_my_searches_save" - ], - [ - "/api/v2/media/searches/default", - "media", - "media_my_searches_default" - ], - [ - "/api/v2/media", - "media", - "save" - ], - [ - "/api/v2/media/versions", - "media", - "version_add" - ], - [ - "/api/v2/media/archive", - "media", - "archive" - ], - [ - "/api/v2/media/archive/undo", - "media", - "unarchive" - ], - [ - "/api/v2/playlists", - "playlists", - "save" - ], - [ - "/api/v2/players", - "players", - "save" - ], - [ - "/api/v2/alerts", - "alerts", - "save" - ], - [ - "/api/v2/timeslots", - "timeslots", - "save" - ], - [ - "/api/v2/account/login", - "account", - "login" - ], - [ - "/api/v2/account/logout", - "account", - "logout" - ], - [ - "/api/v2/account/forgot", - "account", - "forgotpass" - ], - [ - "/api/v2/account/new", - "account", - "newaccount" - ], + "GET": [ [ - "/api/v2/account/key", - "account", - "key_new" + "/api/v2/downloads/media/(:id:)", + "downloads", + "media" ], [ - "/api/v2/shows", - "shows", - "save" + "/api/v2/downloads/stream/(:id:)", + "downloads", + "stream" ], [ - "/api/v2/metadata", - "metadata", - "metadata_save" - ], - [ - "/api/v2/metadata/recording", - "metadata", - "recording_default_values_save" - ], - [ - "/api/v2/metadata/categories", - "metadata", - "category_save" + "/api/v2/downloads/thumbnail/(:id:)", + "downloads", + "thumbnail" ], - [ - "/api/v2/metadata/genres", - "metadata", - "genre_save" - ] - ], - "GET": [ [ "/api/v2/clientsettings/login-message", "clientsettings", @@ -327,6 +210,11 @@ "account", "key_load" ], + [ + "/api/v2/account/nonce", + "account", + "nonce" + ], [ "/api/v2/shows/(:id:)", "shows", @@ -418,6 +306,138 @@ "html" ] ], + "POST": [ + [ + "/api/v2/clientsettings/login-message", + "clientsettings", + "set_login_message" + ], + [ + "/api/v2/clientsettings/welcome-page", + "clientsettings", + "set_welcome_page" + ], + [ + "/api/v2/users", + "users", + "user_manage_addedit" + ], + [ + "/api/v2/users/appkeys", + "users", + "user_manage_key_new" + ], + [ + "/api/v2/users/permissions", + "users", + "permissions_manage_addedit" + ], + [ + "/api/v2/media/formats", + "media", + "formats_save" + ], + [ + "/api/v2/media/searches", + "media", + "media_my_searches_save" + ], + [ + "/api/v2/media/searches/default", + "media", + "media_my_searches_default" + ], + [ + "/api/v2/media", + "media", + "save" + ], + [ + "/api/v2/media/versions", + "media", + "version_add" + ], + [ + "/api/v2/media/archive", + "media", + "archive" + ], + [ + "/api/v2/media/archive/undo", + "media", + "unarchive" + ], + [ + "/api/v2/playlists", + "playlists", + "save" + ], + [ + "/api/v2/players", + "players", + "save" + ], + [ + "/api/v2/alerts", + "alerts", + "save" + ], + [ + "/api/v2/timeslots", + "timeslots", + "save" + ], + [ + "/api/v2/account/login", + "account", + "login" + ], + [ + "/api/v2/account/logout", + "account", + "logout" + ], + [ + "/api/v2/account/forgot", + "account", + "forgotpass" + ], + [ + "/api/v2/account/new", + "account", + "newaccount" + ], + [ + "/api/v2/account/key", + "account", + "key_new" + ], + [ + "/api/v2/shows", + "shows", + "save" + ], + [ + "/api/v2/metadata", + "metadata", + "metadata_save" + ], + [ + "/api/v2/metadata/recording", + "metadata", + "recording_default_values_save" + ], + [ + "/api/v2/metadata/categories", + "metadata", + "category_save" + ], + [ + "/api/v2/metadata/genres", + "metadata", + "genre_save" + ] + ], "PUT": [ [ "/api/v2/users/can-register",