{"id":20757081,"url":"https://github.com/puhoy/lspace","last_synced_at":"2026-03-07T06:03:20.445Z","repository":{"id":34877047,"uuid":"186178023","full_name":"puhoy/lspace","owner":"puhoy","description":"a cli ebook manager","archived":false,"fork":false,"pushed_at":"2024-08-01T11:09:32.000Z","size":694,"stargazers_count":34,"open_issues_count":9,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-18T15:51:44.547Z","etag":null,"topics":["cli","ebook","ebook-manager","epub","pdf"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/puhoy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-11T20:21:51.000Z","updated_at":"2025-07-27T13:24:00.000Z","dependencies_parsed_at":"2024-08-01T12:07:12.497Z","dependency_job_id":null,"html_url":"https://github.com/puhoy/lspace","commit_stats":{"total_commits":172,"total_committers":2,"mean_commits":86.0,"dds":"0.011627906976744207","last_synced_commit":"2be54522dc0c2953d4fec926f9a5f514b9048f4b"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/puhoy/lspace","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/puhoy%2Flspace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/puhoy%2Flspace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/puhoy%2Flspace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/puhoy%2Flspace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/puhoy","download_url":"https://codeload.github.com/puhoy/lspace/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/puhoy%2Flspace/sbom","scorecard":{"id":749267,"data":{"date":"2025-08-11","repo":{"name":"github.com/puhoy/lspace","commit":"c6dd5a6d685f1b8472cd893ec21b65c1884090c4"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/21 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU Affero General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 10 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T19:46:59.861Z","repository_id":34877047,"created_at":"2025-08-22T19:46:59.861Z","updated_at":"2025-08-22T19:46:59.861Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30208802,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T05:23:27.321Z","status":"ssl_error","status_checked_at":"2026-03-07T05:00:17.256Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","ebook","ebook-manager","epub","pdf"],"created_at":"2024-11-17T09:39:45.096Z","updated_at":"2026-03-07T06:03:20.395Z","avatar_url":"https://github.com/puhoy.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# l-space\n\na cli ebook manager built around [isbnlib](https://github.com/xlcnd/isbnlib)\n\non import, lspace tries to find isbns in the files metadata and in the text. \nwith the isbn it tries to fetch metadata about the book from google books and openlibrary. \nif no isbn is found, it queries metadata based on the filename.\n\nafter this, your properly renamed files will be stored in your library folder.\n\ncurrently supports epub and pdf.\n\n\n[![Build Status](https://travis-ci.org/puhoy/lspace.svg?branch=master)](https://travis-ci.org/puhoy/lspace)\n\n[![codecov](https://codecov.io/gh/puhoy/lspace/branch/master/graph/badge.svg)](https://codecov.io/gh/puhoy/lspace)\n\n## requirements\n\npython \u003e=3.5 and pip\n\n\n## installation\n\n#### from pypi (latest release)\n\n`pip install lspace`\n\n#### from github (probably-not-so-stable-dev-stuff)\n\n`pip install git+https://github.com/puhoy/lspace.git`\n\n\n## setup \n\nafter installation, you should run\n\n`lspace init`\n\nthis will setup a new configuration file, which you can edit to specify the structure of your library, for example.\n\na default config file would look like this:\n```\ndatabase_path: sqlite:////home/USER/.config/lspace/lspace.db\nfile_format: '{SHELF}/{AUTHORS}_{TITLE}'\nlibrary_path: ~/library\nloglevel: error\ndefault_shelf: misc\ndefault_author: no author\ndefault_language: no language\ndefault_publisher: no publisher\n```\n\n#### database path\n\npath to your database. \nthe project uses sqlalchemy, so all databases supported by sqlalchemy should be fine.\n\n#### file_format\n\ntemplate string for storing the plain files in the library.\n\n`{SHELF}/{AUTHORS}_{TITLE}` would produce files like `scifi/cixin-liu_three-body-problem.epub`\n\nauthor and title will be automatically slugified for this.\n\npossible variables to use are: AUTHORS, TITLE, SHELF, YEAR, LANGUAGE, PUBLISHER\n\n#### library path\n\nwhere the imported files are stored\n\n#### loglevel\n\nthe default python loglevels (debug, info, error, exception)\n\n#### default_{shelf, author, language, publisher}\n\nthe default field names, in case nothing is specified in import\n\n\n## usage\n\n### importing\n\n`lspace import path/to/ebook.epub`\n\n`lspace import path/to/folder/*`\n\nif you already have a folder you are happy with and, for example, just want to use it to serve your books or search through your files, you can add the `--inplace` switch on import, which will not copy them over to your library folder, but instead keep the book as an \"external\" reference.\n\n`lspace import --inplace path/to/ebook.epub`\n\n#### import from calibre library\n\n`lspace import path/to/calibre_library/metadata.db`\n\n#### import from lspace api\n\n`lspace import http://\u003csome-address\u003e/api/v1/`\n\nthe web interface (`lspace web` - scroll down a bit!) generates import strings based on your search! \n\n\n### searching your library\n\n`lspace list QUERY [--path]`\n\nfor example, \n\n`lspace list programming --path`\n\nwould return something like\n\n    /home/USER/library/donald-e-knuth/art-of-computer-programming-volume-2.pdf\n    /home/USER/library/donald-e-knuth/the-art-of-computer-programming-volume-1-fascicle-1.pdf\n\nand \n\n`lspace list dwarf`\n\nwould return return\n\n    Peter Tyson - Getting Started With Dwarf Fortress\n\n### removing stuff\n\n`lspace remove QUERY`\n\nthis command will ask you before it actually deletes stuff :)\n\n    Peter Tyson - Getting Started With Dwarf Fortress\n    /home/USER/library/peter-tyson/getting-started-with-dwarf-fortress.epub\n    delete this book from library? [y/N]:\n\n### exporting books\n\n\n`lspace export QUERY ~/some/folder/ --format mobi`\n\nwould convert all books matching on QUERY to 'mobi' and export them to ~/some/folder\n\nto actually export to another format, you need \"ebook-convert\", which is part of [calibre](https://calibre-ebook.com/)!\n\n### browse \u0026 share your books via webserver\n\n`lspace web --host 0.0.0.0 --port 5000` \n\n![L-Space web interface](https://raw.githubusercontent.com/puhoy/lspace/master/lspace_screenshot.png \"screenshot of the L-Space web interface\")\n\nthis also gives you the import command for your current search results!\n\n(or you can just download them manually..)\n\n## setting up a dev env\n\n#### 1. clone this repo \n\n#### 2. make a virtualenv and activate it\n\n```bash\npython -m venv env\n\nsource env/bin/activate  # for bash\n\n# or\n#. env/bin/activate.fish  # for fish\n```\n\n#### 3. install requirements\n\n```bash\npip install  -e .[dev]\n```\n\n#### 4. set up a separate config to not mess up your regular installation\n\n```bash\n# initialize a new config file at a separate path\nLSPACE_CONFIG=~/.config/lspace_dev/config.yml lspace init\n\n# change the database and library path! (otherwise it would still use the regular db)\nsed -i 's/lspace\\/lspace.db/lspace_dev\\/lspace.db/g' ~/.config/lspace_dev/config.yml\nsed -i 's/~\\/library/~\\/library_dev/g' ~/.config/lspace_dev/config.yml\n\n# also, if you want, set the loglevel to something else\n\n```\n    \nafter this, just set LSPACE_CONFIG to your new config file before you start to try new stuff\n\n```bash\nexport LSPACE_CONFIG=~/.config/lspace_dev/config.yml  # bash\nset -gx LSPACE_CONFIG ~/.config/lspace_dev/config.yml  # fish \n```\n\n#### migrations\n\nthe db command is available when running in dev mode.\n\ncreate a new migration with `LSPACE_DEV=1 lspace db migrate`\n\nafterwards, running any lspace command will automatically update the database\n\n\n#### making a release\n\ncommands to build, test-release and release are wrapped in a `doit` script `dodo.py`.\n\nbumping is requires bump2version, twine is used for uploading to pypi.\n\n\n## why \"L-space\"?\n\nits named after discworlds [library-space](https://en.wikipedia.org/wiki/List_of_dimensions_of_the_Discworld#L-space) dimension :)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpuhoy%2Flspace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpuhoy%2Flspace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpuhoy%2Flspace/lists"}