5 // You can either provide them as environment variables
6 // or hard-code them in the empty strings below.
7 $baseUrl = getenv('BS_URL') ?: '';
8 $clientId = getenv('BS_TOKEN_ID') ?: '';
9 $clientSecret = getenv('BS_TOKEN_SECRET') ?: '';
14 // Define the time we wait in between making API requests,
15 // to help keep within rate limits and avoid exhausting resources.
16 $apiPauseMicrosecs = 100;
18 // Clean up the base path
19 $baseUrl = rtrim($baseUrl, '/');
21 // Get all items from the system keyed by ID
22 $shelvesById = keyById(getAllOfAtListEndpoint("api/shelves", []));
23 $booksById = keyById(getAllOfAtListEndpoint("api/books", []));
25 // Fetch books that are on each shelf
26 foreach ($shelvesById as $id => $shelf) {
27 $shelvesById[$id]['books'] = getBooksForShelf($id);
28 usleep($apiPauseMicrosecs);
31 // For each book, fetch its contents list
32 foreach ($booksById as $id => $book) {
33 $booksById[$id]['contents'] = apiGetJson("api/books/{$id}")['contents'] ?? [];
34 usleep($apiPauseMicrosecs);
37 // Cycle through the shelves and display their contents
38 $isBookShownById = [];
39 foreach ($shelvesById as $id => $shelf) {
40 output($shelf, 'bookshelf', [false]);
41 $bookCount = count($shelf['books']);
42 for ($i=0; $i < $bookCount; $i++) {
43 $bookId = $shelf['books'][$i];
44 $book = $booksById[$bookId] ?? null;
46 outputBookAndContents($book, [false, $i === $bookCount - 1]);
47 $isBookShownById[strval($book['id'])] = true;
52 // Cycle through books and display any that have not been
53 // part of a shelve's output
54 foreach ($booksById as $id => $book) {
55 if (isset($isBookShownById[$id])) {
59 outputBookAndContents($book, [false]);
63 * Output a book for display, along with its contents.
65 function outputBookAndContents(array $book, array $depthPath): void
67 output($book, 'book', $depthPath);
68 $childCount = count($book['contents']);
69 for ($i=0; $i < $childCount; $i++) {
70 $child = $book['contents'][$i];
71 $childPath = array_merge($depthPath, [($i === $childCount - 1)]);
72 output($child, $child['type'], $childPath);
73 $pages = $child['pages'] ?? [];
74 $pageCount = count($pages);
75 for ($j=0; $j < count($pages); $j++) {
77 $innerPath = array_merge($childPath, [($j === $pageCount - 1)]);
78 output($page, 'page', $innerPath);
84 * Output a single item for display.
86 function output(array $item, string $type, array $depthPath): void
88 $upperType = strtoupper($type);
90 $depth = count($depthPath);
91 for ($i=0; $i < $depth; $i++) {
92 $isLastAtDepth = $depthPath[$i];
93 $end = ($i === $depth - 1);
95 $prefix .= $isLastAtDepth ? '└' : '├';
97 $prefix .= $isLastAtDepth ? ' ' : '│ ';
100 echo $prefix . "── {$upperType} {$item['id']}: {$item['name']}\n";
104 * Key an array of array-based data objects by 'id' value.
106 function keyById(array $data): array
109 foreach ($data as $item) {
117 * Get the books for the given shelf ID.
118 * Returns an array of the book IDs.
120 function getBooksForShelf(int $shelfId): array
122 $resp = apiGetJson("api/shelves/{$shelfId}");
123 return array_map(function ($bookData) {
124 return $bookData['id'];
125 }, $resp['books'] ?? []);
129 * Consume all items from the given API listing endpoint.
131 function getAllOfAtListEndpoint(string $endpoint, array $params): array
133 global $apiPauseMicrosecs;
139 $endpoint = $endpoint . '?' . http_build_query(array_merge($params, ['count' => $count, 'offset' => $offset]));
140 $resp = apiGetJson($endpoint);
142 $total = $resp['total'] ?? 0;
143 $new = $resp['data'] ?? [];
144 array_push($all, ...$new);
146 usleep($apiPauseMicrosecs);
147 } while ($offset < $total);
153 * Make a simple GET HTTP request to the API.
155 function apiGet(string $endpoint): string
157 global $baseUrl, $clientId, $clientSecret;
158 $url = rtrim($baseUrl, '/') . '/' . ltrim($endpoint, '/');
159 $opts = ['http' => ['header' => "Authorization: Token {$clientId}:{$clientSecret}"]];
160 $context = stream_context_create($opts);
161 return @file_get_contents($url, false, $context);
165 * Make a simple GET HTTP request to the API &
166 * decode the JSON response to an array.
168 function apiGetJson(string $endpoint): array
170 $data = apiGet($endpoint);
171 $array = json_decode($data, true);
173 if (!is_array($array)) {
174 dd("Failed request to {$endpoint}", $data);
181 * DEBUG: Dump out the given variables and exit.
183 function dd(...$args)
185 foreach ($args as $arg) {