def GetAllBooks(user: User, outputPath: str) -> None: kobo = Kobo(user) kobo.LoadInitializationSettings() bookList = kobo.GetMyBookList() for entitlement in bookList: newEntitlement = entitlement.get("NewEntitlement") if newEntitlement is None: continue try: bookMetadata = newEntitlement["BookMetadata"] except Exception: # skip audiobooks continue fileName = __MakeFileNameForBook(bookMetadata) outputFilePath = os.path.join(outputPath, fileName) # Skip archived books. if __IsBookArchived(newEntitlement): title = bookMetadata["Title"] author = __GetBookAuthor(bookMetadata) if len(author) > 0: title += " by " + author click.echo(f"Skipping archived book {title}") continue try: output = kobo.Download(bookMetadata["RevisionId"], outputFilePath) click.echo(f"Downloaded to {output}", err=True) except Exception as e: click.echo("could not download".format(bookMetadata["Title"])) click.echo(e)
def GetBook(user: User, revisionId: str, outputPath: str) -> None: kobo = Kobo(user) kobo.LoadInitializationSettings() book = kobo.GetBookInfo(revisionId) fileName = __MakeFileNameForBook(book) outputFilePath = os.path.join(outputPath, fileName) return kobo.Download(revisionId, outputFilePath)
def ListBooks(users: List[User], listAll: bool) -> List[Book]: for user in users: kobo = Kobo(user) kobo.LoadInitializationSettings() rows = __GetBookList(kobo, listAll) for columns in rows: yield Book( RevisionId=columns[0], Title=columns[1], Author=columns[2], Archived=columns[3], Owner=user, )
def ListBooks(users: List[User], listAll: bool, exportFile: Union[TextIO, None]) -> List[Book]: '''list all books currently in the account''' for user in users: kobo = Kobo(user) kobo.LoadInitializationSettings() rows = __GetBookList(kobo, listAll, exportFile) for columns in rows: yield Book( RevisionId=columns[0], Title=columns[1], Author=columns[2], Archived=columns[3], Audiobook=columns[4], Owner=user, )
def GetBookOrBooks(user: User, outputPath: str, productId: str = '') -> Union[None, str]: ''' download 1 or all books to file returns output filepath if identifier is passed, otherwise returns None ''' outputPath = os.path.abspath(outputPath) kobo = Kobo(user) kobo.LoadInitializationSettings() # Must call GetBookList every time, even if you're only getting 1 book, # because it invokes a library sync endpoint. # This is the only known endpoint that returns # download URLs along with book metadata. bookList = kobo.GetMyBookList() for entitlement in bookList: newEntitlement = entitlement.get('NewEntitlement') if newEntitlement is None: continue bookMetadata, book_type = __GetBookMetadata(newEntitlement) if book_type is None: click.echo('Skipping book of unknown type') continue elif book_type == BookType.SUBSCRIPTION: click.echo('Skipping subscribtion entity') continue fileName = __MakeFileNameForBook(bookMetadata) if book_type == BookType.EBOOK: # Audiobooks go in sub-directories # but epub files go directly in outputPath fileName += '.epub' outputFilePath = os.path.join(outputPath, fileName) if not productId and os.path.exists(outputFilePath): # when downloading ALL books, skip books we've downloaded before click.echo(f'Skipping already downloaded book {outputFilePath}') continue if productId and productId != Kobo.GetProductId(bookMetadata): # user only asked for a single title, # and this is not the book they want continue # Skip archived books. if __IsBookArchived(newEntitlement): click.echo(f'Skipping archived book {fileName}') continue kobo.Download(bookMetadata, book_type == BookType.AUDIOBOOK, outputFilePath) click.echo(f'Downloaded {productId} to {outputFilePath}', err=True) if productId: # TODO: support audiobook downloads from web return outputFilePath return None
def __GetBookList(kobo: Kobo, listAll: bool, exportFile: Union[TextIO, None]) -> list: bookList = kobo.GetMyBookList() rows = [] if exportFile: exportFile.write(json.dumps(bookList, indent=2)) for entitlement in bookList: newEntitlement = entitlement.get('NewEntitlement') if newEntitlement is None: continue bookEntitlement = newEntitlement.get('BookEntitlement') if bookEntitlement is not None: # Skip saved previews. if bookEntitlement.get('Accessibility') == 'Preview': continue # Skip refunded books. if bookEntitlement.get('IsLocked'): continue if (not listAll) and __IsBookRead(newEntitlement): continue bookMetadata, book_type = __GetBookMetadata(newEntitlement) if book_type is None: click.echo('Skipping book of unknown type') continue elif book_type in SUPPORTED_BOOK_TYPES: book = [ bookMetadata['RevisionId'], bookMetadata['Title'], __GetBookAuthor(bookMetadata), __IsBookArchived(newEntitlement), book_type == BookType.AUDIOBOOK, ] rows.append(book) rows = sorted(rows, key=lambda columns: columns[1].lower()) return rows
def __GetBookList(kobo: Kobo, listAll: bool) -> list: bookList = kobo.GetMyBookList() rows = [] for entitlement in bookList: newEntitlement = entitlement.get("NewEntitlement") if newEntitlement is None: continue bookEntitlement = newEntitlement.get("BookEntitlement") if bookEntitlement is not None: # Skip saved previews. if bookEntitlement.get("Accessibility") == "Preview": continue # Skip refunded books. if bookEntitlement.get("IsLocked"): continue if (not listAll) and __IsBookRead(newEntitlement): continue try: bookMetadata = newEntitlement["BookMetadata"] except Exception as e: # skip audiobook continue book = [ bookMetadata["RevisionId"], bookMetadata["Title"], __GetBookAuthor(bookMetadata), __IsBookArchived(newEntitlement), ] rows.append(book) rows = sorted(rows, key=lambda columns: columns[1].lower()) return rows
def Login(user: User, password: str, captcha: str) -> None: '''perform device initialization and get token''' kobo = Kobo(user) kobo.AuthenticateDevice() kobo.LoadInitializationSettings() kobo.Login(user.Email, password, captcha)
def Login(user: User, password: str, captcha: str) -> None: kobo = Kobo(user) kobo.AuthenticateDevice() kobo.LoadInitializationSettings() kobo.Login(user.Email, password, captcha)
def GetBookOrBooks(user: User, outputPath: str, productId: str = '') -> Union[None, str]: """ download 1 or all books to file returns output filepath if identifier is passed, otherwise returns None """ outputPath = os.path.abspath(outputPath) kobo = Kobo(user) kobo.LoadInitializationSettings() # Must call GetBookList every time, even if you're only getting 1 book, # because it invokes a library sync endpoint. # This is the only known endpoint that returns # download URLs along with book metadata. bookList = kobo.GetMyBookList() for entitlement in bookList: newEntitlement = entitlement.get('NewEntitlement') if newEntitlement is None: continue bookMetadata, book_type = __GetBookMetadata(newEntitlement) if book_type is None: click.echo('Skipping book of unknown type') continue elif book_type == BookType.SUBSCRIPTION: click.echo('Skipping subscribtion entity') continue fileName = __MakeFileNameForBook(bookMetadata) if book_type == BookType.EBOOK: # Audiobooks go in sub-directories # but epub files go directly in outputPath fileName += '.epub' outputFilePath = os.path.join(outputPath, fileName) if not productId and os.path.exists(outputFilePath): # when downloading ALL books, skip books we've downloaded before click.echo(f'Skipping already downloaded book {outputFilePath}') continue currentProductId = Kobo.GetProductId(bookMetadata) if productId and productId != currentProductId: # user only asked for a single title, # and this is not the book they want continue # Skip archived books. if __IsBookArchived(newEntitlement): click.echo(f'Skipping archived book {fileName}') continue try: click.echo(f'Downloading {currentProductId} to {outputFilePath}', err=True) kobo.Download(bookMetadata, book_type == BookType.AUDIOBOOK, outputFilePath) except KoboException as e: if productId: raise e else: click.echo( (f'Skipping failed download for {currentProductId}: {str(e)}' '\n -- Try downloading it as a single book to get the complete exception details' ' and open an issue on the project GitHub page: https://github.com/subdavis/kobo-book-downloader/issues' ), err=True, ) if productId: # TODO: support audiobook downloads from web return outputFilePath return None