Created
February 12, 2025 21:04
-
-
Save andrewgross/09f08c4e3679950efe05f2ccdb607531 to your computer and use it in GitHub Desktop.
Remarkable API Upload
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import base64 | |
import json | |
import requests | |
from pathlib import Path | |
from typing import Union, Optional | |
class RemarkableError(Exception): | |
pass | |
def upload_to_remarkable( | |
file_path: Union[str, Path, bytes], | |
filename: str, | |
device_token: str, | |
user_token: str, | |
content_type: str = "application/pdf" | |
) -> dict: | |
""" | |
Upload a document to Remarkable Cloud. | |
Args: | |
file_path: Path to file or bytes content | |
filename: Name to use for the document on Remarkable | |
device_token: Remarkable device token | |
user_token: Remarkable user token | |
content_type: Content type (default: PDF) | |
Returns: | |
dict: Response from the upload API | |
Raises: | |
RemarkableError: If upload fails | |
""" | |
# Validate tokens | |
if not (device_token and user_token): | |
raise RemarkableError("Both device_token and user_token are required") | |
if not (device_token.startswith('ey') and user_token.startswith('ey')): | |
raise RemarkableError("Invalid token format") | |
# Extract tectonic from user token | |
try: | |
token_parts = user_token.split('.') | |
payload = json.loads(base64.b64decode(token_parts[1] + "=" * (-len(token_parts[1]) % 4))) | |
tectonic = payload.get('tectonic') | |
except Exception as e: | |
raise RemarkableError(f"Failed to parse user token: {str(e)}") | |
# Determine base URL | |
base_url = (f"https://{tectonic}.tectonic.remarkable.com" | |
if tectonic | |
else "https://internal.cloud.remarkable.com") | |
# Prepare file data | |
if isinstance(file_path, (str, Path)): | |
with open(file_path, 'rb') as f: | |
file_data = f.read() | |
else: | |
file_data = file_path | |
# Prepare upload request | |
upload_url = f"{base_url}/doc/v2/files" | |
headers = { | |
'Authorization': f'Bearer {user_token}', | |
'Content-Type': content_type, | |
'rM-Meta': base64.b64encode( | |
json.dumps({"file_name": filename}).encode() | |
).decode() | |
} | |
# Upload with retry | |
for _ in range(2): # Retry once on auth error | |
try: | |
response = requests.post( | |
upload_url, | |
headers=headers, | |
data=file_data | |
) | |
if response.status_code == 401: # Unauthorized | |
# In a real implementation, you'd refresh the token here | |
continue | |
response.raise_for_status() | |
return response.json() | |
except requests.exceptions.RequestException as e: | |
if _ == 1: # Last retry | |
raise RemarkableError(f"Upload failed: {str(e)}") | |
raise RemarkableError("Upload failed after retries") | |
# Example usage: | |
if __name__ == "__main__": | |
try: | |
result = upload_to_remarkable( | |
"document.pdf", | |
"My Document", | |
device_token="eyJ...", # Your device token | |
user_token="eyJ..." # Your user token | |
) | |
print(f"Upload successful: {result}") | |
except RemarkableError as e: | |
print(f"Upload failed: {str(e)}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment