Skip to content

feat: Add comprehensive batch upload and context menu enhancements#48

Open
LazyZane wants to merge 2 commits into
jvsteiner:mainfrom
LazyZane:main
Open

feat: Add comprehensive batch upload and context menu enhancements#48
LazyZane wants to merge 2 commits into
jvsteiner:mainfrom
LazyZane:main

Conversation

@LazyZane

Copy link
Copy Markdown

This commit introduces several major features and bug fixes to improve the plugin's functionality:

New Features

1. Batch Upload Capabilities

  • Add "Batch upload all local images in vault" command
  • Add "Batch upload local images in current folder" command
  • Add right-click context menu on folders to batch upload images
  • Add "Upload all local images in this file" in editor context menu
  • Implement deduplication using file hash to skip already uploaded files
  • Generate detailed batch upload logs in markdown format

2. Enhanced Context Menu

  • Add "Upload to cloud" option for local images
  • Add "Download to local" option for remote S3 images
  • Add "Delete from cloud" option for S3 images (with confirmation)
  • Add "Delete local file" option for local images (with confirmation)
  • Add "Rename description" option to edit image alt text

3. Upload Failure Handling

  • Implement fallback to local save when S3 upload fails
  • Use hash-based filenames for unique naming
  • Respect Obsidian's attachment folder settings

4. New Settings

  • Fallback to local on upload failure (default: enabled)
  • Delete local file after successful upload (default: disabled)
  • Enable batch upload logs (default: enabled)
  • Batch log folder configuration (default: .s3-logs)

Bug Fixes

1. Fix Auto-Upload Trigger on Download

  • Prevent downloaded files from triggering auto-upload
  • Add skipAutoUploadPaths tracking to exclude internal operations
  • Apply same logic to fallback local saves

2. Fix Batch Upload Path Variables

  • Implement variable replacement in batch upload (${year}, ${month}, ${day}, ${basename})
  • Extract note basename for proper path generation
  • Ensure consistency with single file upload behavior

3. Fix Download Path Handling

  • Pass current note path to getAttachmentFolder for proper relative path resolution
  • Support "./" prefix for same-folder and relative subfolder attachments

Technical Improvements

  • Add TypeScript interfaces: BatchTask, LogEntry, MediaLink
  • Import additional Obsidian types: TFolder, Menu, Modal
  • Import AWS SDK commands: DeleteObjectCommand, HeadObjectCommand
  • Add helper methods: parseMediaLink, s3ObjectExists, deleteS3Object, extractS3KeyFromUrl
  • Add modal components: BatchConfirmModal, RenameAltModal, DeleteConfirmModal
  • Implement comprehensive error handling with user-friendly notices

LazyZane and others added 2 commits January 25, 2026 21:54
This commit introduces several major features and bug fixes to improve the plugin's functionality:

## New Features

### 1. Batch Upload Capabilities
- Add "Batch upload all local images in vault" command
- Add "Batch upload local images in current folder" command
- Add right-click context menu on folders to batch upload images
- Add "Upload all local images in this file" in editor context menu
- Implement deduplication using file hash to skip already uploaded files
- Generate detailed batch upload logs in markdown format

### 2. Enhanced Context Menu
- Add "Upload to cloud" option for local images
- Add "Download to local" option for remote S3 images
- Add "Delete from cloud" option for S3 images (with confirmation)
- Add "Delete local file" option for local images (with confirmation)
- Add "Rename description" option to edit image alt text

### 3. Upload Failure Handling
- Implement fallback to local save when S3 upload fails
- Use hash-based filenames for unique naming
- Respect Obsidian's attachment folder settings

### 4. New Settings
- Fallback to local on upload failure (default: enabled)
- Delete local file after successful upload (default: disabled)
- Enable batch upload logs (default: enabled)
- Batch log folder configuration (default: .s3-logs)

## Bug Fixes

### 1. Fix Auto-Upload Trigger on Download
- Prevent downloaded files from triggering auto-upload
- Add skipAutoUploadPaths tracking to exclude internal operations
- Apply same logic to fallback local saves

### 2. Fix Batch Upload Path Variables
- Implement variable replacement in batch upload (${year}, ${month}, ${day}, ${basename})
- Extract note basename for proper path generation
- Ensure consistency with single file upload behavior

### 3. Fix Download Path Handling
- Pass current note path to getAttachmentFolder for proper relative path resolution
- Support "./" prefix for same-folder and relative subfolder attachments

## Technical Improvements
- Add TypeScript interfaces: BatchTask, LogEntry, MediaLink
- Import additional Obsidian types: TFolder, Menu, Modal
- Import AWS SDK commands: DeleteObjectCommand, HeadObjectCommand
- Add helper methods: parseMediaLink, s3ObjectExists, deleteS3Object, extractS3KeyFromUrl
- Add modal components: BatchConfirmModal, RenameAltModal, DeleteConfirmModal
- Implement comprehensive error handling with user-friendly notices

Co-Authored-By: Claude <noreply@anthropic.com>
- Add i18n framework with $() translation function
- Create English (en.ts) and Simplified Chinese (zh-cn.ts) locale files
- Translate all UI text: settings, commands, context menus, notices, modals
- Add URL encoding for Chinese characters in S3 path segments

Co-Authored-By: Claude <noreply@anthropic.com>
@jvsteiner

Copy link
Copy Markdown
Owner

hey, thanks for stopping, using the plugin, and contributing. could you explain what is the use case and motivation for this feature? also the solution - thanks

@LazyZane

Copy link
Copy Markdown
Author

Thanks for your feedback! Here are the primary use cases and motivations for this PR:

  1. Efficient Migration of Legacy Images:
    Many users (myself included) have a large number of local image attachments accumulated over time. I wanted a seamless way to migrate these local assets to S3 to keep the vault lightweight and synchronized across devices. The "Batch Upload" and "Upload all in current file" features are designed to handle this bulk transition effortlessly.

  2. Full Lifecycle Management via Context Menu:
    The current workflow mainly focuses on the initial upload. By adding context menu options (Upload, Delete, Download), I aim to provide a complete management loop directly within the editor:

Upload/Batch Upload: For converting existing local attachments to S3 links.
Delete: To clean up both cloud and local storage without leaving Obsidian.
Download: This is crucial when I occasionally need to edit a remote image using a local editor; I can quickly bring it back, modify it, and re-upload.
3. Robustness & Deduplication:
To avoid messy storage, I implemented file hashing to prevent duplicate uploads and added a local fallback mechanism to ensure no data is lost if the S3 service is temporarily unavailable.
"I've also added i18n support for Chinese localization to help more users in the community."

@jvsteiner

Copy link
Copy Markdown
Owner

Thanks for the contribution — the use cases make sense and the code is generally well-structured. Here's my review:

Scope

This PR bundles 5-6 distinct features (batch upload, context menus, local fallback, i18n, new settings, bug fixes). Splitting into smaller PRs would make review and potential reverts much easier. Consider starting with bug fixes and local fallback, then building up to batch upload, context menus, and i18n separately.

Key Concerns

Data Safety

  • deleteS3Object: there's no check that the S3 delete succeeded before removing the link from the editor. If the delete fails after link removal, the reference is lost.
  • updateLinkInNote uses content.split(oldLink).join(newLink) — this replaces all instances of a wikilink in a file, even if only one was intended for the batch task.

Race Condition in skipAutoUploadPaths

The 1s setTimeout to remove paths from the skip set is fragile. If Obsidian's create event fires after 1s (slow disk, large file), the downloaded/fallback file will be re-uploaded. An event-driven approach would be more reliable.

extractS3KeyFromUrl

This doesn't verify the URL belongs to the user's configured bucket — it extracts a "key" from any URL. This means "Delete from cloud" could appear on non-S3 images and fail or worse, delete the wrong object if the user has multiple buckets at the same endpoint.

URL Encoding Change

The encodeURIComponent change for S3 keys affects all uploads, not just batch. This is a behavioral change for existing users — URLs will now be percent-encoded, which could break if their CDN or proxy doesn't decode them. This should be called out separately and considered carefully.

Context Menu Cursor Assumption

The context menu handler re-reads the cursor line and re-parses the link at click time, but the cursor position may have shifted between the right-click and menu item click. The parsed mediaLink from the initial right-click should be captured in the closure instead.

i18n

  • Wrapping technical terms like "AWS Access Key ID" and "AWS Secret Key" with $() is unnecessary — these are proper nouns that shouldn't be translated.
  • The $ function name shadows a common global (jQuery, DOM). Something like t() or i18n() would be less ambiguous.

Minor

  • Duplicated file-scanning regex between scanLocalImages and uploadAllImagesInFile — could be extracted.
  • error.message used without type narrowing in multiple places.

What's Good

  • TypeScript interfaces (BatchTask, LogEntry, MediaLink) are well-defined
  • Hash-based deduplication in batch upload is solid
  • Confirmation modals for destructive actions
  • Batch logging is a nice touch for auditing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants