class SmugMugClient: def __init__(self, api_key, gallery, link_type, nickname): self.smugmug = SmugMug(api_key=api_key, api_version="1.3.0", app_name="TwiMug") self.gallery = gallery self.link_type = link_type self.nickname = nickname def get_albums(self): albums = self.smugmug.albums_get(NickName=self.nickname) return albums def get_album_info(self, album_name): for album in self.get_albums()["Albums"]: if album["Title"] == album_name: return album def get_images_for_album(self, album_id, album_key): images = self.smugmug.images_get(AlbumID=album_id, AlbumKey=album_key) return images def get_image_urls(self, image_id, image_key): urls = self.smugmug.images_getURLs(ImageID=image_id, ImageKey=image_key) return urls def get_last_image_urls(self): last_image = self.get_last_image_info() urls = self.get_image_urls(last_image["id"], last_image["Key"]) return urls def get_last_image_info(self): album = self.get_album_info(self.gallery) images = self.get_images_for_album(album["id"], album["Key"]) last_image = images["Album"]["Images"][-1] return last_image def get_last_image_extended_info(self): last_image = self.get_last_image_info() extended_info = self.smugmug.images_getInfo(ImageID=last_image["id"], ImageKey=last_image["Key"]) return extended_info["Image"] def get_last_image_url(self): urls = self.get_last_image_urls() return urls["Image"][self.link_type] def save_last_image_url(self): url = self.get_last_image_url() with open("last_image_url", "w") as file: file.write(url) def load_last_image_url(self): url = "" with open("last_image_url", "r") as file: url = file.readline() return url
class SmugLine(object): def __init__(self, api_key, email=None, password=None): self.api_key = api_key self.email = email self.password = password self.smugmug = SmugMug(api_key=api_key, api_version="1.2.2", app_name="SmugLine") self.login() self.md5_sums = {} def get_filter(self, media_type="images"): if media_type == "videos": return VIDEO_FILTER if media_type == "images": return IMG_FILTER if media_type == "all": return ALL_FILTER def upload_file(self, album, image): result = "-1" retries = 0 while (result != "smugmug.images.upload") and (retries < 5): try: retries = retries + 1 if result == "-2": print ("Exception, retrying (attempt {0}).".format(retries)) time.sleep(retries * 3) rsp = self.smugmug.images_upload(AlbumID=album["id"], **image) result = rsp["method"] except Exception as inst: print inst result = "-2" pass if result == "-2": print ("ERROR: File upload failed.") # source: http://stackoverflow.com/a/16696317/305019 def download_file(self, url, folder, filename=None): local_filename = os.path.join(folder, filename or url.split("/")[-1]) if os.path.exists(local_filename): print ("{0} already exists...skipping".format(local_filename)) return r = requests.get(url, stream=True) with open(local_filename, "wb") as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() return local_filename def set_file_timestamp(self, filename, image): # apply the image date image_info = self.get_image_info(image) timestamp = time.strptime(image_info["Image"]["Date"], "%Y-%m-%d %H:%M:%S") t = time.mktime(timestamp) os.utime(filename, (t, t)) def upload_json(self, source_folder, json_file): images = json.load(open(json_file)) # prepend folder for image in images: image["File"] = source_folder + image["File"] # group by album groups = [] images.sort(key=lambda x: x["AlbumName"]) for k, g in groupby(images, key=lambda x: x["AlbumName"]): groups.append(list(g)) for group in groups: album_name = group[0]["AlbumName"] album = self.get_or_create_album(album_name) self._upload(group, album_name, album) def upload_folder(self, source_folder, album_name, file_filter=IMG_FILTER): album = self.get_or_create_album(album_name) images = self.get_images_from_folder(source_folder, file_filter) self._upload(images, album_name, album) def account_folder_number(self, source_folder): images = self.get_images_from_folder(source_folder, file_filter) return len(images) def upload_folder_structure(self, album_title, source_folder, file_filter, uploaded_files, total_files): album_name = source_folder.replace("./", "").split("/", 1)[1] subcategory_name = source_folder.replace("./", "").split("/", 1)[0] categories = self.smugmug.categories_get() category = None for candidate_category in categories["Categories"]: if candidate_category["Name"] == album_title: category = candidate_category if category is None: category = self.smugmug.categories_create(Name=album)["Category"] subcategories = self.smugmug.subcategories_get(CategoryID=category["id"]) subcategory = None for candidate_subcategory in subcategories["SubCategories"]: if candidate_subcategory["Name"] == subcategory_name: subcategory = candidate_subcategory if subcategory is None: subcategory = self.smugmug.subcategories_create(Name=subcategory_name, CategoryID=category["id"])[ "SubCategory" ] album = self.create_album(album_name, "unlisted", subcategory["id"]) images = self.get_images_from_folder(source_folder, file_filter) self._upload(images, album_name, album, uploaded_files, total_files) def download_album(self, album_name, dest_folder, file_filter=IMG_FILTER): album = self.get_album_by_name(album_name) if album is None: print ("Album {0} not found".format(album_name)) return images = self._get_images_for_album(album, file_filter) self._download(images, dest_folder) def _upload(self, images, album_name, album, uploaded_files, total_files): images = self._remove_duplicates(images, album) for image in images: print ("[{0:03d}/{1:03d}] Uploading {2}".format(uploaded_files, total_files, image)) self.upload_file(album, image) uploaded_files = uploaded_files + 1 def _download(self, images, dest_folder): for img in images: print ("downloading {0} -> {1}".format(img["FileName"], dest_folder)) filename = self.download_file(img["OriginalURL"], dest_folder, img["FileName"]) self.set_file_timestamp(filename, img) def _get_remote_images(self, album, extras=None): remote_images = self.smugmug.images_get(AlbumID=album["id"], AlbumKey=album["Key"], Extras=extras) return remote_images def _get_md5_hashes_for_album(self, album): remote_images = self._get_remote_images(album, "MD5Sum") md5_sums = [x["MD5Sum"] for x in remote_images["Album"]["Images"]] self.md5_sums[album["id"]] = md5_sums return md5_sums def _get_images_for_album(self, album, file_filter=IMG_FILTER): extras = "FileName,OriginalURL" images = self._get_remote_images(album, extras)["Album"]["Images"] for image in [img for img in images if file_filter.match(img["FileName"])]: yield image def _file_md5(self, filename, block_size=2 ** 20): md5 = hashlib.md5() f = open(filename, "rb") while True: data = f.read(block_size) if not data: break md5.update(data) return md5.hexdigest() def _include_file(self, f, md5_sums): try: if self._file_md5(f) in md5_sums: print ("skipping {0} (duplicate)".format(f)) return False return True except IOError as err: errno, strerror = err print ("I/O Error({0}): {1}...skipping".format(errno, strerror)) return False def _remove_duplicates(self, images, album): md5_sums = self._get_md5_hashes_for_album(album) return [x for x in images if self._include_file(x.get("File"), md5_sums)] def get_albums(self): albums = self.smugmug.albums_get(NickName=self.nickname) return albums def list_albums(self): print ("available albums:") for album in self.get_albums()["Albums"]: if album["Title"]: print (album["Title"]) def get_or_create_album(self, album_name): album = self.get_album_by_name(album_name) if album: return album return self.create_album(album_name) def get_album_by_name(self, album_name): albums = self.get_albums() try: matches = [x for x in albums["Albums"] if x.get("Title").lower() == album_name.lower()] return matches[0] except: return None def _format_album_name(self, album_name): return album_name[0].upper() + album_name[1:] def get_album_info(self, album): return self.smugmug.albums_getInfo(AlbumID=album["id"], AlbumKey=album["Key"]) def get_image_info(self, image): return self.smugmug.images_getInfo(ImageKey=image["Key"]) def create_album(self, album_name, privacy="unlisted"): public = privacy == "public" album_name = self._format_album_name(album_name) album = self.smugmug.albums_create(Title=album_name, Public=public) album_info = self.get_album_info(album["Album"]) return album_info["Album"] def create_album(self, album_name, privacy, category): public = privacy == "public" album_name = self._format_album_name(album_name) album = self.smugmug.albums_create(Title=album_name, Public=public, CategoryID=category) album_info = self.get_album_info(album["Album"]) return album_info["Album"] def get_images_from_folder(self, folder, img_filter=IMG_FILTER): matches = [] for root, dirnames, filenames in os.walk(folder): matches.extend({"File": os.path.join(root, name)} for name in filenames if img_filter.match(name)) return matches def _set_email_and_password(self): # for python2 try: input = raw_input except NameError: pass if self.email is None: self.email = input("Email address: ") if self.password is None: self.password = getpass.getpass() def login(self): self._set_email_and_password() self.user_info = self.smugmug.login_withPassword(EmailAddress=self.email, Password=self.password) self.nickname = self.user_info["Login"]["User"]["NickName"] return self.user_info def _delete_image(self, image): print ("deleting image {0} (md5: {1})".format(image["FileName"], image["MD5Sum"])) self.smugmug.images_delete(ImageID=image["id"]) def clear_duplicates(self, album_name): album = self.get_album_by_name(album_name) remote_images = self._get_remote_images(album, "MD5Sum,FileName") md5_sums = [] for image in remote_images["Album"]["Images"]: if image["MD5Sum"] in md5_sums: self._delete_image(image) md5_sums.append(image["MD5Sum"])
class SmugMugCommandLine(object): def __init__(self, api_key=None, email=None, password=None): """initalize class""" self.api_key = api_key self.email = email self.password = password # using the old API (1.2.2), which is easier to use for one shot scripts (avoids OAuth) self.smugmug = SmugMug(api_key=options.api_key, api_version="1.2.2", app_name=__script_name__) # login self.smugmug.login_withPassword(EmailAddress=options.email, Password=options.password) def template_get(self, template_name=None): """find template by name""" response = self.smugmug.albumtemplates_get() for template in response['AlbumTemplates']: if template['AlbumTemplateName'] == template_name: return template return None def category_get(self, category_name=None): """find category by name""" response = self.smugmug.categories_get() for category in response['Categories']: if category['Name'] == category_name: return category return None def subcategory_get(self, subcategory_name=None): """find subcategory by name""" response = self.smugmug.subcategories_get(CategoryID=self.category['id']) for subcategory in response['SubCategories']: if subcategory['Name'] == subcategory_name: return subcategory return None def album_get(self, album_name=None): """find album by name""" response = self.smugmug.albums_get() for album in response['Albums']: if album['Title'] == album_name: return album return None def album_getInfo(self, album_id=None, album_key=None): """get album info""" response = self.smugmug.albums_getInfo(AlbumID=album_id, AlbumKey=album_key) album = response['Album'] return album def album_create(self, title=None, category_id=None, subcategory_id=None, album_template_id=None): """create ablum""" response = self.smugmug.albums_create(AlbumTemplateID=album_template_id, CategoryID=category_id, SubCategoryID=subcategory_id, Title=title) album = response['Album'] return album def images_getInfo(self, image_id=None, image_key=None): """get image info""" response = self.smugmug.images_getInfo(ImageID=image_id, ImageKey=image_key) image_info = response['Image'] return image_info def images_get(self, album_id=None, album_key=None): """get all image for an ablum""" response = self.smugmug.images_get(AlbumID=album_id, AlbumKey=album_key) images = response['Album']['Images'] return images def images_get_filenames(self, album_id=None, album_key=None): """get all images for an ablum as a collection of filenames""" filename_list = list() images = self.images_get(album_id=album_id, album_key=album_key) for image in images: image_info = self.images_getInfo(image_id=image['id'], image_key=image['Key']) filename_list.append(image_info['FileName']) return filename_list
class SmugLine(object): def __init__(self, api_key=None, email=None, password=None): if api_key is None: self.api_key = os.environ.get('SMUGMUG_API', None) else: self.api_key = api_key self.email = email self.password = password self.smugmug = SmugMug(api_key=self.api_key, api_version="1.2.2", app_name="SmugLine") self.login() self.md5_sums = {} def get_filter(self, media_type='images'): if media_type == 'videos': return VIDEO_FILTER if media_type == 'images': return IMG_FILTER if media_type == 'all': return ALL_FILTER def upload_file(self, album, image): retries = 5 while retries: try: self.smugmug.images_upload(AlbumID=album['id'], **image) return except HTTPError: print("retry ", image) retries -= 1 # source: http://stackoverflow.com/a/16696317/305019 def download_file(self, url, folder, filename=None): local_filename = os.path.join(folder, filename or url.split('/')[-1]) if os.path.exists(local_filename): print('{0} already exists...skipping'.format(local_filename)) return r = requests.get(url, stream=True) with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() return local_filename def set_file_timestamp(self, filename, image): if filename is None: return # apply the image date image_info = self.get_image_info(image) timestamp = time.strptime(image_info['Image']['Date'], '%Y-%m-%d %H:%M:%S') t = time.mktime(timestamp) os.utime(filename, (t, t)) def upload_json(self, source_folder, json_file): images = json.load(open(json_file)) # prepend folder for image in images: image['File'] = source_folder + image['File'] # group by album groups = [] images.sort(key=lambda x: x['AlbumName']) for k, g in groupby(images, key=lambda x: x['AlbumName']): groups.append(list(g)) for group in groups: album_name = group[0]['AlbumName'] album = self.get_or_create_album(album_name) self._upload(group, album_name, album) def upload_folder(self, source_folder, album_name, file_filter=IMG_FILTER): album = self.get_or_create_album(album_name) images = self.get_images_from_folder(source_folder, file_filter) self._upload(images, album_name, album) def download_album(self, album_name, dest_folder, file_filter=IMG_FILTER): album = self.get_album_by_name(album_name) if album is None: print('Album {0} not found'.format(album_name)) return images = self._get_images_for_album(album, file_filter) self._download(images, dest_folder) def _upload(self, images, album_name, album): images = self._remove_duplicates(images, album) print('uploading {0} images'.format(len(images))) for image in images: print(' uploading {0} -> {1}'.format(image, album_name)) self.upload_file(album, image) print('Done uploading {0} images'.format(len(images))) def _download(self, images, dest_folder): for img in images: print('downloading {0} -> {1}'.format(img['FileName'], dest_folder)) if 'OriginalURL' not in img: print('no permission to download {0}...skipping'.format( img['FileName'])) continue filename = self.download_file(img['OriginalURL'], dest_folder, img['FileName']) self.set_file_timestamp(filename, img) def _get_remote_images(self, album, extras=None): remote_images = self.smugmug.images_get(AlbumID=album['id'], AlbumKey=album['Key'], Extras=extras) return remote_images def _get_md5_hashes_for_album(self, album): remote_images = self._get_remote_images(album, 'MD5Sum') md5_sums = [x['MD5Sum'] for x in remote_images['Album']['Images']] self.md5_sums[album['id']] = md5_sums return md5_sums def _get_images_for_album(self, album, file_filter=IMG_FILTER): extras = 'FileName,OriginalURL' images = self._get_remote_images(album, extras)['Album']['Images'] for image in [img for img in images \ if file_filter.match(img['FileName'])]: yield image def _file_md5(self, filename, block_size=2**20): md5 = hashlib.md5() f = open(filename, 'rb') while True: data = f.read(block_size) if not data: break md5.update(data) return md5.hexdigest() def _include_file(self, f, md5_sums): try: if self._file_md5(f) in md5_sums: print('skipping {0} (duplicate)'.format(f)) return False return True except IOError as err: # see https://github.com/PyCQA/pylint/issues/165 # pylint: disable=unpacking-non-sequence errno, strerror = err print('I/O Error({0}): {1}...skipping'.format(errno, strerror)) return False def _remove_duplicates(self, images, album): md5_sums = self._get_md5_hashes_for_album(album) return [ x for x in images if self._include_file(x.get('File'), md5_sums) ] def get_albums(self): albums = self.smugmug.albums_get(NickName=self.nickname) return albums def list_albums(self): print('available albums:') for album in self.get_albums()['Albums']: if album['Title']: print(album['Title'].encode('utf-8')) def get_or_create_album(self, album_name): album = self.get_album_by_name(album_name) if album: return album return self.create_album(album_name) def get_album_by_name(self, album_name): albums = self.get_albums() try: matches = [x for x in albums['Albums'] \ if x.get('Title').lower() == album_name.lower()] return matches[0] except: return None def _format_album_name(self, album_name): return album_name[0].upper() + album_name[1:] def get_album_info(self, album): return self.smugmug.albums_getInfo(AlbumID=album['id'], AlbumKey=album['Key']) def get_image_info(self, image): return self.smugmug.images_getInfo(ImageKey=image['Key']) def create_album(self, album_name, privacy='unlisted'): public = (privacy == 'public') album_name = self._format_album_name(album_name) album = self.smugmug.albums_create(Title=album_name, Public=public) album_info = self.get_album_info(album['Album']) print('{0} album {1} created. URL: {2}'.format( privacy, album_name, album_info['Album']['URL'])) return album_info['Album'] def get_images_from_folder(self, folder, img_filter=IMG_FILTER): matches = [] for root, dirnames, filenames in os.walk(folder): matches.extend( {'File': os.path.join(root, name)} for name in filenames \ if img_filter.match(name)) return matches def _set_email_and_password(self): # for python2 try: input = raw_input except NameError: pass if self.email is None: self.email = os.environ.get('SMUGMUG_EMAIL', None) if self.email is None: self.email = input('Email address: ') if self.password is None: self.password = os.environ.get('SMUGMUG_PASSWORD', None) if self.password is None: self.password = getpass.getpass() def login(self): self._set_email_and_password() self.user_info = self.smugmug.login_withPassword( EmailAddress=self.email, Password=self.password) self.nickname = self.user_info['Login']['User']['NickName'] return self.user_info def _delete_image(self, image): print('deleting image {0} (md5: {1})'.format(image['FileName'], image['MD5Sum'])) self.smugmug.images_delete(ImageID=image['id']) def clear_duplicates(self, album_name): album = self.get_album_by_name(album_name) remote_images = self._get_remote_images(album, 'MD5Sum,FileName') md5_sums = [] for image in remote_images['Album']['Images']: if image['MD5Sum'] in md5_sums: self._delete_image(image) md5_sums.append(image['MD5Sum'])
class SmugLine(object): def __init__(self, api_key, email=None, password=None): self.api_key = api_key self.email = email self.password = password self.smugmug = SmugMug( api_key=api_key, api_version="1.2.2", app_name="SmugLine") self.login() self.md5_sums = {} def get_filter(self, media_type='images'): if media_type == 'videos': return VIDEO_FILTER if media_type == 'images': return IMG_FILTER if media_type == 'all': return ALL_FILTER def upload_file(self, album, image): retries = 5 while retries: try: self.smugmug.images_upload(AlbumID=album['id'], **image) return except HTTPError: print("retry ", image) retries -= 1 # source: http://stackoverflow.com/a/16696317/305019 def download_file(self, url, folder, filename=None): local_filename = os.path.join(folder, filename or url.split('/')[-1]) if os.path.exists(local_filename): print('{0} already exists...skipping'.format(local_filename)) return r = requests.get(url, stream=True) with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): if chunk: # filter out keep-alive new chunks f.write(chunk) f.flush() return local_filename def set_file_timestamp(self, filename, image): if filename is None: return # apply the image date image_info = self.get_image_info(image) timestamp = time.strptime(image_info['Image']['Date'], '%Y-%m-%d %H:%M:%S') t = time.mktime(timestamp) os.utime(filename, (t, t)) def upload_json(self, source_folder, json_file): images = json.load(open(json_file)) # prepend folder for image in images: image['File'] = source_folder + image['File'] # group by album groups = [] images.sort(key=lambda x: x['AlbumName']) for k, g in groupby(images, key=lambda x: x['AlbumName']): groups.append(list(g)) for group in groups: album_name = group[0]['AlbumName'] album = self.get_or_create_album(album_name) self._upload(group, album_name, album) def upload_folder(self, source_folder, album_name, file_filter=IMG_FILTER): album = self.get_or_create_album(album_name) images = self.get_images_from_folder(source_folder, file_filter) self._upload(images, album_name, album) def download_album(self, album_name, dest_folder, file_filter=IMG_FILTER): album = self.get_album_by_name(album_name) if album is None: print('Album {0} not found'.format(album_name)) return images = self._get_images_for_album(album, file_filter) self._download(images, dest_folder) def _upload(self, images, album_name, album): images = self._remove_duplicates(images, album) for image in images: print('uploading {0} -> {1}'.format(image, album_name)) self.upload_file(album, image) def _download(self, images, dest_folder): for img in images: print('downloading {0} -> {1}'.format(img['FileName'], dest_folder)) if 'OriginalURL' not in img: print('no permission to download {0}...skipping'.format(img['FileName'])) continue filename = self.download_file(img['OriginalURL'], dest_folder, img['FileName']) self.set_file_timestamp(filename, img) def _get_remote_images(self, album, extras=None): remote_images = self.smugmug.images_get( AlbumID=album['id'], AlbumKey=album['Key'], Extras=extras) return remote_images def _get_md5_hashes_for_album(self, album): remote_images = self._get_remote_images(album, 'MD5Sum') md5_sums = [x['MD5Sum'] for x in remote_images['Album']['Images']] self.md5_sums[album['id']] = md5_sums return md5_sums def _get_images_for_album(self, album, file_filter=IMG_FILTER): extras = 'FileName,OriginalURL' images = self._get_remote_images(album, extras)['Album']['Images'] for image in [img for img in images \ if file_filter.match(img['FileName'])]: yield image def _file_md5(self, filename, block_size=2**20): md5 = hashlib.md5() f = open(filename, 'rb') while True: data = f.read(block_size) if not data: break md5.update(data) return md5.hexdigest() def _include_file(self, f, md5_sums): try: if self._file_md5(f) in md5_sums: print('skipping {0} (duplicate)'.format(f)) return False return True except IOError as err: # see https://github.com/PyCQA/pylint/issues/165 # pylint: disable=unpacking-non-sequence errno, strerror = err print('I/O Error({0}): {1}...skipping'.format(errno, strerror)) return False def _remove_duplicates(self, images, album): md5_sums = self._get_md5_hashes_for_album(album) return [x for x in images if self._include_file(x.get('File'), md5_sums)] def get_albums(self): albums = self.smugmug.albums_get(NickName=self.nickname) return albums def list_albums(self): print('available albums:') for album in self.get_albums()['Albums']: if album['Title']: print(album['Title'].encode('utf-8')) def get_or_create_album(self, album_name): album = self.get_album_by_name(album_name) if album: return album return self.create_album(album_name) def get_album_by_name(self, album_name): albums = self.get_albums() try: matches = [x for x in albums['Albums'] \ if x.get('Title').lower() == album_name.lower()] return matches[0] except: return None def _format_album_name(self, album_name): return album_name[0].upper() + album_name[1:] def get_album_info(self, album): return self.smugmug.albums_getInfo(AlbumID=album['id'], AlbumKey=album['Key']) def get_image_info(self, image): return self.smugmug.images_getInfo(ImageKey=image['Key']) def create_album(self, album_name, privacy='unlisted'): public = (privacy == 'public') album_name = self._format_album_name(album_name) album = self.smugmug.albums_create(Title=album_name, Public=public) album_info = self.get_album_info(album['Album']) print('{0} album {1} created. URL: {2}'.format( privacy, album_name, album_info['Album']['URL'])) return album_info['Album'] def get_images_from_folder(self, folder, img_filter=IMG_FILTER): matches = [] for root, dirnames, filenames in os.walk(folder): matches.extend( {'File': os.path.join(root, name)} for name in filenames \ if img_filter.match(name)) return matches def _set_email_and_password(self): # for python2 try: input = raw_input except NameError: pass if self.email is None: self.email = input('Email address: ') if self.password is None: self.password = getpass.getpass() def login(self): self._set_email_and_password() self.user_info = self.smugmug.login_withPassword( EmailAddress=self.email, Password=self.password) self.nickname = self.user_info['Login']['User']['NickName'] return self.user_info def _delete_image(self, image): print('deleting image {0} (md5: {1})'.format(image['FileName'], image['MD5Sum'])) self.smugmug.images_delete(ImageID=image['id']) def clear_duplicates(self, album_name): album = self.get_album_by_name(album_name) remote_images = self._get_remote_images(album, 'MD5Sum,FileName') md5_sums = [] for image in remote_images['Album']['Images']: if image['MD5Sum'] in md5_sums: self._delete_image(image) md5_sums.append(image['MD5Sum'])