Skip to content

Instantly share code, notes, and snippets.

@marccarre
Created October 24, 2020 08:46
Show Gist options
  • Save marccarre/645fe68da31678f9191cd3aafecfea1b to your computer and use it in GitHub Desktop.
Save marccarre/645fe68da31678f9191cd3aafecfea1b to your computer and use it in GitHub Desktop.
List all available versions of Kindle for Mac and Kindle for PC.
#!/usr/bin/env python
'''
List all available versions of Kindle for Mac and Kindle for PC.
Dependencies:
- asyncio==3.4.3
- aiohttp==3.6.3
'''
import os
import sys
import asyncio
import aiohttp
MAX_ATTEMPTS = int(os.environ.get('MAX_ATTEMPTS', 3)) # Maximum number of attempts per URL.
RATE_LIMIT = int(os.environ.get('RATE_LIMIT', 64)) # Number of active requests at any given time.
'''
Build numbers (5XXXX) increment:
- by 1000 for minor versions
- by 1 for patch versions -- with gaps, as some builds are not released.
'''
VERSIONS = [
{'build': 55000, 'version': '1.26'},
{'build': 56000, 'version': '1.27'},
{'build': 57000, 'version': '1.28'},
{'build': 58000, 'version': '1.29'},
{'build': 59000, 'version': '1.30'},
]
'''
For example:
- Kindle 1.26
- https://s3.amazonaws.com/kindleforpc/55076/KindleForPC-installer-1.26.55076.exe
- https://s3.amazonaws.com/kindleformac/55093/KindleForMac-55093.dmg
- Kindle 1.30
- https://s3.amazonaws.com/kindleforpc/59056/KindleForPC-installer-1.30.59056.exe
- https://s3.amazonaws.com/kindleformac/59055/KindleForMac-1.30.59055.dmg
'''
URL_PATTERNS = [
'https://s3.amazonaws.com/kindleforpc/{build}/KindleForPC-installer-{version}.{build}.exe',
'https://s3.amazonaws.com/kindleformac/{build}/KindleForMac-{build}.dmg',
'https://s3.amazonaws.com/kindleformac/{build}/KindleForMac-{version}.{build}.dmg',
]
async def main():
async with aiohttp.ClientSession() as session:
urls = await fetch_all(session)
print('\n'.join(sorted([url for url in urls if url])))
async def fetch_all(session):
tasks = []
semaphore = asyncio.Semaphore(RATE_LIMIT)
for v in VERSIONS:
for build in range(v['build'], v['build'] + 1000):
for pattern in URL_PATTERNS:
url = pattern.format(build=build, version=v['version'])
task = asyncio.create_task(fetch(session, semaphore, url))
tasks.append(task)
results = await asyncio.gather(*tasks)
return results
async def fetch(session, semaphore, url):
for attempt in range(1, MAX_ATTEMPTS + 1):
try:
async with semaphore:
response = await session.head(url)
return url if response.status == 200 else None
except Exception as e:
if attempt < MAX_ATTEMPTS:
print('Attempt #%d: Retrying on %s as got: %s.' % (attempt, url, e), file=sys.stderr)
else:
print('Failed on %s with: %s' % (url, e), file=sys.stderr)
if __name__ == '__main__':
asyncio.run(main())
@Young-Lord
Copy link

AttributeError: module 'asyncio' has no attribute 'run'

Python version too old. Update to at least Python 3.7 should fix the issue.

@mateusfccp
Copy link

Has anyone the link for the new Mac versions? I can only find it in App Store, but I have no access to the App Store.

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