diff --git a/.gitignore b/.gitignore index ff89acd..e813ddd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ __pycache__ *.bak +venv/ +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index f17f46f..896d858 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ to allow scripted access to a few very basic commands: - delete an ebook / upload - list devices connected to an account - unregister a device from an account +- update meta data +- update cover Status ====== @@ -43,6 +45,7 @@ Works with these partners: - Thalia.de (3) - Thalia.at (4) - Buch.de (6) +- books.ch / orellfuessli.ch (8) - Hugendubel.de (13) - Buecher.de (30) @@ -55,7 +58,7 @@ To-Do Better error handling. -More REST API calls (e.g. meta data edit, cover image upload). +More REST API calls (e.g. collection management). Support for more resellers. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..93495e9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +certifi==2020.4.5.1 +chardet==3.0.4 +idna==2.9 +requests==2.23.0 +urllib3==1.25.9 diff --git a/tolinoclient.py b/tolinoclient.py index 5cb0391..01d581c 100755 --- a/tolinoclient.py +++ b/tolinoclient.py @@ -91,6 +91,23 @@ def delete(args): c.logout() print('deleted {} from tolino cloud.'.format(args.document_id)) +def meta(args): + c = TolinoCloud(args.partner) + c.login(args.user, args.password) + c.register() + c.metadata(args.document_id, args.title, args.subtitle, args.author, args.publisher, args.isbn, args.edition, args.issued, args.language) + c.unregister() + c.logout() + print('successfully modified book {}'.format(args.document_id)) + +def cover(args): + c = TolinoCloud(args.partner) + c.login(args.user, args.password) + c.register() + c.cover(args.document_id, args.image) + c.unregister() + c.logout() + print('successfully modified cover for book {}'.format(args.document_id)) parser = argparse.ArgumentParser( description='cmd line client to access personal tolino cloud storage space.' @@ -115,6 +132,23 @@ def delete(args): s = subparsers.add_parser('inventory', help='fetch and print inventory') s.set_defaults(func=inventory) +s = subparsers.add_parser('meta', help='set new meta data') +s.add_argument('document_id') +s.add_argument('--title', help='set a new title eg. "Book Title 1"') +s.add_argument('--subtitle', help='set a new subtitle eg. "Subtitle 1"') +s.add_argument('--author', help='set a new author eg. "Hans Muster"') +s.add_argument('--publisher', help='set a new publisher eg. "Carlsen Verlag"') +s.add_argument('--isbn', help='set a new isbn eg."977-3-958-39650-2"') +s.add_argument('--edition', help='set a new edition eg. "3"') +s.add_argument('--issued', help='set a new issued date eg. "01.01.2019" ') +s.add_argument('--language', help='set a new language eg. "en"') +s.set_defaults(func=meta) + +s = subparsers.add_parser('cover', help='upload a cover for a specific book') +s.add_argument('document_id') +s.add_argument('image', metavar='IMAGE', help="must be a .png or .jpg") +s.set_defaults(func=cover) + s = subparsers.add_parser('upload', help='upload a file (must be either .pdf or .epub)') s.add_argument('filename', metavar='FILE') s.add_argument('--name', help='specify an alternative name') diff --git a/tolinoclient.py.fish b/tolinoclient.py.fish index 50b24c6..2d2a338 100644 --- a/tolinoclient.py.fish +++ b/tolinoclient.py.fish @@ -36,6 +36,7 @@ function __fish_tolino_partners echo "0 List partners" echo "3 thalia.de" echo "6 buch.de" + echo "8 book.ch / orellfuessli.ch" echo "13 hugendubel.de" end @@ -45,6 +46,8 @@ complete -c $PROG --no-files # Subcommands complete -c $PROG -n '__fish_tolino_needs_command' -a 'inventory' -d 'Fetch and print inventory' +complete -c $PROG -n '__fish_tolino_needs_command' -a 'meta' -d 'Update meta data of a book' +complete -c $PROG -n '__fish_tolino_needs_command' -a 'cover' -d 'Update cover of a book' complete -c $PROG -n '__fish_tolino_needs_command' -a 'upload' -d 'Upload a file (must be either .pdf or .epub)' complete -c $PROG -n '__fish_tolino_needs_command' -a 'download' -d 'Download a document' complete -c $PROG -n '__fish_tolino_needs_command' -a 'delete' -d 'Delete a document (be careful!)' diff --git a/tolinocloud.py b/tolinocloud.py index b7cf220..b01e22b 100755 --- a/tolinocloud.py +++ b/tolinocloud.py @@ -1,4 +1,4 @@ -#tolino cloud access module +# tolino cloud access module # Hey, tolino developers at Telekom / T-Systems: # @@ -31,8 +31,10 @@ import re from urllib.parse import urlparse, parse_qs import logging +import datetime from pprint import pformat + class TolinoException(Exception): pass @@ -53,15 +55,15 @@ def _hardware_id(): # X = the result of a fingerprinting image os_id = { - 'Windows' : '1', - 'Darwin' : '2', - 'Linux' : '3' - }.get(platform.system(), 'x') + 'Windows': '1', + 'Darwin': '2', + 'Linux': '3' + }.get(platform.system(), 'x') # The hardware id contains some info about the browser # # Hey, tolino developers: Let me know which id values to use here - engine_id = 'x' + engine_id = 'x' browser_id = 'xx' version_id = '00' @@ -77,190 +79,233 @@ def _hardware_id(): fingerprint = 'ABCDEFGHIJKLMNOPQR' return (os_id + - engine_id + - browser_id + - fingerprint[0:1] + - '-' + - version_id + - fingerprint[1:4] + - '-' + - fingerprint[4:9] + - '-' + - fingerprint[9:14] + - '-' + - fingerprint[14:18] + - 'h') + engine_id + + browser_id + + fingerprint[0:1] + + '-' + + version_id + + fingerprint[1:4] + + '-' + + fingerprint[4:9] + + '-' + + fingerprint[9:14] + + '-' + + fingerprint[14:18] + + 'h') hardware_id = _hardware_id() partner_name = { - 1 : 'Telekom', - 3 : 'Thalia.de', - 4 : 'Thalia.at', - 5 : 'Thalia.ch', - 6 : 'Buch.de', - 7 : 'buch.ch', - 8 : 'Books.ch', - 10 : 'Weltbild.de', - 11 : 'Weltbild.at', - 12 : 'Weltbild.ch', - 13 : 'Hugendubel.de', - 20 : 'derclub.de', - 21 : 'otto-media.de', - 22 : 'donauland.at', - 30 : 'bücher.de', - 40 : 'Bild.de', # defunct? - 60 : 'StandaardBoekhandel.be', - 80 : 'Libri.de', - 81 : 'eBook.de', - 90 : 'ibs.it' + 1: 'Telekom', + 3: 'Thalia.de', + 4: 'Thalia.at', + 5: 'Thalia.ch', + 6: 'Buch.de', + 7: 'buch.ch', + 8: 'books.ch / orellfuessli.ch', + 10: 'Weltbild.de', + 11: 'Weltbild.at', + 12: 'Weltbild.ch', + 13: 'Hugendubel.de', + 20: 'derclub.de', + 21: 'otto-media.de', + 22: 'donauland.at', + 30: 'bücher.de', + 40: 'Bild.de', # defunct? + 60: 'StandaardBoekhandel.be', + 80: 'Libri.de', + 81: 'eBook.de', + 90: 'ibs.it' } partner_settings = { 3: { # Thalia.de - 'client_id' : 'webshop01', - 'scope' : 'SCOPE_BOSH SCOPE_BUCHDE', - 'signup_url' : 'https://www.thalia.de/shop/home/kunde/neu/', - 'profile_url' : 'https://www.thalia.de/shop/home/kunde/', - 'token_url' : 'https://www.thalia.de/de.buch.appservices/api/2004/oauth2/token', - 'login_form_url' : 'https://auth.buch.de/de.thalia.ecp.authservice.application/oauth2/login', - 'x_buchde.skin_id' : '17', - 'x_buchde.mandant_id' :'2', - 'auth_url' : 'https://auth.buch.de/de.thalia.ecp.authservice.application/oauth2/authorize', - 'login_url' : 'https://auth.buch.de/de.thalia.ecp.authservice.application/login.do', + 'client_id': 'webshop01', + 'scope': 'SCOPE_BOSH SCOPE_BUCHDE', + 'signup_url': 'https://www.thalia.de/shop/home/kunde/neu/', + 'profile_url': 'https://www.thalia.de/shop/home/kunde/', + 'token_url': 'https://www.thalia.de/de.buch.appservices/api/2004/oauth2/token', + 'login_form_url': 'https://auth.buch.de/de.thalia.ecp.authservice.application/oauth2/login', + 'x_buchde.skin_id': '17', + 'x_buchde.mandant_id': '2', + 'auth_url': 'https://auth.buch.de/de.thalia.ecp.authservice.application/oauth2/authorize', + 'login_url': 'https://auth.buch.de/de.thalia.ecp.authservice.application/login.do', # 'revoke_url' : 'https://www.thalia.de/de.buch.appservices/api/2004/oauth2/revoke', - 'login_form' : { - 'username' : 'j_username', - 'password' : 'j_password', - 'extra' : { - 'login' : '' - } - }, - 'login_cookie' : 'OAUTH-JSESSIONID', - 'logout_url' : 'https://www.thalia.de/shop/home/login/logout/', - 'reader_url' : 'https://webreader.mytolino.com/library/index.html#/mybooks/titles', - 'register_url' : 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', - 'devices_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', - 'unregister_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', - 'upload_url' : 'https://bosh.pageplace.de/bosh/rest/upload', - 'delete_url' : 'https://bosh.pageplace.de/bosh/rest/deletecontent', - 'inventory_url' : 'https://bosh.pageplace.de/bosh/rest/inventory/delta', - 'downloadinfo_url' : 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + 'login_form': { + 'username': 'j_username', + 'password': 'j_password', + 'extra': { + 'login': '' + } }, + 'login_cookie': 'OAUTH-JSESSIONID', + 'logout_url': 'https://www.thalia.de/shop/home/login/logout/', + 'reader_url': 'https://webreader.mytolino.com/library/index.html#/mybooks/titles', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + }, 4: { # Thalia.at - 'client_id' : 'webshop01', - 'scope' : 'SCOPE_BOSH SCOPE_BUCHDE', - 'signup_url' : 'https://www.thalia.at/shop/home/kunde/neu/', - 'profile_url' : 'https://www.thalia.at/shop/home/kunde/', - 'token_url' : 'https://www.thalia.at/de.buch.appservices/api/4004/oauth2/token', - 'login_form_url' : 'https://www.thalia.at/de.thalia.ecp.authservice.application/oauth2/login', - 'x_buchde.skin_id' : '17', - 'x_buchde.mandant_id' : '4', - 'auth_url' : 'https://www.thalia.at/de.thalia.ecp.authservice.application/oauth2/authorize', - 'login_url' : 'https://www.thalia.at/de.thalia.ecp.authservice.application/login.do', + 'client_id': 'webshop01', + 'scope': 'SCOPE_BOSH SCOPE_BUCHDE', + 'signup_url': 'https://www.thalia.at/shop/home/kunde/neu/', + 'profile_url': 'https://www.thalia.at/shop/home/kunde/', + 'token_url': 'https://www.thalia.at/de.buch.appservices/api/4004/oauth2/token', + 'login_form_url': 'https://www.thalia.at/de.thalia.ecp.authservice.application/oauth2/login', + 'x_buchde.skin_id': '17', + 'x_buchde.mandant_id': '4', + 'auth_url': 'https://www.thalia.at/de.thalia.ecp.authservice.application/oauth2/authorize', + 'login_url': 'https://www.thalia.at/de.thalia.ecp.authservice.application/login.do', # 'revoke_url' : 'https://www.thalia.de/de.buch.appservices/api/2004/oauth2/revoke', - 'login_form' : { - 'username' : 'j_username', - 'password' : 'j_password', - 'extra' : { - 'login' : '' + 'login_form': { + 'username': 'j_username', + 'password': 'j_password', + 'extra': { + 'login': '' } - }, - 'login_cookie' : 'OAUTH-JSESSIONID', - 'logout_url' : 'https://www.thalia.at/shop/home/show/', - 'reader_url' : 'https://webreader.mytolino.com/library/index.html#/mybooks/titles', - 'register_url' : 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', - 'devices_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', - 'unregister_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', - 'upload_url' : 'https://bosh.pageplace.de/bosh/rest/upload', - 'delete_url' : 'https://bosh.pageplace.de/bosh/rest/deletecontent', - 'inventory_url' : 'https://bosh.pageplace.de/bosh/rest/inventory/delta', - 'downloadinfo_url' : 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' }, - 6: { + 'login_cookie': 'OAUTH-JSESSIONID', + 'logout_url': 'https://www.thalia.at/shop/home/show/', + 'reader_url': 'https://webreader.mytolino.com/library/index.html#/mybooks/titles', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + }, + 6: { # Buch.de' - 'client_id' : 'webshop01', - 'scope' : 'SCOPE_BOSH SCOPE_BUCHDE', - 'signup_url' : 'https://ssl.buch.de/shop/home/kunde/neu/', - 'profile_url' : 'https://ssl.buch.de/shop/home/kunde/', - 'login_url' : 'https://ssl.buch.de/shop/home/login/dologin/', - 'login_form' : { - 'username' : 'username', - 'password' : 'password', - 'extra' : {} - }, - 'login_cookie' : 'KUNDE', - 'tat_url' : 'https://ssl.buch.de/shop/home/ebook/anzeigen/', - 'logout_url' : 'https://ssl.buch.de/shop/home/login/logout/', - 'reader_url' : 'https://html5reader.buch.de/library/library.html#!/library', - 'register_url' : 'https://bosh.pageplace.de/bosh/rest/registerhw', - 'devices_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', - 'unregister_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', - 'upload_url' : 'https://bosh.pageplace.de/bosh/rest/upload', - 'delete_url' : 'https://bosh.pageplace.de/bosh/rest/deletecontent', - 'inventory_url' : 'https://bosh.pageplace.de/bosh/rest/inventory/delta', - 'downloadinfo_url' : 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + 'client_id': 'webshop01', + 'scope': 'SCOPE_BOSH SCOPE_BUCHDE', + 'signup_url': 'https://ssl.buch.de/shop/home/kunde/neu/', + 'profile_url': 'https://ssl.buch.de/shop/home/kunde/', + 'login_url': 'https://ssl.buch.de/shop/home/login/dologin/', + 'login_form': { + 'username': 'username', + 'password': 'password', + 'extra': {} }, + 'login_cookie': 'KUNDE', + 'tat_url': 'https://ssl.buch.de/shop/home/ebook/anzeigen/', + 'logout_url': 'https://ssl.buch.de/shop/home/login/logout/', + 'reader_url': 'https://html5reader.buch.de/library/library.html#!/library', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + }, + 8: { + # books.ch / orellfuessli.ch + 'client_id': 'webreader', + 'scope': 'SCOPE_BOSH', + 'signup_url': 'https://www.orellfuessli.ch/registrierung/privatkunde', + 'profile_url': 'https://www.orellfuessli.ch/shop/home/kunde/', + 'token_url': 'https://www.orellfuessli.ch/auth/oauth2/token', + 'login_form_url': 'https://www.orellfuessli.ch/de.thalia.ecp.authservice.application/oauth2/login', + 'x_buchde.skin_id': '17', + 'x_buchde.mandant_id': '37', + 'auth_url': 'https://www.orellfuessli.ch/de.thalia.ecp.authservice.application/oauth2/authorize', + 'login_url': 'https://www.orellfuessli.ch/de.thalia.ecp.authservice.application/login.do', + 'revoke_url': 'https://www.orellfuessli.ch//auth/oauth2/revoke', + 'login_form': { + 'username': 'j_username', + 'password': 'j_password', + 'extra': { + 'login': '' + } + }, + 'login_cookie': 'OAUTH-JSESSIONID', + 'logout_url': 'https://www.orellfuessli.ch/shop/home/login/logout/', + 'reader_url': 'https://webreader.mytolino.com/library/index.html#/mybooks/titles', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + }, 13: { # Hugendubel.de - 'client_id' : '4c20de744aa8b83b79b692524c7ec6ae', - 'scope' : 'ebook_library', - 'signup_url' : 'https://www.hugendubel.de/go/my_my/my_newRegistration/', - 'profile_url' : 'https://www.hugendubel.de/go/my_my/my_data/', - 'token_url' : 'https://api.hugendubel.de/rest/oauth2/token', + 'client_id': '4c20de744aa8b83b79b692524c7ec6ae', + 'scope': 'ebook_library', + 'signup_url': 'https://www.hugendubel.de/go/my_my/my_newRegistration/', + 'profile_url': 'https://www.hugendubel.de/go/my_my/my_data/', + 'token_url': 'https://api.hugendubel.de/rest/oauth2/token', # 'revoke_url' : 'https://api.hugendubel.de/rest/oauth2/revoke', - 'auth_url' : 'https://www.hugendubel.de/oauth/authorize', - 'login_url' : 'https://www.hugendubel.de/de/account/login', - 'login_form' : { - 'username' : 'username', - 'password' : 'password', - 'extra' : { - 'evaluate' : 'true', - 'isOrdering' : '', - 'isOneClickOrdering' : '' + 'auth_url': 'https://www.hugendubel.de/oauth/authorize', + 'login_url': 'https://www.hugendubel.de/de/account/login', + 'login_form': { + 'username': 'username', + 'password': 'password', + 'extra': { + 'evaluate': 'true', + 'isOrdering': '', + 'isOneClickOrdering': '' } }, - 'login_cookie' : 'JSESSIONID', - 'logout_url' : 'https://www.hugendubel.de/de/account/logout', - 'reader_url' : 'https://webreader.hugendubel.de/library/index.html', - 'register_url' : 'https://bosh.pageplace.de/bosh/rest/registerhw', - 'devices_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', - 'unregister_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', - 'upload_url' : 'https://bosh.pageplace.de/bosh/rest/upload', - 'delete_url' : 'https://bosh.pageplace.de/bosh/rest/deletecontent', - 'inventory_url' : 'https://bosh.pageplace.de/bosh/rest/inventory/delta', - 'downloadinfo_url' : 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + 'login_cookie': 'JSESSIONID', + 'logout_url': 'https://www.hugendubel.de/de/account/logout', + 'reader_url': 'https://webreader.hugendubel.de/library/index.html', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' }, 30: { # buecher.de - 'client_id' : 'dte_ereader_app_01', - 'scope' : 'ebook_library', - 'signup_url' : 'https://www.buecher.de/go/my_dry/my_register_aos/', - 'profile_url' : 'https://www.buecher.de/go/my_dry/my_login/receiver_object/my_login/', - 'token_url' : 'https://www.buecher.de/oauth2/token', - 'revoke_url' : 'https://www.buecher.de/oauth2/revoke', - 'auth_url' : 'https://www.buecher.de/oauth2/authorize', - 'login_url' : 'https://www.buecher.de/go/my_dry/my_login/', - 'login_form' : { - 'username' : 'form[login]', - 'password' : 'form[password]', - 'extra' : { - 'form_send' : '1' + 'client_id': 'dte_ereader_app_01', + 'scope': 'ebook_library', + 'signup_url': 'https://www.buecher.de/go/my_dry/my_register_aos/', + 'profile_url': 'https://www.buecher.de/go/my_dry/my_login/receiver_object/my_login/', + 'token_url': 'https://www.buecher.de/oauth2/token', + 'revoke_url': 'https://www.buecher.de/oauth2/revoke', + 'auth_url': 'https://www.buecher.de/oauth2/authorize', + 'login_url': 'https://www.buecher.de/go/my_dry/my_login/', + 'login_form': { + 'username': 'form[login]', + 'password': 'form[password]', + 'extra': { + 'form_send': '1' } }, - 'x_buecherde.skin_id' : 'de_dte_tolino', - 'login_cookie' : 'session', - 'logout_url' : 'https://www.buecher.de/go/my_dry/my_logout/', - 'reader_url' : 'https://webreader.mytolino.com/library/', - 'register_url' : 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', - 'devices_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', - 'unregister_url' : 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', - 'upload_url' : 'https://bosh.pageplace.de/bosh/rest/upload', - 'delete_url' : 'https://bosh.pageplace.de/bosh/rest/deletecontent', - 'inventory_url' : 'https://bosh.pageplace.de/bosh/rest/inventory/delta', - 'downloadinfo_url' : 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' + 'x_buecherde.skin_id': 'de_dte_tolino', + 'login_cookie': 'session', + 'logout_url': 'https://www.buecher.de/go/my_dry/my_logout/', + 'reader_url': 'https://webreader.mytolino.com/library/', + 'register_url': 'https://bosh.pageplace.de/bosh/rest/v2/registerhw', + 'devices_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/list', + 'unregister_url': 'https://bosh.pageplace.de/bosh/rest/handshake/devices/delete', + 'upload_url': 'https://bosh.pageplace.de/bosh/rest/upload', + 'delete_url': 'https://bosh.pageplace.de/bosh/rest/deletecontent', + 'inventory_url': 'https://bosh.pageplace.de/bosh/rest/inventory/delta', + 'meta_url': 'https://bosh.pageplace.de/bosh/rest/meta', + 'cover_url': 'https://bosh.pageplace.de/bosh/rest/cover', + 'downloadinfo_url': 'https://bosh.pageplace.de/bosh/rest//cloud/downloadinfo/{}/{}/type/external-download' } } @@ -288,13 +333,13 @@ def login(self, username, password): # Login with partner site # to retrieve site's cookies within browser session if 'login_form_url' in c: - r = s.get(c['login_form_url'], params = { - 'client_id' : c['client_id'], - 'response_type' : 'code', - 'scope' : c['scope'], - 'redirect_uri' : c['reader_url'], + r = s.get(c['login_form_url'], params={ + 'client_id': c['client_id'], + 'response_type': 'code', + 'scope': c['scope'], + 'redirect_uri': c['reader_url'], 'x_buchde.skin_id': c['x_buchde.skin_id'], - 'x_buchde.mandant_id' : c['x_buchde.mandant_id'] + 'x_buchde.mandant_id': c['x_buchde.mandant_id'] }, verify=True, allow_redirects=False) data = c['login_form']['extra'] data[c['login_form']['username']] = username @@ -305,23 +350,23 @@ def login(self, username, password): self._debug(r) if not c['login_cookie'] in s.cookies: raise TolinoException('login to {} failed.'. - format(self.partner_name[self.partner_id])) + format(self.partner_name[self.partner_id])) auth_code = "" if 'tat_url' in c: try: r = s.get(c['tat_url'], verify=True) self._debug(r) b64 = re.search(r'\&tat=(.*?)%3D', r.text).group(1) - self.access_token = base64.b64decode(b64+'==').decode('utf-8') + self.access_token = base64.b64decode(b64 + '==').decode('utf-8') except: raise TolinoException('oauth access token request failed.') else: # Request OAUTH code params = { - 'client_id' : c['client_id'], - 'response_type' : 'code', - 'scope' : c['scope'], - 'redirect_uri' : c['reader_url'] + 'client_id': c['client_id'], + 'response_type': 'code', + 'scope': c['scope'], + 'redirect_uri': c['reader_url'] } if 'login_form_url' in c: params['x_buchde.skin_id'] = c['x_buchde.skin_id'] @@ -335,12 +380,12 @@ def login(self, username, password): raise TolinoException('oauth code request failed.') # Fetch OAUTH access token - r = s.post(c['token_url'], data = { - 'client_id' : c['client_id'], - 'grant_type' : 'authorization_code', - 'code' : auth_code, - 'scope' : c['scope'], - 'redirect_uri' : c['reader_url'] + r = s.post(c['token_url'], data={ + 'client_id': c['client_id'], + 'grant_type': 'authorization_code', + 'code': auth_code, + 'scope': c['scope'], + 'redirect_uri': c['reader_url'] }, verify=True, allow_redirects=False) self._debug(r) try: @@ -357,12 +402,12 @@ def logout(self): if 'revoke_url' in c: r = s.post(c['revoke_url'], - data = { - 'client_id' : c['client_id'], - 'token_type' : 'refresh_token', - 'token' : self.refresh_token - } - ) + data={ + 'client_id': c['client_id'], + 'token_type': 'refresh_token', + 'token': self.refresh_token + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('logout failed.') @@ -372,51 +417,50 @@ def logout(self): if r.status_code != 200: raise TolinoException('logout failed.') - def register(self): s = self.session; c = self.partner_settings[self.partner_id] # Register our hardware r = s.post(c['register_url'], - data = json.dumps({'hardware_name':'other'}), - headers = { - 'content-type': 'application/json', - 't_auth_token': self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id), - 'client_type': 'TOLINO_WEBREADER', - 'client_version': '4.4.1', - 'hardware_type': 'HTML5' - } - ) + data=json.dumps({'hardware_name': 'other'}), + headers={ + 'content-type': 'application/json', + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id), + 'client_type': 'TOLINO_WEBREADER', + 'client_version': '4.4.1', + 'hardware_type': 'HTML5' + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('register {} failed.'.format(TolinoCloud.hardware_id)) - def unregister(self, device_id = hardware_id): + def unregister(self, device_id=hardware_id): s = self.session; c = self.partner_settings[self.partner_id] r = s.post(c['unregister_url'], - data = json.dumps({ - 'deleteDevicesRequest':{ - 'accounts' : [ { - 'auth_token' : self.access_token, - 'reseller_id' : self.partner_id - } ], - 'devices' : [ { - 'device_id' : device_id, - 'reseller_id' : self.partner_id - } ] - } - }), - headers = { - 'content-type': 'application/json', - 't_auth_token': self.access_token, - 'reseller_id' : str(self.partner_id) - } - ) + data=json.dumps({ + 'deleteDevicesRequest': { + 'accounts': [{ + 'auth_token': self.access_token, + 'reseller_id': self.partner_id + }], + 'devices': [{ + 'device_id': device_id, + 'reseller_id': self.partner_id + }] + } + }), + headers={ + 'content-type': 'application/json', + 't_auth_token': self.access_token, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: try: @@ -430,20 +474,20 @@ def devices(self): c = self.partner_settings[self.partner_id] r = s.post(c['devices_url'], - data = json.dumps({ - 'deviceListRequest':{ - 'accounts' : [ { - 'auth_token' : self.access_token, - 'reseller_id' : self.partner_id - } ] - } - }), - headers = { - 'content-type': 'application/json', - 't_auth_token': self.access_token, - 'reseller_id' : str(self.partner_id) - } - ) + data=json.dumps({ + 'deviceListRequest': { + 'accounts': [{ + 'auth_token': self.access_token, + 'reseller_id': self.partner_id + }] + } + }), + headers={ + 'content-type': 'application/json', + 't_auth_token': self.access_token, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('device list request failed.') @@ -453,16 +497,16 @@ def devices(self): j = r.json() for item in j['deviceListResponse']['devices']: devs.append({ - 'id' : item['deviceId'], - 'name' : item['deviceName'], - 'type' : { - 'unknown_imx50_rdp_1' : 'tolino shine', - 'tolino_vison' : 'tolino vision', - 'HTML5_1' : 'web browser' - }.get(item['deviceType'], item['deviceType']), - 'partner' : int(item['resellerId']), - 'registered' : int(item['deviceRegistered']), - 'lastusage' : int(item['deviceLastUsage']) + 'id': item['deviceId'], + 'name': item['deviceName'], + 'type': { + 'unknown_imx50_rdp_1': 'tolino shine', + 'tolino_vison': 'tolino vision', + 'HTML5_1': 'web browser' + }.get(item['deviceType'], item['deviceType']), + 'partner': int(item['resellerId']), + 'registered': int(item['deviceRegistered']), + 'lastusage': int(item['deviceLastUsage']) }) return devs except: @@ -471,14 +515,14 @@ def devices(self): def _parse_metadata(self, j): try: md = { - 'partner' : int(j['resellerId']), - 'id' : j['epubMetaData']['identifier'], - 'title' : j['epubMetaData']['title'], - 'subtitle' : j['epubMetaData']['subtitle'], - 'author' : [a['name'] for a in j['epubMetaData']['author']], - 'mime' : j['epubMetaData']['deliverable'][0]['contentFormat'], - 'type' : j['epubMetaData']['type'].lower(), - 'purchased' : int(j['epubMetaData']['deliverable'][0]['purchased']) + 'partner': int(j['resellerId']), + 'id': j['epubMetaData']['identifier'], + 'title': j['epubMetaData']['title'], + 'subtitle': j['epubMetaData']['subtitle'], + 'author': [a['name'] for a in j['epubMetaData']['author']], + 'mime': j['epubMetaData']['deliverable'][0]['contentFormat'], + 'type': j['epubMetaData']['type'].lower(), + 'purchased': int(j['epubMetaData']['deliverable'][0]['purchased']) } if j['epubMetaData']['issued']: md['issued'] = int(j['epubMetaData']['issued']) @@ -491,13 +535,13 @@ def inventory(self): c = self.partner_settings[self.partner_id] r = s.get(c['inventory_url'], - params = {'strip': 'true'}, - headers = { - 't_auth_token' : self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id) - } - ) + params={'strip': 'false'}, + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('inventory list request failed.') @@ -515,7 +559,7 @@ def inventory(self): except: raise TolinoException('inventory list request failed.') - def upload(self, filename, name = None, ext = None): + def upload(self, filename, name=None, ext=None): s = self.session; c = self.partner_settings[self.partner_id] @@ -525,18 +569,18 @@ def upload(self, filename, name = None, ext = None): ext = filename.split('.')[-1] mime = { - 'pdf' : 'application/pdf', - 'epub' : 'application/epub+zip' + 'pdf': 'application/pdf', + 'epub': 'application/epub+zip' }.get(ext.lower(), 'application/pdf') r = s.post(c['upload_url'], - files = [('file', (name, open(filename, 'rb'), mime))], - headers = { - 't_auth_token' : self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id) - } - ) + files=[('file', (name, open(filename, 'rb'), mime))], + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('file upload failed.') @@ -552,15 +596,15 @@ def delete(self, id): c = self.partner_settings[self.partner_id] r = s.get(c['delete_url'], - params = { - 'deliverableId': id - }, - headers = { - 't_auth_token' : self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id) - } - ) + params={ + 'deliverableId': id + }, + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: try: @@ -575,12 +619,12 @@ def download_info(self, id): b64 = base64.b64encode(bytes(id, 'utf-8')).decode('utf-8') r = s.get(c['downloadinfo_url'].format(b64, b64), - headers = { - 't_auth_token' : self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id) - } - ) + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: raise TolinoException('download info request failed.') @@ -588,9 +632,9 @@ def download_info(self, id): j = r.json() url = j['DownloadInfo']['contentUrl'] return { - 'url' : url, - 'filename' : url.split('/')[-1], - 'filetype' : j['DownloadInfo']['format'], + 'url': url, + 'filename': url.split('/')[-1], + 'filetype': j['DownloadInfo']['format'], } def download(self, path, id): @@ -600,13 +644,13 @@ def download(self, path, id): di = self.download_info(id) r = s.get(di['url'], - stream=True, - headers = { - 't_auth_token' : self.access_token, - 'hardware_id' : TolinoCloud.hardware_id, - 'reseller_id' : str(self.partner_id) - } - ) + stream=True, + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) self._debug(r) if r.status_code != 200: try: @@ -615,7 +659,6 @@ def download(self, path, id): except KeyError: raise TolinoException('download request : reason unknown.') - filename = path + '/' + di['filename'] if path else di['filename'] with open(filename, 'wb') as f: for chunk in r.iter_content(chunk_size=1024): @@ -624,3 +667,102 @@ def download(self, path, id): f.flush() return filename + + def metadata(self, book_id, title=None, subtitle=None, author=None, publisher=None, isbn=None, edition=None, + issued=None, language=None): + s = self.session; + c = self.partner_settings[self.partner_id] + + if 'meta_url' not in c: + raise TolinoException('no meta url defined for this provider.') + + b = c['meta_url'] + '/?deliverableId={book_id}'.format(book_id=book_id) + r = s.get(b, + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) + + meta = r.json() + + self._debug(r) + + if title is not None: + meta['metadata']['title'] = str(title) + + if subtitle is not None: + meta['metadata']['subtitle'] = str(subtitle) + + if author is not None: + meta['metadata']['author'] = str(author) + + if publisher is not None: + meta['metadata']['publisher'] = str(publisher) + + if isbn is not None: + meta['metadata']['isbn'] = str(isbn) + + if edition is not None: + meta['metadata']['edition'] = int(edition) + + if issued is not None: + meta['metadata']['issued'] = datetime.datetime.strptime(issued, "%d.%m.%Y").timestamp() + + if language is not None: + meta['metadata']['language'] = str(language) + + payload = { + 'uploadMetaData': meta['metadata'] + } + + r = s.put(b, + data=json.dumps(payload), + headers={ + 'content-type': 'application/json', + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) + self._debug(r) + if r.status_code != 200: + raise TolinoException('meta data update failed.') + + try: + return meta['metadata']['deliverableId'] + except: + raise TolinoException('meta data update failed.') + + def cover(self, book_id, image, name=None, ext=None): + s = self.session; + c = self.partner_settings[self.partner_id] + + if 'cover_url' not in c: + raise TolinoException('no meta url defined for this provider.') + + if name is None: + name = image.split('/')[-1] + + if ext is None: + ext = image.split('.')[-1] + + mime = { + 'png': 'image/png', + 'jpeg': 'image/jpeg', + 'jpg': 'image/jpeg' + }.get(ext.lower(), 'application/jpeg') + + r = s.post(c['cover_url'], + files=[('file', (name, open(image, 'rb'), mime)), ('deliverableId', (None, book_id))], + headers={ + 't_auth_token': self.access_token, + 'hardware_id': TolinoCloud.hardware_id, + 'reseller_id': str(self.partner_id) + } + ) + + self._debug(r) + if r.status_code != 200: + raise TolinoException('cover upload failed.')