ping / odmpy

A simple command line manager for OverDrive/Libby loans. Download your library loans from the command line.
GNU General Public License v3.0
288 stars 19 forks source link

[Feature] Libby non-interactive/batch use #38

Closed wtanksleyjr closed 1 year ago

wtanksleyjr commented 1 year ago

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)?

ping commented 1 year ago

The --select option is the non-interactive answer to the interactive prompt to "Choose from 1-N", so it's a 1-based index of the loans listing ordered by checked out datetime (which is already the sort order for --exportloans).

For example, odmpy libby --select 3 4 will download the 3rd and 4th loan in the list.

If you're using python, you can script it fairly simply.

# example-automate-select.py 
import json

from odmpy.odm import run as odmpy_run_cmd

def run_select_example():
    loans_json_file_name = "loans.json"
    odmpy_run_cmd(["libby", "--exportloans", loans_json_file_name])

    with open(loans_json_file_name, "r", encoding="utf-8") as f:
        loans = json.load(f)

    selection = []
    # use start=1 because `--select` uses a 1-based index
    for idx, loan in enumerate(loans, start=1):
        # filter the loans you want
        if loan["title"].startswith("Example"):
            # For example, we want loans with titles starting with "Example"
            selection.append(str(idx))

    # the odmpy command options we want
    odmpy_command_options = [
        "libby",
        "--direct",
        "--downloaddir",
        "downloads",
        "--overwritetags",
        "--select",
    ]
    odmpy_command_options.extend(selection)
    # equivalent to running:
    # odmpy libby --direct --downloaddir downloads --overwritetags --select X Y Z

    if selection:
        odmpy_run_cmd(odmpy_command_options)

if __name__ == "__main__":
    run_select_example()

Running the script above will give you something like:

$ python example-automate-select.py 
odmpy Interactive Client for Libby
----------------------------------------------------------------------
Non-interactive mode. Exporting loans json to loans.json...
Saved loans as "loans.json"
odmpy Interactive Client for Libby
----------------------------------------------------------------------
Non-interactive mode. Downloading selected loan 4...
Opening audiobook "Example"...
Downloading "Example" by "Author" in 2 parts...
Part  1: 100%|████████████████████████████████████████████████████████| 36.0M/36.0M [00:05<00:00, 6.67MB/s]
Saved "downloads/Example - Author/example-part-01.mp3"
Part  2: 100%|████████████████████████████████████████████████████████| 29.3M/29.3M [00:04<00:00, 6.59MB/s]
Saved "downloads/Example - Author/example-part-02.mp3"
wtanksleyjr commented 1 year ago

That's not bad at all! I see how that'll work. Thank you, and I'll use this.

Hm, as an aside, there's a minor race condition possible with expirations, which (in the incredibly unlikely event it ever happened) would result in being wrong about all selections, ew. Obviously I could pre-detect that, but man that's annoying...

ping commented 1 year ago

Hm, as an aside, there's a minor race condition possible with expirations, which (in the incredibly unlikely event it ever happened) would result in being wrong about all selections, ew. Obviously I could pre-detect that, but man that's annoying...

The assumption is that you would export the loans json and process it immediately after. The risk should be reasonably small.

wtanksleyjr commented 1 year ago

Oh, absolutely. I doubt I'd even create a file, just use odmpy libby --exportloans >(mypython.py).

-Wm

On Fri, Mar 3, 2023 at 5:21 PM ping @.***> wrote:

Hm, as an aside, there's a minor race condition possible with expirations, which (in the incredibly unlikely event it ever happened) would result in being wrong about all selections, ew. Obviously I could pre-detect that, but man that's annoying...

The assumption is that you would export the loans json and process it immediately after. The risk should be reasonably small.

— Reply to this email directly, view it on GitHub https://github.com/ping/odmpy/issues/38#issuecomment-1454320237, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ7H6IFURIZFIVN4HBLNPTW2KKKFANCNFSM6AAAAAAVO4IOHE . You are receiving this because you authored the thread.Message ID: @.***>

ping commented 1 year ago

Version 0.7.5 now supports --selectid ID which may work better for you.

# where 1234 and 9876 are loan IDs
odmpy libby --selectid 1234 9876

The relevant part of the previous example will now look like this:

    selection = []
    for loan in loans:
        # filter the loans you want
        if loan["title"].startswith("Example"):
            # For example, we want loans with titles starting with "Example"
            selection.append(loan["id"])

    # the odmpy command options we want
    odmpy_command_options = [
        "libby",
        "--direct",
        "--downloaddir",
        "downloads",
        "--overwritetags",
        "--selectid",
    ]
    odmpy_command_options.extend(selection)
wtanksleyjr commented 1 year ago

ping @.***> wrote:

Version 0.7.5 now supports --selectid ID which may work better for you.

Ooh, I see -- that's perfect with the JSON!

-Wm

Message ID: @.***>