Git Product home page Git Product logo

Comments (34)

mkb79 avatar mkb79 commented on May 25, 2024

Which version you are using? You can try v0.5.2 which prevent CAPTCHAs in most cases.

Your error sounds like a wrong password, username or a selected marketplace where you don’t have a account. If all is correct and you have v0.5.2 already installed please try to authenticate with v0.5.0.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

If you have at least v0.5.1 you can try to login

import audible

auth = audible.Authenticator.from_login_external("us")
...

Then you will get a login link and copy them to your browser. Login. When you get a error page that page not found copy the url from address bar and insert them to the input field.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

I inserted the URL into the input field, but nothing happens after I insert it.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Do you have a openid.oa2.access_token key/value pair in the url given after a successful login?

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Yes, I believe I do. I get the error page which is as expected, and I copy that URL

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Can you try this code?!

import audible

auth = audible.Authenticator.from_login_external(
   'us'
)

with audible.Client(auth=auth) as client:
    library = client.get(
        "1.0/library",
        num_results=1000,
        response_groups="product_desc, product_attrs",
        sort_by="-PurchaseDate"
    )
    for book in library["items"]:
        print(book)

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

And please take a look on the provided url link after login.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Yeah, I copied this code, and I did the amazon authentication, and then I pasted the URL into the terminal, however, the program doesn't commence. I press enter, and nothing happens. I'm not sure if the URL is too long. I would show a screen recording of myself doing it, but of course it has sensitive information.

How are you doing it? Do you just paste the URL? Thank you so much for your time

from audible.

mkb79 avatar mkb79 commented on May 25, 2024
from urllib.parse import parse_qs
import httpx

def extract_token_from_url(url: httpx.URL) -> str:
    """Extracts the access token from url query after login."""
    parsed_url = parse_qs(url.query.decode())
    return parsed_url["openid.oa2.access_token"][0]

url = "INSERT THE COPIED LINK HERE"
url = httpx.URL(url)
token = extract_token_from_url(url)

Please try this with your link and check if you got an token!

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Hint:
The returned link after login is valid for 60 minutes. So you don’t need to login every time you execute your script. Simply copy the link in the input field if you are in time.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

If created a script. Can you test this please:

from datetime import datetime, timedelta
from urllib.parse import parse_qs

import audible
import httpx


def do_extra_stuff_with_url(rurl):
    response_url = httpx.URL(rurl)
    parsed_url = parse_qs(response_url.query.decode())

    if not "openid.oa2.access_token" in parsed_url:
        print("No access token found in url")
        print("The provided url contains the following query:")
        print(parsed_url)
        return
    
    access_token = parsed_url["openid.oa2.access_token"][0]
    print(f"Following access token found:\n{access_token}\n")

    auth_time = parsed_url["openid.pape.auth_time"][0]
    auth_time = datetime.strptime(auth_time, "%Y-%m-%dT%H:%M:%SZ")
    expires = (auth_time + timedelta(seconds=3600)).timestamp()

    expires_dt = datetime.fromtimestamp(expires)
    if expires_dt > datetime.utcnow():
        print("Token expires in:")
        print(expires_dt - datetime.utcnow())
    else:
        print("Access token is expired. Please login again.")


def login_url_callback(url: str) -> str:
    print("Please copy the following url and insert it in a webbrowser of "
          "your choice:")
    print("\n" + url + "\n")
    print("Now you have to login with your Amazon credentials. After submit "
          "your username and password you have to do this a second time "
          "and solving a captcha before sending the login form.\n")
    print("After login, your browser will show you a error page (not found). "
          "Do not worry about this. It has to be like this. Please copy the "
          "url from the address bar in your browser now.\n")

    rurl = input("Please insert the copied url (after login):\n")
    do_extra_stuff_with_url(rurl)    
    return rurl

auth = audible.Authenticator.from_login_external(
    'us',
    login_url_callback=login_url_callback
)

with audible.Client(auth=auth) as client:
    library = client.get(
        "1.0/library",
        num_results=1000,
        response_groups="product_desc, product_attrs",
        sort_by="-PurchaseDate"
    )
    for book in library["items"]:
        print(book)

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024
from urllib.parse import parse_qs
import httpx

def extract_token_from_url(url: httpx.URL) -> str:
    """Extracts the access token from url query after login."""
    parsed_url = parse_qs(url.query.decode())
    return parsed_url["openid.oa2.access_token"][0]

url = "INSERT THE COPIED LINK HERE"
url = httpx.URL(url)
token = extract_token_from_url(url)

Please try this with your link and check if you got an token!

I ran this, and I do get a token.

I ran your script, and I think the problem lies with this line

rurl = input("Please insert the copied url (after login):\n")

I insert the copied url, but nothing happens. I type enter, and nothing happens. So it appears to be a problem with the input. I copied my URL in the place of the input, and it passed. However, I get the following error.

Token expires in:
0:54:48.997241
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 143, in _request
    resp.raise_for_status()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/httpx/_models.py", line 1103, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: 403 Client Error: Forbidden for url: https://api.audible.com/1.0/library?num_results=1000&response_groups=product_desc%2C+product_attrs&sort_by=-PurchaseDate
For more information check: https://httpstatuses.com/403

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 59, in <module>
    sort_by="-PurchaseDate"
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 217, in get
    return self._request("GET", path, params=params, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 157, in _request
    self._raise_for_status_error(exc.response)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 106, in _raise_for_status_error
    raise Unauthorized(resp, data)
audible.exceptions.Unauthorized: Forbidden (403): Request could not be authenticated

Any advice? Really appreciate your time

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Okay, if you have a valid access token you can do the following:

import audible

auth = audible.Authenticator()
auth.locale = "us"
auth.access_token = "YOUR ACCESS TOKEN"
auth.register_device()
auth.to_file("FILENAME FOR AUTH_FILE")

This saves an auth file to given location. You don‘t need to login again. With a device registration you use another form of authentication. Only run this to load the file:

import audible

auth = audible.Authenticator.from_file("FILENAME")
client = ...

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

I‘ve an idea with this line of code:

rurl = input("Please insert the copied url (after login):\n")

The field expected the input in a new line. Can you replace the line above with these:

rurl = input("Please insert the copied url (after login):")

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Mac OS uses \r\n instead of \n for a newline.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

The input line didn't work either unfortunately. What I ended up doing is taking the url from the error page, and setting rurl equal to that url, but that seems to only work once. Any other ways to do it besides just the input thing?

Also, does this package allow me to download .aax files? If not, is there a way I can use the authentication returned from this api to access audible.com to webscrape it?

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Your issue sounds really strange. I doesn’t hear about any issues like this.

You only can try to obtain the access token the way you do. And then instantiate the Authenticator like above and set locale and access_token. After that, register a device and save the credentials. After that you don’t have to worry about this step anymore.

My package can‘t download the aax file for you. But it can give you the download link. Or you can use audible-cli which can download aax files using the command line.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

My package can‘t download the aax file for you. But it can give you the download link. Or you can use audible-cli which can download aax files using the command line.

How can I get the download link using your api?

You only can try to obtain the access token the way you do. And then instantiate the Authenticator like above and set locale and access_token. After that, register a device and save the credentials. After that you don’t have to worry about this step anymore.

Interesting, I will try that and circle back. I'll try and see if its any problems with my settings in my terminal or whatever that's giving the issues. I'll update you on that

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Here you can find an example for downloading aax files. This example doesn’t care about available codecs. This can result in a download link which doesn’t work.

Hint:
Audible Plus books can‘t be downloaded in aax format. They use an advanced sort of encryption. If you want such books you have to download them in aaxc format.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Will definitely check this out. But i really don't know why the input isn't working. theoretically, I should just input the URL and press enter, but my terminal isn't letting me.

A problem with the URL i've experienced is that it is only valid for about 5 minutes. I can run the api a few times, and then afterwards, it keeps telling me that the request cannot be authorized.

0:57:27.128982
Traceback (most recent call last):
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 143, in _request
    resp.raise_for_status()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/httpx/_models.py", line 1103, in raise_for_status
    raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: 403 Client Error: Forbidden for url: https://api.audible.com/1.0/library?num_results=1000&response_groups=product_desc%2C+product_attrs&sort_by=-PurchaseDate
For more information check: https://httpstatuses.com/403

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 59, in <module>
    sort_by="-PurchaseDate"
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 217, in get
    return self._request("GET", path, params=params, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 157, in _request
    self._raise_for_status_error(exc.response)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/audible/client.py", line 106, in _raise_for_status_error
    raise Unauthorized(resp, data)
audible.exceptions.Unauthorized: Forbidden (403): Request could not be authenticated

I could store the access code in a file and such, but I don't want to have to store user passwords. So getting the URL thing to work would be really great.

Here you can find an example for downloading aax files. This example doesn’t care about available codecs. This can result in a download link which doesn’t work.

How can I figure out the available codecs? It worked for a few of my personal books, but not with some others.

Again, I appreciate you helping me out!

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Maybe the issue with the url input and the constant CAPTCHA are the same. The input field will not be properly submitted I think. To verify that please try this:

import audible

def login_url_callback(url: str) -> str:
    print("Please copy the following url and insert it in a webbrowser of "
          "your choice:")
    print("\n" + url + "\n")
    print("Now you have to login with your Amazon credentials. After submit "
          "your username and password you have to do this a second time "
          "and solving a captcha before sending the login form.\n")
    print("After login, your browser will show you a error page (not found). "
          "Do not worry about this. It has to be like this. Please copy the "
          "url from the address bar in your browser now.\n")

    rurl = input("Please insert the copied url (after login):\n")
    
    print(f"You are entered the following: {rurl}")
    
    return rurl

auth = audible.Authenticator.from_login_external(
    'us',
    login_url_callback=login_url_callback
)

This will print out the entered url. Please check if the copied url match the printed url. If they don’t match you can install click (pip install click). And then replace input("Please insert the copied url (after login):\n") with click.prompt("Please insert the copied url (after login):\n").

I strongly recommended that you don’t login to often in short time. You could be need to change your password or other things. So best way is to register a device once and reuse the given credentials after registration. This credentials will never expire until you deregister your device.

The available codecs will be displayed in the api response when getting the library. Each item (book) contains a available_codecs key which holds the allowed codecs. If available_codecs is None please make sure that the response_groups param contains at least product_desc and product_attrs.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Right. So the problem lies with the input line. I copy the url and I paste it into the input, and it doesn't allow me to enter. Also, I've found that the link is only valid for a minute or two, so I am able to access the api's and such. However, after a minute or two passes, then I have to re-login. Why is this the case?

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

That’s really strange. My tests with access token are month ago. My access tokens from login where valid for 60 minutes. Maybe Amazon changed something.

So I would suggest you to register a device immediately after login and save the registration data to file.

Have you try to make use of click.prompt?

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Yes, I tried click.prompt() , and it was the same behavior.

I think saving the registration data to a file is a good idea. However, that means that I'd need to use the user's password, which I do not think is safe to store on the database I am making, which is why I am a bit concerned about the longevity of the link.

This is what I am running

from datetime import datetime, timedelta
from urllib.parse import parse_qs

import audible
import click
import httpx


def do_extra_stuff_with_url(rurl):
    response_url = httpx.URL(rurl)
    parsed_url = parse_qs(response_url.query.decode())

    if not "openid.oa2.access_token" in parsed_url:
        print("No access token found in url")
        print("The provided url contains the following query:")
        print(parsed_url)
        return

    access_token = parsed_url["openid.oa2.access_token"][0]
    print(f"Following access token found:\n{access_token}\n")

    auth_time = parsed_url["openid.pape.auth_time"][0]
    auth_time = datetime.strptime(auth_time, "%Y-%m-%dT%H:%M:%SZ")
    expires = (auth_time + timedelta(seconds=3600)).timestamp()

    expires_dt = datetime.fromtimestamp(expires)
    if expires_dt > datetime.utcnow():
        print("Token expires in:")
        print(expires_dt - datetime.utcnow())
    else:
        print("Access token is expired. Please login again.")


def login_url_callback(url: str) -> str:
    print("Please copy the following url and insert it in a webbrowser of "
          "your choice:")
    print("\n" + url + "\n")
    print("Now you have to login with your Amazon credentials. After submit "
          "your username and password you have to do this a second time "
          "and solving a captcha before sending the login form.\n")
    print("After login, your browser will show you a error page (not found). "
          "Do not worry about this. It has to be like this. Please copy the "
          "url from the address bar in your browser now.\n")

    rurl = click.prompt("insert url")
    do_extra_stuff_with_url(rurl)
    return rurl


auth = audible.Authenticator.from_login_external(
    'us',
    login_url_callback=login_url_callback
)

with audible.Client(auth=auth) as client:
    library = client.get(
        "1.0/library",
        num_results=1000,
        response_groups="product_desc, product_attrs",
        sort_by="-PurchaseDate"
    )
    for book in library["items"]:
        print(book)

and after 2-3 minutes, or so I get the access denied error

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

To register a device simply add

auth.register_device()
auth.to_file("FILENAME")

# if you want to encrypt the auth file
auth.to_file(
    "FILENAME",
    password="YOUR SUPER STRONG PASSWORD",
    encryption="json"
)

after auth = audible.Authenticator.from_login_external(...).

And to load the file auth = audible.Authenticator.from_file("FILENAME", password="YOUR PW")

The auth file doesn’t store your Amazon password but other critical data. So encrypting your auth file is recommended if other persons can access your PC.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Here you can read some information about load and save auth files.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Ahh ok this is perfect I think. I was under the assumption that I needed the amazon password for the auth.to_file() call, but I guess I don't, which is great. If I don't register the device, can I use the authentication file forever? Or do I need to register the device to make the authentication file valid indefinitely?

I really appreciate your time answering my questions.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

You can save the auth file after a login too. This would save your access token. But you say that your token expires after some minutes. So saving and reusing the session after login is useless in your case.

My test for months results in a token lifetime for 60 minutes. So I reused this auth file in that time. After expiration I had to login again. Device registration (like the iOS and Android Audible App does) make this all obsolete.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Maybe this could be your issue with input fields. Can you verify that?

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

I close this issue now. If there is any progress I will reopen the issue.

from audible.

hossam-zaki avatar hossam-zaki commented on May 25, 2024

Yeah, I was using VS code's terminal, then switched to my Mac's proper terminal, and it didn't solve the issue.

I think registering the device is the best way to go now. Can I register more than one account on one device?

Thank you very much for your time assisting me.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

You can register multiple devices for one account. And you can have multiple accounts each with his own auth file.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

If you have books on multiple marketplaces you only need to register once.

from audible.

mkb79 avatar mkb79 commented on May 25, 2024

Maybe I have a solution for your issue with insert the url in terminal after a successful login. As you can read here this could be a Mac OS related problem. A solution is to enter the stty -icanon command before executing your script or you try to import the readline module in your script.

from audible.

Related Issues (20)

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.