Creling / obsidian-image-uploader

MIT License
36 stars 6 forks source link

Feature Suggestion: Bulk Upload of Local Images #9

Open xaya1001 opened 1 year ago

xaya1001 commented 1 year ago

Hello,

I'm a user of your Image Uploader plugin for Obsidian, and I've found it to be a very useful tool. However, I found myself needing to upload a large number of local images (over 2000) to my self-hosted image server.

With some assistance from GPT-4, I've written some code to add this bulk image upload functionality to the plugin. It allowed me to successfully upload all my local images to my server, and I thought that this feature might be beneficial to other users of your plugin who are in a similar situation.

Here is the code that I've written:

  async uploadLocalImages(): Promise<void> {
    // Get the current active MarkdownView
    const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
    if (!markdownView) return;
    // Get Editor
    const editor = markdownView.editor;
    // Get all the text
    const lines = editor.getValue().split("\n");

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i];
      const matches = line.match(/!\[\[.*?\]\]/gm);

      if (!matches) continue;

      for (let match of matches) {
        try {
          await this.uploadImage(match, i, line, editor, markdownView);
          await new Promise(resolve => setTimeout(resolve, 300));  // Wait for 0.3 second before the next upload
        } catch (error) {
          console.error("Stopping function due to error: ", error);
          return;
        }
      }
    }
  }

  async uploadImage(match: string, i: number, line: string, editor: any, markdownView: any): Promise<void> {

    let imagePath = match.substring(match.lastIndexOf("[[") + 2, match.lastIndexOf("]]"));
    console.log('Uploading: ', imagePath);

    // Read image file
    const data = await this.app.vault.adapter.readBinary(imagePath);
    // Convert data to Blob
    const blob = new Blob([data], { type: 'image/png' });
    // Convert Blob to File
    const file = new File([blob], imagePath.split("/").pop(), { type: 'image/png' });

    // Use your pasteHandler function to upload the image
    const clipboardData = new DataTransfer();
    clipboardData.items.add(file);

    const fakeEvent = new ClipboardEvent("paste", { clipboardData: clipboardData });

    // Remove original link
    const newLine = line.replace(match, "");
    editor.replaceRange(newLine, { line: i, ch: 0 }, { line: i, ch: line.length + 1 });

    // Set the cursor to the start of the line
    editor.setCursor({ line: i, ch: 0 });

    await this.pasteHandler(fakeEvent, editor, markdownView);
  }

  async onload(): Promise<void> {
    console.log("loading Image Uploader");
    await this.loadSettings();
    // this.setupPasteHandler()
    this.addSettingTab(new ImageUploaderSettingTab(this.app, this));

    this.pasteFunction = this.pasteHandler.bind(this);

    this.registerEvent(
      this.app.workspace.on('editor-paste', this.pasteFunction)
    );

    this.addCommand({
      id: 'upload-local-images',
      name: 'Upload Local Images of This Page',
      callback: this.uploadLocalImages.bind(this),
    });
  }

Please feel free to use this code as you see fit. If you find it useful and believe it could be beneficial for other users (especially new users), consider incorporating it into the plugin as a new feature. I am not a programmer, and although I have tested the code and it works for my use case, it may not be perfect. As such, I'm providing it here for your reference rather than submitting a pull request.

xaya1001 commented 1 year ago

by the way, I'm using lsky-pro as self-hosted image server. https://github.com/lsky-org/lsky-pro

here are my settings:

api endpoint:https://img.domain.com/api/v1/upload
upload header: 
{
  "Authorization": "Bearer xxxx",
  "Accept": "application/json",
  "Content-Type": "multipart/form-data"
}
upload body: 
{
  "file": "$FILE"
}
Image Url Path: data.links.url
africa1207 commented 1 year ago

Yes, I also have the need to upload local images. Although the "Image Auto Upload Plugin" can achieve this functionality, it requires the installation of an additional tool called PicGo. It would be perfect if the ability to batch upload local existing images could be added without the need for PicGo.

Creling commented 1 year ago

@xaya1001 Good jobs. I implement this feature by referencing your code. A new version will be released soon. Please refer https://github.com/Creling/obsidian-image-uploader/commit/5cd9743b9e7d8a94b0f990b114402b8a6e159244 for more details.

Creling commented 1 year ago

@africa1207 Yes, I also don't want to install extra tools, which is why I developed this plugin.