28 Commits

Author SHA1 Message Date
Stijn Van Campenhout
42430b0eb8 🚀 bump version to 0.3.0 2021-07-11 11:38:42 +02:00
Samy Abidib
934e270c6d feat: Add highlight support (#22)
* Add support for downloading highlights
* Add to docs
* Respond to feedback
* Add doc line
* Bug and doc fix

 Thanks @sabidib!
2021-07-11 11:37:20 +02:00
Stijn Van Campenhout
8b120202af fix: fix: CVE-2021-33503 2021-07-11 11:33:54 +02:00
Stijn Van Campenhout
5595ade375 fix: CVE-2021-33503 2021-07-11 11:26:30 +02:00
Stijn Van Campenhout
18865083d7 Merge pull request #21
ZipDocument class vars should be instance vars
2021-05-30 13:27:32 +02:00
Stijn Van Campenhout
10a27bcdb2 Bump version 2021-05-30 15:01:13 +02:00
Stijn Van Campenhout
011cb2f221 Merge pull request #24
Updated the API URL to new domain name
2021-05-30 12:56:16 +02:00
Dan Corne
7e2a3033e0 Updated the API URL to new domain name
This has changed recently and the old domain no longer works.
2021-05-20 19:20:04 +01:00
Samy Abidib
fbc75858d7 ZipDocument class vars should be instance vars 2021-05-11 11:53:32 -04:00
Stijn Van Campenhout
50fef896d3 bump version 2021-03-21 11:14:22 +01:00
dependabot[bot]
a0965d58b3 Bump jinja2 from 2.10.1 to 2.11.3 (#16)
Bumps [jinja2](https://github.com/pallets/jinja) from 2.10.1 to 2.11.3.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/master/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/2.10.1...2.11.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-03-21 11:06:17 +01:00
Reece Mathews
544bd11dd1 Ignore missing files when used with Remarkable 2 (#14)
* Ignore missing thumbnails during dump

* Ignore missing metadata file during load
2021-03-21 11:05:31 +01:00
Stijn Van Campenhout
b5f0367dbb Updated version number 2021-01-30 18:02:43 +01:00
Stijn Van Campenhout
bc36d7b7c6 Updated to latest release 2021-01-30 17:50:49 +01:00
Adeel Khan
15d8090e79 doc: change refresh to renew (#11) 2021-01-30 14:46:21 +01:00
Jacob Kaplan-Moss
8bb29e3bd7 fix(download): Ignore missing thumbnail files (#9)
* Ignore missing thumbnail files

* Change logger name
2021-01-30 14:35:32 +01:00
Stijn Van Campenhout
29d113f3af Added release workflow 2020-11-13 08:16:08 +01:00
ruebeckscube
433eb8e722 Change Collection.items to instance variable (#8)
fix: Change Collection.items to instance variable
2020-11-13 08:10:39 +01:00
Stijn Van Campenhout
7ceac42fdb Merge branch 'bsdz-patch-1' 2020-11-12 16:31:17 +01:00
Stijn Van Campenhout
f5e4972775 Merge branch 'patch-1' of https://github.com/bsdz/rmapy into bsdz-patch-1 2020-11-12 16:30:37 +01:00
Stijn Van Campenhout
cca7ea05fd Merge pull request #4 from jodergrosse/correction_quickstart_doc
Closes #3
2020-11-11 18:13:57 +01:00
Stijn Van Campenhout
48239c9b9f Merge pull request #2 from jodergrosse/bugfix/register_new_device
fix: #1
2020-11-11 18:11:49 +01:00
Blair Azzopardi
3554640693 Correct typo "Succes" -> "Success" 2020-06-20 23:54:38 +01:00
Johannes Gross
f20916bd34 replace wrong function name 2020-03-07 22:18:55 +01:00
Johannes Gross
7d70d58f6e fix:
body was passed to register_device as argument 'data'
2020-03-07 22:05:52 +01:00
Stijn Van Campenhout
506b34d40a Code cleanup & Refactoring 2019-12-26 15:16:30 +01:00
Stijn Van Campenhout
3f0e0e36a2 Rename rmapi rmapy 2019-12-19 14:25:26 +01:00
Stijn Van Campenhout
00dc50550f Rename rmapi rmapy 2019-12-19 13:42:26 +01:00
23 changed files with 505 additions and 394 deletions

31
.github/workflows/python-publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: Upload Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*

View File

@@ -9,6 +9,7 @@ verify_ssl = true
requests = "*"
pyaml = "*"
sphinx = "*"
urllib3 = ">=1.26.5"
[requires]
python_version = "3.7"

268
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "880b0f4dc28652bfa8674e7ebeaae37eb25ec47d136a03791bcef5b6f7a5518d"
"sha256": "f7fb5bcb7b1ad271a4b43ccd956c97f6d6a530869b7d9b8454aaf4defcadf09f"
},
"pipfile-spec": 6,
"requires": {
@@ -23,227 +23,253 @@
],
"version": "==0.7.12"
},
"attrs": {
"hashes": [
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
],
"version": "==19.1.0"
},
"babel": {
"hashes": [
"sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab",
"sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28"
"sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9",
"sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"
],
"version": "==2.7.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.9.1"
},
"certifi": {
"hashes": [
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
],
"version": "==2019.9.11"
"version": "==2021.5.30"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
"sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
"sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
],
"version": "==3.0.4"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==4.0.0"
},
"docutils": {
"hashes": [
"sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0",
"sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827",
"sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"
"sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125",
"sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"
],
"version": "==0.15.2"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.17.1"
},
"idna": {
"hashes": [
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
"sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
"sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
"version": "==2.8"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.10"
},
"imagesize": {
"hashes": [
"sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8",
"sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5"
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
"version": "==1.1.0"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
"jinja2": {
"hashes": [
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
"sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4",
"sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"
],
"version": "==2.10.1"
"markers": "python_version >= '3.6'",
"version": "==3.0.1"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
"sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298",
"sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64",
"sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b",
"sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567",
"sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff",
"sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74",
"sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35",
"sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26",
"sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7",
"sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75",
"sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f",
"sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135",
"sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8",
"sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a",
"sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914",
"sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18",
"sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8",
"sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2",
"sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d",
"sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b",
"sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f",
"sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb",
"sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833",
"sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415",
"sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902",
"sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9",
"sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d",
"sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066",
"sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f",
"sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5",
"sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94",
"sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509",
"sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51",
"sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"
],
"version": "==1.1.1"
"markers": "python_version >= '3.6'",
"version": "==2.0.1"
},
"packaging": {
"hashes": [
"sha256:a7ac867b97fdc07ee80a8058fe4435ccd274ecc3b0ed61d852d7d53055528cf9",
"sha256:c491ca87294da7cc01902edbe30a5bc6c4c28172b5138ab4e4aa1b9d7bfaeafe"
"sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7",
"sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"
],
"version": "==19.1"
"markers": "python_version >= '3.6'",
"version": "==21.0"
},
"pyaml": {
"hashes": [
"sha256:a2dcbc4a8bb00b541efd1c5a064d93474d4f41ded1484fbb08bec9d236523931",
"sha256:c79ae98ececda136a034115ca178ee8bf3aa7df236c488c2f55d12f177b88f1e"
"sha256:29a5c2a68660a799103d6949167bd6c7953d031449d08802386372de1db6ad71",
"sha256:67081749a82b72c45e5f7f812ee3a14a03b3f5c25ff36ec3b290514f8c4c4b99"
],
"index": "pypi",
"version": "==19.4.1"
"version": "==20.4.0"
},
"pygments": {
"hashes": [
"sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127",
"sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297"
"sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f",
"sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"
],
"version": "==2.4.2"
"markers": "python_version >= '3.5'",
"version": "==2.9.0"
},
"pyparsing": {
"hashes": [
"sha256:6f98a7b9397e206d78cc01df10131398f1c8b8510a2f4d97d9abd82e1aacdd80",
"sha256:d9338df12903bbf5d65a0e4e87c2161968b10d2e489652bb47001d82a9b028b4"
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
"version": "==2.4.2"
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytz": {
"hashes": [
"sha256:26c0b32e437e54a18161324a2fca3c4b9846b74a8dccddd843113109e1116b32",
"sha256:c894d57500a4cd2d5c71114aaab77dbab5eabd9022308ce5ac9bb93a60a6f0c7"
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
],
"version": "==2019.2"
"version": "==2021.1"
},
"pyyaml": {
"hashes": [
"sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
"sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
"sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
"sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
"sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
"sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
"sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
"sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
"sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
"sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
"sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
"sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
"sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
"sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
"sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
"sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
"sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
"sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
"sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
"sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
"sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
"sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
"sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
"sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
"sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
"sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347",
"sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
"sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541",
"sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
"sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
"sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc",
"sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
"sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa",
"sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
"sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122",
"sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
"sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
"sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
"sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc",
"sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247",
"sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6",
"sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"
],
"version": "==5.1.2"
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
"version": "==5.4.1"
},
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
"sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
"sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
],
"index": "pypi",
"version": "==2.22.0"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"version": "==1.12.0"
"version": "==2.25.1"
},
"snowballstemmer": {
"hashes": [
"sha256:713e53b79cbcf97bc5245a06080a33d54a77e7cce2f789c835a143bcdb5c033e"
"sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
"sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
],
"version": "==1.9.1"
"version": "==2.1.0"
},
"sphinx": {
"hashes": [
"sha256:0d586b0f8c2fc3cc6559c5e8fd6124628110514fda0e5d7c82e682d749d2e845",
"sha256:839a3ed6f6b092bb60f492024489cc9e6991360fb9f52ed6361acd510d261069"
"sha256:5747f3c855028076fcff1e4df5e75e07c836f0ac11f7df886747231092cfe4ad",
"sha256:dff357e6a208eb7edb2002714733ac21a9fe597e73609ff417ab8cf0c6b4fbb8"
],
"index": "pypi",
"version": "==2.2.0"
"version": "==4.0.3"
},
"sphinxcontrib-applehelp": {
"hashes": [
"sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897",
"sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"
"sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
"sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
"version": "==1.0.1"
"markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
"hashes": [
"sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34",
"sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"
"sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
"sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
"version": "==1.0.1"
"markers": "python_version >= '3.5'",
"version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
"hashes": [
"sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
"sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
],
"version": "==1.0.2"
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"sphinxcontrib-jsmath": {
"hashes": [
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
"markers": "python_version >= '3.5'",
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
"hashes": [
"sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
"sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
"sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
"sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
"version": "==1.0.2"
"markers": "python_version >= '3.5'",
"version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
"hashes": [
"sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
"sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
"sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd",
"sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"
],
"version": "==1.1.3"
"markers": "python_version >= '3.5'",
"version": "==1.1.5"
},
"urllib3": {
"hashes": [
"sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1",
"sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232"
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
],
"version": "==1.25.3"
"index": "pypi",
"version": "==1.26.6"
}
},
"develop": {}

View File

@@ -1,4 +1,4 @@
# rMapi
# rMapy
This is an (unofficial) Remarkable Cloud API Client written in Python.
@@ -14,4 +14,3 @@ This is an (unofficial) Remarkable Cloud API Client written in Python.
* ❎ cli interface
* ❎ export pdf with annotations

View File

@@ -18,7 +18,7 @@ sys.path.insert(0, os.path.abspath('../..'))
# -- Project information -----------------------------------------------------
project = 'rmapi'
project = 'rmapy'
copyright = '2019, Stijn Van Campenhout'
author = 'Stijn Van Campenhout'

View File

@@ -1,9 +1,9 @@
.. rmapi documentation master file, created by
.. rmapy documentation master file, created by
sphinx-quickstart on Tue Sep 17 19:24:29 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to rmapi's documentation!
Welcome to rmapy's documentation!
=================================
This is an (unofficial) Remarkable Cloud API Client written in Python.
@@ -50,9 +50,9 @@ API Documentation
.. toctree::
:maxdepth: 2
rmapi
rmapy
.. automodule:: rmapi
.. automodule:: rmapy
Indices and tables

View File

@@ -1,11 +1,11 @@
Pip
===
Like any other package, you can install rmapi using pip:
Like any other package, you can install rmapy using pip:
.. code-block:: bash
pip install rmapi
pip install rmapy

View File

@@ -1,7 +1,7 @@
rmapi
rmapy
=====
.. toctree::
:maxdepth: 4
rmapi
rmapy

View File

@@ -1,4 +1,4 @@
quickstart
quick start
==========
If you previously used the go package `rmapi`_ ,the keys for authorization
@@ -12,7 +12,7 @@ If not, you'll need to register the client as a new device on `my remarkable`_.
.. _rmapi: https://github.com/juruen/rmapi
Registering the API CLient
Registering the API Client
~~~~~~~~~~~~~~~~~~~~~~~~~~
Registering the device is easy. Go to `my remarkable`_ to register a new device
@@ -22,20 +22,20 @@ and use the code you see on the webpage
:linenos:
from rmapi.api import Client
from rmapy.api import Client
rmapi = Client()
# Shoud return False
rmapi.is_authenticated()
rmapy = Client()
# Should return False
rmapy.is_auth()
# This registers the client as a new device. The received device token is
# stored in the users directory in the file ~/.rmapi, the same as with the
# go rmapi client.
rmapi.register_device("fkgzzklrs")
# It's always a good idea to refresh the user token everytime you start
rmapy.register_device("fkgzzklrs")
# It's always a good idea to renew the user token every time you start
# a new session.
rmapi.refresh_token()
# Shoud return True
rmapi.is_authenticated()
rmapy.renew_token()
# Should return True
rmapy.is_auth()
Working with items
~~~~~~~~~~~~~~~~~~
@@ -50,21 +50,21 @@ We can list the items in the Cloud
.. code-block:: python
:linenos:
>>> from rmapi.api import Client
>>> rmapi = Client()
>>> rmapi.renew_token()
>>> from rmapy.api import Client
>>> rmapy = Client()
>>> rmapy.renew_token()
True
>>> collection = rmapi.get_meta_items()
>>> collection = rmapy.get_meta_items()
>>> collection
<rmapi.collections.Collection object at 0x7fa1982d7e90>
<rmapy.collections.Collection object at 0x7fa1982d7e90>
>>> len(collection)
181
>>> # Count the amount of documents
... from rmapi.document import Document
... from rmapy.document import Document
>>> len([f for f in collection if isinstance(f, Document)])
139
>>> # Count the amount of folders
... from rmapi.folder import Folder
... from rmapy.folder import Folder
>>> len([f for f in collection if isinstance(f, Folder)])
42
@@ -74,7 +74,7 @@ DocumentType
````````````
A DocumentType is a document. This can be a pdf, epub or notebook.
These types are represented by the object :class:`rmapi.document.Document`
These types are represented by the object :class:`rmapy.document.Document`
Changing the metadata is easy
@@ -83,28 +83,28 @@ Changing the metadata is easy
:linenos:
>>> from rmapi.api import Client
>>> rmapi = Client()
>>> rmapi.renew_token()
>>> from rmapy.api import Client
>>> rmapy = Client()
>>> rmapy.renew_token()
True
>>> collection = rmapi.get_meta_items()
>>> collection = rmapy.get_meta_items()
>>> doc = [ d for d in collection if d.VissibleName == 'ModernC'][0]
>>> doc
<rmapi.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
<rmapy.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
>>> doc.to_dict()
{'ID': 'a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3', 'Version': 1, 'Message': '', 'Succes': True, 'BlobURLGet': '', 'BlobURLGetExpires': '0001-01-01T00:00:00Z', 'BlobURLPut': '', 'BlobURLPutExpires': '', 'ModifiedClient': '2019-09-18T20:12:07.206206Z', 'Type': 'DocumentType', 'VissibleName': 'ModernC', 'CurrentPage': 0, 'Bookmarked': False, 'Parent': ''}
>>> doc.VissibleName = "Modern C: The book of wisdom"
>>> # push the changes back to the Remarkable Cloud
... rmapi.update_metadata(doc)
... rmapy.update_metadata(doc)
True
>>> collection = rmapi.get_meta_items()
>>> collection = rmapy.get_meta_items()
>>> doc = [ d for d in docs if d.VissibleName == 'ModernC'][0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>> doc = [ d for d in docs if d.VissibleName == 'Modern C: The book of wisdom'][0]
>>> doc
<rmapi.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
<rmapy.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
>>> doc.to_dict()
{'ID': 'a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3', 'Version': 1, 'Message': '', 'Succes': True, 'BlobURLGet': '', 'BlobURLGetExpires': '0001-01-01T00:00:00Z', 'BlobURLPut': '', 'BlobURLPutExpires': '', 'ModifiedClient': '2019-09-18T20:12:07.206206Z', 'Type': 'DocumentType', 'VissibleName': 'Modern C: The book of wisdom', 'CurrentPage': 0, 'Bookmarked': False, 'Parent': ''}
@@ -114,7 +114,7 @@ CollectionType
A CollectionType is a Folder.
These types are represented by the object :class:`rmapi.folder.Folder`
These types are represented by the object :class:`rmapy.folder.Folder`
Working with folders is easy!
@@ -122,42 +122,42 @@ Working with folders is easy!
:linenos:
>>> from rmapi.api import Client
>>> rmapi = Client()
>>> rmapi.renew_token()
>>> from rmapy.api import Client
>>> rmapy = Client()
>>> rmapy.renew_token()
True
>>> collection = rmapi.get_meta_items()
>>> collection = rmapy.get_meta_items()
>>> collection
<rmapi.collections.Collection object at 0x7fc4718e1ed0>
>>> from rmapi.folder import Folder
<rmapy.collections.Collection object at 0x7fc4718e1ed0>
>>> from rmapy.folder import Folder
>>> # Get all the folders. Note that the fs of Remarkable is flat in the cloud
... folders = [ f for f in collection if isinstance(f, Folder) ]
>>> folders
[<rmapi.folder.Folder 028400f5-b258-4563-bf5d-9a47c314668c>, <rmapi.folder.Folder 06a36729-f91e-47da-b334-dc088c1e73d2>, ...]
[<rmapy.folder.Folder 028400f5-b258-4563-bf5d-9a47c314668c>, <rmapy.folder.Folder 06a36729-f91e-47da-b334-dc088c1e73d2>, ...]
>>> # Get the root folders
... root = [ f for f in folders if f.Parent == "" ]
>>> root
[<rmapi.folder.Folder 028400f5-b258-4563-bf5d-9a47c314668c>, <rmapi.folder.Folder 5005a085-d7ee-4867-8859-4cd90dee0d62>, ...]
[<rmapy.folder.Folder 028400f5-b258-4563-bf5d-9a47c314668c>, <rmapy.folder.Folder 5005a085-d7ee-4867-8859-4cd90dee0d62>, ...]
>>> # Create a new folder
... new_folder = Folder("New Folder")
>>> new_folder
<rmapi.folder.Folder 579df08d-7ee4-4f30-9994-887e6341cae3>
>>> rmapi.create_folder(new_folder)
<rmapy.folder.Folder 579df08d-7ee4-4f30-9994-887e6341cae3>
>>> rmapy.create_folder(new_folder)
True
>>> # verify
... [ f for f in rmapi.get_meta_items() if f.VissibleName == "New Folder" ]
[<rmapi.folder.Folder 579df08d-7ee4-4f30-9994-887e6341cae3>]
>>> [ f for f in rmapi.get_meta_items() if f.VissibleName == "New Folder" ][0].ID == new_folder.ID
... [ f for f in rmapy.get_meta_items() if f.VissibleName == "New Folder" ]
[<rmapy.folder.Folder 579df08d-7ee4-4f30-9994-887e6341cae3>]
>>> [ f for f in rmapy.get_meta_items() if f.VissibleName == "New Folder" ][0].ID == new_folder.ID
True
>>> # Move a document in a folder
... doc = rmapi.get_doc("a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3")
... doc = rmapy.get_doc("a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3")
>>> doc
<rmapi.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
<rmapy.document.Document a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3>
>>> doc.Parent = new_folder.ID
>>> # Submit the changes
... rmapi.update_metadata(doc)
... rmapy.update_metadata(doc)
True
>>> doc = rmapi.get_doc("a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3")
>>> doc = rmapy.get_doc("a969fcd6-64b0-4f71-b1ce-d9533ec4a2a3")
>>> doc.Parent == new_folder.ID
True
@@ -175,10 +175,13 @@ Here is the content of an archive retried on the tablet as example:
* 384326f5-133e-49c8-82ff-30aa19f3cfa40.pdf
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.pagedata
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.thumbnails/0.jpg
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.highlights/9b75d8df-1d06-4c59-8f3e-4cf69aa96cd9.json
As the .zip file from remarkable is simply a normal .zip file
containing specific file formats.
Highlights are stored in the `{uuid}.highlights/` folder.
You can find some help about the format at the following URL:
https://remarkablewiki.com/tech/filesystem
@@ -193,14 +196,14 @@ the remarkable file format:
:linenos:
>>> from rmapi.document import ZipDocument
>>> from rmapi.api import Client
>>> from rmapy.document import ZipDocument
>>> from rmapy.api import Client
>>> rm = Client()
>>> rm.renew_token()
True
>>> rawDocument = ZipDocument(doc="/home/svancampenhout/27-11-2019.pdf")
>>> rawDocument
<rmapi.document.ZipDocument b926ffc2-3600-460e-abfa-0fcf20b0bf99>
<rmapy.document.ZipDocument b926ffc2-3600-460e-abfa-0fcf20b0bf99>
>>> rawDocument.metadata["VissibleName"]
'27-11-2019'

View File

@@ -1,77 +1,77 @@
rmapi package
rmapy package
=============
Submodules
----------
rmapi.api module
rmapy.api module
----------------
.. automodule:: rmapi.api
.. automodule:: rmapy.api
:members:
:undoc-members:
:show-inheritance:
rmapi.collections module
rmapy.collections module
------------------------
.. automodule:: rmapi.collections
.. automodule:: rmapy.collections
:members:
:undoc-members:
:show-inheritance:
rmapi.config module
rmapy.config module
-------------------
.. automodule:: rmapi.config
.. automodule:: rmapy.config
:members:
:undoc-members:
:show-inheritance:
rmapi.const module
rmapy.const module
------------------
.. automodule:: rmapi.const
.. automodule:: rmapy.const
:members:
:undoc-members:
:show-inheritance:
rmapi.document module
rmapy.document module
---------------------
.. automodule:: rmapi.document
.. automodule:: rmapy.document
:members:
:undoc-members:
:show-inheritance:
rmapi.exceptions module
rmapy.exceptions module
-----------------------
.. automodule:: rmapi.exceptions
.. automodule:: rmapy.exceptions
:members:
:undoc-members:
:show-inheritance:
rmapi.folder module
rmapy.folder module
-------------------
.. automodule:: rmapi.folder
.. automodule:: rmapy.folder
:members:
:undoc-members:
:show-inheritance:
rmapi.meta module
rmapy.meta module
-----------------
.. automodule:: rmapi.meta
.. automodule:: rmapy.meta
:members:
:undoc-members:
:show-inheritance:
rmapi.types module
rmapy.types module
------------------
.. automodule:: rmapi.types
.. automodule:: rmapy.types
:members:
:undoc-members:
:show-inheritance:
@@ -80,7 +80,7 @@ rmapi.types module
Module contents
---------------
.. automodule:: rmapi
.. automodule:: rmapy
:members:
:undoc-members:
:show-inheritance:

View File

@@ -3,4 +3,5 @@ pyaml==19.4.1
sphinx==2.2.0
sphinx-autodoc-typehints==1.8.0
guzzle-sphinx-theme==0.7.11
urllib3>=1.26.5

View File

@@ -1,7 +0,0 @@
RFC3339Nano = "%Y-%m-%dT%H:%M:%SZ"
USER_AGENT = "rmapipy"
BASE_URL = "https://document-storage-production-dot-remarkable-production.appspot.com" # noqa
DEVICE_TOKEN_URL = "https://my.remarkable.com/token/json/2/device/new"
USER_TOKEN_URL = "https://my.remarkable.com/token/json/2/user/new"
DEVICE = "desktop-windows"

View File

@@ -1,7 +1,6 @@
import requests
from logging import getLogger
from datetime import datetime
import json
from typing import Union, Optional
from uuid import uuid4
from .collections import Collection
@@ -20,19 +19,15 @@ from .const import (RFC3339Nano,
USER_TOKEN_URL,
DEVICE,)
log = getLogger("rmapipy.rmapi")
log = getLogger("rmapy")
DocumentOrFolder = Union[Document, Folder]
class Client(object):
"""API Client for Remarkable Cloud
This allows you to authenticate & communiticate with the Remarkable Cloud
This allows you to authenticate & communicate with the Remarkable Cloud
and does all the heavy lifting for you.
Attributes:
token_set: the authentication tokens
"""
token_set = {
@@ -49,7 +44,7 @@ class Client(object):
def request(self, method: str, path: str,
data=None,
body=None, headers={},
body=None, headers=None,
params=None, stream=False) -> requests.Response:
"""Creates a request against the Remarkable Cloud API
@@ -63,12 +58,14 @@ class Client(object):
body: the body to request with. This will be converted to json.
headers: a dict of additional headers to add to the request.
params: Query params to append to the request.
steam: Should the response be a stream?
stream: Should the response be a stream?
Returns:
A Response instance containing most likely the response from
the server.
"""
if headers is None:
headers = {}
if not path.startswith("http"):
if not path.startswith('/'):
path = '/' + path
@@ -118,7 +115,7 @@ class Client(object):
"deviceID": uuid,
}
response = self.request("POST", DEVICE_TOKEN_URL, body)
response = self.request("POST", DEVICE_TOKEN_URL, body=body)
if response.ok:
self.token_set["devicetoken"] = response.text
dump(self.token_set)
@@ -185,13 +182,13 @@ class Client(object):
return collection
def get_doc(self, ID: str) -> Optional[DocumentOrFolder]:
def get_doc(self, _id: str) -> Optional[DocumentOrFolder]:
"""Get a meta item by ID
Fetch a meta item from the Remarkable Cloud by ID.
Args:
ID: The id of the meta item.
_id: The id of the meta item.
Returns:
A Document or Folder instance of the requested ID.
@@ -199,10 +196,10 @@ class Client(object):
DocumentNotFound: When a document cannot be found.
"""
log.debug(f"GETTING DOC {ID}")
log.debug(f"GETTING DOC {_id}")
response = self.request("GET", "/document-storage/json/2/docs",
params={
"doc": ID,
"doc": _id,
"withBlob": True
})
log.debug(response.url)
@@ -215,7 +212,7 @@ class Client(object):
elif data_response[0]["Type"] == "DocumentType":
return Document(**data_response[0])
else:
raise DocumentNotFound(f"Cound not find document {ID}")
raise DocumentNotFound(f"Could not find document {_id}")
return None
def download(self, document: Document) -> ZipDocument:
@@ -251,7 +248,7 @@ class Client(object):
Args:
doc: A Document or folder to delete.
Raises:
ApiError: an error occured while uploading the document.
ApiError: an error occurred while uploading the document.
"""
response = self.request("PUT", "/document-storage/json/2/delete",
@@ -260,28 +257,29 @@ class Client(object):
"Version": doc.Version
}])
return self.check_reponse(response)
return self.check_response(response)
def upload(self, zipDoc: ZipDocument, to: Folder = Folder(ID="")):
def upload(self, zip_doc: ZipDocument, to: Folder = Folder(ID="")):
"""Upload a document to the cloud.
Add a new document to the Remarkable Cloud.
Args:
zipDoc: A ZipDocument instance containing the data of a Document.
zip_doc: A ZipDocument instance containing the data of a Document.
to: the parent of the document. (Default root)
Raises:
ApiError: an error occured while uploading the document.
ApiError: an error occurred while uploading the document.
"""
BlobURLPut = self._upload_request(zipDoc)
zipDoc.dump(zipDoc.zipfile)
response = self.request("PUT", BlobURLPut, data=zipDoc.zipfile.read())
blob_url_put = self._upload_request(zip_doc)
zip_doc.dump(zip_doc.zipfile)
response = self.request("PUT", blob_url_put, data=zip_doc.zipfile.read())
# Reset seek
zipDoc.zipfile.seek(0)
zip_doc.zipfile.seek(0)
if response.ok:
doc = Document(**zipDoc.metadata)
doc.ID = zipDoc.ID
doc = Document(**zip_doc.metadata)
doc.ID = zip_doc.ID
doc.Parent = to.ID
return self.update_metadata(doc)
else:
@@ -305,7 +303,7 @@ class Client(object):
"/document-storage/json/2/upload/update-status",
body=[req])
return self.check_reponse(res)
return self.check_response(res)
def get_current_version(self, docorfolder: DocumentOrFolder) -> int:
"""Get the latest version info from a Document or Folder
@@ -319,7 +317,7 @@ class Client(object):
the version information.
Raises:
DocumentNotFound: cannot find the requested Document or Folder.
ApiError: An error occured while processing the request.
ApiError: An error occurred while processing the request.
"""
try:
@@ -330,8 +328,8 @@ class Client(object):
return 0
return int(d.Version)
def _upload_request(self, zdoc: ZipDocument) -> dict:
zipFile, req = zdoc.create_request()
def _upload_request(self, zip_doc: ZipDocument) -> str:
zip_file, req = zip_doc.create_request()
res = self.request("PUT", "/document-storage/json/2/upload/request",
body=[req])
if not res.ok:
@@ -363,7 +361,7 @@ class Client(object):
True if the folder is created.
"""
zipFolder, req = folder.create_request()
zip_folder, req = folder.create_request()
res = self.request("PUT", "/document-storage/json/2/upload/request",
body=[req])
if not res.ok:
@@ -374,7 +372,7 @@ class Client(object):
if len(response) > 0:
dest = response[0].get("BlobURLPut", None)
if dest:
res = self.request("PUT", dest, data=zipFolder.read())
res = self.request("PUT", dest, data=zip_folder.read())
else:
raise ApiError(
"Cannot create a folder. because BlobURLPut is not set",
@@ -383,7 +381,8 @@ class Client(object):
self.update_metadata(folder)
return True
def check_reponse(self, response: requests.Response):
@staticmethod
def check_response(response: requests.Response):
"""Check the response from an API Call
Does some sanity checking on the Response
@@ -417,4 +416,3 @@ class Client(object):
raise ApiError(
f"Got An invalid HTTP Response: {response.status_code}",
response=response)
return True

View File

@@ -9,71 +9,71 @@ DocumentOrFolder = Union[Document, Folder]
class Collection(object):
"""A collection of meta items
This is basicly the content of the Remarkable Cloud.
This is basically the content of the Remarkable Cloud.
Attributes:
items: A list containing the items.
"""
items: List[DocumentOrFolder] = []
def __init__(self, *items: List[DocumentOrFolder]):
self.items: List[DocumentOrFolder] = []
for i in items:
self.items.append(i)
def add(self, docdict: dict) -> None:
def add(self, doc_dict: dict) -> None:
"""Add an item to the collection.
It wraps it in the correct class based on the Type parameter of the
dict.
Args:
docdict: A dict representing a document or folder.
doc_dict: A dict representing a document or folder.
"""
if docdict.get("Type", None) == "DocumentType":
self.add_document(docdict)
elif docdict.get("Type", None) == "CollectionType":
self.add_folder(docdict)
if doc_dict.get("Type", None) == "DocumentType":
self.add_document(doc_dict)
elif doc_dict.get("Type", None) == "CollectionType":
self.add_folder(doc_dict)
else:
raise TypeError("Unsupported type: {_type}"
.format(_type=docdict.get("Type", None)))
.format(_type=doc_dict.get("Type", None)))
def add_document(self, docdict: dict) -> None:
def add_document(self, doc_dict: dict) -> None:
"""Add a document to the collection
Args:
docdict: A dict respresenting a document.
doc_dict: A dict representing a document.
"""
self.items.append(Document(**docdict))
self.items.append(Document(**doc_dict))
def add_folder(self, dirdict: dict) -> None:
def add_folder(self, dir_dict: dict) -> None:
"""Add a document to the collection
Args:
dirdict: A dict respresenting a folder.
dir_dict: A dict representing a folder.
"""
self.items.append(Folder(**dirdict))
self.items.append(Folder(**dir_dict))
def parent(self, docorfolder: DocumentOrFolder) -> Folder:
def parent(self, doc_or_folder: DocumentOrFolder) -> Folder:
"""Returns the paren of a Document or Folder
Args:
docorfolder: A document or folder to get the parent from
doc_or_folder: A document or folder to get the parent from
Returns:
The parent folder.
"""
results = [i for i in self.items if i.ID == docorfolder.ID]
results = [i for i in self.items if i.ID == doc_or_folder.ID]
if len(results) > 0 and isinstance(results[0], Folder):
return results[0]
else:
raise FolderNotFound("Could not found the parent of the document.")
def children(self, folder: Folder = None) -> List[DocumentOrFolder]:
"""Get all the childern from a folder
"""Get all the children from a folder
Args:
folder: A folder where to get the children from. If None, this will

View File

@@ -6,7 +6,7 @@ from typing import Dict
def load() -> dict:
"""Load the .rmapi config file"""
"""Load the .rmapy config file"""
config_file_path = Path.joinpath(Path.home(), ".rmapi")
config: Dict[str, str] = {}
@@ -18,7 +18,7 @@ def load() -> dict:
def dump(config: dict) -> None:
"""Dump config to the .rmapi config file
"""Dump config to the .rmapy config file
Args:
config: A dict containing data to dump to the .rmapi

7
rmapy/const.py Normal file
View File

@@ -0,0 +1,7 @@
RFC3339Nano = "%Y-%m-%dT%H:%M:%SZ"
USER_AGENT = "rmapy"
BASE_URL = "https://document-storage-production-dot-remarkable-production.appspot.com" # noqa
DEVICE_TOKEN_URL = "https://webapp-production-dot-remarkable-production.appspot.com/token/json/2/device/new"
USER_TOKEN_URL = "https://webapp-production-dot-remarkable-production.appspot.com/token/json/2/user/new"
DEVICE = "desktop-windows"
SERVICE_MGR_URL = "https://service-manager-production-dot-remarkable-production.appspot.com" # noqa

View File

@@ -4,12 +4,13 @@ from zipfile import ZipFile, ZIP_DEFLATED
import shutil
from uuid import uuid4
import json
from typing import NoReturn, TypeVar, List, Tuple
from typing import TypeVar, List, Tuple
from logging import getLogger
from requests import Response
from .meta import Meta
log = getLogger("rmapy")
BytesOrString = TypeVar("BytesOrString", BytesIO, str)
BytesOrNone = TypeVar("BytesOrNone", BytesIO, None)
class RmPage(object):
@@ -18,7 +19,7 @@ class RmPage(object):
Contains the metadata, the page itself & thumbnail.
"""
def __init__(self, page, metadata=None, order=0, thumbnail=None, ID=None):
def __init__(self, page, metadata=None, order=0, thumbnail=None, _id=None):
self.page = page
if metadata:
self.metadata = metadata
@@ -28,19 +29,46 @@ class RmPage(object):
self.order = order
if thumbnail:
self.thumbnail = thumbnail
if ID:
self.ID = ID
if _id:
self.ID = _id
else:
self.ID = str(uuid4())
def __str__(self) -> str:
"""String representation of this object"""
return f"<rmapi.document.RmPage {self.order} for {self.ID}>"
return f"<rmapy.document.RmPage {self.order} for {self.ID}>"
def __repr__(self) -> str:
"""String representation of this object"""
return self.__str__()
class Highlight(object):
""" Highlight represents all highlights on a page created using the highligher pen
in EPUB documents.
Functionality introduced in Remarkable 2.7 software.
Contains the page_id where the highlights are located and the highlights
metadata for the page from the Remarkable Cloud.
Corresponds to single .json file in the .highlights/ folder.
Attributes:
page_id: The ID of the page where the highlight is located.
highlight_data: A dictionary containing all highlight data.
"""
def __init__(self, page_id: str, highlight_data: str):
self.page_id = page_id
self.highlight_data = json.loads(highlight_data)
def __str__(self) -> str:
"""String representation of this object"""
return f"<rmapy.document.Highlight {self.page_id}>"
def __repr__(self) -> str:
"""String representation of this object"""
return self.__str__()
class Document(Meta):
""" Document represents a real object expected in most
@@ -51,7 +79,7 @@ class Document(Meta):
Attributes:
ID: Id of the meta object.
Version: The version of this object.
Success: If the last API Call was a succes.
Success: If the last API Call was a success.
BlobURLGet: The url to get the data blob from. Can be empty.
BlobURLGetExpires: The expiration date of the Get url.
BlobURLPut: The url to upload the data blob to. Can be empty.
@@ -73,7 +101,7 @@ class Document(Meta):
def __str__(self):
"""String representation of this object"""
return f"<rmapi.document.Document {self.ID}>"
return f"<rmapy.document.Document {self.ID}>"
def __repr__(self):
"""String representation of this object"""
@@ -89,6 +117,7 @@ class ZipDocument(object):
* 384326f5-133e-49c8-82ff-30aa19f3cfa40.rm
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.pagedata
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.thumbnails/0.jpg
* 384327f5-133e-49c8-82ff-30aa19f3cfa40.highlights/9b75d8df-1d06-4c59-8f3e-4cf69aa96cd9.json
As the .zip file from remarkable is simply a normal .zip file
containing specific file formats, this package is a helper to
@@ -105,91 +134,94 @@ class ZipDocument(object):
Attributes:
content: Sane defaults for the .content file in the zip.
metadata: parameters describing this blob.
highlights: list of contents of the .highlights folder
pagedata: the content of the .pagedata file.
zipfile: The raw zipfile in memory.
pdf: the raw pdf file if there is one.
epub: the raw epub file if there is one.
rm: A list of :class:rmapi.document.RmPage in this zip.
rm: A list of :class:rmapy.document.RmPage in this zip.
"""
# {"extraMetadata": {},
# "fileType": "pdf",
# "pageCount": 0,
# "lastOpenedPage": 0,
# "lineHeight": -1,
# "margins": 180,
# "textScale": 1,
# "transform": {}}
content = {
"extraMetadata": {
# "LastBrushColor": "Black",
# "LastBrushThicknessScale": "2",
# "LastColor": "Black",
# "LastEraserThicknessScale": "2",
# "LastEraserTool": "Eraser",
# "LastPen": "Ballpoint",
# "LastPenColor": "Black",
# "LastPenThicknessScale": "2",
# "LastPencil": "SharpPencil",
# "LastPencilColor": "Black",
# "LastPencilThicknessScale": "2",
# "LastTool": "SharpPencil",
# "ThicknessScale": "2"
},
# "FileType": "",
# "FontName": "",
"lastOpenedPage": 0,
"lineHeight": -1,
"margins": 180,
# "Orientation": "portrait",
"pageCount": 0,
# "Pages": [],
"textScale": 1,
"transform": {
# "M11": 1,
# "M12": 0,
# "M13": 0,
# "M21": 0,
# "M22": 1,
# "M23": 0,
# "M31": 0,
# "M32": 0,
# "M33": 1,
}
}
metadata = {
"deleted": False,
"lastModified": "1568368808000",
"metadatamodified": False,
"modified": False,
"parent": "",
"pinned": False,
"synced": True,
"type": "DocumentType",
"version": 1,
"VissibleName": "New Document"
}
pagedata = "b''"
zipfile = BytesIO()
pdf = None
epub = None
rm: List[RmPage] = []
ID = None
def __init__(self, ID=None, doc=None, file=None):
def __init__(self, _id=None, doc=None, file=None):
"""Create a new instance of a ZipDocument
Args:
ID: Can be left empty to generate one
_id: Can be left empty to generate one
doc: a raw pdf, epub or rm (.lines) file.
file: a zipfile to convert from
"""
if not ID:
ID = str(uuid4())
self.ID = ID
# {"extraMetadata": {},
# "fileType": "pdf",
# "pageCount": 0,
# "lastOpenedPage": 0,
# "lineHeight": -1,
# "margins": 180,
# "textScale": 1,
# "transform": {}}
self.content = {
"extraMetadata": {
# "LastBrushColor": "Black",
# "LastBrushThicknessScale": "2",
# "LastColor": "Black",
# "LastEraserThicknessScale": "2",
# "LastEraserTool": "Eraser",
# "LastPen": "Ballpoint",
# "LastPenColor": "Black",
# "LastPenThicknessScale": "2",
# "LastPencil": "SharpPencil",
# "LastPencilColor": "Black",
# "LastPencilThicknessScale": "2",
# "LastTool": "SharpPencil",
# "ThicknessScale": "2"
},
# "FileType": "",
# "FontName": "",
"lastOpenedPage": 0,
"lineHeight": -1,
"margins": 180,
# "Orientation": "portrait",
"pageCount": 0,
# "Pages": [],
"textScale": 1,
"transform": {
# "M11": 1,
# "M12": 0,
# "M13": 0,
# "M21": 0,
# "M22": 1,
# "M23": 0,
# "M31": 0,
# "M32": 0,
# "M33": 1,
}
}
self.metadata = {
"deleted": False,
"lastModified": "1568368808000",
"metadatamodified": False,
"modified": False,
"parent": "",
"pinned": False,
"synced": True,
"type": "DocumentType",
"version": 1,
"VissibleName": "New Document"
}
self.pagedata = "b''"
self.zipfile = BytesIO()
self.pdf = None
self.epub = None
self.rm: List[RmPage] = []
self.ID = None
self.highlights: List[Highlight] = []
if not _id:
_id = str(uuid4())
self.ID = _id
if doc:
ext = doc[-4:]
if ext.endswith("pdf"):
@@ -207,7 +239,7 @@ class ZipDocument(object):
elif ext.endswith("rm"):
self.content["fileType"] = "notebook"
with open(doc, 'rb') as fb:
self.rm.append(RmPage(page=BytesIO(doc.read())))
self.rm.append(RmPage(page=BytesIO(fb.read())))
name = os.path.splitext(os.path.basename(doc))[0]
self.metadata["VissibleName"] = name
@@ -216,7 +248,7 @@ class ZipDocument(object):
def __str__(self) -> str:
"""string representation of this class"""
return f"<rmapi.document.ZipDocument {self.ID}>"
return f"<rmapy.document.ZipDocument {self.ID}>"
def __repr__(self) -> str:
"""string representation of this class"""
@@ -252,6 +284,10 @@ class ZipDocument(object):
zf.writestr(f"{self.ID}.epub",
self.epub.read())
for highlight in self.highlights:
zf.writestr(f"{self.ID}.highlights/{highlight.page_id}.json",
json.dumps(highlight.highlight_data))
for page in self.rm:
zf.writestr(f"{self.ID}/{page.order}.rm",
@@ -260,8 +296,12 @@ class ZipDocument(object):
zf.writestr(f"{self.ID}/{page.order}-metadata.json",
json.dumps(page.metadata))
page.page.seek(0)
zf.writestr(f"{self.ID}.thumbnails/{page.order}.jpg",
page.thumbnail.read())
try:
zf.writestr(f"{self.ID}.thumbnails/{page.order}.jpg",
page.thumbnail.read())
except AttributeError:
log.debug(f"missing thumbnail during dump: {self.ID}: {page.order}")
pass
if isinstance(file, BytesIO):
file.seek(0)
@@ -311,54 +351,69 @@ class ZipDocument(object):
except KeyError:
pass
# Get the RM pages
# Get Highlights
highlights = [x for x in zf.namelist()
if x.startswith(f"{self.ID}.highlights/") and x.endswith('.json')]
for highlight in highlights:
with zf.open(highlight, 'r') as highlight_fp:
page_id = highlight.replace(f"{self.ID}.highlights/", "").replace(".json", "")
self.highlights.append(Highlight(page_id, highlight_fp.read()))
# Get the RM pages
pages = [x for x in zf.namelist()
if x.startswith(f"{self.ID}/") and x.endswith('.rm')]
for p in pages:
pagenumber = int(p.replace(f"{self.ID}/", "")
page_number = int(p.replace(f"{self.ID}/", "")
.replace(".rm", ""))
page = BytesIO()
thumbnail = BytesIO()
with zf.open(p, 'r') as rm:
page = BytesIO(rm.read())
page.seek(0)
with zf.open(p.replace(".rm", "-metadata.json"), 'r') as md:
metadata = json.load(md)
p_meta = p.replace(".rm", "-metadata.json")
try:
with zf.open(p_meta, 'r') as md:
metadata = json.load(md)
except KeyError:
log.debug(f"missing metadata: {p_meta}")
metadata = None
thumbnail_name = p.replace(".rm", ".jpg")
thumbnail_name = thumbnail_name.replace("/", ".thumbnails/")
with zf.open(thumbnail_name, 'r') as tn:
thumbnail = BytesIO(tn.read())
thumbnail.seek(0)
try:
with zf.open(thumbnail_name, 'r') as tn:
thumbnail = BytesIO(tn.read())
thumbnail.seek(0)
except KeyError:
log.debug(f"missing thumbnail: {thumbnail_name}")
thumbnail = None
self.rm.append(RmPage(page, metadata, pagenumber, thumbnail,
self.rm.append(RmPage(page, metadata, page_number, thumbnail,
self.ID))
self.zipfile.seek(0)
def from_zip(ID: str, file: str) -> ZipDocument:
def from_zip(_id: str, file: str) -> ZipDocument:
"""Return A ZipDocument from a zipfile.
Create a ZipDocument instance from a zipfile.
Args:
ID: The object ID this zipfile represents.
_id: The object ID this zipfile represents.
file: the filename of the zipfile.
Returns:
An instance of the supplied zipfile.
"""
return ZipDocument(ID, file=file)
return ZipDocument(_id, file=file)
def from_request_stream(ID: str, stream: Response) -> ZipDocument:
def from_request_stream(_id: str, stream: Response) -> ZipDocument:
"""Return a ZipDocument from a request stream containing a zipfile.
This is used with the BlobGETUrl from a :class:`rmapi.document.Document`.
This is used with the BlobGETUrl from a :class:`rmapy.document.Document`.
Args:
ID: The object ID this zipfile represents.
_id: The object ID this zipfile represents.
stream: a stream containing the zipfile.
Returns:
the object of the downloaded zipfile.
@@ -367,6 +422,6 @@ def from_request_stream(ID: str, stream: Response) -> ZipDocument:
tmp = BytesIO()
for chunk in stream.iter_content(chunk_size=8192):
tmp.write(chunk)
zd = ZipDocument(ID=ID)
zd = ZipDocument(_id=_id)
zd.load(tmp)
return zd

View File

@@ -27,4 +27,3 @@ class ApiError(Exception):
def __init__(self, msg, response=None):
self.response = response
super(ApiError, self).__init__(msg)

View File

@@ -6,20 +6,21 @@ from zipfile import ZipFile, ZIP_DEFLATED
from .const import RFC3339Nano
from typing import Tuple, Optional
class ZipFolder(object):
"""A dummy zipfile to create a folder
This is needed to create a folder on the Remarkable Cloud
"""
def __init__(self, ID: str):
def __init__(self, _id: str):
"""Creates a zipfile in memory
Args:
ID: the ID to create a zipFolder for
_id: the ID to create a zipFolder for
"""
super(ZipFolder, self).__init__()
self.ID = ID
self.ID = _id
self.file = BytesIO()
self.Version = 1
with ZipFile(self.file, 'w', ZIP_DEFLATED) as zf:
@@ -37,7 +38,7 @@ class Folder(Meta):
Args:
name: An optional name for this folder. In the end, a name is
really needed, but can be ommitted to set a later time.
really needed, but can be omitted to set a later time.
"""
super(Folder, self).__init__(**kwargs)
@@ -48,9 +49,9 @@ class Folder(Meta):
self.ID = str(uuid4())
def create_request(self) -> Tuple[BytesIO, dict]:
"""Prepares the nessesary parameters to create this folder.
"""Prepares the necessary parameters to create this folder.
This creates a ZipFolder & the nessesary json body to
This creates a ZipFolder & the necessary json body to
create an upload request.
"""
@@ -61,9 +62,9 @@ class Folder(Meta):
}
def update_request(self) -> dict:
"""Perpares the nessesary parameters to update a folder.
"""Prepares the necessary parameters to update a folder.
This sets some parameters in the datastructure to submit to the API.
This sets some parameters in the data structure to submit to the API.
"""
data = self.to_dict()
@@ -72,10 +73,7 @@ class Folder(Meta):
return data
def __str__(self):
return f"<rmapi.folder.Folder {self.ID}>"
return f"<rmapy.folder.Folder {self.ID}>"
def __repr__(self):
return self.__str__()

View File

@@ -7,7 +7,7 @@ class Meta(object):
Attributes:
ID: Id of the meta object.
Version: The version of this object.
Success: If the last API Call was a succes.
Success: If the last API Call was a success.
BlobURLGet: The url to get the data blob from. Can be empty.
BlobURLGetExpires: The expiration date of the Get url.
BlobURLPut: The url to upload the data blob to. Can be empty.
@@ -26,7 +26,7 @@ class Meta(object):
ID = ""
Version = 0
Message = ""
Succes = True
Success = True
BlobURLGet = ""
BlobURLGetExpires = ""
BlobURLPut = ""
@@ -39,8 +39,8 @@ class Meta(object):
Parent = ""
def __init__(self, **kwargs):
kkeys = self.to_dict().keys()
for k in kkeys:
k_keys = self.to_dict().keys()
for k in k_keys:
setattr(self, k, kwargs.get(k, getattr(self, k)))
def to_dict(self) -> dict:
@@ -56,7 +56,7 @@ class Meta(object):
"ID": self.ID,
"Version": self.Version,
"Message": self.Message,
"Succes": self.Succes,
"Success": self.Success,
"BlobURLGet": self.BlobURLGet,
"BlobURLGetExpires": self.BlobURLGetExpires,
"BlobURLPut": self.BlobURLPut,

View File

@@ -22,14 +22,14 @@ setup(
# package, this name will be registered for you. It will determine how
# users can install this project, e.g.:
#
# $ pip install sampleproject
# $ pip install sampleprojectk
#
# And where it will live on PyPI: https://pypi.org/project/sampleproject/
#
# There are some restrictions on what makes a valid project name
# specification here:
# https://packaging.python.org/specifications/core-metadata/#name
name='rmapi', # Required
name='rmapy', # Required
# Versions should comply with PEP 440:
# https://www.python.org/dev/peps/pep-0440/
@@ -37,7 +37,7 @@ setup(
# For a discussion on single-sourcing the version across setup.py and the
# project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version='0.2.0', # Required
version='0.3.0', # Required
# This is a one-line description or tagline of what your project does. This
# corresponds to the "Summary" metadata field:
@@ -70,7 +70,7 @@ setup(
#
# This field corresponds to the "Home-Page" metadata field:
# https://packaging.python.org/specifications/core-metadata/#home-page-optional
url='https://github.com/subutux/rmapi', # Optional
url='https://github.com/subutux/rmapy', # Optional
# This should be your name or the name of the organization which owns the
# project.
@@ -88,8 +88,8 @@ setup(
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 3 - Alpha',
#'Development Status :: 4 - Beta',
#'Development Status :: 3 - Alpha',
'Development Status :: 4 - Beta',
#'Development Status :: 5 - Production/Stable'
#'Development Status :: 6 - Mature'
# Indicate who your project is intended for
@@ -110,7 +110,7 @@ setup(
# project page. What does your project relate to?
#
# Note that this is a string of words separated by whitespace, not a list.
keywords='remarkable rmapi cloud paper tablet', # Optional
keywords='remarkable rmapy cloud paper tablet', # Optional
# You can just specify package directories manually here if your project is
# simple. Or you can use find_packages().
@@ -180,7 +180,7 @@ setup(
#
# For example, the following would provide a command called `sample` which
# executes the function `main` from this package when invoked:
# TODO
# TODO Create a command line tool
# entry_points={ # Optional
# 'console_scripts': [
# 'sample=sample:main',
@@ -197,9 +197,9 @@ setup(
# maintainers, and where to support the project financially. The key is
# what's used to render the link text on PyPI.
project_urls={ # Optional
'Bug Reports': 'https://github.com/subutux/rmapi/issues',
'Bug Reports': 'https://github.com/subutux/rmapy/issues',
'Funding': 'https://donate.pypi.org',
'Say Thanks!': 'https://www.paypal.me/subutux',
'Source': 'https://github.com/subutux/rmapi/',
'Source': 'https://github.com/subutux/rmapy/',
},
)