Download ALL photos from Amazon Photos

It's kind of unbelievable that Amazon is forcing their customers into a vendor lock and not allowing them an easy or straightforward way to download ALL of their photos in bulk.

The explanation is simple. They don't want you go to another service and lose the revenue. It would certainly appear that their bottom line is more important to them than the customers rights.

Provided is a Javascript snippet that can be used to download all your photos without any size or count limitations. You can just plop this in your web console and let it run.

How do I open the chrome web console?

However, since this is running serially, it will take some time. there are timeouts in the script and waiters for elements to be visible before downloading the image to help make the process more reliable and prevent photos from being skipped.

Bonus

This script will allow you to delete the photos from Amazon Photos after they have been downloaded. Just set the varible to true at the bottom of the script before running.

// Set to true to delete files after downloading
const deleteFiles = true;

Let's go!

  • Open amazon photos in chrome
  • Select the first photo so you can see it in a "slide show"
  • Open the web console
  • Paste the entire code snippet
  • Press Enter
  • Wait
  • Profit
function waitForElement(selector, timeout = 100) {
  return new Promise((resolve, reject) => {
    const startTime = Date.now();

    const checkElement = () => {
      const element = document.querySelector(selector);
      if (element) {
        resolve(element);
      } else if (Date.now() - startTime > timeout) {
        reject(new Error(`Element ${selector} not found within ${timeout}ms`));
      } else {
        setTimeout(checkElement, 100);
      }
    };

    checkElement();
  });
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function run(deleteFiles=false) {
  let downloadCount = 0;

  // Click the Information button at the beginning
  const spans = document.querySelectorAll('#main-content > section > header > ul > li > button > span.label');
  const infoButton = Array.from(spans).find(span => span.textContent.trim() === 'Information');
  if (infoButton) {
    infoButton.closest('button').click();
    console.log('Clicked Information button');
    await sleep(500);
  }

  while (true) {
    await ensureAsideVisible();
    await clickMainContentButton();
    await waitForElement('button.download[title="Download"]');
    await clickDownloadButton(++downloadCount);
    await sleep(1200); // Wait for download to start

    if (deleteFiles) {
      await clickDeleteButton();
      await confirmDelete();
      await sleep(500);
    }

    const hasNext = await clickNextButton();
    if (!hasNext) {
      break;
    }
    await waitForElement('#main-content > section > header > div > button');
  }

  console.log(`Finished downloading ${downloadCount} files`);
}

async function ensureAsideVisible() {
  const aside = document.querySelector('#main-content > section > aside');
  if (!aside || window.getComputedStyle(aside).display === 'none' || !aside.offsetParent) {
    const button = document.querySelector('#main-content > section > header > div > button');
    if (button) {
      button.click();
      await sleep(300);
      console.log('Opened aside panel');
    }
  }
}

function clickMainContentButton() {
  const button = document.querySelector('#main-content > section > header > div > button');
  if (button) {
    button.click();
  }
}

function clickDownloadButton(count) {
  const downloadButton = document.querySelector('button.download[title="Download"]');
  const fileInfoDiv = document.querySelector('.detail-item.file-info');
  const imageName = fileInfoDiv ? fileInfoDiv.querySelector('span.label').innerText : 'unknown';
  if (downloadButton) {
    downloadButton.click();
    console.log(`Downloading file ${count} - ${imageName}`);
  }
}

function clickDeleteButton() {
  const deleteButton = document.querySelector('#main-content > section > header > ul > li:nth-child(6) > button');
  if (deleteButton) {
    deleteButton.click();
    console.log('Opened delete modal');
  }
}

async function confirmDelete() {
  await sleep(500);
  const confirmButton = document.querySelector('#dialog-container > div:nth-child(1) > div > aside > footer > div > button:nth-child(2)');
  if (confirmButton) {
    confirmButton.click();
    console.log('Confirmed delete');
  }
}

function clickNextButton() {
  const nextButton = document.querySelector('a.next');
  if (nextButton) {
    nextButton.click();
    return true;
  }
  return false;
}

// Set to true to delete files after downloading
const deleteFiles = true;

// Start the script
run(deleteFiles);