u/mkb79
mkb79
mkb79
I’ve been experimenting with Rust for quite a while, but the idea of porting audible-cli only came to me after seeing the performance improvements Rust can offer for database-heavy operations. I also really like how quickly Cargo checks, Clippy, and rustfmt catch many problems before they turn into actual bugs.
So I started building it — partly for the technical benefits and partly because it gave me a good excuse to dig deeper into Rust. :)
The longer-term goal is to use the agent built into audible-rs as a backend, so that every CLI operation can also be controlled through a browser-based web interface.
At the moment, though, the biggest thing holding the project back is a lack of testers. I’d like to get it out of alpha and publish the first 0.1.0 release, but broader testing and real-world feedback would help a lot.
mkb79
Thank you — that genuinely made my day. Comments like this are a big part of what keeps an alpha moving, so I really appreciate you taking the moment to say it.
If you (or anyone reading) end up trying it, I'm grateful for feedback of any kind — bug reports, rough edges, "this was confusing", or feature ideas — via a GitHub issue, a Discussion, or simply an email. Even a plain "worked fine on my setup" is valuable, because at this stage I mostly only hear about things when they break.
One honest heads-up: quite a few features that already work aren't in the README yet.
I've been holding off on writing proper docs while everything is still in flux — in an alpha, commands and flags can still shift, and I'd rather not document something today and have it change next week. Full docs are firmly on the list once the surface settles.
So in case they're useful before then, a few of the less obvious things it can already do:
- Download Widevine-only titles — the Audible Plus / AYCL titles that ship no `aaxc` asset — via Widevine. That needs a Widevine CDM, which you provision yourself through an Android device registration (none is bundled).
- Flexible passphrase sources for your auth file — a prompt, an env var, an external command (your keyring, gpg, …) or a file — so fully headless/automated setups work.
- download reorganize — change your filename template or download folder later and it migrates both the files on disk *and* their database records to match.
- Listening stats & membership status — `audible stats` (per month/year/marketplace) and an account overview, straight from the API.
There's also a plugin system with a small Python SDK. As a first example it ships a plugin that opens your whole library in the browser — sortable, searchable, with a detail panel per title and per-item download status. It's read-only for now, but it's really a starting point; a lot more is possible on top of it. If you want to try it:
# 1) the plugin SDK isn't on PyPI yet, so install it from the repo:
pip install "git+https://github.com/mkb79/audible-rs.git#subdirectory=sdk/python"
# 2) install the example plugin straight from the repo (it shows you the checksum + requested scopes and asks you to confirm before installing):
audible plugin add https://raw.githubusercontent.com/mkb79/audible-rs/main/examples/plugins/cmd_library-gui.py
# 3) run it — opens a local dashboard of your library in your browser:
audible -m all library-gui
(`-m all` just means "show every marketplace of the account"; it binds to localhost with a
one-time token, and the plugin only ever talks to the API through the CLI — it never sees
your credentials.)
Thanks again for the kind words — it really means a lot this early on. 🙏
mkb79
Sorry about that — the repo is very much online, it was just a broken link in my
post (the markdown swallowed a trailing character and turned it into a 404). Here's
the plain URL:
https://github.com/mkb79/audible-rs
Thanks for the heads-up!
mkb79
Hi openaudible,
I does not know yet, that you have open sourced your code. I can not find it on GitHub.
I know that you have taken my code, how to download aaxc files and how to decrypt the encrypted license response. Then you have translate this in your programming language. I've wrote with you or something from your project about this thematic in early 2021.
Edit:
Sorry, I misunderstood your above post. Yes, I can confirm that the source code of a very old version of OpenAudible is public. But it should be noted that you include the discoveries of the open source community into your commercial product. The question will then arise whether you will share your discoveries about Audible with us free of charge?
mkb79
Libation don't upload the user credentials. So Libation is safe for use. Another great tool with GUI is BookLibConnect. That’s a great tool, open source and free.
mkb79
OpenAudible using open source code under AGPLv3 license and keep there code private.
mkb79
There are some good free tools for this purpose. Libation and Book Lib Connect is the best choice if you need a GUI. Or you can use my Audible CLI app.
mkb79
Yeah, I know. I've written audible-cli which can download audiobooks from Audible, too. But my app does not have any GUI, so you have to work with the CLI. That’s why my app is not so well known as Libation or other great (most) free apps.
mkb79
If you download it from the Audible website, then you have AAX files. But the website will not let you download books from the Plus catalog.
mkb79
Audible deliver there audiobooks in AAXC format since some time. AAXC is an encrypted M4A container. Instead of the previous AAX format, AAXC uses an unique key for each account/book combination. So inAudible will don't help you here.
mkb79
mkb79
One user created a plugin for my app. With these you are able to import your audible library data to Goodreads. Take a look here for more information.
mkb79
I think so too. Maybe they will bring the AAXC format to all devices. They have done this with the iOS and Android app already.
mkb79
It seems all blank lines are removed in the code. You have to adjust them by yourself.
mkb79
How looks this for you?
```python
import random import string
while True:
def ask_for_yes_no(question: str) -> bool:
while True:
answer = input(question + " [yes/no]: ").lower()
if answer in ["yes", "no"]:
break
print("Please answer with yes or no!\n")
return True if answer == "yes" else False
def get_random_string(length: int, allowed_chars: str):
return ''.join(random.choices(allowed_chars, k=length))
lenght = int(input("How long do you want the password to be? [in numbers] "))
wantNums = ask_for_yes_no("Do you want numbers in the password?")
wantUppers = ask_for_yes_no("Do you want uppercase letters?")
def passGen(lenght):
if wantNums and wantUppers:
chars = string.ascii_letters + string.digits
print(get_random_string(lenght, chars))
elif not wantNums and wantUppers:
chars = string.ascii_letters
print(get_random_string(lenght, chars))
elif wantNums and not wantUppers:
chars = string.ascii_lowercase + string.digits
print(get_random_string(lenght, chars))
elif not wantNums and not wantUppers:
chars = string.ascii_lowercase
print(get_random_string(lenght, chars))
passGen(lenght)
restart = ask_for_yes_no("Do you want another password?")
if not restart:
print("Goodbye!")
break
```
mkb79
Hi,
you can write while True: instead using a variable.
To exit the while loop you can write:
```python
if restart.lower() != "yes":
print("Goodbye!")
break
```
mkb79
You can find here https://www.epubor.com/how-to-convert-audible-aaxc-to-mp3.html some infos about that problem.
When I download books with my audible api client (https://github.com/mkb79/Audible) I have same problems. I can’t convert them. But I get some additional informations when I make a licenserequest to the api. Maybe they help to decode the file.
Here are a sample output:
{
"content_license": {
"acr": "CR!...",
"asin": "B07DDKJH33",
"content_metadata": {
"content_url": {
"offline_url": "https://dyrrggeck87jc.cloudfront.net/.../bk_adko_003749ade_lc_128_44100_2.aax?voucherId=cdn:...&Policy=...&Signature=...&Key-Pair-Id=..."
}
},
"drm_type": "Adrm",
"license_response": "...==",
"message": "Eligibility details:[GrantRightsReason [reasonCode=MEMBERSHIP, reason=No need to verify active offline license when request is not for offline consumption. OWNERSHIP-user does not have expected ownership rights over the parent title asin. OWNERSHIP-title does not qualify for long title child part ownership or customer does not own child part. OWNERSHIP-user has ownership rights. GEO_RIGHTS-user has geo-rights[DE] over the title[B07DDKJH33] in marketplaceId[AN7V1F1VY261K]. TITLE_ATTRIBUTES-title does not have rodizio plan association. GEO_RIGHTS-user has geo-rights[DE] over the title[B07DDKJH33] in marketplaceId[AN7V1F1VY261K]. No need to verify active offline license when request is not for offline consumption. BENEFIT-user has valid Radio benefit associated[RadioStub]. TITLE_ATTRIBUTES-title does not have radio plan association. Client Asin Mapping validation skipped since client id is null. AAA Client with id: ApolloEnv:AudibleApiExternalRouterService/EU/Prod, does not have access to asin: B07DDKJH33. Client does not have plans that support asin benefits.]]. Licensing details:[ADRM license granted]",
"request_id": "...",
"status_code": "Granted",
"voucher_id": "cdn:..."
},
"response_groups": [
"always-returned"
]
}
I have shorten the ouput on many places with ...! You can use my audible api client and create your own.
Greets
mkb79
I‘ve tested this now a bit. I make 200 requests (retrieve my own library) in ~ 4 mins and don’t be blocked.
mkb79
I‘m testing the API for month with hundreds of requests. Some of these tests are done within very short time interval and I don’t get in any request blocking. But you can test this out. Use omarroth‘s or my client.
mkb79
Please check out my audible API client (https://github.com/mkb79/Audible) or omarroths client (https://github.com/omarroth/audible.cr) for informations about the api.
mkb79
Why scrapping the whole site and don’t use the audible api?
mkb79
Hi Lynn_K I have updated the audible api on pypi. You can crawl the webpage from audible with this code now:
``` import audible import requests
client = audible.Client(EMAIL, USERNAME, local="your local code here")
example for get library
url = "https://www.audible.com/a/library" payload = { "purchaseDateFilter": "all", "programFilter": 'all', "sortBy": "SHORT_TITLE.asc", "pageSize": "20", "page": 1 } response = requests.get( url, params=payload, cookies=client.login_cookies ) page = response.text
```
Maybe this helps you. And don’t forget to update the audible package with pip install --update audible before.
mkb79
You‘re fast ;)
mkb79
2FA isn’t that difficult. I have implement them in my code in audible.py at line 189:211 (cvf part). I tested this with my german account. The code will be send to my mobile phone.
If you have a device backup a restore could help you.