GithubHelp home page GithubHelp logo

bayang / jelu Goto Github PK

View Code? Open in Web Editor NEW
322.0 3.0 12.0 8.65 MB

Self hosted read and to-read list book tracker

License: MIT License

Kotlin 53.30% Java 0.31% Shell 0.12% Dockerfile 0.18% JavaScript 0.29% HTML 0.07% Vue 35.45% TypeScript 9.59% CSS 0.70%
reading reading-list to-read goodreads tracker books self-hosted kotlin vue3 vue

jelu's Introduction

JELU

GitHub Workflow Status GitHub GitHub release (latest by date) Docker Image Version (tag latest semver) Discord Crowdin

There is a matrix channel available : https://matrix.to/#/#jelu:matrix.org

Check the Official documentation for more detailed information.

Like Jelu or find it useful ? Offer me a coffee ☕

Purpose

This app main purpose is to track what you have read, what you are reading and what you want to read.

It acts as a self hosted "personal Goodreads" because I became tired of switching providers every time an online service was shut.

I also became tired of having to export and reimport my data each time with data loss in the process.

You have control on your data since Jelu offers an API you can script or integrate with any third party tool or service (which you cannot do with the vast majority of other online services).

All your data is now located into a single-file database which can be saved anywhere.

Features

  • track read books so you don't have to remember everything, and view your history (by year and month)
  • manage to-read list
  • Import history (from goodreads via csv export or a file with a list of ISBNs, one by line)
  • Export your data in a csv file
  • Import single books manually or automatically via online search (through title, authors or isbn)
  • Mark books as currently reading, finished or dropped
  • Books can be tagged and a tag page can display all books with that tag
  • You can use tags to create custom shelves
  • Links to third party providers are fetched online (google books, amazon, goodreads, librarythings) or computed from those providers id you could enter manually.
  • Author page with author detail and books from this author
  • Auto import author details from wikipedia
  • Auto merge authors (to fix duplicates resulting from automatic imports for example)
  • Provide embed code snippets so that books can be tracked in other sites, blogs or even markdown notes/journal
  • Provide some stats about your readings
  • Multi user support (ldap login, proxy authentication, see other user libraries)
  • provides an API
  • Write reviews and share them, see the reviews of other users on your instance
  • Fallback metadata providers (see official doc for configuration)
  • Metadata import from epub or opf files
  • Isbn scanning via camera on mobile

Usage

  • Import your existing history if you have a Goodreads account
  • Start recording your read books
  • Add books you want to read
  • Edit tags, books, import and change covers (either from a file on disk or from a url) ...

Installation

Java

  • download the java Jar from the releases section in a dedicated folder
  • go to this folder
  • start the jar (it is a spring fat jar so dependencies are included) : eg java -jar jelu-0.13.0.jar
  • If you want to tweak the default config (see src/main/resources/application.yml), just create a yaml file called application.yml in the same folder as the jar.

For example if you want the database to be located next to the jar file (instead of being located in the default ${user.home}/.jelu/database/ folder) :

jelu:
  database:
    path: .

The automatic metadata online search is provided for the moment through a calibre tool called fetch-ebook-metadata (whether you like it or not).

So if you want to use it with the java install, provide the path to the executable in the config, like so :

jelu:
  metadata:
    calibre:
      path: /usr/bin/fetch-ebook-metadata

If you run into a cors issue, update the config with the desired origins like so :

jelu:
  cors.allowed-origins:
    - https://jelu.myserver.org

Then open the web UI in your web browser at localhost:11111

Concerning Cors, the default is to accept everything, which you might not not want to do.

No config in the config file is equivalent to :

jelu:
  cors.allowed-origins:
    - "*"

Docker

An image is available here :

https://hub.docker.com/repository/docker/wabayang/jelu

This one is the easiest if you are used to it.

The docker image we provide embeds the fetch-ebook-metadata executable to automatically import books based on their title, authors or isbn.

A sample docker compose would look like that :

version: '3.3'
services:
  jelu:
    image: wabayang/jelu
    container_name: jelu
    volumes:
      - ~/jelu/config:/config
      - ~/jelu/database:/database
      - ~/jelu/files/images:/files/images
      - ~/jelu/files/imports:/files/imports
      - /etc/timezone:/etc/timezone:ro
    ports:
      - 11111:11111
    restart: unless-stopped

!!!! WARNING : ARM versions must add this environment variable for automatic metadata fetching : JELU_METADATA_CALIBRE_PATH=/usr/bin/fetch-ebook-metadata

Kubernetes (Helm)

An unofficial Helm-Chart to deploy Jelu to Kubernetes is available here:

https://artifacthub.io/packages/helm/tibuntu/jelu

Screenshots

Home page :

home page

Auto import form (empty) :

Auto import form (filled) :

Auto import form (result preview) :

Auto import form (edit pre-filled results before importing to your account, eg : modify tags etc...) :

Books list :

Book detail page :

Book detail, events part :

Author page :

Embed code and preview :

Review creation :

Translations

You can help with translations : everything is hosted on crowdin https://crowdin.com/project/jelu

Contributions :

  • Czech translation : @filcuk, @ondrejk
  • Danish translation : @SirBogner
  • German translation : @puckzuck
  • Polish translation: @Sebastian Jasiński (PrinceNorris)
  • Italian translation : @AleCornella

(if you should be here or want your name modified just contact me)

jelu's People

Contributors

alecornella avatar bayang avatar darthnerdus avatar jcgoette avatar kaya-sem avatar quinncuatro avatar semantic-release-bot avatar skiingwiz avatar tibuntu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

jelu's Issues

Feature Request: Filter by Owned books or Tags

I am trying to find what books are on my "to-read" list and not owned so I can go aquire those books. I don't see a way to filter by the owned flag or but excluding a certain tag. As a temporary workaround I can add an Unowned tag but my to-read list is long and I'd like to not have to do that.

Feature request: Translators field in books

Hi,

It would be nice to have a separated (and searchable) "Translators" new field for translators. After importing my goodread list, authors and translators get mixed in the same "Authors" field.

Months in History view not sorted properly

Hello and again thanks for this useful program,

There is a small thing on the history view. It does not sort months correctly for me. When I select a year (for instance 2022) months are sorted almost randomly (starts in December but does not end in January). I think this might be due to modifying reading dates where I add/delete events from books to fix previous issues.

Maybe is reading dates from where events were modified and not actually start/stop dates?

Best regards,

macOS

Do you know if this works on mac? Or only for windows/linux

Feature request: bulk book import

Hi,
first of all, thank you for this tool, looks much better than my evernote list :)

Well, in that list i have like 200+ books so idea of manual import looks kinda paintful, it would be really cool if jelu could do bulk imports e.g. from:

  • list of urls (maybe would be possible to scrap ISBN as it is usually included here, e.g. amazon and www.databazeknih.cz provides this)
  • list of ISBN
  • list of book names (this one would be supercool but i suppose it could be also the hardest one :) )

Thank you! :)

Custom bookshelves

Would it be possible to add custom bookshelves, in addition to the defaults?

Feature request: Improve search

Hi,

I think it would be more intuitive if the basic search box does a search through all the fields (titles,authors,series,etc.) and then in the advanced search you can really separate the search in fields. It seems right now the basic search box only searches in titles. It would also be nice to add more fields to the advanced search (language, summary and personal notes).

Furthermore, search should not consider accentuated letters so "traición" also finds "traicion".

Again, thanks for making Jelu available for all! Please feel completely free to ignore all my requests. ;)

Strange bug

If you are in the My Books section, go to page 2 of your books list, and click on an author's name, then click back the page will flash back and forth between page 2 and page 1. Sometimes indefinitely, sometimes for a short while.

Ability to re-import Cover Art

Feature Request

After my upgrade to 0.31.0 I lost the cover art for my recent imports (see screenshot). Older art is maintained. I can delete the book and re-import it, and the cover art is restored, but several books have reviews or other post-import data added that will difficult to recover.
Screenshot 2022-09-22 090021

I know we can add custom cover art, but It would be nice to have the ability to re-fetch the book cover art.

P.S. maybe an issue - if I try to view the link for a cover that's missing I get a 502 error (Bad Gateway) I would have expected a 404 (file not found) but maybe that's a Tomcat thing? Never mind, the missing images showed up eventually. The delay was strange, but after a few refreshes, all of a sudden the missing covers appeared. It would still be nice to be able to re-fetch the cover art manually.

Feature request: support openid connect as alternative authentication method

Hi,

I am recently moving most of my selfhosted apps to cloudflare zero trust behind a single authentication method (currently authelia). It would be very nice if jelu could support other forward authentication methods so we don't need to login twice. An example of an app supporting this is vikunja for instance.

Thanks again for such a great app.

Cannot edit book cover image

Hi Bayang! Great piece of software, thank you for developing and sharing it! I've inserted a bunch of books, mostly using the automatic fetch via ISBN. So far so good, all imported well!
Some books did not have a cover imported so I wanted to add one. But every time I try to upload via the web I get a 500 error and this in the logs:

2022-06-22 08:52:29.232 ERROR 1 --- [http-nio-11111-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.nio.file.NoSuchFileException: /files/images/https:/www.ibs.it/images/9788856661156_0_536_0_75.jpg
        at java.base/sun.nio.fs.UnixException.translateToIOException(Unknown Source) ~[na:na]
        at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source) ~[na:na]
        at java.base/sun.nio.fs.UnixException.rethrowAsIOException(Unknown Source) ~[na:na]
        at java.base/sun.nio.fs.UnixCopyFile.move(Unknown Source) ~[na:na]
        at java.base/sun.nio.fs.UnixFileSystemProvider.move(Unknown Source) ~[na:na]
        at java.base/java.nio.file.Files.move(Unknown Source) ~[na:na]
        at io.github.bayang.jelu.service.BookService.update(BookService.kt:120) ~[classes/:0.24.2]
        at io.github.bayang.jelu.service.BookService$$FastClassBySpringCGLIB$$1de64f34.invoke(<generated>) ~[classes/:0.24.2]
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20]
        at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.20.jar:5.3.20]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.20.jar:5.3.20]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20]
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar:5.3.20]
        at io.github.bayang.jelu.service.BookService$$EnhancerBySpringCGLIB$$ee2fecb8.update(<generated>) ~[classes/:0.24.2]
        at io.github.bayang.jelu.controllers.BooksController.updateUserBook(BooksController.kt:260) ~[classes/:0.24.2]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Unknown Source) ~[na:na]
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:920) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:684) ~[tomcat-embed-core-9.0.63.jar:4.0.FR]
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.20.jar:5.3.20]
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.63.jar:4.0.FR]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:166) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:112) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:82) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.session.DisableEncodeUrlFilter.doFilterInternal(DisableEncodeUrlFilter.java:42) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183) ~[spring-security-web-5.7.1.jar:5.7.1]
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267) ~[spring-web-5.3.20.jar:5.3.20]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:142) ~[spring-session-core-2.7.0.jar:2.7.0]
        at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:82) ~[spring-session-core-2.7.0.jar:2.7.0]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.20.jar:5.3.20]
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.20.jar:5.3.20]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:890) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1743) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.63.jar:9.0.63]
        at java.base/java.lang.Thread.run(Unknown Source) ~[na:na]

I've altro tried to add directly from file, bu downloading it first on my local PC and oddly enough I get exactly the same error, including the java.nio.file.NoSuchFileException: /files/images/https:/www.ibs.it/images/9788856661156_0_536_0_75.jpg which I found strange since I'm uploading a file locally now, not from the web). Any hint on what might cause this? Using latest version 0.24.2, thanks!

Add Google Books ISBN search for book autofill feature

The book auto fill feature is neat, but a lot of my books are not found, event though I can find them from their ISBN on google book.
The following API endpoint can be used :
https://www.googleapis.com/books/v1/volumes?q=isbn:<insert_isbn_here>

I could contribute an implementation if you would like me to

Best way to enable and see logs when Tomcat serves HTTP 400 error

This may just be a request for some additional detail in the documentation (which I'm happy to PR once I figure this out) on how to get logs out of jelu.

I've deployed jelu using the docker container behind Authelia and SWAG (an Nginx reverse proxy).

When I browse to jelu through SWAG I get a HTTP 400 error back from Tomcat. The jelu.log file mentioned in the docs doesn't create a new log line when the 400 is served.

Here is what the request that is arriving at jelu looks like (I used docker-http-https-echo to see what a request being passed through SWAG after Authelia authentication looks like)

{
  "path": "/",
  "headers": {
    "remote-user": "jdoe",
    "remote-groups": "admins,jelu",
    "remote-name": "John Doe",
    "remote-email": "[email protected]",
    "connection": "close",
    "host": "jelu.example.com",
    "x-forwarded-for": "192.168.0.105",
    "x-forwarded-host": "jelu.example.com:443, jelu.example.com",
    "x-forwarded-method": "GET",
    "x-forwarded-proto": "https",
    "x-forwarded-server": "jelu.example.com",
    "x-forwarded-ssl": "on",
    "x-forwarded-uri": "/",
    "x-original-url": "https://jelu.example.com/",
    "x-real-ip": "192.168.1.105",
    "user-agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/111.0",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "accept-language": "en-US,en;q=0.5",
    "accept-encoding": "gzip, deflate, br",
    "upgrade-insecure-requests": "1",
    "sec-fetch-dest": "document",
    "sec-fetch-mode": "navigate",
    "sec-fetch-site": "same-site",
    "sec-fetch-user": "?1",
    "cookie": "authelia_session=BWy-REDACTED-onb"
  },
  "method": "GET",
  "body": "",
  "fresh": false,
  "hostname": "jelu.example.com",
  "ip": "192.168.1.105",
  "ips": [
    "192.168.1.105"
  ],
  "protocol": "https",
  "query": {},
  "subdomains": [
    "jelu"
  ],
  "xhr": false,
  "os": {
    "hostname": "f27ee356b422"
  },
  "connection": {}
}

And my application.yml contains

jelu:
  auth:
    proxy:
      enabled: true
      adminName: "jdoe"
      header: Remote-User  # https://github.com/linuxserver/docker-swag/blob/ce32306873ded414980e60d9c46eb87464ce2f3b/root/defaults/nginx/authelia-location.conf.sample#L20
    ldap:
      enabled: false
server:
  port: 8400

I've confirmed that if I bypass SWAG and Authelia and just do a curl -i http://jelu:8400 from the SWAG container, I get a HTTP 200 and HTML from jelu, so there's something about the request after it goes through SWAG that is causing jelu to choke.

So my main question is, how do I enable additional logging to see why it is that Tomcat is returning an HTTP 400 when I browse to jelu through SWAG?

How to re-import exported books ?

I exported my books from my instance, and can't find how to re-import the CSV file in another, since the only only is "Goodreads".

[Feature Request] Common booklist among users

My use case: I'm setting this up for my wife and I to track our book collection. While we each have genres of focus, the collection is basically shared, e.g. there are no "his" and "hers" bookshelves.

However, we would each like to be able to track our own "to read" lists, book completion status, etc. So a single, shared account isn't great.
But having two separate accounts would mean a lot of work keeping them in sync as we acquire new books (or some scripting to automatically mirror entries in the "user_book" table).

This is similar to #61, but a little different. There the desire is for other people's collections to be visible but distinct. Here I'm hoping to avoid having to assign ownership to a particular user.

In case it's helpful (feel free to ignore this if it's not), here are some further thoughts I had that veer firmly into "personal preference" territory:

  • I don't need granular control over sharing; it would be sufficient to add a simple toggle to make all books visible to all users regardless of who added them.
  • Edits to book-wide metadata would apply universally (this implies a small and trusted set of users, or maybe only admins can edit). In theory the original creator could be tracked and changes by others could be "copy on write", but that feels like overkill.
  • Edits to any book progress/status metadata would be local to the user performing them.
  • Owned/borrowed is treated similarly to "to read"/progress in the database but feels more like it would be server-wide here because there is only one physical copy being referenced. However, the handling of these fields isn't critical to me personally, as the main objective is to organize all of the books that we own.

Basically, the above are my initial thoughts about how I would start out if I were to try and add this in myself (which I'm open to doing if I get a suitable amount some free time before you do); with the disclaimer that I've just done a quick perusal of the database structure and am therefore likely missing some important details.

Thanks for all your work on this project!

Feature Request: Better formatted reviews

I often copy/paste quotes or passages from book in my reviews, and I've found that the review text box does not respect line breaks. (See screenshot)
At the least could it be configured to break lines, so the text flows in the text box?
A better (but potentially more difficult) would be to enable Markdown formatting the review text box.

Screenshot 2022-09-01 160800

Book cover image has weird aspect ratio in book list

Hi,
when i go to /books or /to-read, it shows list of books with weird cover image aspect ratio:
Snímek obrazovky 2022-07-28 v 15 42 38

It looks higher that it should be (and than it's shown in book detail), it happens on:
MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports), macOS 12.3.1
Google Chrome 103.0.5060.134
and even on Safari 15.4 (17613.1.17.1.13)

Let me know in case you'll need more info or help with debug.

Thank you! :)

Backend Issue

After adding about 50 books the last book I tried to add wouldn't add, then a Error 500 popped up. Tried reloading the page, then restarting the container, then eventually removing Jelu and doing another docker-compose and I get a "login error, backend seems down or unreachable" on the login page

keep track of physical books?

Hi, I am looking into ways of tracking the physical books in our library. Can Jelu do this? Or does Jelu only track read and to read?

Edit: I have checked through the docs but couldn't find anything mentioning it.

Thanks

csv import from goodreads fails

i just set up my jelu instance (on an arm64 server) manual importing of books works. when i try to import my exported data from goodreeds i get the following errors:

2022-12-17 10:02:38.327 DEBUG 1 --- [io-11111-exec-7] i.g.b.jelu.controllers.ImportController  : target import file at /files/imports/goodreads_library_export.csv
2022-12-17 10:02:38.408 ERROR 1 --- [         task-1] i.g.b.j.s.imports.CsvImportService       : Failed to process line or save data from file /files/imports/goodreads_library_export.csv, line : null
2022-12-17 10:02:38.409 ERROR 1 --- [         task-1] i.g.b.j.s.imports.CsvImportService       : Failed to process line or save data from file /files/imports/goodreads_library_export.csv, line : null
2022-12-17 10:02:38.409 ERROR 1 --- [         task-1] i.g.b.j.s.imports.CsvImportService       : Failed to process line or save data from file /files/imports/goodreads_library_export.csv, line : null
2022-12-17 10:02:38.434 DEBUG 1 --- [         task-1] i.g.b.j.s.imports.CsvImportService       : Missing isbn for line with goodreadsId 15881, title Harry Potter and the Chamber of Secrets (Harry Potter, #2) and author J.K. Rowling, import it yourself manually
2022-12-17 10:02:38.435 ERROR 1 --- [         task-1] i.g.b.j.s.imports.CsvImportService       : Failed to process line or save data from file /files/imports/goodreads_library_export.csv, line : null
2022-12-17 10:03:05.936 DEBUG 1 --- [         task-2] i.g.b.j.s.imports.CsvImportService       : parsing finished, 0 entries recorded
2022-12-17 10:03:05.936  INFO 1 --- [         task-2] i.g.b.j.s.imports.CsvImportService       : csv parsing of /files/imports/goodreads_library_export.csv ended after : 0 seconds
2022-12-17 10:03:05.938 DEBUG 1 --- [         task-2] i.g.b.j.s.imports.CsvImportService       : File /files/imports/goodreads_library_export.csv was successfully renamed after processing : true
2022-12-17 10:03:05.938  INFO 1 --- [         task-2] i.g.b.j.s.imports.CsvImportService       : Import for /files/imports/goodreads_library_export.csv ended after : 0 seconds, with 0 imports and 0 failures
2022-12-17 10:07:27.544  WARN 1 --- [o-11111-exec-10] o.s.web.servlet.PageNotFound             : No mapping for GET /profile/messages

(the actual log is longer, but i removed the duplicate messages between 2022-12-17 10:02:38.435 and 2022-12-17 10:03:05.936

Page does not work via reverse proxy + vivaldi

Hi, in my first attempt to get this running in docker I get a db error and the container loops.

I pre-created the volumes and set everything according to the compose example. I think there's some steps im missing

Feature request: Ability to add goodreads (or any) link to book entries

Hello,

Sometimes I find myself wanting to go to the Goodreads page of a book just to check for external reviews, or to see other books in the series that I don't have.

I think it would be useful to add a new option in a book as a link, so we can add a reference to Goodreads (or any other link you want) so you can easily click and check there. I tried to use the personal notes but links are not parsed so you cannot click on them when viewing the book in jelu.

Regards,

Export does not handle multiple read / finish / drop events in books

Hello,

Really thank you for this tool, I have been waiting for something to keep track of my read books. I started using a single text file, then an excel sheet and finally goodreads. However, I would like to keep my data personal and also to be able to export it completely to move on if needed (not like goodreads as I painfully realice now).

Anyways, I am testing the export functionality before I commit myself fully to Jelu and realised that events are not really exported. Multiple "reading" events are ignored (only the last one is exported). Also "drop" events are ignored. Multiple "finished" seem to be supported and exported as alist though.

Is it possible to export the full list of events? Best regards,

Feature Request: Proxy authentication by http header value

When self-hosting multiple applications, you really want to have a single point for user management and authentication. It is annoying to login to each and every app seperately.

A pretty simple way to centralize authentication is achieved by deploying apps behind a reverse proxy, and use proxy auth. The proxy handles authentication in some way and sets http headers containing the username that was successfully logged-in. The apps read the headers and associate incoming requests to that user.

The perfect proxy auth feature for me would work like this:

  1. Start the app with additional environment variables:
  • containing the name of the initial admin user (e.g. admin=admin_user)
  • enabling proxy auth (e.g. proxy_auth=true)
  • setting the key of the http header that contains the username (e.g. auth_header=X-Authenticated-User)
  1. Configure the reverse proxy to authenticate incoming requests in any way you like.
  2. Let the reverse proxy set X-Authenticated-User to the authenticated username on every request.
  3. The app treats the requests as if they belong to the appropriate user session.
  4. Bonus: if the app does not know the username, it creates a new user with that name.

Other SSO methods like OIDC still require the user to login with each app, even it no credentials are required. It is still an additional step that is unneeded and hurting the user experience.

Additional context:
I am using the app for this product. Since this is a single-user platform, users really should see no login screen at all, not even for SSO.

Bug: Unable to modify event dates on 0.33.0

Hi,

First of all, let me say thanks for your new release 0.33.0 @bayang. It really fits my needs now. However, I am experiencing some problems updating from my previous version. Let me show you an example: This is the status of a book in the previous version:
image

Updating to 0.33.0 it results in the following events. This is correct as I was understanding events wrong and duplicated them to see both start and end dates. So the result is this:
image

The problem is that I seem to be unable to change the event dates on them. What I do is delete the reading event of the 30th of July (works) but then editing the finished event to change the start date to the 26th of May and nothing happens, the submit button seems unresponsive (and no log is printed):

image

I need to remove all events and then create a new finished one with the same start and end dates and it works. Is there something I need to do to be able to update them? Thanks again for taking your time to look at this.

Add book auto Fill doesn't work

I've just spun up an instance of Jelu using the exact docker-compose example file, on a Raspberry Pi 4 (ARM64).
I've not used bind mounts before (I normally use volumes: - "./config:/config" , etc) and it was a bit odd having to manually create all the folders manually one at a time on my file system. I tried the way I normally do it but it had permissions errors for the database when I did that, which is a bit unusual as docker created the folders when doing it that way.

Anyway, to get to my issue, when I go to Add Book, then click Auto Fill, I search for something (say Harry Potter for the title) and it shows me words that say "cover image" (not an actual cover image). Inspecting the page I see it is trying to show the image "/files/null"
There are no details retrieved for the book.
I've tried a bunch of different books that should exist but no luck on any.

Features request: Series view

Hi,

First, I want to thank you for this works, it's exactly what I looked for, online library of my offline library, with nice interface and ISBN support,

I might ask for one feature, the ability to browse the library by series, and having a series view, wich display of which book is the series is missing and which is not

Will you consider to add this later ?
Thanks

Bug: Wrong handling of missing dates

It seems the app is treating a 'null' date as 'unix 0 timestamp'.

After importing from Goodreads, for all the entries that were 'read' but did not have a date; as well as for all 'currently reading' books; Jelu shows the completion date as 1. 1. 1970.

For instance, this 'currently reading' (with no finish date):

image

[ Feature Request ] Be able to see other's libraries

Hello!

I recently set this up for my kid to track their books and one feature that'd be nice is to be able other user's libraries. What they read, what they want to read etc.

Right now once created I can't even see other users anyway even as an Admin xD

Widen Review Textbox

It would be nice to have the book review text box take up more horizontal space on the screen if available.
As is, it seems optimized for mobile devices. I've played with the CSS and gotten satisfactory results (see screenshots), but don't know how to persist this behavior.

current
Screenshot 2022-10-11 164919

after modifying css (see circled area)
Screenshot 2022-10-11 164851

feature request: ldap support

It would be nice to have a ldap integration to allow users inside the same network to use their standard credentials. This would remove the requirement to use a specific account in jelu.

Swagger docs "Try it out" calls not working

When using the interactive UI on the swagger docs, the error TypeError: NetworkError when attempting to fetch resource pops up.
I authenticated using the "Authorize" button at the top of the page, with my credentials.

Errors importing books

Hi! I tried importing a goodreads export and ran into some issues.
It errors out on a great number of isbns, not sure why. The error message doesn't inform me much into what I could do to fix this issue.
I'm attaching a goodreads export and my log (I killed the process after the errors were repetitive).
log.txt
goodreads_library_export.csv

Password field limited to 30 characters

The login form has the PW field limited to 30 characters, the password I originally created was 32 characters. Logging in does not work, neither by limiting to the first 30 characters of the password, nor by forcing 32 characters into the field.

Wrong covers and/or metadata after automatic import

Jelu uses an external tool to automatically fetch metadata and covers online.
It seems, however, that results are becoming more and more irrelevant see #56 and #57.
After more investigations, it appears that the tool hits google and gets heavily rate limited.
So, its usage is still fine for one-off imports (like fetching the metadata for one book at a time on daily usage) but for the initial bulk import of your existing history, from goodreads for example, some covers or metadata will be wrong, it is still more useful than importing everything by hand though).

I will try to find alternatives to this, but in the meantime we will have to stick to that.
If it really bothers you, you can still turn off automatic metadata fetching before starting your import (there is a checkbox on the UI).

Image of auto-fill imported book doesn’t update after refining search

I was lazy, and only searched for "Elder Race". That gave me some paranormal romance book with Series: Elder Races (maybe a title match should have precedence? Anyway, different issue :D), so I chose "Discard". I added Author: Adrian Tchaikovsky and it found the proper book. But the image stayed as the paranormal romance cover, and I needed to manually import the correct one.

Import of existing ISBN will replace cover

Version: Jelu 0.35.1 Docker

Actual behavior:

During testing, i noticed, that when importing a ISBN via the Bulk import, turning cover Download OFF, it will replace existing covers for existing books.

Start Situation:
(The two books in the middle have already been re-imported)
image

"Re-import" of the book on the right and suddenly the cover is also replaced
image

Expected behavior:

Books imported via ISBN which are already existing, should not be touched.
Or if that is a valid usecase for some, it would be nice to have this as an option ("Do not reimport existing ISBNs")

fetch cover do not work

Hi and thanks for this app

Unfortunately It doesn't fetch the cover from most of the books I add (auto fill):

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Sun May 15 15:44:39 UTC 2022

There was an unexpected error (type=Not Found, status=404)

debug:
2022-05-16 16:17:37.718 DEBUG 1 --- [io-11111-exec-8] i.g.b.j.s.metadata.FetchMetadataService : parsed dto MetadataDto(title=Das Sissi-Feuerwerk: Ein Ischl-Krimi, isbn10=null, isbn13=9783492502504, summary=null, image=null, publisher=Piper, pageCount=null, publishedDate=2019-05-15T16:17:35.252073+00:00, authors=[Jenna Theiss], tags=[], series=null, numberInSeries=null, language=deu, googleId=9qp-wgEACAAJ, amazonId=null, goodreadsId=null)

Are there optional settings to fix or improve this?

Error while importing metadata on Synology docker

When running Autofill, I do not get any data and the log shows the following error:

fetch ebookmetadata process exited abnormally with code 1

If I try to run fetch-ebook-metadata manually from within the docker container, it shows the following error:

Failed to import PyQt module: PyQt6.QtCore with error: libQt6Core.so.6: cannot open shared object file: No such file or directory

It seems the problem is related to the Qt libraries required to run Calibre (you can read more info about it in here):

  • libQt6Core.so.6 contains an ABI tag that denotes the minimum kernel version required
  • When shared object is loaded the ABI tag is compared to the current kernel's version. If it doesn't match, the file is not loaded.
  • This is a problem in Synology DSM as the kernel version is quite old

A workaround to fix this issue is to run the following commands:

apt-get update
apt-get install binutils
strip --remove-section=.note.ABI-tag /calibre/lib/libQt6Core.so.6

Obviously, this problem will reappear when a new container is created, so a permanent solution could be achieved by creating a custom image (including those command in Dockerfile)

Optimize resource usage

The app uses more than 250MB of memory while running. This might be fine on a homelab but I would like to run it on a cloud VM that is a little more resource constrained. Any chance to get the resource usage down a bit?

Wrong covers imported, covers do not update manually

Essentially the title. When a goodreads csv import is done, wrong covers get uploaded. Eg:

image

Some are hilariously wrong.

When you edit the book and upload the cover manually via manual file upload (not the URL), it never changes. the Upload progress bar does work, but old cover persists.

Request for page stats

In addition to book stats I like to see how many pages I read. Often I change between short novels and large epics, so the number of books don't tell the whole story.

Maybe its easy to give the user notice if a certain book is missing that metadata. Although, in my case I always check that kind of stuff, because on goodreads its also hit & miss.

Bug: currently reading events deleted after adding a finished one.

Hello,

I am experiencing a strange bug when adding reading events to a book. Basically I import a new book into Jelu and create a "currently reading" event with a today date. Everything works fine. Then after some days that I finish reading the book I go to Jelu again and add (+event) a new finished event with the current date (several days after the "currently reading" date). I am expecting to have two events now in the book: a started"currently reading" and a "finished" one. However, I always end up having just one "finished" event, and the "currently reading" is deleted.

Is this the expected behaviour? If I manually add the "currently reading" again, it is kept.

I hope I have managed to explain myself....

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.