šŸ” Code Extractor

function move_document_to_trash

Maturity: 46

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.

File:
/tf/active/vicechatdev/e-ink-llm/cloudtest/apply_working_trash_move.py
Lines:
23 - 226
Complexity:
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

  • requests
  • json
  • hashlib
  • base64
  • binascii

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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function move_documents_to_trash 94.6% similar

    Moves specified reMarkable Cloud documents to trash by updating their metadata parent field to 'trash' and propagating changes through the document hierarchy.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/move_remaining_to_trash_fixed.py
  • class DocumentToTrashMover 90.9% similar

    A class that moves reMarkable documents to the trash by updating their metadata parent field to 'trash' and synchronizing changes through the reMarkable cloud API.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/move_documents_to_trash.py
  • function simple_move_to_trash 84.7% similar

    Moves all documents from the reMarkable tablet's root directory to trash by uploading an empty root.docSchema file and updating the roothash.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/simple_clean_root.py
  • function apply_working_trash_move 83.1% similar

    Moves a hardcoded list of reMarkable cloud documents to trash by authenticating with the reMarkable API and applying trash operations to each document.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/apply_working_trash_move.py
  • function main_v45 76.7% similar

    Command-line interface function that moves a single reMarkable document to trash by accepting a document UUID as a command-line argument.

    From: /tf/active/vicechatdev/e-ink-llm/cloudtest/move_documents_to_trash.py
← Back to Browse