function move_document_to_trash
Moves a reMarkable document to trash by updating its metadata parent field to 'trash', then propagating the changes through the document schema hierarchy and updating the root hash.
/tf/active/vicechatdev/e-ink-llm/cloudtest/apply_working_trash_move.py
23 - 226
complex
Purpose
This function implements the complete workflow for moving a document to trash in the reMarkable cloud sync system (v3 API). It performs a multi-step process: fetches the current document schema, extracts and updates the metadata to set parent to 'trash', uploads the modified metadata, creates and uploads a new document schema with the updated metadata hash, updates the root.docSchema to reference the new document hash, and finally updates the roothash to commit all changes. This is designed to work with the reMarkable EU cloud endpoint.
Source Code
def move_document_to_trash(session, user_token, uuid, current_hash, doc_name):
"""Move a single document to trash using our proven method"""
print(f"\nšļø Moving: {doc_name}")
print(f" UUID: {uuid}")
print(f" Current hash: {current_hash[:16]}...")
try:
# Step 1: Get current document schema
doc_response = session.get(f"https://eu.tectonic.remarkable.com/sync/v3/files/{current_hash}")
doc_response.raise_for_status()
doc_content = doc_response.text
print(f" š DocSchema fetched: {len(doc_content)} bytes")
# Parse components
lines = doc_content.strip().split('\n')
metadata_hash = None
metadata_size = None
for line in lines[1:]: # Skip version line
if ':' in line and f'{uuid}.metadata' in line:
parts = line.split(':')
metadata_hash = parts[0]
metadata_size = parts[4]
break
if not metadata_hash:
print(f" ā Metadata component not found")
return False
print(f" š Metadata hash: {metadata_hash[:16]}...")
# Step 2: Get current metadata
metadata_response = session.get(f"https://eu.tectonic.remarkable.com/sync/v3/files/{metadata_hash}")
metadata_response.raise_for_status()
metadata = json.loads(metadata_response.text)
current_parent = metadata.get('parent', '')
print(f" š Current parent: '{current_parent}'")
if current_parent == 'trash':
print(f" ā ļø Already in trash, skipping")
return True
# Step 3: Update metadata - set parent to "trash"
metadata['parent'] = 'trash'
updated_metadata_json = json.dumps(metadata, separators=(',', ':'))
# Upload updated metadata
metadata_crc = calculate_crc32c(updated_metadata_json)
metadata_headers = {
'Authorization': f'Bearer {user_token}',
'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
'rm-filename': f'{uuid}.metadata',
'rm-crc32c': metadata_crc,
'Content-Type': 'application/octet-stream'
}
print(f" š¤ PUT #1: Uploading updated metadata...")
print(f" URL: https://eu.tectonic.remarkable.com/sync/v3/files")
print(f" Filename: {uuid}.metadata")
print(f" CRC32C: {metadata_crc}")
print(f" Size: {len(updated_metadata_json)} bytes")
metadata_upload = session.put(
'https://eu.tectonic.remarkable.com/sync/v3/files',
headers=metadata_headers,
data=updated_metadata_json
)
print(f" Response: {metadata_upload.status_code}")
if metadata_upload.status_code != 201:
print(f" Error body: {metadata_upload.text}")
print(f" ā FAILED at PUT #1: Metadata upload ({metadata_upload.status_code})")
raise Exception(f"Metadata upload failed: {metadata_upload.status_code}")
metadata_upload.raise_for_status()
new_metadata_hash = metadata_upload.text.strip()
print(f" ā
Updated metadata: {new_metadata_hash[:16]}...")
# Step 4: Build new document schema with updated metadata hash
new_doc_lines = [lines[0]] # Keep version
for line in lines[1:]:
if f'{uuid}.metadata' in line:
# Replace metadata hash and size
parts = line.split(':')
parts[0] = new_metadata_hash
parts[4] = str(len(updated_metadata_json))
new_doc_lines.append(':'.join(parts))
else:
new_doc_lines.append(line)
new_doc_content = '\n'.join(new_doc_lines)
# Upload new document schema
doc_crc = calculate_crc32c(new_doc_content)
doc_headers = {
'Authorization': f'Bearer {user_token}',
'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
'rm-filename': f'{uuid}.docSchema',
'rm-crc32c': doc_crc,
'Content-Type': 'application/octet-stream'
}
print(f" š¤ PUT #2: Uploading new document schema...")
print(f" URL: https://eu.tectonic.remarkable.com/sync/v3/files")
print(f" Filename: {uuid}.docSchema")
print(f" CRC32C: {doc_crc}")
print(f" Size: {len(new_doc_content)} bytes")
doc_upload = session.put(
'https://eu.tectonic.remarkable.com/sync/v3/files',
headers=doc_headers,
data=new_doc_content
)
print(f" Response: {doc_upload.status_code}")
if doc_upload.status_code != 201:
print(f" Error body: {doc_upload.text}")
print(f" ā FAILED at PUT #2: Document schema upload ({doc_upload.status_code})")
raise Exception(f"Document schema upload failed: {doc_upload.status_code}")
doc_upload.raise_for_status()
new_doc_hash = doc_upload.text.strip()
print(f" ā
New document schema: {new_doc_hash[:16]}...")
# Step 5: Update root.docSchema
print(f" š Updating root.docSchema...")
# Get current root
root_response = session.get('https://eu.tectonic.remarkable.com/sync/v3/files/root.docSchema')
root_response.raise_for_status()
root_content = root_response.text
# Replace old hash with new hash
updated_root_content = root_content.replace(current_hash, new_doc_hash)
# Upload updated root
root_crc = calculate_crc32c(updated_root_content)
root_headers = {
'Authorization': f'Bearer {user_token}',
'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
'rm-filename': 'root.docSchema',
'rm-crc32c': root_crc,
'Content-Type': 'application/octet-stream'
}
print(f" š¤ PUT #3: Uploading updated root.docSchema...")
print(f" URL: https://eu.tectonic.remarkable.com/sync/v3/files")
print(f" Filename: root.docSchema")
print(f" CRC32C: {root_crc}")
print(f" Size: {len(updated_root_content)} bytes")
root_upload = session.put(
'https://eu.tectonic.remarkable.com/sync/v3/files',
headers=root_headers,
data=updated_root_content
)
print(f" Response: {root_upload.status_code}")
if root_upload.status_code != 201:
print(f" Error body: {root_upload.text}")
print(f" ā FAILED at PUT #3: Root.docSchema upload ({root_upload.status_code})")
raise Exception(f"Root upload failed: {root_upload.status_code}")
root_upload.raise_for_status()
new_root_hash = root_upload.text.strip()
print(f" ā
Root updated: {new_root_hash[:16]}...")
# Step 6: Update roothash
print(f" š¤ PUT #4: Updating roothash...")
print(f" URL: https://eu.tectonic.remarkable.com/sync/v3/roothash")
print(f" Data: {new_root_hash}")
roothash_response = session.put(
'https://eu.tectonic.remarkable.com/sync/v3/roothash',
headers={
'Authorization': f'Bearer {user_token}',
'User-Agent': 'reMarkable-desktop-win/3.11.1.1951',
'Content-Type': 'text/plain'
},
data=new_root_hash
)
print(f" Response: {roothash_response.status_code}")
if roothash_response.status_code != 200:
print(f" Error body: {roothash_response.text}")
print(f" ā FAILED at PUT #4: Roothash update ({roothash_response.status_code})")
raise Exception(f"Roothash update failed: {roothash_response.status_code}")
roothash_response.raise_for_status()
print(f" ā
Roothash updated")
print(f" šļø SUCCESS: '{doc_name}' moved to trash!")
return True
except Exception as e:
print(f" ā Error: {e}")
return False
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
session |
- | - | positional_or_keyword |
user_token |
- | - | positional_or_keyword |
uuid |
- | - | positional_or_keyword |
current_hash |
- | - | positional_or_keyword |
doc_name |
- | - | positional_or_keyword |
Parameter Details
session: A requests.Session object (or compatible) with authentication configured for making HTTP requests to the reMarkable API. Should maintain cookies and connection pooling.
user_token: String containing the Bearer authentication token for the reMarkable API. Used in Authorization headers for all API requests.
uuid: String containing the unique identifier (UUID) of the document to be moved to trash. Used to identify metadata and docSchema files.
current_hash: String containing the current hash/identifier of the document's docSchema in the reMarkable system. Used to locate the document in the root schema and replace it with the updated version.
doc_name: String containing the human-readable name of the document. Used only for logging and display purposes to show which document is being processed.
Return Value
Returns a boolean value: True if the document was successfully moved to trash (or was already in trash), False if any error occurred during the process. The function handles exceptions internally and returns False on failure.
Dependencies
requestsjsonhashlibbase64binascii
Required Imports
import json
import hashlib
import base64
import binascii
import requests
Usage Example
import requests
import json
# Assume calculate_crc32c is defined elsewhere
def calculate_crc32c(data):
# Implementation needed
pass
# Setup authenticated session
session = requests.Session()
user_token = 'your_remarkable_auth_token_here'
# Document details
doc_uuid = 'abc123-def456-ghi789'
current_doc_hash = 'hash_of_current_document_schema'
doc_name = 'My Document'
# Move document to trash
success = move_document_to_trash(
session=session,
user_token=user_token,
uuid=doc_uuid,
current_hash=current_doc_hash,
doc_name=doc_name
)
if success:
print(f'Successfully moved {doc_name} to trash')
else:
print(f'Failed to move {doc_name} to trash')
Best Practices
- Ensure the calculate_crc32c function is properly implemented and available before calling this function
- Use a properly authenticated requests.Session object with valid bearer token
- Handle the boolean return value to determine if the operation succeeded
- Be aware this function makes 7 HTTP requests (3 GETs, 4 PUTs) and may take several seconds to complete
- The function includes extensive logging via print statements - consider redirecting stdout if running in production
- This function is specific to the EU endpoint (eu.tectonic.remarkable.com) - modify URLs for other regions
- The function checks if document is already in trash and returns True without making changes
- All API calls use User-Agent 'reMarkable-desktop-win/3.11.1.1951' to mimic official client
- Errors are caught and logged but not re-raised - check return value for success/failure
- The function modifies the root.docSchema which affects the entire document tree - ensure proper error handling in calling code
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function move_documents_to_trash 94.6% similar
-
class DocumentToTrashMover 90.9% similar
-
function simple_move_to_trash 84.7% similar
-
function apply_working_trash_move 83.1% similar
-
function main_v45 76.7% similar