Terminal epub reader with inline images

Overview

nuber

Inspired by epy, nuber is an Epub terminal reader with inline images written with Rust and Python using รœberzug.

title

Features

  • Display images in terminal.
  • Movement with vim keys hjkl.
  • Table of content navigation with t.
  • Dynamic window resize.
  • Rememebers last position per book.

Installation

Requirements: maturin, pip

$ git clone https://github.com/mtshrmn/nuber.git --recursive && cd nuber
$ cd rust-html2text && git apply ../html2text.patch && cd ..
$ maturin build --release
$ pip install .

Usage

$ nuber --help
Usage: nuber [OPTIONS] BOOK

Options:
  --help  Show this message and exit.

Contribute

Requirements: maturin, poetry

$ git clone https://github.com/mtshrmn/nuber.git --recursive && cd nuber
$ cd rust-html2text && git apply ../html2text.patch && cd ..
$ poetry install && poetry shell
$ maturin develop && exit
$ poetry run nuber
Comments
  • Add build instructions

    Add build instructions

    I'm having a hard time trying to build this project.

    I'm not able to make the rust module build by itself if I invoke maturin directly, even if I specifically use maturin 11.3 (as pyproject.toml specifies maturin>=0.11,<0.12)

    output from maturin build
    $ maturin build
    ๐Ÿน Building a mixed python/rust project
    ๐Ÿ”— Found pyo3 bindings
    ๐Ÿ Found CPython 3.10 at python3.10
    ๐Ÿ“ฆ Built source distribution to /home/h7x4/git/nuber/target/wheels/nuber-1.1.0.tar.gz
     Compiling cfg-if v1.0.0
     Compiling libc v0.2.101
     Compiling autocfg v1.0.1
     Compiling proc-macro2 v1.0.28
     Compiling unicode-xid v0.2.2
     Compiling syn v1.0.74
     Compiling getrandom v0.1.16
     Compiling ppv-lite86 v0.2.10
     Compiling lazy_static v1.4.0
     Compiling siphasher v0.3.6
     Compiling crossbeam-utils v0.8.5
     Compiling scopeguard v1.1.0
     Compiling crossbeam-epoch v0.9.5
     Compiling new_debug_unreachable v1.0.4
     Compiling serde v1.0.127
     Compiling rayon-core v1.9.1
     Compiling crc32fast v1.2.1
     Compiling pyo3-build-config v0.14.3
     Compiling proc-macro-hack v0.5.19
     Compiling mac v0.1.1
     Compiling log v0.4.16
     Compiling once_cell v1.8.0
     Compiling utf-8 v0.7.6
     Compiling precomputed-hash v0.1.1
     Compiling pkg-config v0.3.19
     Compiling cc v1.0.69
     Compiling adler v1.0.2
     Compiling fnv v1.0.7
     Compiling byteorder v1.4.3
     Compiling strsim v0.10.0
     Compiling ident_case v1.0.1
     Compiling memchr v2.4.0
     Compiling adler32 v1.2.0
     Compiling bitflags v1.2.1
     Compiling either v1.6.1
     Compiling smallvec v1.6.1
     Compiling unindent v0.1.8
     Compiling anyhow v1.0.43
     Compiling weezl v0.1.5
     Compiling regex-syntax v0.6.25
     Compiling color_quant v1.1.0
     Compiling xml-rs v0.8.4
     Compiling bytemuck v1.7.2
     Compiling percent-encoding v2.1.0
     Compiling scoped_threadpool v0.1.9
     Compiling unicode-width v0.1.8
     Compiling remove_dir_all v0.5.3
     Compiling instant v0.1.10
     Compiling lock_api v0.4.4
     Compiling phf_shared v0.8.0
     Compiling memoffset v0.6.4
     Compiling miniz_oxide v0.4.4
     Compiling num-traits v0.2.14
     Compiling num-integer v0.1.44
     Compiling rayon v1.5.1
     Compiling num-rational v0.3.2
     Compiling num-iter v0.1.42
     Compiling futf v0.1.4
     Compiling miniz_oxide v0.3.7
     Compiling deflate v0.8.6
     Compiling bzip2-sys v0.1.11+1.0.8
     Compiling gif v0.11.2
     Compiling phf v0.8.0
     Compiling tendril v0.4.2
     Compiling crossbeam-channel v0.5.1
     Compiling png v0.16.8
     Compiling time v0.1.44
     Compiling num_cpus v1.13.0
     Compiling getrandom v0.2.3
     Compiling parking_lot_core v0.8.3
     Compiling quote v1.0.9
     Compiling aho-corasick v0.7.18
     Compiling rand_core v0.5.1
     Compiling rand_core v0.6.3
     Compiling flate2 v1.0.20
     Compiling paste-impl v0.1.18
     Compiling pyo3 v0.14.3
     Compiling bzip2 v0.4.3
     Compiling parking_lot v0.11.1
     Compiling rand_chacha v0.2.2
     Compiling rand_pcg v0.2.1
     Compiling rand_chacha v0.3.1
     Compiling crossbeam-deque v0.8.1
     Compiling paste v0.1.18
     Compiling rand v0.7.3
     Compiling regex v1.5.4
     Compiling rand v0.8.4
     Compiling string_cache v0.8.1
     Compiling phf_generator v0.8.0
     Compiling tempfile v3.2.0
     Compiling string_cache_codegen v0.5.1
     Compiling phf_codegen v0.8.0
     Compiling markup5ever v0.10.1
     Compiling jpeg-decoder v0.1.22
     Compiling tiff v0.6.1
     Compiling xml5ever v0.16.1
     Compiling darling_core v0.13.0
     Compiling pyo3-macros-backend v0.14.3
     Compiling html5ever v0.25.1
     Compiling thiserror-impl v1.0.26
     Compiling indoc-impl v0.3.6
     Compiling image v0.23.14
     Compiling indoc v0.3.6
     Compiling thiserror v1.0.26
     Compiling zip v0.5.13
     Compiling darling_macro v0.13.0
     Compiling epub v1.2.3
     Compiling pyo3-macros v0.14.3
     Compiling markup5ever_rcdom v0.1.0
     Compiling darling v0.13.0
     Compiling enumset_derive v0.5.5
     Compiling html2text v0.2.1 (/home/h7x4/git/nuber/rust-html2text)
     Compiling enumset v1.0.7
     Compiling nuber v1.1.0 (/home/h7x4/git/nuber)
    error[E0407]: method `decorate_header_start` is not a member of trait `TextDecorator`
     --> src/parser.rs:270:5
      |
    270 |       fn decorate_header_start(&mut self, level: usize) -> (String, Self::Annotation) {
      |       ^  --------------------- help: there is an associated function with a similar name: `decorate_code_start`
      |  _____|
      | |
    271 | |         ("".to_string(), RichAnnotation::Header(level))
    272 | |     }
      | |_____^ not a member of trait `TextDecorator`
    
    
    error[E0407]: method `decorate_header_end` is not a member of trait `TextDecorator`
     --> src/parser.rs:274:5
      |
    274 |       fn decorate_header_end(&mut self) -> String {
      |       ^  ------------------- help: there is an associated function with a similar name: `decorate_code_end`
      |  _____|
      | |
    275 | |         "".to_string()
    276 | |     }
      | |_____^ not a member of trait `TextDecorator`
    
    
    error[E0532]: expected tuple struct or tuple variant, found unit variant `RichAnnotation::Image`
      --> src/parser.rs:134:13
       |
    134  |             RichAnnotation::Image(_) => Some(Effect::Image.into()),
       |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: use this syntax instead: `RichAnnotation::Image`
       |
      ::: /home/h7x4/git/nuber/rust-html2text/src/render/text_renderer.rs:1471:5
       |
    1471 |     Image,
       |     ----- `RichAnnotation::Image` defined here
    
    
    error[E0532]: expected tuple struct or tuple variant, found unit variant `RichAnnotation::Image`
      --> src/parser.rs:146:41
       |
    146  |             RichAnnotation::Link(url) | RichAnnotation::Image(url) => Some(url),
       |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this syntax instead: `RichAnnotation::Image`
       |
      ::: /home/h7x4/git/nuber/rust-html2text/src/render/text_renderer.rs:1471:5
       |
    1471 |     Image,
       |     ----- `RichAnnotation::Image` defined here
    
    
    error[E0532]: expected tuple struct or tuple variant, found unit variant `RichAnnotation::Image`
      --> src/parser.rs:207:16
       |
    207  |         if let RichAnnotation::Image(url) = annotation {
       |                ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this syntax instead: `RichAnnotation::Image`
       |
      ::: /home/h7x4/git/nuber/rust-html2text/src/render/text_renderer.rs:1471:5
       |
    1471 |     Image,
       |     ----- `RichAnnotation::Image` defined here
    
    
    error[E0050]: method `decorate_image` has 3 parameters but the declaration in trait `decorate_image` has 2
     --> src/parser.rs:286:23
      |
    286 |     fn decorate_image(&mut self, _title: &str, src: &str) -> (String, Self::Annotation) {
      |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 parameters, found 3
      |
      = note: `decorate_image` from trait: `fn(&mut Self, &str) -> (String, <Self as TextDecorator>::Annotation)`
    
    
    error[E0046]: not all trait items implemented, missing: `header_prefix`
     --> src/parser.rs:215:1
      |
    215 | impl<'a> TextDecorator for Decorator<'a> {
      | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `header_prefix` in implementation
      |
      = help: implement the missing item: `fn header_prefix(&mut self, _: usize) -> String { todo!() }`
    
    
    error[E0599]: no variant or associated item named `Header` found for enum `RichAnnotation` in the current scope
     --> src/parser.rs:140:29
      |
    140 |             RichAnnotation::Header(_) => Some(Effect::Bold.into()),
      |                             ^^^^^^ variant or associated item not found in `RichAnnotation`
    
    
    error[E0599]: no variant or associated item named `Header` found for enum `RichAnnotation` in the current scope
     --> src/parser.rs:271:42
      |
    271 |         ("".to_string(), RichAnnotation::Header(level))
      |                                          ^^^^^^ variant or associated item not found in `RichAnnotation`
    
    
    error[E0618]: expected function, found enum variant `RichAnnotation::Image`
     --> src/parser.rs:300:21
      |
    300 |         (first_row, RichAnnotation::Image(src.to_string()))
      |                     ^^^^^^^^^^^^^^^^^^^^^-----------------
      |                     |
      |                     call expression requires function
      |
    help: `RichAnnotation::Image` is a unit variant, you need to write it without the parentheses
      |
    300 -         (first_row, RichAnnotation::Image(src.to_string()))
    300 +         (first_row, RichAnnotation::Image)
      |
    
    
      Buildingerror: aborting due to 10 previous errors
    
    
    Some errors have detailed explanations: E0046, E0050, E0407, E0532, E0599, E0618.
    
    For more information about an error, try `rustc --explain E0046`.
    
    error: could not compile `nuber` due to 11 previous errors
    ๐Ÿ’ฅ maturin failed
    Caused by: Failed to build a native library through cargo
    Caused by: Cargo build finished with "exit status: 101": `cargo rustc --message-format json --manifest-path Cargo.toml --lib --`
    

    I would suspect that the rust module was broken, but it seems like you made a commit to the project ~2 weeks ago, so that might not be the case?

    This problem disappears if I rather try to invoke maturin through poetry. But based on the error, it seems like maturin doesn't even get invoked at all? Rust-Python bindings seems to be missing.

    output from poetry run nuber
    $ poetry run nuber
    Traceback (most recent call last):
    File "<string>", line 1, in <module>
    File "/usr/lib/python3.10/importlib/__init__.py", line 126, in import_module
      return _bootstrap._gcd_import(name[level:], package, level)
    File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
    File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
    File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
    File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
    File "<frozen importlib._bootstrap_external>", line 883, in exec_module
    File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
    File "/home/h7x4/git/nuber/nuber/__init__.py", line 1, in <module>
      from .reader import Reader
    File "/home/h7x4/git/nuber/nuber/reader.py", line 6, in <module>
      from .rust_module.nuber import Book, Image
    ModuleNotFoundError: No module named 'nuber.rust_module.nuber'
    

    Do you think you could add some documentation on how to build this project?

    opened by h7x4 2
  • Shuting ebooks unorthodoxly

    Shuting ebooks unorthodoxly

    Dunno if you or anyone else thinks this is a problem but shuting down a book with ctrl+c or shuting the window itself won't save current progress

    Understanding that saving in every keystroke is infact bloat I'd like to propose savestates to happen in page flipping as well not just when pressing q,this would mitigate the problem of forgeting your last read chapter or having to travel accros multiple pages when reading lots of diffrent books but still not causing unecessery resource usage(if it happens at all)when going up and down a page

    to summerize I think it wourld be cool to save on q h and l keystrokes and if not resource heavy ok j and k as well

    (PS Seriously I love this e-reader thanks for your effort and Id do a commit myself to help out here if I knew rust at all)

    enhancement 
    opened by Coalms 2
  • Not working with Konsole terminal.

    Not working with Konsole terminal.

    Got garbled error output from Konsole (bash) yet works perfectly in Alacritty , unable to log errors from Konsole as cursor disappeared and so is any input. Screenshot to help: Screenshot_20220306_000438

    opened by TheGalaxiaN 1
  • Display progress

    Display progress

    Similar to epy, nuber should display the progress of the book in percentages. I've been busy lately and I don't know when I'll have more free time to continue working on this project (Hopefully in 4 months). So I hope maybe someone can help me create this reader.

    Because I know that not a lot of people here know rust, I've written the rust part myself:

    --- a/src/book.rs
    +++ b/src/book.rs
    @@ -90,6 +90,27 @@ impl Book {
                 .collect()
         }
    
    +    fn number_of_lines(&mut self) -> Vec<usize> {
    +        let current_chapter = self.book.get_current_page();
    +        self.set_current_chapter(0);
    +
    +        let temp_dir = self.temp_dir.path().to_owned();
    +        let decorator = Decorator::new(temp_dir.as_path(), self.term_info);
    +
    +        let mut number_of_lines = Vec::new();
    +
    +        // iterate over all of the chapters and sum up the lines
    +        while self.next_chapter() {
    +            let render_tree = parse(self.get_current_str().as_bytes());
    +            let lines = render_tree
    +                .render(self.term_info.col as usize, decorator)
    +                .into_lines();
    +            number_of_lines.push(lines.len());
    +        }
    +        self.set_current_chapter(current_chapter);
    +        number_of_lines
    +    }
    +
    

    This adds the function Book().number_of_lines() which returns all the line numbers of each chapter. All that is left is to display the progress of the book. epy does it quite nicely so I would do something similar.

    enhancement help wanted 
    opened by mtshrmn 1
  • on resize - chgat() returns ERR when query is an empty string

    on resize - chgat() returns ERR when query is an empty string

    How to recreate:

    1. search for nothing (press on / and then return)
    2. resize

    for some reason this doesn't crash on specific chapters (or vise versa, crashes on specific pages).

    Traceback:

    Traceback (most recent call last):
      File "/home/suerflowz/.local/bin/nuber", line 8, in <module>
        sys.exit(main())
      File "/home/suerflowz/.local/lib/python3.10/site-packages/click/core.py", line 1128, in __call__
        return self.main(*args, **kwargs)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/click/core.py", line 1053, in main
        rv = self.invoke(ctx)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/click/core.py", line 1395, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/click/core.py", line 754, in invoke
        return __callback(*args, **kwargs)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/nuber/__init__.py", line 18, in main
        reader.loop() # type: ignore
      File "/home/suerflowz/.local/lib/python3.10/site-packages/ueberzug/lib/v0/__init__.py", line 378, in decorator
        return function(*args, canvas=self, **kwargs)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/nuber/reader.py", line 434, in loop
        self.on_key(ch, canvas)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/nuber/reader.py", line 388, in on_key
        action(canvas)
      File "/home/suerflowz/.local/lib/python3.10/site-packages/nuber/reader.py", line 362, in action_resize
        self.highlight_query()
      File "/home/suerflowz/.local/lib/python3.10/site-packages/nuber/reader.py", line 164, in highlight_query
        self.pad.chgat(row + row_offset, start_col, chars_len, f)
    _curses.error: chgat() returned ERR
    
    bug 
    opened by mtshrmn 0
Releases(v1.1.1)
Owner
Moshe Sherman
I just write random stuff that sometimes compiles.
Moshe Sherman
๐Ÿ“ฆ A command line utility to put text in a box.

boxie A command line utility to put text in a box. Installation pip install boxie If you are on Linux you may need to use sudo to access this globally

Eliaz Bobadilla 10 Jun 30, 2022
dotfilery, configuration, environment settings, automation, etc.

โ”Œโ”ฌโ”โ”Œโ”€โ”โ”Œโ”€โ”โ”Œโ”€โ”โ”ฌ โ”ฌโ”Œโ”ฌโ”โ”ฌ โ”ฌโ”ฌโ”Œโ”€โ” โ”‚โ”‚โ”‚โ”œโ”ค โ”‚ โ”ฌโ”œโ”€โ”คโ”‚ โ”‚ โ”‚ โ”œโ”€โ”คโ”‚โ”‚ :: bits & bobs, dots & things. โ”ด โ”ดโ””โ”€โ”˜โ””โ”€โ”˜โ”ด โ”ดโ”ดโ”€โ”˜โ”ด โ”ด โ”ด โ”ดโ”ดโ””โ”€โ”˜ @megalithic ๐Ÿš€ Instal

Seth Messer 89 Dec 25, 2022
A command-line utility that creates projects from cookiecutters (project templates), e.g. Python package projects, VueJS projects.

Cookiecutter A command-line utility that creates projects from cookiecutters (project templates), e.g. creating a Python package project from a Python

18.6k Dec 30, 2022
slipit is a command line utility for creating archives with path traversal elements.

slipit is a command line utility for creating archives with path traversal elements. It is basically a successor of the famous evilarc utility with an extended feature set and improved base functiona

usd AG 35 Dec 23, 2022
CmdTube is a Python CLI library for searching, downloading, and watching YouTube tutorials

CmdTube is a Python CLI library for searching, downloading, and watching YouTube tutorials. This library was made with programmers in mind and it's dedicated to every programmer who watches YouTube v

Samuel Ayomide Ogunleke 2 Aug 22, 2022
Pequeno joguinho pra vocรช rodar no seu terminal

JokenPython Pequeno joguinho pra vocรช rodar no seu terminal Olรก! Joguinho legal pra vc rodar no seu terminal!! (rode no terminal, pra melhor experienc

Scott 4 Nov 25, 2021
A supercharged AWS command line interface (CLI).

SAWS Motivation AWS CLI Although the AWS CLI is a great resource to manage your AWS-powered services, it's tough to remember usage of: 70+ top-level c

Donne Martin 5.1k Jan 05, 2023
Official AIdea command line tool

AIdea CLI Official AIdea command line tool for https://aidea-web.tw. Installation Make sure you have installed both Python 3 and pip package manager.

AIdea 5 Dec 15, 2021
A very simple and lightweight ToDo app using python that can be used from the command line

A very simple and lightweight ToDo app using python that can be used from the command line

Nilesh Sengupta 2 Jul 20, 2022
๐ŸŽฎ An easy to use tool to change the mapping of your input device buttons.

Input Remapper Formerly Key Mapper An easy to use tool to change the mapping of your input device buttons. Supports mice, keyboards, gamepads, X11, Wa

Tobi 1.9k Jan 05, 2023
Interactive Redis: A Terminal Client for Redis with AutoCompletion and Syntax Highlighting.

Interactive Redis: A Cli for Redis with AutoCompletion and Syntax Highlighting. IRedis is a terminal client for redis with auto-completion and syntax

2.2k Dec 29, 2022
A very simple OpenContest command line client written in Python

OpenContest Client A very simple OpenContest command line client written in Python. The only dependency is the requests library. Tested with Linux onl

Ladue Computer Science 1 May 25, 2022
Apple Silicon 'top' CLI

asitop pip install asitop What A nvtop/htop style/inspired command line tool for Apple Silicon (aka M1) Macs. Note that it requires sudo to run due to

Timothy Liu 1.2k Dec 31, 2022
A command line interface to buy things in stregsystemet

Stregsystemet-CLI This repository is the Stregsystemet CLI, to buy things in Stregsystemet, at AAU. Use of this cli-tool is at your own risk and there

F-klubben 14 Oct 18, 2022
A simple cli utility for importing or exporting dashboard json definitions using the Grafana HTTP API.

P4CMD ๐ŸŒด A Python Perforce package that doesn't bring in any other packages to work. Relies on p4cli installed on the system. p4cmd The p4cmd module h

Beam Connectivity 31 Jan 06, 2023
This is a Command Line program to interact with your NFTs, Cryptocurrencies etc

This is a Command Line program to interact with your NFTs, Cryptocurrencies etc. via the ThirdWeb Platform. This is just a fun little project that I made to be able to connect to blockchains and Web3

Arpan Pandey 5 Oct 02, 2022
A python package to display progress of loops to the user

ProgressBars A python package to display progress of loops to the user. Installation This package can be installed using pip. pip install progressbars

Matthias 3 Jan 16, 2022
๐Ÿ•ฐ The command line tool for scheduling Python scripts

hickory is a simple command line tool for scheduling Python scripts.

Max Humber 146 Dec 07, 2022
Tool for HackMyVM platform

HMV-cli It is a tool for the HackMyVM platform. With this tool you will be able to see the machines you have pending, filter by difficulty, download d

bitc0de 11 Sep 19, 2022
A simple file transfer tools, similar to rz / sz but compatible with tmux (control mode), which works with iTerm2 and has a nice progress bar

trzsz A simple file transfer tools, similar to rz/sz but compatible with tmux (control mode), which works with iTerm2 and has a nice progress bar. Why

561 Jan 05, 2023