Skip to content

Instantly share code, notes, and snippets.

@lfhbento
Forked from spf13/script.js
Last active July 4, 2025 22:28
Show Gist options
  • Save lfhbento/3388607475edc23a571e8eaf568469e3 to your computer and use it in GitHub Desktop.
Save lfhbento/3388607475edc23a571e8eaf568469e3 to your computer and use it in GitHub Desktop.
Download all your Kindle books before Feb 26, 2025
// ==UserScript==
// @name Kindle Download
// @namespace http://tampermonkey.net/
// @version 2025-02-20
// @description Download all your kindle books
// @author You
// @match https://www.amazon.com/hz/mycd/digital-console/contentlist/booksPurchases/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=amazon.com
// @grant none
// ==/UserScript==
// 1. Log in to your Amazon account
// 2. Go to your Content Library > Books - https://www.amazon.com/hz/mycd/digital-console/contentlist/booksPurchases/dateDsc/
(async function () {
// Close the notification if it appears
function closeNotification() {
const notifClose = document.querySelector('span#notification-close');
if (notifClose) {
notifClose.click();
}
}
// Change to whatever device you want to select. 1 = first device, 2 = second device, etc
const DEVICE = 1;
// Pause for a given duration (in milliseconds)
function pause(duration = 1000) {
return new Promise((resolve) => setTimeout(resolve, duration));
}
await pause(5000);
const allPages = Array.from(document.querySelectorAll('a.page-item'));
const lastPage = allPages[allPages.length - 1];
const lastPageNumber = lastPage ? parseInt(lastPage.innerText, 10) : 1;
let currentPage = document.querySelector('a.page-item.active');
let currentPageNumber = parseInt(currentPage.innerText, 10);
do {
await pause(5000);
currentPage = document.querySelector('a.page-item.active');
currentPageNumber = parseInt(currentPage.innerText, 10);
console.log(`downloading page ${currentPageNumber} of ${lastPageNumber}`);
// They removed the id, so we have to use the class name now
// It's a bit more brittle but it should do.
const menus = Array.from(document.querySelectorAll('div[class*="Dropdown-module_dropdown_container"]'))
.map((container) =>
Array.from(container.children).find(
(child) => child.innerHTML.indexOf('DOWNLOAD_AND_TRANSFER_DIALOG') !== -1,
),
)
.filter((item) => !!item);
for (let menu of menus) {
// Extract the ASIN from the menu's id.
// E.g. "DOWNLOAD_AND_TRANSFER_ACTION_B07HYK662L" -> "B07HYK662L"
const dialog = menu.querySelector(`div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_']`);
if (!dialog) {
console.warn(`No dialog found for menu`);
continue;
}
const parts = dialog.id.split('_');
const asin = parts[parts.length - 1];
console.log(`Processing book with ASIN: ${asin}`);
// Click the menu to open the dialog
menu.click();
const menuItem = Array.from(menu.childNodes).find((node) => node.querySelector(`div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_']`));
menuItem.click();
await pause(500);
// Within the dialog, select the first radio button (device) to download.
// This selector targets the list for this ASIN.
const inputSelector = `ul#download_and_transfer_list_${asin} li[class^='ActionList-module_action_list_item__'] > div > label`;
const inputList = Array.from(menu.querySelectorAll(inputSelector));
console.log(inputList.length);
if (!inputList) {
console.warn(`No download option found for ASIN ${asin}`);
continue;
}
const deviceToCheck = inputList.length >= DEVICE ? DEVICE - 1 : 0;
const input = inputList[deviceToCheck];
if (!input) {
console.log(`No download option found for ASIN ${asin}`);
continue;
}
input.click();
await pause(500);
// Find the confirm button within the dialog for this ASIN.
const buttonSelector = `div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_${asin}'] div[class^='DeviceDialogBox-module_button_container__'] > div[id$='_CONFIRM']`;
const button = document.querySelector(buttonSelector);
if (!button) {
console.warn(`No confirm button found for ASIN ${asin}`);
continue;
}
button.click();
await pause(1000);
closeNotification();
await pause(500);
}
if (currentPage) {
const nextPage = currentPage.nextElementSibling;
if (nextPage) {
nextPage.click();
}
}
} while (currentPageNumber < lastPageNumber);
})();
@403-html
Copy link

403-html commented Feb 22, 2025

if there's no more than 1 page, then you get Uncaught (in promise) TypeError: lastPage is undefined. Easy to fix I think, with checking if pagination is even there, and if not then page number is just 1.

Edit: my take on this, works for me

(async function () {
  // Close the notification if it appears
  function closeNotification() {
    const notifClose = document.querySelector("span#notification-close");
    if (notifClose) {
      notifClose.click();
    }
  }

  // Pause for a given duration (in milliseconds)
  function pause(duration = 1000) {
    return new Promise((resolve) => setTimeout(resolve, duration));
  }

  await pause(5000);

  // #### MAIN CHANGE: different way of tracking pages #####
  let currentPageNumber = 1;
  let lastPageNumber = 1;

  // Get the last page number. If there's only one page, the pagination elements won't exist.
  const lastPageElement = document.querySelector("a.page-item:last-of-type");
  if (lastPageElement) {
    lastPageNumber = parseInt(lastPageElement.innerText, 10);
  }

  do {
    await pause(5000);

    console.log(`Downloading page ${currentPageNumber} of ${lastPageNumber}`);

    const menus = Array.from(
      document.querySelectorAll(
        'div[class*="Dropdown-module_dropdown_container"]'
      )
    )
      .map((container) => Array.from(container.children).find((child) => child.innerHTML && child.innerHTML.indexOf("DOWNLOAD_AND_TRANSFER_DIALOG") !== -1))
      .filter((item) => !!item);

    for (let menu of menus) {
      const dialog = menu.querySelector(`div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_']`);

      if (!dialog) {
        console.warn(`No dialog found for menu element. Skipping.`);
        continue;
      }

      const parts = dialog.id.split("_");
      const asin = parts[parts.length - 1];
      console.log(`Processing book with ASIN: ${asin}`);

      menu.click();
      await pause(500);

      const inputSelector = `ul#download_and_transfer_list_${asin} li[class^='ActionList-module_action_list_item__'] > div > label`;
      const input = document.querySelector(inputSelector);
      if (!input) {
        console.warn(`No download option found for ASIN ${asin}`);
        closeNotification(); // Close the notification even if download option isn't found
        continue;
      }
      input.click();
      await pause(500);


      const buttonSelector = `div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_${asin}'] div[class^='DeviceDialogBox-module_button_container__'] > div[id$='_CONFIRM']`;
      const button = document.querySelector(buttonSelector);
      if (!button) {
        console.warn(`No confirm button found for ASIN ${asin}`);
        closeNotification(); // Close the notification even if confirm button isn't found
        continue;
      }

      button.click();
      await pause(1000);

      closeNotification();
      await pause(500);
    }

    if (currentPageNumber < lastPageNumber) { // CHANGE: Check if there are pages, before going further
      const nextPage = document.querySelector("a.page-item.next:not(.disabled)");
      if (nextPage) {
        nextPage.click();
        currentPageNumber++;
      }
    }
  } while (currentPageNumber < lastPageNumber); // CHANGE: Use < instead of <=, so it doesn't re-download stuff
})();

@coopy
Copy link

coopy commented Feb 22, 2025

I would recommend starting on this URL which shows only books you've purchased:
https://www.amazon.com/hz/mycd/digital-console/contentlist/booksPurchases/dateDsc?pageNumber=1

It will filter out samples (which can't be downloaded) and content not purchased in the Kindle store to make the whole process a bit faster.

Edit: This requires you to modify line 7 of the original script to read

// @match        https://www.amazon.com/hz/mycd/digital-console/contentlist/booksPurchases/dateDsc*

@2ShedsJackson
Copy link

lastPageNumber will max out at 400. Users with more than 10000 books will keep seeing a "next page" button without a page number.

@webframp
Copy link

This worked for me as of 2/22

@knittingmommy
Copy link

I should ask, "Once on the page you need to continue from, did you then re-enable the extension, and then refresh on that page?" Landing on a page or refreshing a page seems to be needed to trigger the extention to activate.

Yes, I'm using TamperMonkey and yes, I did refresh the screen after re-enabling the script again. So what I'm seeing is that the script will trigger if you go to your books by clicking through the content link on the Amazon page. But the script won't activate if you specify certain page numbers. Even if I go directly to page 1 by using the link that @coopy said in his reply to someone, that link won't activate the script or get it started. The script is only activating by going directly to the books via the Content link on the Amazon page. It's like the script doesn't recognize any pages by the numbers.

So what I ended up doing to get things moving, is that I went to the content page as normal. Selected, purchased from the dropdown, waited for the script to start, and then I quickly started changing pages by using the page buttons at the bottom. If I use those to forward through to the next pages, I could skip some pages until I got back to page 89 which was just a couple of pages before where I wanted to start. Once I got there, I just let the script keep going. I figured it was better to have duplicates than to miss a book after all that. I'll have to go back and delete the duplicates later, but each time I hit the button to skip some pages, the script would just start downloading the first book on the page I landed on. But it won't trigger if you go directly to a page. You have to use the buttons to trigger the next page portion of the script and then it will work.

It's not a great workaround, but it does work. I'm now downloading from the page I wanted to start from. I don't know if this information will help the developer of the script or not. But I think he hard-coded the script so that it has to start on that one page. And you have to use the buttons on the page to advance if you want the script to work. I don't know if that's because of how the script is written or if it's because of how Amazon set up the website. Amazon isn't the most competent when it comes to making things work efficiently!

But I'm not a coder so, that's just a guess as a layperson. It would be nice if the script would start on whatever page a person is on. But it doesn't do that.

But at least, I'm downloading books again until I get to page 400, I guess. Not sure what I'm going to do once I get there!

@angelakmiller
Copy link

Thanks so much for this. I had more than 2000 books so this was so appreciated.

@lfhbento
Copy link
Author

Oh, that's a lot of pages hahaha. I didn't have as many books, so I don't know if anything changes on the pagination structure in order to update the code 😅
Thanks @Martin-Collins for answering questions here!
@knittingmommy @therenavigatedlife If tampermonkey is not appearing/not working, I'd recommend maybe disabling it altogether, copying the code, and pasting in the console directly (you can open the console with cmd+option+J on mac or ctrl+shift+J on windows). It should continue navigating from there too.
If anyone with a lot of pages could get me some info on whether the page structure changes or not, that would be immensely helpful. Steps:

  1. Right click -> inspect on the button for the current page (where the script stopped working)
  2. Find the line <div id="pagination" class="pagination"> (should be a few lines above where's highlighted)
  3. Right click that line and choose "Copy element"
  4. Paste that HTML here

It should look something like this (in this case I have 27 total pages):

<div id="pagination" class="pagination"><a id="page-1" class="page-item">1</a><a id="page-LEFT_PAGE" class="page-link" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a><a id="page-6" class="page-item">6</a><a id="page-7" class="page-item">7</a><a id="page-8" class="page-item active">8</a><a id="page-9" class="page-item">9</a><a id="page-10" class="page-item">10</a><a id="page-RIGHT_PAGE" class="page-link" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a><a id="page-27" class="page-item">27</a></div>```

I didn't try the pasting the code into the console. Just opening that up and looking at that page was scary!!! But I was able to inspect my problematic page and here is the code that I copied:

<div id="pagination" class="pagination"><a id="page-1" class="page-item">1</a><a id="page-LEFT_PAGE" class="page-link" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a><a id="page-89" class="page-item">89</a><a id="page-90" class="page-item">90</a><a id="page-91" class="page-item active">91</a><a id="page-92" class="page-item">92</a><a id="page-93" class="page-item">93</a><a id="page-RIGHT_PAGE" class="page-link" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a><a id="page-400" class="page-item">400</a></div>

Other than the page numbers, I can't tell any difference between the two codes, but I have no idea what I'm looking for. But if you want me to look for anything else on the page, let me know.

@knittingmommy Oh thank you! From what I've been seeing other people mention, things break right around the 400 mark. Would you be able to get the same when you're on page 399?

Or if anyone else that's having problems with 400+ pages could do the same? That would be very helpful 🙏 I don't have a way to test with that many pages 😅

@knittingmommy
Copy link

Oh, that's a lot of pages hahaha. I didn't have as many books, so I don't know if anything changes on the pagination structure in order to update the code 😅
Thanks @Martin-Collins for answering questions here!
@knittingmommy @therenavigatedlife If tampermonkey is not appearing/not working, I'd recommend maybe disabling it altogether, copying the code, and pasting in the console directly (you can open the console with cmd+option+J on mac or ctrl+shift+J on windows). It should continue navigating from there too.
If anyone with a lot of pages could get me some info on whether the page structure changes or not, that would be immensely helpful. Steps:

  1. Right click -> inspect on the button for the current page (where the script stopped working)
  2. Find the line <div id="pagination" class="pagination"> (should be a few lines above where's highlighted)
  3. Right click that line and choose "Copy element"
  4. Paste that HTML here

It should look something like this (in this case I have 27 total pages):

<div id="pagination" class="pagination"><a id="page-1" class="page-item">1</a><a id="page-LEFT_PAGE" class="page-link" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a><a id="page-6" class="page-item">6</a><a id="page-7" class="page-item">7</a><a id="page-8" class="page-item active">8</a><a id="page-9" class="page-item">9</a><a id="page-10" class="page-item">10</a><a id="page-RIGHT_PAGE" class="page-link" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a><a id="page-27" class="page-item">27</a></div>```

I didn't try the pasting the code into the console. Just opening that up and looking at that page was scary!!! But I was able to inspect my problematic page and here is the code that I copied:
<div id="pagination" class="pagination"><a id="page-1" class="page-item">1</a><a id="page-LEFT_PAGE" class="page-link" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a><a id="page-89" class="page-item">89</a><a id="page-90" class="page-item">90</a><a id="page-91" class="page-item active">91</a><a id="page-92" class="page-item">92</a><a id="page-93" class="page-item">93</a><a id="page-RIGHT_PAGE" class="page-link" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a><a id="page-400" class="page-item">400</a></div>
Other than the page numbers, I can't tell any difference between the two codes, but I have no idea what I'm looking for. But if you want me to look for anything else on the page, let me know.

@knittingmommy Oh thank you! From what I've been seeing other people mention, things break right around the 400 mark. Would you be able to get the same when you're on page 399?

Or if anyone else that's having problems with 400+ pages could do the same? That would be very helpful 🙏 I don't have a way to test with that many pages 😅

The script is up to page 237 now, so I should be able to test what happens at 400 in a few hours. Given what others have said, I'm expecting it to just stop working when it gets to 400.

But until then, I had a thought. Assuming that @therenavigatedlife might be able to go at it from a different way. I was thinking they could start their content over again, only when he gets to the page with the books, click on the part of the page that says sort by and choose: Acquired: Oldest to Newest and see if they can get it started again. Only this time they'd be starting with their oldest books on page 1. I'm not sure what to do about books in the middle that might be missed if they have that many books.

I'm also not sure if that would work. It's just a thought. I might try that myself if I need to when I get to page 400.

@tagoofy
Copy link

tagoofy commented Feb 22, 2025

FYI, worked for me on Chrome. There was a message early asking me to confirm it was ok to do multiple downloads from Amazon. Maybe I missed that message on Edge and that was why the download never appeared in my download folder.

Still would like to know how to have the script select a Kindle in the list that is not the first one (3rd one in my case).

Thanks,
Todd

@btate8
Copy link

btate8 commented Feb 23, 2025

I am also trying to figure out how to get a device besides the 1st device listed to work (2nd in my case).

I have another script I can use that does 1 page at a time for a 2nd device, but with hundreds of books, I would love to have this script work with the 2nd listed device!

If anyone figures it out, please share.

Thanks,

BT

@lfhbento
Copy link
Author

@tagoofy @btate8 I've modified the script to allow you to select the device.

Change const DEVICE = 1; on line 25 to whatever device you want to select and then run the script

@hiroo916
Copy link

in case this helps somebody else. The expired library books in my collection were interfering with the script so I switched the view to Purchases only. And to start on another page, I changed the activation URL at the beginning of the script to the second page of the Purchases URL. Then reloaded on that page and it started downloading.

example:
// @match https://www.amazon.com/hz/mycd/digital-console/contentlist/booksPurchases/dateDsc?pageNumber=2

@Mirah444
Copy link

Unfortunately we were never able to locate the <div id="pagination" class="pagination" under Inspect.

Another user suggested downloading books by Collections. Because all of the Collections are less than 10000 books the 400 page limit would not be a problem.

My issue now is that the script starts downloads immediately and there is not time to navigate to the Collections page to open a Collection. Despite clicking with reckless abandon, I've only been successful in downloading a single 200 book Collection.

Is there a way to slow down or control the initiation of the automatic downloads? If not can this script map to the Collections page instead of all books?

Thanks for all the help!

@knittingmommy
Copy link

knittingmommy commented Feb 23, 2025

@lfhbento So, I'm now on page 400 and the script just stopped downloading. Tampermonkey shows a red number 1 and the download script is there, but there's no way to trigger it. I looked at the inspect element and as @therenavigatedlife stated, the pagination isn't there anymore. This is what is there for the previous and next buttons if this helps:

<div id="PREVIOUS_INFINTE_LOADER_ID" class="action_button" tabindex="0" style="display: inline-block; border-radius: 3px; padding: 0px 10px; margin-bottom: 10px; min-height: 1.8rem; border-width: 1px; border-style: solid; border-color: rgb(173, 177, 184) rgb(162, 166, 172) rgb(141, 144, 150); border-image: initial; cursor: pointer; background: linear-gradient(rgb(247, 248, 250), rgb(231, 233, 236)); word-break: break-word; outline: none;"><div style="vertical-align: middle; padding-top: 0.5em; padding-bottom: 0.5em; font-size: 13px;"><span>Previous Page</span></div></div>
<div id="NEXT_INFINTE_LOADER_ID" class="action_button" tabindex="0" style="display: inline-block; border-radius: 3px; padding: 0px 10px; margin-bottom: 10px; min-height: 1.8rem; border-width: 1px; border-style: solid; border-color: rgb(173, 177, 184) rgb(162, 166, 172) rgb(141, 144, 150); border-image: initial; cursor: pointer; background: linear-gradient(rgb(247, 248, 250), rgb(231, 233, 236)); word-break: break-word; outline: none;"><div style="vertical-align: middle; padding-top: 0.5em; padding-bottom: 0.5em; font-size: 13px;">
<span>Next Page</span></div></div>

<span>Next Page</span>

I hope that helps. For the moment I'm not touching that browser tab. However, I did open a new tab and was able to have enough time to quickly sort books by purchased and oldest to newest so that my oldest books were now on page 1 and the script started running and downloading my books going in the other direction now. I'll figure out what's missing in the middle when get there.

@SpikeTheLobster
Copy link

SpikeTheLobster commented Feb 23, 2025

I have a monstrous number of titles on Kindle (just under 15,000), so I ran into the difficulty of the script stopping at page 400 (9.975th to 10,000th). I figured I'd jury-rig/workaround simply by switching from dateDsc to dateAsc (in the #match on line 7), as @knittingmommy did in the previous comment...

Unfortunately, I'm now getting "Invalid access token" when the script runs on the first item on page 1 (ascending). Tried removing cookies and relogging, in case that'd help, but no joy. Switched browser (from Chrome to Edge), same error. :(

@coopy
Copy link

coopy commented Feb 23, 2025

@SpikeTheLobster Just checking, I’m guessing this was exactly what you did: after you switched the sort order in the #match URL, did you also change the actual URL you’re visiting to the same?

@nacht
Copy link

nacht commented Feb 23, 2025

Anyone else getting an XML error message?

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<error>
<div id="in-page-channel-node-id" data-channel-name="in_page_channel_QSMWKf"/>
<message>Invalid access token</message>
</error>

@CharlieGB
Copy link

CharlieGB commented Feb 23, 2025

I am getting the same error. I thought I had things working earlier today, but it was downloading for my Fire device which meant I couldn't get it to remove the drm. Hence I changed the device.

@TysonCodes
Copy link

Just worked for me on amazon.ca but note that I had to remove the final / for the matching pattern.
https://www.amazon.ca/hz/mycd/digital-console/contentlist/booksPurchases*
not
https://www.amazon.ca/hz/mycd/digital-console/contentlist/booksPurchases/*

@SpikeTheLobster
Copy link

@SpikeTheLobster Just checking, I’m guessing this was exactly what you did: after you switched the sort order in the #match URL, did you also change the actual URL you’re visiting to the same?

Yes. Easy to make that mistake, but otherwise the script wouldn't run.

@SpikeTheLobster
Copy link

SpikeTheLobster commented Feb 23, 2025

Just worked for me on amazon.ca but note that I had to remove the final / for the matching pattern. https://www.amazon.ca/hz/mycd/digital-console/contentlist/booksPurchases* not https://www.amazon.ca/hz/mycd/digital-console/contentlist/booksPurchases/*

Oh, you clever, clever person, you. Zero idea why taking the last / off works, but mine's running again (on .co.uk) with that change. THANKS!

(Using amazon.co.uk/hz/mycd/digital-console/contentlist/booksAll/dateAsc* on line 7)

@JohanKlos
Copy link

Great script, thanks! I have more than 1000 books, so I needed some automation to go to the next page. Find my fork of your script here: https://gist.github.com/JohanKlos/3a92f6a55b8f1a8e712ce3c4b1510dd3

@tir38
Copy link

tir38 commented Feb 23, 2025

For some reason @403-html 's update https://gist.github.com/lfhbento/3388607475edc23a571e8eaf568469e3?permalink_comment_id=5449612#gistcomment-5449612 was not navigating to the next page and just re-downloading the same 25 books. Switching to OP's script fixed it.

@CJoshuaV
Copy link

Is there an easy way to tweak this to batch download Comixology content? I tried manually filtering for it, but the script no longer worked.

@crasher35
Copy link

This worked really well. I did have to register my husband's old kindle to my account though, otherwise, Amazon wasn't letting me download it at all. Not even manually. But once I did that, this script really works well.

For anyone following that YouTube video, this script doesn't have a green download button anymore. It just starts downloading on it's own. It also skips to the next page on its own now too. Really nice!

@Cassieh1111
Copy link

can you make a script so I can use on other sites such as ibookpile.in and https://oceanofpdf.com/ under the authors so I can get all the authors books in 1 go thank you hope you can help me

@hkeach
Copy link

hkeach commented Feb 24, 2025

I have 6 registered devices but when I try do download manually I get the "you don't have any compatible devices" message. The script seems to be running without error as it pages to the end of 300 books but they're not in my download directlory and it completed in only about 90 seconds.

Edit: I checked the console and this is what I see over and over:

Processing book with ASIN: B0042XA2Y0
userscript.html?name=Kindle-Download.user.js&id=1af2cd89-b9fd-4f18-91fc-b92fe022083a:83 0
userscript.html?name=Kindle-Download.user.js&id=1af2cd89-b9fd-4f18-91fc-b92fe022083a:94 No download option found for ASIN B0042XA2Y0

Edit#2: I don't have a real kindle registered to my account, only apps on ios devices. I'll try registering an old one.

@wilssearch
Copy link

Thank you for doing this! I have 12520 books to download. I didn't think I could get very many downloaded without your help!

@bookdragon9
Copy link

I'm sure someone must have said this before but you can actually get at the last 400 pages by telling it to sort in ascending date order. So the black hole is only there if you have more than 20,000. I only have 17.5k but my daughter has 28k and having to do the middle manually using the page at a time downloader. She hasn't got this working yet.
I currently have 2 machines going - one forwards and the other backwards.
It is working well although showing errors that it just seems to get around on the win10 machine while the win 11 machine stops dead and needs the urls changing to continue.
As long as the url following match is exactly the same as the url in the browser window it works well.

Thank you so much for providing this script :)

@wilssearch
Copy link

if there's no more than 1 page, then you get Uncaught (in promise) TypeError: lastPage is undefined. Easy to fix I think, with checking if pagination is even there, and if not then page number is just 1.

Edit: my take on this, works for me

(async function () {
  // Close the notification if it appears
  function closeNotification() {
    const notifClose = document.querySelector("span#notification-close");
    if (notifClose) {
      notifClose.click();
    }
  }

  // Pause for a given duration (in milliseconds)
  function pause(duration = 1000) {
    return new Promise((resolve) => setTimeout(resolve, duration));
  }

  await pause(5000);

  // #### MAIN CHANGE: different way of tracking pages #####
  let currentPageNumber = 1;
  let lastPageNumber = 1;

  // Get the last page number. If there's only one page, the pagination elements won't exist.
  const lastPageElement = document.querySelector("a.page-item:last-of-type");
  if (lastPageElement) {
    lastPageNumber = parseInt(lastPageElement.innerText, 10);
  }

  do {
    await pause(5000);

    console.log(`Downloading page ${currentPageNumber} of ${lastPageNumber}`);

    const menus = Array.from(
      document.querySelectorAll(
        'div[class*="Dropdown-module_dropdown_container"]'
      )
    )
      .map((container) => Array.from(container.children).find((child) => child.innerHTML && child.innerHTML.indexOf("DOWNLOAD_AND_TRANSFER_DIALOG") !== -1))
      .filter((item) => !!item);

    for (let menu of menus) {
      const dialog = menu.querySelector(`div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_']`);

      if (!dialog) {
        console.warn(`No dialog found for menu element. Skipping.`);
        continue;
      }

      const parts = dialog.id.split("_");
      const asin = parts[parts.length - 1];
      console.log(`Processing book with ASIN: ${asin}`);

      menu.click();
      await pause(500);

      const inputSelector = `ul#download_and_transfer_list_${asin} li[class^='ActionList-module_action_list_item__'] > div > label`;
      const input = document.querySelector(inputSelector);
      if (!input) {
        console.warn(`No download option found for ASIN ${asin}`);
        closeNotification(); // Close the notification even if download option isn't found
        continue;
      }
      input.click();
      await pause(500);


      const buttonSelector = `div[id^='DOWNLOAD_AND_TRANSFER_DIALOG_${asin}'] div[class^='DeviceDialogBox-module_button_container__'] > div[id$='_CONFIRM']`;
      const button = document.querySelector(buttonSelector);
      if (!button) {
        console.warn(`No confirm button found for ASIN ${asin}`);
        closeNotification(); // Close the notification even if confirm button isn't found
        continue;
      }

      button.click();
      await pause(1000);

      closeNotification();
      await pause(500);
    }

    if (currentPageNumber < lastPageNumber) { // CHANGE: Check if there are pages, before going further
      const nextPage = document.querySelector("a.page-item.next:not(.disabled)");
      if (nextPage) {
        nextPage.click();
        currentPageNumber++;
      }
    }
  } while (currentPageNumber < lastPageNumber); // CHANGE: Use < instead of <=, so it doesn't re-download stuff
})();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment