Giter Club home page Giter Club logo

odmpy's People

Contributors

diegus83 avatar holyspiritomb avatar jvandervort avatar ping avatar xavdid 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar

odmpy's Issues

FILE_PART_RE does not always match second_stamp

FILE_PART_RE = re.compile(
    r"(?P<part_name>{[A-F0-9\-]{36}}[^#]+)(#(?P<second_stamp>\d+))?$"
)

I recently received an issue on my repository which uses the same regex. The regex will not match with this:

"path": "{31984D68-AD8F-4EF1-B00C-4A595BADE0D1}Fmt425-Part01.mp3#275.253"

The timestamp does not appear to be an integer (275.253) and will make odmpy crash when using libby with --direct. The book in question is the book with id 114614. Downloading works fine without --direct.

Cover jpg preventing download

When trying to download a particular odm file, it errors out on the cover art. An output folder is created, but nothing is downloaded as the process exits out after tripping up on the cover art download. The file does appear to be truly broken, but would be great if odmpy could catch and handle the error.

# odmpy dl BjrkanSagas-64.odm
Downloading "The Björkan Sagas" by "Harold R. Johnson" in 4 parts...
Traceback (most recent call last):
File "/usr/sbin/odmpy", line 33, in
sys.exit(load_entry_point('odmpy==0.4.8', 'console_scripts', 'odmpy')())
File "/usr/lib/python3.10/site-packages/odmpy/main.py", line 26, in main
run()
File "/usr/lib/python3.10/site-packages/odmpy/odm.py", line 508, in run
cover_res.raise_for_status()
File "/usr/lib/python3.10/site-packages/requests/models.py", line 960, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://images.contentreserve.com/ImageType-100/2016-1/%7B00000000-0000-0000-0000-000000000232%7DImg100.jpg

Running version 0.4.8 within an alpine docker, but also able to reproduce this on Windows. Please let me know if further info is needed, or a copy of the .odm file.

[Suggestion] allow inserting Libby IDs in book folder format

When using Libby IDs to download, and perhaps when using Libby in general, it would be nice to be able to insert the Libby ID into the book folder. Right now it appears that %(id) inserts the ODM ID instead, which is inconvenient when batch-downloading by Libby ID (since my script doesn't know the ODM ID in advance, it can't tell where the resulting folder will be created).

Workaround: the ODM ID looks like it's in the --exportloans file, so if that turns out to be true I can complexify my script to convert between the two. I'll see what I can figure out.

In some books chapter names contain leading whitespaces

Some books contain leading whitespaces in the chapter names after merging. eg. The Invincible by Stanislaw Lem.

Command used
odmpy -v dl -c -m -j -k multipart.odm

Ouput

Added chap tag => ch13: 6:21:21.215000-6:50:08.522000 "      Invincible (38:33)" to "./The Invincible - Stanislaw Lem/The Invincible - Stanislaw Lem.mp3"

ffprobe output

  Chapter #0:12: start 22881.215000, end 24608.522000
  Metadata:
      title           :       Invincible (38:33)

Missing CoverUrl in ODM causes crash

Some ODMs do not include a link to a cover image (they show up in the Overdrive application with a question mark image in place of the cover image). In ODMs of this sort, odmpy will crash due to a conditional that seems to test the possibility of a missing image, but which actually doesn't have any alternate behavior.

The one-line conditional shown below dereferences the pointer whether or not it's null.

Traceback (most recent call last):
  File "/home/william/.local/bin/odmpy", line 8, in <module>
    sys.exit(main())
  File "/home/william/.local/lib/python3.6/site-packages/odmpy/__main__.py", line 26, in main
    run()
  File "/home/william/.local/lib/python3.6/site-packages/odmpy/odm.py", line 216, in run
    cover_url = metadata.find('CoverUrl').text if metadata.find('CoverUrl') else metadata.find('CoverUrl').text
AttributeError: 'NoneType' object has no attribute 'text'

Suggestion - Add %(ReadingOrder) to the --bookfolderformat options

I am using AudioBookShelf to host audiobooks. it wants books that are series to be stored in the following folder format

.%AuthorName%%SeriesName%\Book %ReadingOrder% - %Title%\

having the ability to specify the ReadingOrder in the --bookfolderformat would be SUPER helpful!

Thanks much!

Snarf

Feature: Generate file containing metadata information

It's not easy to get good metadata about audiobooks (Overdrive tags are terrible); would it be possible to save the metadata Overdrive/Libby provides in a durable form alongside the MP3s? I used to parse the ODM files, but many projects think they're being retired, and anyhow the libby interface of odmpy doesn't save them anymore.

My favorite format is OPF, but a json dump would be adequate.

Found an ODM that produces parse errors and won't load, "undefined entity"

I get the following backtrace when running odmpy on the attached ODM file; the cause is either the &#34; or the &eacute;, confirmed by editing them both out, it loaded just fine.

Hm, not sure how to attach, I'll submit this and see if I can figure it out.

Traceback (most recent call last):
  File "/home/william/.local/bin/odmpy", line 8, in <module>
    sys.exit(main())
  File "/home/william/.local/lib/python3.8/site-packages/odmpy/__main__.py", line 26, in main
    run()
  File "/home/william/.local/lib/python3.8/site-packages/odmpy/odm.py", line 231, in run
    metadata = xml.etree.ElementTree.fromstring(
  File "/usr/lib/python3.8/xml/etree/ElementTree.py", line 1320, in XML
    parser.feed(text)
xml.etree.ElementTree.ParseError: undefined entity: line 1, column 1136
./fetchodm.sh: cannot odmpy dl -r 10 -k -d /mnt/hdd-8TB/sharing/downloads/pyodm-dl/tmp.H7nwXYVOpa /mnt/hdd-8TB/sharing/downloads/pyodm-dl/tmp.H7nwXYVOpa/SkylarkofSpace9781596078707.odm

Crash on 0.7.1 running any command

odmpy immediately crashes when running any command. Please see command line output below. I wasn't able to resolve it by running pip3 uninstall odmpy followed by pip3 install git+https://[email protected]/ping/odmpy.git --upgrade

$ odmpy
Traceback (most recent call last):
  File "/opt/homebrew/bin/odmpy", line 5, in <module>
    from odmpy.__main__ import main
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/__main__.py", line 21, in <module>
    from .odm import run, LibbyNotConfiguredError
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/odm.py", line 42, in <module>
    from .processing import (
ModuleNotFoundError: No module named 'odmpy.processing'

Does not handle downloads with inordinately long filenames

Traceback (most recent call last):
File "/home/zoidberg/.local/bin/odmpy", line 8, in
sys.exit(main())
File "/home/zoidberg/.local/lib/python3.8/site-packages/odmpy/main.py", line 26, in main
run()
File "/home/zoidberg/.local/lib/python3.8/site-packages/odmpy/odm.py", line 281, in run
os.makedirs(book_folder)
File "/usr/lib/python3.8/os.py", line 223, in makedirs
mkdir(name, mode)
OSError: [Errno 36] File name too long: "audiobooks/Orson Scott Card's Intergalactic Medicine Show - Orson Scott Card, Edmund R. Schubert, Wayne Wightman, Stefan Rudnicki, Gabrielle De Cuir, Roxanne Hernandez, Arthur Morey, Emily Janice Card, Paul Boehmer, Various Narrators, Claire Bloom, Susan Hanfield, Mary Robinette Kowal, Eric James Stone, Aliette de Bodard, Others"

On a new borrow getting 406 error with default odm file download

On a new borrow getting 406 error with default odm file download.
Not sure if this is my library no longer allowing an odm file access or something else, but I thought I'd report it.
I had an old borrow in my loans, it worked, but both new borrows had this error.
NOTE: --direct worked fine

commit 5af3cac

An unexpected error has occured
Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 349, in make_request
    res.raise_for_status()
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\requests\models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 406 Client Error: Not Acceptable for url: https://sentry-read.svc.overdrive.com/card/35351901/loan/74599/fulfill/LibbyFormats.AudioBookMP3

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\odm.py", line 1035, in run
    extract_loan_file(libby_client, selected_loan, args),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\odm.py", line 358, in extract_loan_file
    odm_res_content = libby_client.fulfill_loan_file(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 683, in fulfill_loan_file
    res: requests.Response = self.make_request(
                             ^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 358, in make_request
    ErrorHandler.process(http_err)
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby_errors.py", line 103, in process
    raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=406, msg='406 Client Error: Not Acceptable for url: https://sentry-read.svc.overdrive.com/card/35351901/loan/74599/fulfill/LibbyFormats.AudioBookMP3', error_response=' ''>
Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 349, in make_request
    res.raise_for_status()
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\requests\models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 406 Client Error: Not Acceptable for url: https://sentry-read.svc.overdrive.com/card/35351901/loan/74599/fulfill/LibbyFormats.AudioBookMP3

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\__main__.py", line 34, in <module>
    main()
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\__main__.py", line 26, in main
    run()
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\odm.py", line 1035, in run
    extract_loan_file(libby_client, selected_loan, args),
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\odm.py", line 358, in extract_loan_file
    odm_res_content = libby_client.fulfill_loan_file(
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 683, in fulfill_loan_file
    res: requests.Response = self.make_request(
                             ^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 358, in make_request
    ErrorHandler.process(http_err)
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby_errors.py", line 103, in process
    raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=406, msg='406 Client Error: Not Acceptable for url: https://sentry-read.svc.overdrive.com/card/35351901/loan/74599/fulfill/LibbyFormats.AudioBookMP3', error_response=' ''>

Chapters and Merge Using Existing mp3 files

Hi, the tool works great although I'm trying to merge the existing downloaded mp3 files from the windows app into one m4b file with the chapters and cover. Is this possible? The goal is to take the existing book folder and combine it with the chapter and cover info into an m4b file. Thanks!

Window/unix difference with os.rename

To get the tests to pass I had to update os.rename to os.replace

shared.py
image

Pretty crazy difference between Windows and Unix behavior

On Windows, if dst exists a FileExistsError is always raised. The operation may fail if src and dst are on different filesystems. Use shutil.move() to support moves to a different filesystem.

On Unix, if src is a file and dst is a directory or vice-versa, an IsADirectoryError or a NotADirectoryError will be raised respectively. If both are directories and dst is empty, dst will be silently replaced. If dst is a non-empty directory, an OSError is raised. If both are files, dst will be replaced silently if the user has permission. The operation may fail on some Unix flavors if src and dst are on different filesystems. If successful, the renaming will be an atomic operation (this is a POSIX requirement).

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\nnn\Documents\dev\git\odmpy\odmpy\__main__.py", line 32, in <module>
    main()
  File "C:\Users\nnn\Documents\dev\git\odmpy\odmpy\__main__.py", line 26, in main
    run()
  File "C:\Users\nnn\Documents\dev\git\odmpy\odmpy\odm.py", line 764, in run
    process_odm(args.odm_file, args, logger)
  File "C:\Users\nnn\Documents\dev\git\odmpy\odmpy\processing.py", line 590, in process_odm
    merge_into_mp3(
  File "C:\Users\nnn\Documents\dev\git\odmpy\odmpy\shared.py", line 322, in merge_into_mp3
    os.rename(temp_book_filename, book_filename)
FileExistsError: [WinError 183] Cannot create a file when that file already exists: 'tests/data/downloads/Ceremonies For Christmas - Robert Herrick\\Ceremonies For Christmas - Robert Herrick.mp3.part' -> 'tests/data/downloads/Ceremonies For Christmas - Robert Herrick\\Ceremonies For Christmas - Robert Herrick.mp3'

Long chapters are split into too-small chapters with duplicate names

Describe the problem

When adding chapter markers to a book with very long chapters, duplicate markers are added with the suffix (00:00).

Chapters In Libby:

IMG_8273

Chapters after extraction:

IMG_8272

The book otherwise plays great, so this is fairly minor in the scheme of things.

To Reproduce

I noticed it with this version of Good Omens, but I think I've seen it other places too.

I ran:

odmpy libby --settings ~/.config/odmpy --latest 1 --downloaddir ~/Downloads/audiobooks --chapters --merge --mergeformat m4b --nobookfolder --bookfileformat "%(Author)s - %(Series)s %(ReadingOrder)s - %(Title)s"

Version/Environment

odmpy 0.8.1 [Python 3.11.4-darwin]

ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers
  built with Apple clang version 14.0.3 (clang-1403.0.22.14.1)

option for escaping characters in folder/file names

Describe the problem
I've found that folder/file names which contain a ':' character cause issues on some filesystems. I wonder if it would be possible to provide a mechanism to escape/replace/remove that character (or certain characters) for folder/file names.

To Reproduce
Download an audio book with a ':' in the title.

Version/Environment
odmpy 0.7.7 [Python 3.8.10-linux]

[Feature] Multiple Subfolders

I was hoping I could create multiple subfolders. The obvious example would be /author/title or even /author/series (if series exists)/title.

test error

Current Head:
commit 0f769e71b25c3ce64917b52f195cc47401cdbe9d (HEAD -> master, ping/master)
Author: ping <[email protected]>
Date:   Sat Feb 18 16:23:10 2023 +0800
======================================================================
ERROR: test_loans (tests.libby_tests.LibbyClientTests.test_loans)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connection.py", line 174, in _new_conn
    conn = connection.create_connection(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\util\connection.py", line 95, in create_connection
    raise err
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\util\connection.py", line 85, in create_connection
    sock.connect(sa)
TimeoutError: timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connectionpool.py", line 703, in urlopen
    httplib_response = self._make_request(
                       ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connectionpool.py", line 386, in _make_request
    self._validate_conn(conn)
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connectionpool.py", line 1042, in _validate_conn
    conn.connect()
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connection.py", line 358, in connect
    self.sock = conn = self._new_conn()
                       ^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connection.py", line 179, in _new_conn
    raise ConnectTimeoutError(
urllib3.exceptions.ConnectTimeoutError: (<urllib3.connection.HTTPSConnection object at 0x000001C38FD14550>, 'Connection to sentry-read.svc.overdrive.com timed out. (connect timeout=10)')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\requests\adapters.py", line 489, in send
    resp = conn.urlopen(
           ^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\connectionpool.py", line 787, in urlopen
    retries = retries.increment(
              ^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\urllib3\util\retry.py", line 592, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='sentry-read.svc.overdrive.com', port=443): Max retries exceeded with url: /chip/sync (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000001C38FD14550>, 'Connection to sentry-read.svc.overdrive.com timed out. (connect timeout=10)'))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 309, in make_request
    res = session.send(
          ^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\requests\sessions.py", line 701, in send
    r = adapter.send(request, **kwargs)
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\AppData\Roaming\Python\Python311\site-packages\requests\adapters.py", line 553, in send
    raise ConnectTimeout(e, request=request)
requests.exceptions.ConnectTimeout: HTTPSConnectionPool(host='sentry-read.svc.overdrive.com', port=443): Max retries exceeded with url: /chip/sync (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000001C38FD14550>, 'Connection to sentry-read.svc.overdrive.com timed out. (connect timeout=10)'))

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\tests\libby_tests.py", line 245, in test_loans
    res = self.client.get_loans()
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 515, in get_loans
    return self.sync().get("loans", [])
           ^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 409, in sync
    res: Dict = self.make_request("chip/sync")
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\nnn\Documents\dev\odmpy\odmpy\libby.py", line 321, in make_request
    raise ClientConnectionError(str(conn_err)) from conn_err
odmpy.libby_errors.ClientConnectionError: <odmpy.libby_errors.ClientConnectionError; http_status=0, msg='HTTPSConnectionPool(host='sentry-read.svc.overdrive.com', port=443): Max retries exceeded with url: /chip/sync (Caused by ConnectTimeoutError(<urllib3.connection.HTTPSConnection object at 0x000001C38FD14550>, 'Connection to sentry-read.svc.overdrive.com timed out. (connect timeout=10)'))', error_response='''>

Cosmetic enhancement for embbeded covers

I noticed that the book covers in the library appear stretched in the previews and when you download them, but appear in the correct aspect ratio (1:1) in the web player.

library

library web player

Update: I realized we could resize the cover with ffmpeg and not create a new dependency. 510x510px seems reasonable since the original image is 510px wide and there isn't any gain in scaling it up. Declaring a specific quality seems necessary, otherwise the resulting image is extremely compressed.

ffmpeg -y -i cover.jpg -vf scale=510:510 -qmin 1 -qscale:v 1 cover.jpg

Most players expect the embedded art to be square (like it would be in a regular music file). It is an easy fix but depends on adding the Python Image Library to resize the cover.

from PIL import Image

img = Image.open('cover.jpg')
img = img.resize(((img.size[0]),(img.size[0])), Image.LANCZOS)
img.save('cover.jpg') 

Would you be interested in merging this and resizing all covers by default?

Or maybe making it a new argument? eg. -r "Resize cover image to 1:1 aspect ratio"

--merge command creates a VBR instead of CBR .mp3

Using the --merge command to merge the .mp3s causes issues when trying to seek to an exact time on many platforms.

Here:
https://github.com/ping/odmpy/blob/master/odmpy/odm.py#L486

You are using ffmpeg to concatenate the .mp3s.

The original .mp3s are CBR 64kbps. However, when ffmpeg concatenates .mp3s, it turns them into VBR.

Here is an article explaining this issue in more detail:
https://marco.org/2016/08/15/vbr-mp3-plea

What you really mean to do is concatenate the CBR streams into a CBR output. That output file will be easily seekable. Here is one library that does that:
https://darrenmulholland.com/dev/mp3cat.html

I recommend using that library to concatenate the mp3s instead of ffmpeg.

ffmpeg fails to create m4b file with embedded cover art

When trying to create a merged m4b file with the options
odmpy -v dl -c -m --mergeformat m4b -k -f file.odm

ffmpeg fails because it can't use the embedded cover art

[mp4 @ 0x7fc62a802e00] Could not find tag for codec h264 in stream #0, codec not currently supported in container
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
Error initializing output stream 0:1 -- 
[aac @ 0x7fc62a805e00] Qavg: nan
Conversion failed!
ffmpeg exited with the code: 1

ffmpeg -version output

ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
built with Apple clang version 11.0.0 (clang-1100.0.33.17)

Error on line 1079

Download. Choose from 1-5 (separate choices with a space or leave blank to quit),
then press enter: 4
Opening ebook "Physics for Game Developers"...
Downloading toc.ncx: 100%|███████████████████████████████████████████████████████████| 274/274 [01:28<00:00, 3.09it/s]
An unexpected error has occurred
Traceback (most recent call last):
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\odm.py", line 1079, in run
extract_loan_file(libby_client, selected_loan, args)
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\odm.py", line 366, in extract_loan_file
process_ebook_loan(
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\processing\ebook.py", line 664, in process_ebook_loan
ncx_soup = BeautifulSoup(ncx_f, features="xml")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\bs4_init_.py", line 250, in init
raise FeatureNotFound(
bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: xml. Do you need to install a parser library?
Traceback (most recent call last):
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Scripts\odmpy-script.py", line 33, in
sys.exit(load_entry_point('odmpy==0.7.6', 'console_scripts', 'odmpy')())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy_main_.py", line 27, in main
run()
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\odm.py", line 1079, in run
extract_loan_file(libby_client, selected_loan, args)
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\odm.py", line 366, in extract_loan_file
process_ebook_loan(
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\odmpy\processing\ebook.py", line 664, in process_ebook_loan
ncx_soup = BeautifulSoup(ncx_f, features="xml")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\titus\AppData\Local\Programs\Python\Python311\Lib\site-packages\bs4_init_.py", line 250, in init
raise FeatureNotFound(
bs4.FeatureNotFound: Couldn't find a tree builder with the features you requested: xml. Do you need to install a parser library?

403 Client Error

The Problem
I'm receiving the following error message:

odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=403, msg='403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync', error_response='{"result":"missing_chip"}''>

Once I get the error message, odmpy stops, and I'm unable to do anything with it 😕

To Reproduce
Using the base command of odmpy libby

Version/Environment
odmpy 0.8.1 [Python 3.11.5-darwin]

[Feature] Libby non-interactive/batch use

I'd like to be able to integrate odmpy with my library a bit more, so I don't have to log in to get my books onto my server. But rather than suggesting something complex like automated downloads of all files (like Overdrive and Libby do), I'd like to go a bit simpler, closer to the current design.

I can use the json export odmpy libby --exportloans to get the IDs of all titles, exclude the ones I have already, and thereby get the IDs I need. But how do I ask Libby to download them? Can I ask for a feature to pass the Libby ID (stored as the top-level JSON "id" in each book) into pyodm? Or is there some way to find the download number you're supposed to pass to pyodm libby --select (without having to parse titles out of the human-readable list)?

Invalid date string

Describe the problem
This week odmpy started failing on processing every audiobook. It successfully downloads all the parts but on the transcode to M4B errors when parsing the date field. I'm going to try debugging more, but submitting this issue in case anyone is already aware of a solution. Traceback logs below.

To Reproduce

odmpy libby \
--direct \
--latest 1 \
--downloaddir=<downloaddir> \
--settings=<settingsdir> \
--keepcover \
--chapters \
--merge \
--mergeformat=m4b \
--mergecodec=aac \
--removefrompaths ":," \
--bookfolderformat "%(Author)s/%(Title)s" \
--bookfileformat "%(Author)s - %(Title)s"

Version/Environment
odmpy 0.8.1 [Python 3.11.6-darwin]

Traceback log

odmpy Interactive Client for Libby
----------------------------------------------------------------------
Non-interactive mode. Downloading latest 1 loan...
Opening audiobook "<book title>"...
Downloading "<book title>" by "<book author>" in 19 parts...
...
Already saved /Users/server/Music/Audiobooks/.....part-19.mp3
Generating "/Users/server/Music/Audiobooks/......"...
size=  457164kB time=16:15:16.88 bitrate=  64.0kbits/s speed=6.29e+03x
An unexpected error has occurred
Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/odm.py", line 953, in run
    process_audiobook_loan(
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/processing/audiobook.py", line 362, in process_audiobook_loan
    write_tags(
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/processing/shared.py", line 250, in write_tags
    audiofile.tag.release_date = published_date
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/id3/tag.py", line 506, in _setReleaseDate
    self._setDate(b"TDRL", date)
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/id3/tag.py", line 649, in _setDate
    date = core.Date.parse(date)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/core.py", line 380, in parse
    pdate, fmt = Date._validateFormat(s)
                 ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/core.py", line 368, in _validateFormat
    raise ValueError(f"Invalid date string: {s}")
ValueError: Invalid date string: 2021-02-16T05:00:00+00:00
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/__main__.py", line 35, in <module>
    main()
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/__main__.py", line 27, in main
    run()
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/odm.py", line 953, in run
    process_audiobook_loan(
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/processing/audiobook.py", line 362, in process_audiobook_loan
    write_tags(
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/processing/shared.py", line 250, in write_tags
    audiofile.tag.release_date = published_date
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/id3/tag.py", line 506, in _setReleaseDate
    self._setDate(b"TDRL", date)
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/id3/tag.py", line 649, in _setDate
    date = core.Date.parse(date)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/core.py", line 380, in parse
    pdate, fmt = Date._validateFormat(s)
                 ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/eyed3/core.py", line 368, in _validateFormat
    raise ValueError(f"Invalid date string: {s}")
ValueError: Invalid date string: 2021-02-16T05:00:00+00:00

403 error upon using odmlibby

Using odm libby now produces this

odmpy libby
odmpy Interactive Client for Libby

An unexpected error has occurred
Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 357, in make_request
res.raise_for_status()
File "/usr/local/lib/python3.10/dist-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/odmpy/odm.py", line 846, in run
synced_state = libby_client.sync()
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 457, in sync
res: Dict = self.make_request("chip/sync") File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 366, in make_request
ErrorHandler.process(http_err)
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby_errors.py", line 103, in process raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=403, msg='403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync', error_response='{"result":"missing_chip"}''>
Traceback (most recent call last):
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 357, in make_request
res.raise_for_status()
File "/usr/local/lib/python3.10/dist-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/usr/local/bin/odmpy", line 8, in
sys.exit(main())
File "/usr/local/lib/python3.10/dist-packages/odmpy/main.py", line 27, in main
run()
File "/usr/local/lib/python3.10/dist-packages/odmpy/odm.py", line 846, in run
synced_state = libby_client.sync()
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 457, in sync
res: Dict = self.make_request("chip/sync") File "/usr/local/lib/python3.10/dist-packages/odmpy/libby.py", line 366, in make_request
ErrorHandler.process(http_err)
File "/usr/local/lib/python3.10/dist-packages/odmpy/libby_errors.py", line 103, in process raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=403, msg='403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync', error_response='{"result":"missing_chip"}''>

Installed following the install from source pip instructions, it was working perfectly 24 hours ago.

ffmpeg takes over stdin

The ffmpeg command sometimes reads from stdin even when it isn't asked to. The result is that odmpy will appear to produce strange output about failure to parse debug commands (and it's entirely possible that in some cases stdin will contain some valid debug command that could really mess things up).

In my case, I called odmpy from inside a bash while loop on "ls *.odm | while read line ...", and because ffmpeg was eating stdin, the while loop would only process the first successful file.

This can be resolved by always running ffmpeg with the -nostdin option, or by always running with redirection from /dev/null.

See also https://superuser.com/questions/1492507/why-does-ffmpeg-require-nostdin-in-while-loop

would love an option to just download new books

Describe the problem
I'd like to be able to run this program in a background job to periodically check for new books and automatically download them. the --latest N command sort of does that because it will recognize already downloaded books and not redownload them. however it would be great if there was a command like --new which compares all the available books with what exists locally and then downloads any books that were not currently downloaded.

odmpy libby download issue

Describe the problem
Sometimes I will have the following error on download:

Some personal data redacted.
I think this might have to do with when the book is close to return, odmpy is still trying to download a book which might had been returned?

Also when this happen, this will fail completely. Is there a way to not fail completely, instead will continue to download the next title instead? Thanks

An unexpected error has occurred
Traceback (most recent call last):
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 358, in make_request
    res.raise_for_status()
  File "/home/joshua/.local/lib/python3.9/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://sentry-read.svc.overdrive.com/open/book/card/1234/title/1234

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 952, in run
    extract_bundled_contents(
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 327, in extract_bundled_contents
    extract_loan_file(libby_client, bundled_media, args)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 375, in extract_loan_file
    _, openbook, rosters = libby_client.process_ebook(selected_loan)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 822, in process_ebook
    download_base, meta = self.prepare_loan(loan)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 786, in prepare_loan
    meta = self.open_loan(loan_type, card_id, title_id)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 767, in open_loan
    res: Dict = self.make_request(
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 367, in make_request
    ErrorHandler.process(http_err)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby_errors.py", line 103, in process
    raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=404, msg='404 Client Error: Not Found for url: https://sentry-read.svc.overdrive.com/open/book/card/64855467/title/984832', error_response='{"result":"upstream_failure","upstream":{"errorCode":"PatronDoesNotHaveTitleCheckedOut","service":"THUNDER","httpStatus":404,"userExplanation":"Patron does not have title checked out.","correlationId":"d37redactede"}}''>
Traceback (most recent call last):
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 358, in make_request
    res.raise_for_status()
  File "/home/joshua/.local/lib/python3.9/site-packages/requests/models.py", line 1021, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://sentry-read.svc.overdrive.com/open/book/card/1234/title/1234

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/joshua/.local/bin/odmpy", line 8, in <module>
    sys.exit(main())
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/__main__.py", line 27, in main
    run()
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 952, in run
    extract_bundled_contents(
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 327, in extract_bundled_contents
    extract_loan_file(libby_client, bundled_media, args)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 375, in extract_loan_file
    _, openbook, rosters = libby_client.process_ebook(selected_loan)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 822, in process_ebook
    download_base, meta = self.prepare_loan(loan)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 786, in prepare_loan
    meta = self.open_loan(loan_type, card_id, title_id)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 767, in open_loan
    res: Dict = self.make_request(
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby.py", line 367, in make_request
    ErrorHandler.process(http_err)
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/libby_errors.py", line 103, in process
    raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=404, msg='404 Client Error: Not Found for url: https://sentry-read.svc.overdrive.com/open/book/card/1234/title/1234', error_response='{"result":"upstream_failure","upstream":{"errorCode":"PatronDoesNotHaveTitleCheckedOut","service":"THUNDER","httpStatus":404,"userExplanation":"Patron does not have title checked out.","correlationId":"d37redactede"}}''>

To Reproduce
odmpy libby -c -k -d Folder/ --opf --direct --latest 99 --bookfolderformat "%(Title)s - %(Author)s - %(ID)s" --setting setting_folder/

Version/Environment
odmpy 0.8.0 [Python 3.9.2-linux]

new problem with version update

it seems that pip3 no longer works after install of pipx
uninstalling pipx doesnt help, also my python version is 3.11, some writers on the internet claim that pipx is required
for new ubuntu

should i give up on odmpy for a while? is there an another way to add missing python libraries?
sorry to bother you, im really impressed by your work. but this old hardware engineer can't seem to work problems of this complexity,
to be really clear its not your script its some python3 pip pipx changes.
thank you for any suggestions

bob

.
A clear and concise description of what the bug/error is. If there is an error traceback dump, paste the text here (please avoid screenshots).

To Reproduce
Please include the full command used, for example odmpy libby --direct

Version/Environment
Please paste the contents of odmpy --version here.

Error 1002 Hash Mismatch - HTTP Error while downloading license.

Describe the problem
I logged in via code for the first time today. When I ran odmpy libby, it shows the one audiobook I have. When type 1 and hit enter, the download begin but it errors out with following output:

Downloading "Visual Thinking" by "Temple Grandin, Ph.D." in 14 parts...
b'<LicenseError xmlns="http://license.overdrive.com/2008/03/LicenseError.xsd" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><ErrorCode>1002</ErrorCode><ErrorMessage>Hash Mismatch</ErrorMessage><ErrorDetails i:nil="true"/></LicenseError>'
Error: HTTP Error while downloading license.

To Reproduce
odmpy libby

Version/Environment
odmpy 0.8.1 [Python 3.10.12-linux]

Intermittenly, odmpy libby has time data value error

Describe the problem
Intermittenly, odmpy libby has the following error:
It shows up sometimes, but fine when I rerun it.

Traceback (most recent call last):
  File "/home/joshua/.local/bin/odmpy", line 8, in <module>
An unexpected error has occurred
Traceback (most recent call last):
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 1005, in run
    expiry_date = parse_datetime(loan["expireDate"])
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/utils.py", line 67, in parse_datetime
    dt = datetime.strptime(value, fmt)
  File "/usr/lib/python3.9/_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
:    sys.exit(main())
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/__main__.py", line 27, in main
    run()
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/odm.py", line 1005, in run
    expiry_date = parse_datetime(loan["expireDate"])
  File "/home/joshua/.local/lib/python3.9/site-packages/odmpy/utils.py", line 67, in parse_datetime
    dt = datetime.strptime(value, fmt)
  File "/usr/lib/python3.9/_strptime.py", line 568, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
  File "/usr/lib/python3.9/_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '2023-09-14T07:20:30+00:00' does not match format '%Y-%m-%dT%H:%M:%S.%fZ'

To Reproduce
Please include the full command used, for example odmpy libby

Version/Environment
odmpy 0.8.0 [Python 3.9.2-linux]

Resume an aborted download

I just got a TCP disconnect in the middle of a download; I tried and failed to find a way to resume it. It looks like there's some mechanism for using the license file to resume the download ... but if so, what might it take to actually do it? (For now, of course, I just re-download the ODM file.)

odmpy crashes when downloading audiobooks on 0.8.1

odmpy crashes when trying to download an audiobook with the libby command. I've included the command and crash output below. I was able to revert to odmpy-0.8.0 and execute the same command without this issue.

Relevant command line information
Base command odmpy libby -m -c --nobookfolder --ebooks --direct --settings ~/.odmpy/

Resulting output (with intermediate audiobook selection)

odmpy Interactive Client for Libby
----------------------------------------------------------------------
Found 2 loans.
 1: Homeward Bound                                   🎧 Elaine Tyler May
    * Expires: 2023-10-25  REDACTED
 2: Dealing with China                               🎧 Henry M. Paulson Jr.
    * Expires: 2023-10-25  REDACTED

Download. Choose from 1-2 (separate choices with a space or leave blank to quit),
then press enter: 1 2
Opening audiobook "Homeward Bound"...
An unexpected error has occurred
Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 971, in json
    return complexjson.loads(self.text, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/odm.py", line 1175, in run
    openbook, toc = libby_client.process_audiobook(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 835, in process_audiobook
    download_base, meta = self.prepare_loan(loan)
                          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 817, in prepare_loan
    _ = self.make_request(
        ^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 366, in make_request
    ErrorHandler.process(http_err)
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby_errors.py", line 86, in process
    error = http_err.response.json()
            ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 975, in json
    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 971, in json
    return complexjson.loads(self.text, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/[email protected]/3.11.5/Frameworks/Python.framework/Versions/3.11/lib/python3.11/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/homebrew/bin/odmpy", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/__main__.py", line 27, in main
    run()
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/odm.py", line 1175, in run
    openbook, toc = libby_client.process_audiobook(
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 835, in process_audiobook
    download_base, meta = self.prepare_loan(loan)
                          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 817, in prepare_loan
    _ = self.make_request(
        ^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby.py", line 366, in make_request
    ErrorHandler.process(http_err)
  File "/opt/homebrew/lib/python3.11/site-packages/odmpy/libby_errors.py", line 86, in process
    error = http_err.response.json()
            ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/requests/models.py", line 975, in json
    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)
requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Version/Environment
odmpy-0.8.1

Read/write settings in `XDG_CONFIG_HOME`

Describe the problem

When using the libby commands, the token is written to the local folder. This is convenient if you only work in a single folder, but it would be nice if followed the (fairly standard) XDG Base Directory convention. So, the default settings config (if not specified; you could keep the flag) would be ~/.config/odmpy.

You can put the settings there manually with the --settings flag, but it's annoying to type that for every operation.

Version/Environment

odmpy 0.7.9 [Python 3.11.3-darwin]

Unable to access Libby through odmpy due to Traceback error

Hi, I was wondering if odmpy is still being maintained? I am hoping so! Starting Friday, April 19. I have been unable to access my library books in Libby.

To Reproduce
I'm am on a Mac with OS Sonoma using odmpy Version 0.8.1. [Python 3.11.3-darwin]
In Terminal, if I enter odmpy libby, or my usual command line sequence to access and download my libarary books: "odmpy libby -m -c -d "audiobooks/" I get the following:

Version/Environment
Please paste the contents of odmpy --version here.
odmpy Interactive Client for Libby

An unexpected error has occurred
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 357, in make_request
res.raise_for_status()
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/odm.py", line 846, in run
synced_state = libby_client.sync()
^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 457, in sync
res: Dict = self.make_request("chip/sync")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 366, in make_request
ErrorHandler.process(http_err)
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby_errors.py", line 103, in process
raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=403, msg='403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync', error_response='{"result":"missing_chip"}''>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 357, in make_request
res.raise_for_status()
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/requests/models.py", line 1021, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.11/bin/odmpy", line 8, in
sys.exit(main())
^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/main.py", line 27, in main
run()
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/odm.py", line 846, in run
synced_state = libby_client.sync()
^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 457, in sync
res: Dict = self.make_request("chip/sync")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby.py", line 366, in make_request
ErrorHandler.process(http_err)
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/odmpy/libby_errors.py", line 103, in process
raise ClientError(
odmpy.libby_errors.ClientError: <odmpy.libby_errors.ClientError; http_status=403, msg='403 Client Error: Forbidden for url: https://sentry-read.svc.overdrive.com/chip/sync', error_response='{"result":"missing_chip"}''>


I love this app and am going to "send you a coffee" in hopes that this is still being maintained a fix or some help can be provided. Thanks you!

[Feature] Libby mode places --keepodm files next to MP3s

When we download and keep the ODM files using that --keepodm flag, would it be possible to keep them in the folder with the MP3s, instead of in the current working directory? That would become especially handy when processing multiple books, so I don't lose track of which files go with which book. This wasn't a concern with your old mode of operation when the user provided the ODM file, but when operating in Libby mode things are different.

[Feature] PDF/attached document downloads

It is possible to download PDF documents attached to the ebook at the same time as downloading the MP3 files, or as a separate followup? I know some are available only as ADE documents, which raises more problems, but I don't even know if it's possible to automatically download any attached documents.

Latest change breaks chapters for some books

I tried the latest commit that removes the dependency for eyeD3 and it breaks the chapters function for this particular book: Ball Lightning by Cixin Liu
I think it might have something to do with the special characters in the names of some chapters (@, / ).

I tried other three books with regular characters (a-z, :, -) in the names and it worked fine.

**Chapters generated with 0.3.0**
00:00:00.000 Ball Lightning
00:00:37.988 Prelude
00:13:57.988 Part 1 - College
00:20:10.988 Part 1 - Strange Phenomena 1
00:30:12.988 Part 1 - Ball Lightning
00:58:15.981 Part 1 - Lin Yun 1
01:19:37.981 Part 1 - Zhang Bin
01:44:09.981 Part 1 - Strange Phenomena 2
01:50:22.342 Part 1 - A Bolt from the Blue
02:44:42.342 Part 1 - Seti@home
03:05:49.101 Part 1 - Siberia / Part 2 - Lighthouse Inspiration
04:16:13.545 Part 2 - General Lin Feng
04:34:22.545 Part 2 - Attack Bees
04:41:06.545 Part 2 - Skynet
05:13:31.545 Part 2 - Ball Lightning
05:20:51.545 Part 2 - Thunderballs
05:29:19.870 Part 2 - Ding Yi
05:43:27.870 Part 2 - Empty Bubbles
06:21:54.870 Part 2 - Macro-Electrons
06:34:23.766 Part 2 - Weapons
07:02:16.766 Part 2 - Observers
07:16:22.766 Part 2 - Burnt Chips
07:28:07.766 Part 2 - Strange Phenomena 3
07:33:24.403 Part 2 - The Nuclear Power Plant
08:07:15.403 Part 2 - Strange Phenomena 4
08:23:02.403 Part 3 - Tornadoes
08:37:37.020 Part 3 - Zhufeng
08:51:34.020 Part 3 - Chip Destruction
09:03:39.020 Part 3 - Ambush at Sea
09:52:43.639 Part 3 - Strings
10:19:18.639 Part 3 - The Special Leading Group
10:41:36.639 Part 3 - Macro-Fusion
10:59:45.033 Part 3 - Lin Yun 2
11:40:36.033 Part 3 - Victory
11:48:19.033 Part 3 - The Quantum Rose
12:04:18.033 Afterword
12:11:27.033 Closing
**Chapters generated with 0.4.0**
00:00:00.000 Prelude
12:11:27.033 Closing
12:11:27.034 Afterword
12:11:27.035 Ball Lightning
12:11:27.036 Part 2 - Skynet
12:11:27.037 Part 1 - College
12:11:27.038 Part 2 - Ding Yi
12:11:27.039 Part 2 - Weapons
12:11:27.040 Part 3 - Zhufeng
12:11:27.041 Part 3 - Strings
12:11:27.042 Part 3 - Victory
12:11:27.043 Part 1 - Lin Yun 1
12:11:27.044 Part 1 - Zhang Bin
12:11:27.045 Part 1 - Seti@home
12:11:27.046 Part 2 - Observers
12:11:27.047 Part 3 - Tornadoes
12:11:27.048 Part 3 - Lin Yun 2
12:11:27.049 Part 2 - Attack Bees
12:11:27.050 Part 2 - Burnt Chips
12:11:27.051 Part 2 - Thunderballs
12:11:27.052 Part 3 - Macro-Fusion
12:11:27.053 Part 2 - Empty Bubbles
12:11:27.054 Part 3 - Ambush at Sea
12:11:27.055 Part 1 - Ball Lightning
12:11:27.056 Part 2 - Ball Lightning
12:11:27.057 Part 2 - Macro-Electrons
12:11:27.058 Part 2 - General Lin Feng
12:11:27.059 Part 3 - Chip Destruction
12:11:27.060 Part 3 - The Quantum Rose
12:11:27.061 Part 1 - Strange Phenomena 1
12:11:27.062 Part 1 - Strange Phenomena 2
12:11:27.063 Part 2 - Strange Phenomena 3
12:11:27.064 Part 2 - Strange Phenomena 4
12:11:27.065 Part 1 - A Bolt from the Blue
12:11:27.066 Part 2 - The Nuclear Power Plant
12:11:27.067 Part 3 - The Special Leading Group
12:11:27.068 Part 1 - Siberia / Part 2 - Lighthouse Inspiration

The debug information list the chapters in the correct order in both cases.
debug v030.txt
debug v040.txt

How can i use it? I do not know python.

Describe the problem
A clear and concise description of what the bug/error is. If there is an error traceback dump, paste the text here (please avoid screenshots).

To Reproduce
Please include the full command used, for example odmpy libby --direct

Version/Environment
Please paste the contents of odmpy --version here.

run_tests.sh errors in windows

Under windows...

$ ./run_tests.sh
./run_tests.sh: line 3: coverage: command not found

apparently coverage is not in the path when installed normally.

When I run a single coverage command with python -m: TEST_ODM environment var not defined
Apparently this is expected after reading the run_tests.sh, but it would be nice to be able to run these separately.

$ python -m coverage run --append -m unittest -v tests.OdmpyTests.test_info
test_info (tests.odmpy_tests.OdmpyTests.test_info)
`odmpy info test.odm` ... ERROR

======================================================================
ERROR: test_info (tests.odmpy_tests.OdmpyTests.test_info)
`odmpy info test.odm`
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\tests\odmpy_tests.py", line 33, in setUp
    self.test_file = os.environ["TEST_ODM"]
                     ~~~~~~~~~~^^^^^^^^^^^^
  File "<frozen os>", line 679, in __getitem__
KeyError: 'TEST_ODM'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\nnn\Documents\dev\odmpy\tests\odmpy_tests.py", line 35, in setUp
    raise RuntimeError("TEST_ODM environment var not defined.")
RuntimeError: TEST_ODM environment var not defined.

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (errors=1)
$ TEST_ODM="test_ref24.odm" python -m coverage run --append -m unittest -v tests.OdmpyTests.test_cover_fail_ref24

does work

Version/Environment
current head: 0db8641

The --selectid option gets only the last ID in the list

Describe the problem
When processing a loan export to get the books I hadn't previously downloaded and moved to their final resting place, I find that the --selectid option only downloads one book, the last ID on the list.

To Reproduce
Download your loan file odmpy libby --loans, grab a few IDs from it. Run 'em using the batch mode:

odmpy libby --bookfolderformat './tmp/%(ID)s' --hideprogress --direct --selectid 4790886 4565785

Result:

odmpy Interactive Client for Libby                                                                                                                                                                               ----------------------------------------------------------------------
Non-interactive mode. Downloading loans with IDs 4790886, 4565785...
Opening audiobook "The Lives of Bees"...
Downloading "The Lives of Bees" by "Thomas D. Seeley" in 12 parts...
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-01.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-02.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-03.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-04.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-05.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-06.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-07.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-08.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-09.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-10.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-11.mp3"
Error saving ID3: Invalid date string: 2019-05-28T04:00:00+00:00
Saved "tmp/4565785/the-lives-of-bees-part-12.mp3"
Downloaded acsm to "tmp/4790886/The Lives of Bees - Thomas D. Seeley.acsm"

No error printed, but it only got the one book. I've tried this with multiple IDs, no success.

Version/Environment
odmpy 0.8.1 [Python 3.10.12-linux]

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.