Created
April 11, 2019 14:21
-
-
Save bochoven/4a2ea747670953a6401c30bc014734dd to your computer and use it in GitHub Desktop.
Downloads iBooks from a public Stack repository.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# encoding: utf-8 | |
''' Downloads iBooks from Stack storage | |
Complexity is a result from the CSRF token that is needed for the download. | |
''' | |
import urllib, urllib2, json, re, cookielib, os, xattr | |
from Cocoa import NSWorkspace, NSImage | |
PAGE_URL = 'https://username.stackstorage.com/s/6hjYxxxxKnp6x' | |
BASE_URL = "https://username.stackstorage.com/public-share/6hjYxxxxKnp6x" | |
XATTR_ETAG = 'nl.vu.mlx_downloader.etag' | |
TARGET_DIRECTORY = "/Users/Shared/MLX Book Library/" | |
ICON_PATH = "/usr/local/vu/pictures/mlx.png" | |
def load_list(url): | |
response = urllib.urlopen(url) | |
data = json.loads(response.read()) | |
return data['nodes'] | |
def download(url, formdata): | |
data = urllib.urlencode(formdata) | |
content = urllib2.urlopen(url=url, data=data) | |
filename = full_path(os.path.basename(formdata['paths[]'])) | |
with open(filename, "wb") as local_file: | |
local_file.write(content.read()) | |
xattr.setxattr(filename, XATTR_ETAG, formdata['etag']) | |
def get_csrf_token(url): | |
request = urllib2.Request(url) | |
data = urllib2.urlopen(request).read() | |
result = re.search(r'<meta name="csrf-token" content="([^"]+)">', data) | |
return result.group(1) | |
def init_cookie_jar(): | |
cookie_jar = cookielib.CookieJar() | |
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie_jar)) | |
urllib2.install_opener(opener) | |
def getxattr(pathname, attr): | |
"""Get a named xattr from a file. Return None if not present""" | |
if attr in xattr.listxattr(pathname): | |
return xattr.getxattr(pathname, attr) | |
else: | |
return None | |
def file_exists_and_up_to_date(file, etag): | |
try: | |
if etag == getxattr(file, XATTR_ETAG): | |
return True | |
except Exception as e: | |
pass | |
return False | |
def full_path(file): | |
return TARGET_DIRECTORY + file | |
def is_ibooks_file(file): | |
return re.match(r".*\.ibooks$", file) | |
def set_directory_icon(icon_path, directory): | |
NSWorkspace.sharedWorkspace().setIcon_forFile_options_( | |
NSImage.alloc().initWithContentsOfFile_(icon_path), directory, 0) | |
def main(): | |
"""Main""" | |
if not os.path.exists(TARGET_DIRECTORY): | |
os.makedirs(TARGET_DIRECTORY) | |
set_directory_icon(ICON_PATH, TARGET_DIRECTORY) | |
init_cookie_jar() | |
csrf_token = get_csrf_token(PAGE_URL) | |
booklist = load_list(BASE_URL + '/list') | |
for book in booklist: | |
local_path = full_path(os.path.basename(book['path'])) | |
if not is_ibooks_file(local_path): | |
print "Skipping %s: not an ibook" % local_path | |
continue | |
if file_exists_and_up_to_date(local_path, book['etag']): | |
print "Skipping %s: not changed" % local_path | |
continue | |
formdata = { | |
'paths[]': book['path'], | |
'CSRF-Token': csrf_token, | |
'etag': book['etag'] | |
} | |
try: | |
print "Downloading %s" % local_path | |
download(BASE_URL + '/download', formdata) | |
except Exception as e: | |
print e | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment