From 58a30b6561078fb4a4ae03a43e7717b0472b49a4 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 19 Apr 2026 18:08:39 -0400 Subject: [PATCH] chore(bruno): add multi-library dev setup with scan-all and folder requests Update NewDevDBSetup collection to create Ebook, Comic, and Manga libraries with separate IDs (ebook_library_id, comic_library_id, manga_library_id) instead of a single library_id. - Fix CreateComicLibrary and CreateMangaLibrary to use correct names, descriptions, and types instead of duplicating Ebook values - Update NewDB.sh to run the full setup sequence: register user, create all three libraries, add folders, then scan all - Add AddEbookLibraryFolder, AddComicLibraryFolder, and AddMangaLibraryFolder requests with per-type subfolder paths - Add ScanAllLibraries request using bru.sendRequest() to scan each library sequentially via the /api/scanner/scan endpoint - Update Get Libraries (Admin) to save all three library IDs - Update List Media Items requests to use ebook_library_id - Rename library_id to ebook_library_id in Create Library and Add Library Folder requests - Add comic_library_id and manga_library_id to environment --- bruno/NewDB.sh | 2 +- bruno/NewDevDBSetup/AddComicLibraryFolder.yml | 30 ++++++++ bruno/NewDevDBSetup/AddEbookLibraryFolder.yml | 53 ++++++++++++++ bruno/NewDevDBSetup/AddMangaLibraryFolder.yml | 30 ++++++++ bruno/NewDevDBSetup/CreateComicLibrary.yml | 45 ++---------- bruno/NewDevDBSetup/CreateEbookLibrary.yml | 2 +- bruno/NewDevDBSetup/CreateMangaLibrary.yml | 45 ++---------- bruno/NewDevDBSetup/ScanAllLibraries.yml | 70 +++++++++++++++++++ bruno/environments/Bookhoard.yml | 8 ++- bruno/library/Add Library Folder.yml | 4 +- bruno/library/Create Library.yml | 2 +- bruno/library/Get Libraries (Admin).yml | 40 +++++++++-- bruno/media-items/List Media Items Sorted.yml | 15 +++- bruno/media-items/List Media Items.yml | 12 +++- 14 files changed, 260 insertions(+), 98 deletions(-) create mode 100644 bruno/NewDevDBSetup/AddComicLibraryFolder.yml create mode 100644 bruno/NewDevDBSetup/AddEbookLibraryFolder.yml create mode 100644 bruno/NewDevDBSetup/AddMangaLibraryFolder.yml create mode 100644 bruno/NewDevDBSetup/ScanAllLibraries.yml diff --git a/bruno/NewDB.sh b/bruno/NewDB.sh index 0f671fa..03cc26f 100755 --- a/bruno/NewDB.sh +++ b/bruno/NewDB.sh @@ -1,3 +1,3 @@ #!/bin/bash -bru run --env Bookhoard --delay 500 "user/auth/Register User.yml" "library/Create Library.yml" "library/Add Library Folder.yml" "scanner/Scan Media Items.yml" +bru run --env Bookhoard --delay 500 "NewDevDBSetup/RegisterUser.yml" "NewDevDBSetup/CreateEbookLibrary.yml" "NewDevDBSetup/CreateComicLibrary.yml" "NewDevDBSetup/CreateMangaLibrary.yml" "NewDevDBSetup/AddEbookLibraryFolder.yml" "NewDevDBSetup/AddComicLibraryFolder.yml" "NewDevDBSetup/AddMangaLibraryFolder.yml" "NewDevDBSetup/ScanAllLibraries.yml" diff --git a/bruno/NewDevDBSetup/AddComicLibraryFolder.yml b/bruno/NewDevDBSetup/AddComicLibraryFolder.yml new file mode 100644 index 0000000..a91bed7 --- /dev/null +++ b/bruno/NewDevDBSetup/AddComicLibraryFolder.yml @@ -0,0 +1,30 @@ +info: + name: AddComicLibraryFolder + type: http + seq: 6 + +http: + method: POST + url: "{{base_url}}/api/libraries/{{comic_library_id}}/folders" + body: + type: json + data: |- + { + "folder_path": "{{library_folder}}/Comics" + } + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + ## Add Library Folder + + Adds a new folder path to a library for media scanning and indexing. + + **Method:** POST + + **Endpoint:** /api/libraries/{id} diff --git a/bruno/NewDevDBSetup/AddEbookLibraryFolder.yml b/bruno/NewDevDBSetup/AddEbookLibraryFolder.yml new file mode 100644 index 0000000..00194b4 --- /dev/null +++ b/bruno/NewDevDBSetup/AddEbookLibraryFolder.yml @@ -0,0 +1,53 @@ +info: + name: AddEbookLibraryFolder + type: http + seq: 15 + +http: + method: POST + url: "{{base_url}}/api/libraries/{{ebook_library_id}}/folders" + body: + type: json + data: |- + { + "folder_path": "{{library_folder}}/Ebooks" + } + auth: inherit + +runtime: + scripts: + - type: after-response + code: |- + function onResponse(res) { + try { + const responseBody = res.getBody(); + const id = responseBody.id; + + if (id) { + bru.setEnvVar("library_folder_id", id, { persist: true }); + console.log("Library Folder ID saved:", id); + } + + if (!id) { + console.log("No id found in response."); + } + } catch (error) { + console.error("Error in post-response script:", error.message); + } + } + onResponse(res); + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + ## Add Library Folder + + Adds a new folder path to a library for media scanning and indexing. + + **Method:** POST + + **Endpoint:** /api/libraries/{id} diff --git a/bruno/NewDevDBSetup/AddMangaLibraryFolder.yml b/bruno/NewDevDBSetup/AddMangaLibraryFolder.yml new file mode 100644 index 0000000..83b8119 --- /dev/null +++ b/bruno/NewDevDBSetup/AddMangaLibraryFolder.yml @@ -0,0 +1,30 @@ +info: + name: AddMangaLibraryFolder + type: http + seq: 7 + +http: + method: POST + url: "{{base_url}}/api/libraries/{{manga_library_id}}/folders" + body: + type: json + data: |- + { + "folder_path": "{{library_folder}}/Manga" + } + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + ## Add Library Folder + + Adds a new folder path to a library for media scanning and indexing. + + **Method:** POST + + **Endpoint:** /api/libraries/{id} diff --git a/bruno/NewDevDBSetup/CreateComicLibrary.yml b/bruno/NewDevDBSetup/CreateComicLibrary.yml index ef38541..7efe86e 100644 --- a/bruno/NewDevDBSetup/CreateComicLibrary.yml +++ b/bruno/NewDevDBSetup/CreateComicLibrary.yml @@ -10,9 +10,9 @@ http: type: json data: |- { - "name": "My Ebook Library", - "description": "A collection of technical books and novels", - "type": "ebooks" + "name": "My Comic Library", + "description": "A collection of comics", + "type": "comics" } auth: inherit @@ -26,7 +26,7 @@ runtime: const id = responseBody.id; if (id) { - bru.setEnvVar("library_id", id, { persist: true }); + bru.setEnvVar("comic_library_id", id, { persist: true }); console.log("Library ID saved:", id); } @@ -44,40 +44,3 @@ settings: timeout: 0 followRedirects: true maxRedirects: 5 - -docs: |- - ## Create Library - - Creates a new library for organizing media items. - - **Method:** POST - - **Endpoint:** /api/libraries - - **Authentication:** Required (Bearer token) - - **Request Body:** - - `name` (string, required): Library name - - `description` (string, optional): Library description - - `type` (string, required): Library type - - `"ebooks"`: Electronic books - - `"audiobooks"`: Audio books - - `"videos"`: Video content - - `"other"`: Other media types - - **Response:** Library object - - `id` (string): Library UUID - - `name` (string): Library name - - `description` (string, optional): Library description - - `type` (string): Library type - - `is_visible` (boolean): Library visibility status - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 201: Library created successfully - - 400: Invalid request data - - 401: Unauthorized - - 403: Forbidden (insufficient permissions) - - 409: Library name already exists - - 500: Internal server error diff --git a/bruno/NewDevDBSetup/CreateEbookLibrary.yml b/bruno/NewDevDBSetup/CreateEbookLibrary.yml index 8792d30..fc4daa3 100644 --- a/bruno/NewDevDBSetup/CreateEbookLibrary.yml +++ b/bruno/NewDevDBSetup/CreateEbookLibrary.yml @@ -26,7 +26,7 @@ runtime: const id = responseBody.id; if (id) { - bru.setEnvVar("library_id", id, { persist: true }); + bru.setEnvVar("ebook_library_id", id, { persist: true }); console.log("Library ID saved:", id); } diff --git a/bruno/NewDevDBSetup/CreateMangaLibrary.yml b/bruno/NewDevDBSetup/CreateMangaLibrary.yml index 100bcda..cd4f4d2 100644 --- a/bruno/NewDevDBSetup/CreateMangaLibrary.yml +++ b/bruno/NewDevDBSetup/CreateMangaLibrary.yml @@ -10,9 +10,9 @@ http: type: json data: |- { - "name": "My Ebook Library", - "description": "A collection of technical books and novels", - "type": "ebooks" + "name": "My Manga Library", + "description": "A collection of manga", + "type": "manga" } auth: inherit @@ -26,7 +26,7 @@ runtime: const id = responseBody.id; if (id) { - bru.setEnvVar("library_id", id, { persist: true }); + bru.setEnvVar("manga_library_id", id, { persist: true }); console.log("Library ID saved:", id); } @@ -44,40 +44,3 @@ settings: timeout: 0 followRedirects: true maxRedirects: 5 - -docs: |- - ## Create Library - - Creates a new library for organizing media items. - - **Method:** POST - - **Endpoint:** /api/libraries - - **Authentication:** Required (Bearer token) - - **Request Body:** - - `name` (string, required): Library name - - `description` (string, optional): Library description - - `type` (string, required): Library type - - `"ebooks"`: Electronic books - - `"audiobooks"`: Audio books - - `"videos"`: Video content - - `"other"`: Other media types - - **Response:** Library object - - `id` (string): Library UUID - - `name` (string): Library name - - `description` (string, optional): Library description - - `type` (string): Library type - - `is_visible` (boolean): Library visibility status - - `created_at` (string): Creation timestamp - - `updated_at` (string): Last update timestamp - - **Status Codes:** - - 201: Library created successfully - - 400: Invalid request data - - 401: Unauthorized - - 403: Forbidden (insufficient permissions) - - 409: Library name already exists - - 500: Internal server error diff --git a/bruno/NewDevDBSetup/ScanAllLibraries.yml b/bruno/NewDevDBSetup/ScanAllLibraries.yml new file mode 100644 index 0000000..3f104e2 --- /dev/null +++ b/bruno/NewDevDBSetup/ScanAllLibraries.yml @@ -0,0 +1,70 @@ +info: + name: ScanAllLibraries + type: http + seq: 9 + +http: + method: GET + url: "{{base_url}}/api/scanner/watch/status" + auth: inherit + +runtime: + scripts: + - type: before-request + code: |- + async function scanAllLibraries() { + try { + await bru.runRequest("library/Get Libraries (Admin)"); + const baseUrl = bru.getEnvVar("base_url"); + const token = bru.getEnvVar("token"); + const libraries = [ + { name: "Ebook", id: bru.getEnvVar("ebook_library_id") }, + { name: "Comic", id: bru.getEnvVar("comic_library_id") }, + { name: "Manga", id: bru.getEnvVar("manga_library_id") }, + ].filter(lib => lib.id); + if (libraries.length === 0) { + console.log("No library IDs found. Run Get Libraries (Admin) first."); + return; + } + for (const lib of libraries) { + await bru.sendRequest( + { + method: "POST", + url: `${baseUrl}/api/scanner/scan`, + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + data: { + library_id: lib.id, + force: true, + }, + }, + function (err, res) { + if (err) { + console.error(`Failed to scan ${lib.name}:`, err); + return; + } + console.log(`${libraries.length} libraries queued for scanning`); + }) + } + } catch (error) { + console.error("Error in scan all:", error.message); + } + } + await scanAllLibraries(); + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + +docs: |- + ## Scan All Libraries + Fetches all libraries via "Get Libraries (Admin)" and triggers a + background scan for each one sequentially. + Uses `bru.runRequest()` to get libraries, then loops through each + calling `POST /api/scanner/scan` with `{ library_id, force: true }`. + Job IDs are logged to console. The last job_id is saved to the + environment for use with "Get Scan Status". diff --git a/bruno/environments/Bookhoard.yml b/bruno/environments/Bookhoard.yml index 4a5f132..b6518b7 100644 --- a/bruno/environments/Bookhoard.yml +++ b/bruno/environments/Bookhoard.yml @@ -12,8 +12,8 @@ variables: value: 660f9501-f29b-51d4-b716-446655440001 - name: note_id value: 7710a602-g29b-61d4-c716-446655440002 - - name: library_id - value: 10e2bab6-9029-4892-af12-f888752356c0 + - name: ebook_library_id + value: 0df0ea2b-1965-494a-a0a3-cce8536d5f28 - name: job_id value: 709e0d8e-b866-496c-b260-a59ab8e2014c - name: rating @@ -36,3 +36,7 @@ variables: value: 412c03c3-0843-4bd4-b764-cc9457bb9df2 - name: library_folder_id value: da1f9d91-0c4c-40cc-a050-86f795dfc967 + - name: comic_library_id + value: 7b6d0c8c-73dc-4346-804c-5f6e11c3d658 + - name: manga_library_id + value: b134d16d-9668-4867-b50e-8350037f1a4a diff --git a/bruno/library/Add Library Folder.yml b/bruno/library/Add Library Folder.yml index 3699e20..50eea25 100644 --- a/bruno/library/Add Library Folder.yml +++ b/bruno/library/Add Library Folder.yml @@ -5,12 +5,12 @@ info: http: method: POST - url: "{{base_url}}/api/libraries/{{library_id}}/folders" + url: "{{base_url}}/api/libraries/{{ebook_library_id}}/folders" body: type: json data: |- { - "folder_path": "{{library_folder}}" + "folder_path": "{{library_folder}}/Ebooks" } auth: inherit diff --git a/bruno/library/Create Library.yml b/bruno/library/Create Library.yml index 8a29506..0c081f9 100644 --- a/bruno/library/Create Library.yml +++ b/bruno/library/Create Library.yml @@ -26,7 +26,7 @@ runtime: const id = responseBody.id; if (id) { - bru.setEnvVar("library_id", id, { persist: true }); + bru.setEnvVar("ebook_library_id", id, { persist: true }); console.log("Library ID saved:", id); } diff --git a/bruno/library/Get Libraries (Admin).yml b/bruno/library/Get Libraries (Admin).yml index fe7fbe0..598a07a 100644 --- a/bruno/library/Get Libraries (Admin).yml +++ b/bruno/library/Get Libraries (Admin).yml @@ -18,15 +18,41 @@ runtime: function onResponse(res) { try { const responseBody = res.getBody(); - const id = responseBody.data[0].id; - - if (id) { - bru.setEnvVar("library_id", id, { persist: true }); - console.log("Library ID saved:", id); + let ebook_id + let comic_id + let manga_id + + if (res.status === 200) { + for (var lib of responseBody.data) { + if (lib.name.includes("Ebook")) ebook_id = lib.id + if (lib.name.includes("Comic")) comic_id = lib.id + if (lib.name.includes("Manga")) manga_id = lib.id + } } - if (!id) { - console.log("No id found in response."); + if (ebook_id && ebook_id !== "") { + bru.setEnvVar("ebook_library_id", ebook_id, { persist: true }); + console.log("Ebook Library ID saved:", ebook_id); + } + + if (comic_id && comic_id !== "") { + bru.setEnvVar("comic_library_id", comic_id, { persist: true }); + console.log("Comic Library ID saved:", comic_id); + } + + if (manga_id && manga_id !== "") { + bru.setEnvVar("manga_library_id", manga_id, { persist: true }); + console.log("Manga Library ID saved:", manga_id); + } + + if (!ebook_id || ebook_id === "") { + console.log("No Ebook id found in response."); + } + if (!comic_id || comic_id === "") { + console.log("No Comic id found in response."); + } + if (!manga_id || manga_id === "") { + console.log("No Manga id found in response."); } } catch (error) { console.error("Error in post-response script:", error.message); diff --git a/bruno/media-items/List Media Items Sorted.yml b/bruno/media-items/List Media Items Sorted.yml index 8243d72..baaafef 100644 --- a/bruno/media-items/List Media Items Sorted.yml +++ b/bruno/media-items/List Media Items Sorted.yml @@ -5,10 +5,23 @@ info: http: method: GET - url: "{{base_url}}/api/media-items?library_id={{library_id}}&sort=title+ASC&limit=10&offset=0" + url: "{{base_url}}/api/media-items?library_id={{ebook_library_id}}&sort=title+ASC&limit=10&offset=0" headers: - name: "" value: application/json + params: + - name: library_id + value: "{{ebook_library_id}}" + type: query + - name: sort + value: title+ASC + type: query + - name: limit + value: "10" + type: query + - name: offset + value: "0" + type: query auth: inherit settings: diff --git a/bruno/media-items/List Media Items.yml b/bruno/media-items/List Media Items.yml index f1e8e48..4d428ba 100644 --- a/bruno/media-items/List Media Items.yml +++ b/bruno/media-items/List Media Items.yml @@ -5,10 +5,20 @@ info: http: method: GET - url: "{{base_url}}/api/media-items?library_id={{library_id}}&limit=20&offset=0" + url: "{{base_url}}/api/media-items?library_id={{ebook_library_id}}&limit=20&offset=0" headers: - name: "" value: application/json + params: + - name: library_id + value: "{{ebook_library_id}}" + type: query + - name: limit + value: "20" + type: query + - name: offset + value: "0" + type: query auth: inherit settings: