diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rwxr-xr-x | bin/dev-server | 11 | ||||
-rwxr-xr-x | bin/get-books | 24 | ||||
-rwxr-xr-x | bin/view | 16 | ||||
-rw-r--r-- | flake.nix | 13 | ||||
-rw-r--r-- | src/library/__init__.py | 0 | ||||
-rw-r--r-- | src/library/command.py | 21 | ||||
-rw-r--r-- | src/main.py | 56 | ||||
-rw-r--r-- | src/view/__init__.py | 0 | ||||
-rw-r--r-- | src/view/client/book.ts (renamed from src/book.ts) | 0 | ||||
-rw-r--r-- | src/view/client/lib/functions.ts (renamed from src/lib/functions.ts) | 0 | ||||
-rw-r--r-- | src/view/client/lib/i18n.ts (renamed from src/lib/i18n.ts) | 0 | ||||
-rw-r--r-- | src/view/client/lib/rx.ts (renamed from src/lib/rx.ts) | 0 | ||||
-rw-r--r-- | src/view/client/lib/search.ts (renamed from src/lib/search.ts) | 0 | ||||
-rw-r--r-- | src/view/client/main.ts (renamed from src/main.ts) | 0 | ||||
-rw-r--r-- | src/view/client/view/books.ts (renamed from src/view/books.ts) | 1 | ||||
-rw-r--r-- | src/view/client/view/components/modal.ts (renamed from src/view/components/modal.ts) | 0 | ||||
-rw-r--r-- | src/view/client/view/filters.ts (renamed from src/view/filters.ts) | 0 | ||||
-rw-r--r-- | src/view/command.py | 17 | ||||
-rw-r--r-- | src/view/public/index.html (renamed from public/index.html) | 0 | ||||
-rw-r--r-- | src/view/public/main.css (renamed from public/main.css) | 0 | ||||
-rw-r--r-- | src/view/tsconfig.json (renamed from tsconfig.json) | 4 |
23 files changed, 120 insertions, 60 deletions
@@ -1 +1,2 @@ -public/*.js +__pycache__ +src/view/public/*.js @@ -25,18 +25,12 @@ read = "Read" Each `metadata.toml` 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: +In nix shell (`nix develop`), run: - nix develop + ./bin/dev-server path-to-books -Then run the dev-server: +## Show library - ./bin/dev-server path-to-books + BOOK_LIBRARY=path-to-books python src/main.py library diff --git a/bin/dev-server b/bin/dev-server index 2d1bf99..c98fdc5 100755 --- a/bin/dev-server +++ b/bin/dev-server @@ -3,7 +3,7 @@ set -euo pipefail cd $(dirname "$0")/.. if [ "$#" == 1 ]; then - BOOK_DIR="$1" + BOOK_LIBRARY="$1" else echo "usage: $0 path-to-book-directory" exit 1 @@ -11,17 +11,18 @@ fi # Watch books -BUILD_BOOKS_CMD="./bin/get-books $BOOK_DIR > public/books.js && echo public/books.js updated." +BUILD_BOOKS_CMD="echo \"const bookLibrary=\" > src/view/public/books.js && python src/main.py library >> src/view/public/books.js && echo src/view/public/books.js updated." watchexec \ - --watch "$BOOK_DIR" \ + --watch "$BOOK_LIBRARY" \ -- "$BUILD_BOOKS_CMD" & # Watch TypeScript +cd src/view CHECK="echo -e 'Checking TypeScript…\n' && tsc --checkJs" -BUILD="esbuild --bundle src/main.ts --target=es2017 --outdir=public" +BUILD="esbuild --bundle main.ts --target=es2017 --outdir=public" SHOW="echo -e '\nOpen $PWD/public/index.html'" watchexec \ --clear \ - --watch src \ + --watch client \ -- "$CHECK && $BUILD && $SHOW" diff --git a/bin/get-books b/bin/get-books deleted file mode 100755 index 1c9bf67..0000000 --- a/bin/get-books +++ /dev/null @@ -1,24 +0,0 @@ -#!/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 bookLibrary = [" - -for METADATA in $(find "$BOOK_DIR" -name 'metadata.toml'); do - DIR=$(dirname "$METADATA") - COVER=$(ls $DIR/cover.*) - - TOML=$(cat "$METADATA") - WITH_COVER=$(echo -e "$TOML\ncover = \"$COVER\"") - echo "$WITH_COVER" | toml2json - echo "," -done - -echo "]" diff --git a/bin/view b/bin/view deleted file mode 100755 index 4fd4efc..0000000 --- a/bin/view +++ /dev/null @@ -1,16 +0,0 @@ -#!/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 - -TMP_DIR=$(mktemp --directory) -cp public/* "$TMP_DIR" -bin/get-books "$BOOK_DIR" > "$TMP_DIR/books.js" -eval "$BROWSER $TMP_DIR/index.html" @@ -8,15 +8,24 @@ flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; + + ebook-convert = with pkgs; writeShellScriptBin "ebook-convert" '' + set -euo pipefail + ${calibre}/bin/ebook-convert "$@" + ''; in with pkgs; { devShell = mkShell { buildInputs = [ esbuild nodePackages.typescript psmisc # fuser - python3 - toml2json watchexec + + # CLI + python311 + python311Packages.requests + python311Packages.pillow + ebook-convert ]; }; } diff --git a/src/library/__init__.py b/src/library/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/library/__init__.py diff --git a/src/library/command.py b/src/library/command.py new file mode 100644 index 0000000..6b8577e --- /dev/null +++ b/src/library/command.py @@ -0,0 +1,21 @@ +import glob +import json +import os +import tomllib + +def run(book_library): + print(get(book_library)) + +def get(book_library): + metadatas = [] + + for path in glob.glob(f'{book_library}/**/metadata.toml', recursive=True): + with open(path, 'rb') as f: + directory = os.path.dirname(os.path.realpath(path)) + metadata = tomllib.load(f) + for p in glob.glob(f'{directory}/cover.*'): + metadata['cover'] = p + break + metadatas.append(metadata) + + return json.dumps(metadatas) diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..618cc5a --- /dev/null +++ b/src/main.py @@ -0,0 +1,56 @@ +# Manage book library. +# +# Required dependencies: +# +# - python >= 3.11 +# - requests +# - pillow +# - ebook-convert CLI (from calibre) + +import sys +import os + +import library.command +import view.command + +def print_help(title='Manage book library'): + print(f"""{title} + +- Print library metadata as json: + + $ python {sys.argv[0]} library + +- View books in web page: + + $ python {sys.argv[0]} view browser-cmd + +Environment variables: + + BOOK_LIBRARY: path to book library.""") + +def get_book_library(): + path = os.getenv('BOOK_LIBRARY') + if path is None or not os.path.isdir(path): + print_help(title='BOOK_LIBRARY environment variable is required.') + exit(1) + else: + return path + +def main(): + match sys.argv: + case [ _, 'library' ]: + book_library = get_book_library() + library.command.run(book_library) + case [ _, 'view', browser_cmd ]: + book_library = get_book_library() + view.command.run(book_library, browser_cmd) + case [ _, '--help' ]: + print_help() + case [ _, '-h' ]: + print_help() + case _: + print_help('Command not found.') + exit(1) + +if __name__ == "__main__": + main() diff --git a/src/view/__init__.py b/src/view/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/view/__init__.py diff --git a/src/book.ts b/src/view/client/book.ts index 680cc11..680cc11 100644 --- a/src/book.ts +++ b/src/view/client/book.ts diff --git a/src/lib/functions.ts b/src/view/client/lib/functions.ts index 21fdad9..21fdad9 100644 --- a/src/lib/functions.ts +++ b/src/view/client/lib/functions.ts diff --git a/src/lib/i18n.ts b/src/view/client/lib/i18n.ts index 3716367..3716367 100644 --- a/src/lib/i18n.ts +++ b/src/view/client/lib/i18n.ts diff --git a/src/lib/rx.ts b/src/view/client/lib/rx.ts index bf01b6d..bf01b6d 100644 --- a/src/lib/rx.ts +++ b/src/view/client/lib/rx.ts diff --git a/src/lib/search.ts b/src/view/client/lib/search.ts index 026cb94..026cb94 100644 --- a/src/lib/search.ts +++ b/src/view/client/lib/search.ts diff --git a/src/main.ts b/src/view/client/main.ts index 5885871..5885871 100644 --- a/src/main.ts +++ b/src/view/client/main.ts diff --git a/src/view/books.ts b/src/view/client/view/books.ts index 5ec019a..aba55c1 100644 --- a/src/view/books.ts +++ b/src/view/client/view/books.ts @@ -66,6 +66,7 @@ function viewBook({ book, onSelect }: ViewBookParams): Html { { className: 'g-Book' }, h('img', { src: book.cover, + alt: book.title, className: 'g-Book__Image', onclick: () => onSelect(book) } diff --git a/src/view/components/modal.ts b/src/view/client/view/components/modal.ts index 5e845e1..5e845e1 100644 --- a/src/view/components/modal.ts +++ b/src/view/client/view/components/modal.ts diff --git a/src/view/filters.ts b/src/view/client/view/filters.ts index efe4115..efe4115 100644 --- a/src/view/filters.ts +++ b/src/view/client/view/filters.ts diff --git a/src/view/command.py b/src/view/command.py new file mode 100644 index 0000000..e50027f --- /dev/null +++ b/src/view/command.py @@ -0,0 +1,17 @@ +import os +import shutil +import subprocess +import tempfile +import time + +import library.command + +def run(book_library, browser_cmd): + tmp_dir = tempfile.mkdtemp() + directory = os.path.dirname(os.path.realpath(__file__)) + shutil.copytree(f'{directory}/public', tmp_dir, dirs_exist_ok=True) + with open(f'{tmp_dir}/books.js', 'w') as f: + json = library.command.get(book_library) + f.write(f'const bookLibrary = {json}') + browser_cmd = f'{browser_cmd} {tmp_dir}/index.html' + subprocess.run(browser_cmd.split(' ')) diff --git a/public/index.html b/src/view/public/index.html index ce4d568..ce4d568 100644 --- a/public/index.html +++ b/src/view/public/index.html diff --git a/public/main.css b/src/view/public/main.css index f361cbe..f361cbe 100644 --- a/public/main.css +++ b/src/view/public/main.css diff --git a/tsconfig.json b/src/view/tsconfig.json index 6c9e683..1e07c37 100644 --- a/tsconfig.json +++ b/src/view/tsconfig.json @@ -2,12 +2,12 @@ "compilerOptions": { "module": "amd", "target": "es2020", - "baseUrl": "src", + "baseUrl": "client", "outFile": "public/main.js", "noImplicitAny": true, "strictNullChecks": true, "removeComments": true, "preserveConstEnums": true }, - "include": ["src/**/*"] + "include": ["client/**/*"] } |