From 25abf9bc7fd3e6f5b5aabf745cef7d217cde4262 Mon Sep 17 00:00:00 2001 From: Joris Date: Sat, 21 Jan 2023 14:10:47 +0100 Subject: Setup initial view of covers --- .gitignore | 1 + LICENSE | 24 ++++++++++++++++++++++++ README.md | 38 ++++++++++++++++++++++++++++++++++++++ bin/dev-server | 21 +++++++++++++++++++++ bin/get-data | 23 +++++++++++++++++++++++ bin/view | 16 ++++++++++++++++ flake.lock | 42 ++++++++++++++++++++++++++++++++++++++++++ flake.nix | 20 ++++++++++++++++++++ public/index.html | 6 ++++++ public/main.css | 21 +++++++++++++++++++++ public/main.js | 19 +++++++++++++++++++ 11 files changed, 231 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100755 bin/dev-server create mode 100755 bin/get-data create mode 100755 bin/view create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 public/index.html create mode 100644 public/main.css create mode 100644 public/main.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e34fb35 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +public/data.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e7e6c8 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Books + +Visualize a book library. + +## Book library + +Organize folders as you wish, only `metadata.json` files matter: + +```json +{ + "title": "Title of the Book", + "subtitle": "Optional subtitle", + "authors": [ "Author 1", "Author 2" ], + "authorsSort": "Author sorting", + "genres": [ "Foo", "Bar", "Baz" ], + "year": "1234", + "read": true +} +``` + +Each `metadata.json` file correspond to a book, and there **must** be a cover +named `cover.ext` in the same directory. Any extension works. + +## Show library + +View the book library by running: + + ./bin/view browser-cmd path-to-books + +## Dev server + +Enter nix shell: + + nix develop --command ./watch-command + +Then run the dev-server: + + ./bin/dev-server path-to-books diff --git a/bin/dev-server b/bin/dev-server new file mode 100755 index 0000000..4136091 --- /dev/null +++ b/bin/dev-server @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail +cd $(dirname "$0")/.. + +if [ "$#" == 1 ]; then + BOOK_DIR="$1" +else + echo "usage: $0 path-to-book-directory" + exit 1 +fi + +# Watch + +clear +echo "Open your browser at file://$PWD/public/index.html" +echo + +BUILD_CMD="./bin/get-data $BOOK_DIR > public/data.js && echo public/data.js updated." +watchexec \ + --watch "$BOOK_DIR" \ + -- "$BUILD_CMD" diff --git a/bin/get-data b/bin/get-data new file mode 100755 index 0000000..0fbbd2f --- /dev/null +++ b/bin/get-data @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail +cd $(dirname "$0")/.. + +if [ "$#" == 1 ]; then + BOOK_DIR="$1" +else + echo "usage: $0 path-to-book-directory" + exit 1 +fi + +echo "const books = [" + +for METADATA in $(find "$BOOK_DIR" -name 'metadata.json'); do + DIR=$(dirname "$METADATA") + COVER=$(ls $DIR/cover.*) + + cat "$METADATA" | head -n -1 + echo ", \"cover\": \"$COVER\"" + echo "}," +done + +echo "]" diff --git a/bin/view b/bin/view new file mode 100755 index 0000000..159bc6a --- /dev/null +++ b/bin/view @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail +cd $(dirname "$0")/.. + +if [ "$#" == 2 ]; then + BROWSER="$1" + BOOK_DIR="$2" +else + echo "usage: $0 browser-cmd path-to-book-directory" + exit 1 +fi + +bin/get-data "$BOOK_DIR" > "public/data.js" +TMP_DIR=$(mktemp --directory) +cp public/* "$TMP_DIR" +eval "$BROWSER $TMP_DIR/index.html" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..4775dd0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1674290375, + "narHash": "sha256-yRD5bQWzu6UpDC6cybXwFizWHJCHcJTw35Ms7Q/nzU4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "68403fe04f6c85853ddd389c9e58dd9b9c8b0a36", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d2e3a07 --- /dev/null +++ b/flake.nix @@ -0,0 +1,20 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in with pkgs; { + devShell = mkShell { + buildInputs = [ + psmisc + watchexec + ]; + }; + } + ); +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..13136da --- /dev/null +++ b/public/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/main.css b/public/main.css new file mode 100644 index 0000000..711aaf2 --- /dev/null +++ b/public/main.css @@ -0,0 +1,21 @@ +body { + margin: 2rem; +} + +.g-Books { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + grid-gap: 1rem; + +} + +.g-Book { + width: 13rem; + align-self: center; + border: 1px solid #DDDDDD; +} + +.g-Book:hover { + transform: scale(1.1); + transition: transform 0.1s linear; +} diff --git a/public/main.js b/public/main.js new file mode 100644 index 0000000..29de235 --- /dev/null +++ b/public/main.js @@ -0,0 +1,19 @@ +const sortedBooks = books.sort((a, b) => + a.authorsSort == b.authorsSort + ? a.date > b.date + : a.authorsSort > b.authorsSort) + +const view = h('div', + { className: 'g-Books' }, + ...sortedBooks.map(book => h('img', { className: 'g-Book', src: book.cover }))) + +document.body.appendChild(view) + +// Helpers + +function h(tagName, attrs, ...children) { + let elem = document.createElement(tagName) + elem = Object.assign(elem, attrs) + for (const child of children) elem.append(child) + return elem +} -- cgit v1.2.3