From 39df843c531441af989a4b5db69277855f7e87b9 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sat, 8 Jun 2024 07:23:27 -0400 Subject: [PATCH 01/52] baseline for vault binary file backup --- package.json | 7 ++++--- src/main.ts | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index cd78e54..4600b94 100755 --- a/package.json +++ b/package.json @@ -14,12 +14,13 @@ "eslint": "^7.30.0", "obsidian": "obsidianmd/obsidian-api", "obsidian-plugin-cli": "^0.4.5", - "typescript": "^4.1.5" + "typescript": "^4.1.5", + "retypeapp": "^1.11.0" }, "dependencies": { "axios": "^0.21.1", + "cloudinary": "^2.2.0", "compressorjs": "^1.0.7", - "object-path": "^0.11.5", - "retypeapp": "^1.11.0" + "object-path": "^0.11.5" } } diff --git a/src/main.ts b/src/main.ts index 58cf8e8..783fbca 100755 --- a/src/main.ts +++ b/src/main.ts @@ -3,18 +3,59 @@ import { Notice, Plugin, Editor, + FileSystemAdapter } from "obsidian"; // For API requests import axios from "axios" import objectPath from 'object-path' - +import { v2 as cloudinary } from 'cloudinary'; // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; + private setCommands(){ + this.addCommand({ + id:"backup-files-cloudinary", + name:"Backup media files to Cloudinary", + callback: () =>{ + this.uploadVault(); + } + }); + } + private async uploadVault(){ + let content; + const files = this.app.vault.getFiles() + console.log('this is a file ' +files[0]); + cloudinary.config({ + cloud_name: this.settings.cloudName + }); + for(let i = 0; i < 50; i++){ + if(files[i].extension != 'md'){ + let path; + //content = this.app.vault.readBinary(files[i]).then(res =>{ + //return res; + let adapter = this.app.vault.adapter; + if(adapter instanceof FileSystemAdapter){ + path = adapter.getFullPath(files[i].path) + console.log(path); + } + cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset); + } + /*const printContent = () =>{ + content.then((a)=>{ + const buffer = Buffer.from(a); + const stream = Readable.from(buffer); + return stream; + + }); + };*/ + //printContent(); + // Comment + } + } private clearHandlers() { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); @@ -154,6 +195,7 @@ export default class CloudinaryUploader extends Plugin { this.clearHandlers(); this.setupHandlers(); this.addSettingTab(new CloudinaryUploaderSettingTab(this.app, this)); + this.uploadVault(); } // Plugin shutdown steps From 5e1d0542f0290df022131da1bcbf7662977b1a13 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sat, 8 Jun 2024 09:40:05 -0400 Subject: [PATCH 02/52] add settings for media backup --- src/commands.ts | 29 +++++++++++++++++++++++++++++ src/main.ts | 29 +++++++++-------------------- src/settings-tab.ts | 41 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 21 deletions(-) create mode 100644 src/commands.ts diff --git a/src/commands.ts b/src/commands.ts new file mode 100644 index 0000000..54756ba --- /dev/null +++ b/src/commands.ts @@ -0,0 +1,29 @@ +import { + Plugin, + FileSystemAdapter +} from "obsidian"; +import { CloudinarySettings } from "./settings-tab"; +import { v2 as cloudinary } from 'cloudinary'; + +export + +export default class CloudinaryCommands extends Plugin { + settings:CloudinarySettings; + let uploadVault = () => { + const files = this.app.vault.getFiles() + console.log('this is a file ' + files[0]); + for (let i = 0; i < 50; i++) { + if (files[i].extension != 'md') { + let path; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + path = adapter.getFullPath(files[i].path) + console.log(path); + } + cloudinary.uploader.unsigned_upload(path, this.settings.uploadPreset, { + folder: this.settings.folder + }); + } + } + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 783fbca..782f970 100755 --- a/src/main.ts +++ b/src/main.ts @@ -25,35 +25,21 @@ export default class CloudinaryUploader extends Plugin { } }); } - private async uploadVault(){ - let content; + private uploadVault(){ const files = this.app.vault.getFiles() console.log('this is a file ' +files[0]); - cloudinary.config({ - cloud_name: this.settings.cloudName - }); for(let i = 0; i < 50; i++){ if(files[i].extension != 'md'){ let path; - //content = this.app.vault.readBinary(files[i]).then(res =>{ - //return res; - let adapter = this.app.vault.adapter; + const adapter = this.app.vault.adapter; if(adapter instanceof FileSystemAdapter){ path = adapter.getFullPath(files[i].path) console.log(path); } - cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset); - } - /*const printContent = () =>{ - content.then((a)=>{ - const buffer = Buffer.from(a); - const stream = Readable.from(buffer); - return stream; - + cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset,{ + folder: this.settings.folder }); - };*/ - //printContent(); - // Comment + } } } private clearHandlers() { @@ -62,6 +48,9 @@ export default class CloudinaryUploader extends Plugin { } private setupHandlers() { + cloudinary.config({ + cloud_name: this.settings.cloudName + }); if (this.settings.clipboardUpload) { this.registerEvent(this.app.workspace.on('editor-paste', this.pasteHandler)); } else { @@ -195,7 +184,7 @@ export default class CloudinaryUploader extends Plugin { this.clearHandlers(); this.setupHandlers(); this.addSettingTab(new CloudinaryUploaderSettingTab(this.app, this)); - this.uploadVault(); + this.setCommands(); } // Plugin shutdown steps diff --git a/src/settings-tab.ts b/src/settings-tab.ts index ddeba62..dfc3a56 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -26,6 +26,8 @@ export interface CloudinarySettings { audioSubfolder: string; videoSubfolder: string; rawSubfolder: string; + preserveBackupFilePath: boolean; + backupFolder: string; } export const DEFAULT_SETTINGS: CloudinarySettings = { cloudName: "", @@ -42,7 +44,9 @@ export const DEFAULT_SETTINGS: CloudinarySettings = { imageSubfolder: "", audioSubfolder: "", videoSubfolder: "", - rawSubfolder: "" + rawSubfolder: "", + preserveBackupFilePath: false, + backupFolder: "" }; export default class CloudinaryUploaderSettingTab extends PluginSettingTab { @@ -330,5 +334,40 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) }); + containerEl.createEl("h4", { text: "Local File Backup" }); + textFragment = document.createDocumentFragment(); + textFragment.append("If you run the command to create a backup of vault local assets, these settings apply"); + containerEl.createEl("p", { text: textFragment }); + + new Setting(containerEl) + .setName("Backup folder") + .setDesc("Root folder where backups are stored. If not specified and you run a backup, root is specified as the root of your Cloudinary media library") + .addText((text) => { + text + .setPlaceholder("backups") + .setValue(this.plugin.settings.backupFolder) + .onChange(async (value) => { + this.plugin.settings.backupFolder = value; + await this.plugin.saveSettings(); + }) + }); + + new Setting(containerEl) + .setName("Preserve File Paths") + .setDesc("Preserve file path reslative to root backup folder") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.preserveBackupFilePath) + .onChange(async (value) => { + try { + this.plugin.settings.preserveBackupFilePath = value; + await this.plugin.saveSettings(); + } + catch (e) { + console.log(e) + } + }) + }); + } } \ No newline at end of file From 8fb432163ccff0dc445d480c3d12b2a5ab51279b Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sat, 8 Jun 2024 09:40:29 -0400 Subject: [PATCH 03/52] command file not needed --- src/commands.ts | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 src/commands.ts diff --git a/src/commands.ts b/src/commands.ts deleted file mode 100644 index 54756ba..0000000 --- a/src/commands.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - Plugin, - FileSystemAdapter -} from "obsidian"; -import { CloudinarySettings } from "./settings-tab"; -import { v2 as cloudinary } from 'cloudinary'; - -export - -export default class CloudinaryCommands extends Plugin { - settings:CloudinarySettings; - let uploadVault = () => { - const files = this.app.vault.getFiles() - console.log('this is a file ' + files[0]); - for (let i = 0; i < 50; i++) { - if (files[i].extension != 'md') { - let path; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - path = adapter.getFullPath(files[i].path) - console.log(path); - } - cloudinary.uploader.unsigned_upload(path, this.settings.uploadPreset, { - folder: this.settings.folder - }); - } - } - } -} \ No newline at end of file From a4c3df31ffe11207940b9223729b4ebccedbe91a Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sat, 8 Jun 2024 09:46:18 -0400 Subject: [PATCH 04/52] add messaging --- src/main.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main.ts b/src/main.ts index 782f970..7769f08 100755 --- a/src/main.ts +++ b/src/main.ts @@ -38,6 +38,12 @@ export default class CloudinaryUploader extends Plugin { } cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset,{ folder: this.settings.folder + }).then(res =>{ + new Notice("Backup of local media files completed. ",0) + },err =>{ + new Notice("There was something wrong with your upload. Please try again. ",0) + }) + }); } } From dc1ecc487c2269ea134d84467d3ecfb16a240889 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 08:21:29 -0400 Subject: [PATCH 05/52] add backup functionality --- src/main.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main.ts b/src/main.ts index 7769f08..b041c42 100755 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { // For API requests import axios from "axios" import objectPath from 'object-path' +import path from 'path' import { v2 as cloudinary } from 'cloudinary'; // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' @@ -28,35 +29,34 @@ export default class CloudinaryUploader extends Plugin { private uploadVault(){ const files = this.app.vault.getFiles() console.log('this is a file ' +files[0]); - for(let i = 0; i < 50; i++){ + for(let i = 0; i < 5; i++){ if(files[i].extension != 'md'){ - let path; + let filePath; const adapter = this.app.vault.adapter; if(adapter instanceof FileSystemAdapter){ - path = adapter.getFullPath(files[i].path) - console.log(path); - } - cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset,{ - folder: this.settings.folder + filePath = adapter.getFullPath(files[i].path) + //console.log(files[i].path); + console.log('path '+ path.join(this.settings.backupFolder,path.dirname(files[i].path))); + cloudinary.uploader.unsigned_upload(filePath,this.settings.uploadPreset,{ + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(files[i].path)) : this.settings.backupFolder, + resource_type: 'auto' }).then(res =>{ - new Notice("Backup of local media files completed. ",0) + console.log(res); },err =>{ - new Notice("There was something wrong with your upload. Please try again. ",0) + new Notice("There was something wrong with your upload. Please try again. "+err.message,0); }) + } - }); + } } + new Notice("Backup of local media files completed. Be mindful of any error messages displayed as notices.",0); } - } private clearHandlers() { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); } private setupHandlers() { - cloudinary.config({ - cloud_name: this.settings.cloudName - }); if (this.settings.clipboardUpload) { this.registerEvent(this.app.workspace.on('editor-paste', this.pasteHandler)); } else { @@ -190,6 +190,9 @@ export default class CloudinaryUploader extends Plugin { this.clearHandlers(); this.setupHandlers(); this.addSettingTab(new CloudinaryUploaderSettingTab(this.app, this)); + cloudinary.config({ + cloud_name: this.settings.cloudName + }); this.setCommands(); } From a4b8663a1eb782dc5531d97fee6b5d2810d16118 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 08:43:53 -0400 Subject: [PATCH 06/52] update settings pane --- src/settings-tab.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/settings-tab.ts b/src/settings-tab.ts index dfc3a56..4b51104 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -354,7 +354,7 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { new Setting(containerEl) .setName("Preserve File Paths") - .setDesc("Preserve file path reslative to root backup folder") + .setDesc("Preserve vault file path relative to root backup folder. If disabled, assets will be placed in 'root', whether the above backup folder or root of Cloudinary Media library") .addToggle((toggle) => { toggle .setValue(this.plugin.settings.preserveBackupFilePath) @@ -368,6 +368,15 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { } }) }); + containerEl.createEl("h5", { text: "File names, file conflicts, overwrite behaviour" }); + link = document.createElement("a"); + link.text="plugin documentation "; + link.href="https://google.ca"; + textFragment = document.createDocumentFragment(); + textFragment.append("Assuming all defaults in your Cloudinary Upload Preset settings, all file backups will receive a unique public ID (file name) within the Cloudinary console."+ + " This may make it hard to identify. Additionally, file uploads will always be overwritten. You can use a combination of settings for unique file naming as found in "); + textFragment.append(link); + containerEl.createEl("p", { text: textFragment }); } } \ No newline at end of file From c820f35eb251212f34e4e26104a775202dfb8915 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 08:45:32 -0400 Subject: [PATCH 07/52] improve error messaging --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index b041c42..8c539ab 100755 --- a/src/main.ts +++ b/src/main.ts @@ -43,7 +43,7 @@ export default class CloudinaryUploader extends Plugin { }).then(res =>{ console.log(res); },err =>{ - new Notice("There was something wrong with your upload. Please try again. "+err.message,0); + new Notice("There was something wrong with your upload. Please try again. "+files[i].name+'. '+err.message,0); }) } From 6901f5091afd9abc8360fee55017611ac14058b3 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 10:46:50 -0400 Subject: [PATCH 08/52] improve error messaging --- src/main.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8c539ab..7f96d95 100755 --- a/src/main.ts +++ b/src/main.ts @@ -29,21 +29,22 @@ export default class CloudinaryUploader extends Plugin { private uploadVault(){ const files = this.app.vault.getFiles() console.log('this is a file ' +files[0]); - for(let i = 0; i < 5; i++){ - if(files[i].extension != 'md'){ + for(let file of files){ + if(file.extension != 'md'){ let filePath; const adapter = this.app.vault.adapter; if(adapter instanceof FileSystemAdapter){ - filePath = adapter.getFullPath(files[i].path) - //console.log(files[i].path); - console.log('path '+ path.join(this.settings.backupFolder,path.dirname(files[i].path))); + filePath = adapter.getFullPath(file.path) + console.log('path '+ path.join(this.settings.backupFolder,path.dirname(file.path))); + cloudinary.uploader.unsigned_upload(filePath,this.settings.uploadPreset,{ - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(files[i].path)) : this.settings.backupFolder, + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(file.path)) : this.settings.backupFolder, resource_type: 'auto' }).then(res =>{ console.log(res); },err =>{ - new Notice("There was something wrong with your upload. Please try again. "+files[i].name+'. '+err.message,0); + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. "+file.name+'. '+err.message,0); }) } From 1d518f63bd162de97e570df0b9477fd896dcfc89 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:13:16 -0400 Subject: [PATCH 09/52] create modal --- src/main.ts | 59 +++++++++++++++++++++++++++++++++------------------- src/modal.ts | 34 ++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 src/modal.ts diff --git a/src/main.ts b/src/main.ts index 7f96d95..4880be4 100755 --- a/src/main.ts +++ b/src/main.ts @@ -14,44 +14,61 @@ import { v2 as cloudinary } from 'cloudinary'; // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; +import { WarningModal } from "./modal"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; - private setCommands(){ + private setCommands() { this.addCommand({ - id:"backup-files-cloudinary", - name:"Backup media files to Cloudinary", - callback: () =>{ + id: "backup-files-cloudinary", + name: "Backup media files to Cloudinary", + callback: () => { this.uploadVault(); } }); + this.addCommand({ + id: "note-files-to-cloudinary", + name: "Upload files in current note to Cloudinary", + callback: () => { + this.uploadNote(); + } + }) + } + + private uploadNote() { + new WarningModal(this.app, (result) => { + if (result == 'true') { + return; + } else { + return; + } + }).open(); } - private uploadVault(){ + + private uploadVault() { const files = this.app.vault.getFiles() - console.log('this is a file ' +files[0]); - for(let file of files){ - if(file.extension != 'md'){ + console.log('this is a file ' + files[0]); + for (let file of files) { + if (file.extension != 'md') { let filePath; - const adapter = this.app.vault.adapter; - if(adapter instanceof FileSystemAdapter){ - filePath = adapter.getFullPath(file.path) - console.log('path '+ path.join(this.settings.backupFolder,path.dirname(file.path))); - - cloudinary.uploader.unsigned_upload(filePath,this.settings.uploadPreset,{ - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(file.path)) : this.settings.backupFolder, + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(file.path) + cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, resource_type: 'auto' - }).then(res =>{ + }).then(res => { console.log(res); - },err =>{ + }, err => { console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. "+file.name+'. '+err.message,0); + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); }) } - } - } - new Notice("Backup of local media files completed. Be mindful of any error messages displayed as notices.",0); + } } + new Notice("Backup of local media files completed. Be mindful of any error messages displayed as notices.", 0); + } private clearHandlers() { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); diff --git a/src/modal.ts b/src/modal.ts new file mode 100644 index 0000000..9ac496c --- /dev/null +++ b/src/modal.ts @@ -0,0 +1,34 @@ +import { App, Modal,Setting } from "obsidian"; + +export class WarningModal extends Modal { + result: string; + onSubmit: (result: string) => void; + constructor(app: App, onSubmit: (result:string)=> void) { + super(app); + this.onSubmit = onSubmit; + } + + onOpen() { + let { contentEl } = this; + contentEl.createEl("h1", { text: "Warning" }); + let textFragment = document.createDocumentFragment(); + textFragment.append("This is a potentially dangerous action."+ + " It is HIGHLY recommended that you backup this note elsewhere before performing this operation."+ + " The media files in this note will attempt to be uploaded to Cloudinary"); + contentEl.createEl("p", { text: textFragment }); + new Setting(contentEl) + .addButton((btn) => + btn.setButtonText("Continue action") + .setCta() + .onClick(()=>{ + this.close(); + this.result = 'true'; + this.onSubmit(this.result) + })) + } + + onClose() { + let { contentEl } = this; + contentEl.empty(); + } +} \ No newline at end of file From 9e585eddd709cf13f83aa2ba1eab5b38425ec0f4 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 14:13:33 -0400 Subject: [PATCH 10/52] in-note search for content and upload --- src/main.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main.ts b/src/main.ts index 4880be4..be964c8 100755 --- a/src/main.ts +++ b/src/main.ts @@ -38,6 +38,33 @@ export default class CloudinaryUploader extends Plugin { private uploadNote() { new WarningModal(this.app, (result) => { if (result == 'true') { + let file = this.app.workspace.getActiveFile(); + this.app.vault.process(file, (data) => { + console.log('data '+data); + const found = data.match(/(?:\[\[(?!https?:\/\/).*?\]\])/g); + console.log('found '+found); + for (let find of found) { + let fileString = find.substring(2, find.length-2); + console.log('file string '+fileString ); + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(fileString) + console.log('filepath ' +filePath); + console.log('path ' + path.join(this.settings.backupFolder, path.dirname(file.path))); + cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, + resource_type: 'auto' + }).then(res => { + console.log(res); + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) + } + } + return data; + }) return; } else { return; From ed99a4b92e630fe01ad6043acc05e5d3312de860 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:12:55 -0400 Subject: [PATCH 11/52] replace text in note when upload occurred --- src/main.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main.ts b/src/main.ts index be964c8..d2af3ff 100755 --- a/src/main.ts +++ b/src/main.ts @@ -36,13 +36,15 @@ export default class CloudinaryUploader extends Plugin { } private uploadNote() { + let finalData new WarningModal(this.app, (result) => { if (result == 'true') { + let uploads = []; let file = this.app.workspace.getActiveFile(); - this.app.vault.process(file, (data) => { - console.log('data '+data); + let data = this.app.vault.cachedRead(file).then((result)=>{ + data = result; + }).then(()=>{ const found = data.match(/(?:\[\[(?!https?:\/\/).*?\]\])/g); - console.log('found '+found); for (let find of found) { let fileString = find.substring(2, find.length-2); console.log('file string '+fileString ); @@ -57,14 +59,21 @@ export default class CloudinaryUploader extends Plugin { resource_type: 'auto' }).then(res => { console.log(res); + let url = objectPath.get(res, 'secure_url'); + let replaceMarkdownText = `![](${url})`; + data = data.replace(find,replaceMarkdownText); + this.app.vault.process(file,(oldData)=>{ + console.log('this is data '+data); + return data; + }) }, err => { console.log(JSON.stringify(err)) new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); }) } } - return data; - }) + }); + return; } else { return; From bf410918d087c43d4a256a58d32cd8d5e9b82950 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:37:51 -0400 Subject: [PATCH 12/52] refactor --- src/main.ts | 118 ++++++++++++++++++++++------------------------------ 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/src/main.ts b/src/main.ts index d2af3ff..2ca3bc4 100755 --- a/src/main.ts +++ b/src/main.ts @@ -36,75 +36,15 @@ export default class CloudinaryUploader extends Plugin { } private uploadNote() { - let finalData new WarningModal(this.app, (result) => { if (result == 'true') { - let uploads = []; - let file = this.app.workspace.getActiveFile(); - let data = this.app.vault.cachedRead(file).then((result)=>{ - data = result; - }).then(()=>{ - const found = data.match(/(?:\[\[(?!https?:\/\/).*?\]\])/g); - for (let find of found) { - let fileString = find.substring(2, find.length-2); - console.log('file string '+fileString ); - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(fileString) - console.log('filepath ' +filePath); - console.log('path ' + path.join(this.settings.backupFolder, path.dirname(file.path))); - cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, - resource_type: 'auto' - }).then(res => { - console.log(res); - let url = objectPath.get(res, 'secure_url'); - let replaceMarkdownText = `![](${url})`; - data = data.replace(find,replaceMarkdownText); - this.app.vault.process(file,(oldData)=>{ - console.log('this is data '+data); - return data; - }) - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); - }) - } - } - }); - + this.uploadCurrentNoteFiles(); return; } else { return; } }).open(); } - - private uploadVault() { - const files = this.app.vault.getFiles() - console.log('this is a file ' + files[0]); - for (let file of files) { - if (file.extension != 'md') { - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(file.path) - cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, - resource_type: 'auto' - }).then(res => { - console.log(res); - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); - }) - } - - } - } - new Notice("Backup of local media files completed. Be mindful of any error messages displayed as notices.", 0); - } private clearHandlers() { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); @@ -167,23 +107,20 @@ export default class CloudinaryUploader extends Plugin { }).then(res => { // Get response public URL of uploaded image console.log(res); - let url = objectPath.get(res.data, 'secure_url') - let replaceMarkdownText = ""; - + let url = objectPath.get(res.data, 'secure_url'); + let resType = objectPath.get(res.data,'resource_type'); + let replaceMarkdownText = this.generateResourceUrl(resType,url); // Split URL to allow for appending transformations if (this.settings.transformParams) { const splitURL = url.split("/upload/", 2); url = splitURL[0] += "/upload/" + this.settings.transformParams + "/" + splitURL[1]; - replaceMarkdownText = `![](${url})`; + replaceMarkdownText = this.generateResourceUrl(resType,url); } if (this.settings.f_auto) { const splitURL = url.split("/upload/", 2); url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; - replaceMarkdownText = `![](${url})`; - + replaceMarkdownText = this.generateResourceUrl(resType,url); // leave standard of no transformations added - } else { - replaceMarkdownText = `![](${url})`; } // Change URL format based on content type if (files[0].type.startsWith("audio")) { @@ -237,6 +174,49 @@ export default class CloudinaryUploader extends Plugin { } } + private uploadCurrentNoteFiles(){ + let file = this.app.workspace.getActiveFile(); + let data = this.app.vault.cachedRead(file).then((result)=>{ + data = result; + }).then(()=>{ + const found = data.match(/(?:\[\[(?!https?:\/\/).*?\]\])/g); + for (let find of found) { + let fileString = find.substring(2, find.length-2); + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(fileString) + cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, + resource_type: 'auto' + }).then(res => { + console.log(res); + let url = objectPath.get(res, 'secure_url'); + let resType = objectPath.get(res,'resource_type'); + let replaceMarkdownText = this.generateResourceUrl(resType,url); + data = data.replace(find,replaceMarkdownText); + this.app.vault.process(file,(oldData)=>{ + console.log('this is data '+data); + return data; + }) + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) + } + } + }); + } + private generateResourceUrl(type,url){ + if(type == 'audio'){ + return `\n`; + }else if(type == 'video'){ + return `\n`; + }else{ + return `![](${url})`; + } + } + // Plugin load steps async onload(): Promise { console.log("loading Cloudinary Uploader"); From a6dc6ab6c778a6ea41d7107a1f3e7d75281d8297 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:42:46 -0400 Subject: [PATCH 13/52] function typing --- src/main.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2ca3bc4..27a1061 100755 --- a/src/main.ts +++ b/src/main.ts @@ -36,7 +36,7 @@ export default class CloudinaryUploader extends Plugin { } private uploadNote() { - new WarningModal(this.app, (result) => { + new WarningModal(this.app, (result) :void => { if (result == 'true') { this.uploadCurrentNoteFiles(); return; @@ -45,12 +45,12 @@ export default class CloudinaryUploader extends Plugin { } }).open(); } - private clearHandlers() { + private clearHandlers() : void { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); } - private setupHandlers() { + private setupHandlers() : void { if (this.settings.clipboardUpload) { this.registerEvent(this.app.workspace.on('editor-paste', this.pasteHandler)); } else { @@ -62,11 +62,11 @@ export default class CloudinaryUploader extends Plugin { this.app.workspace.off('editor-drop', this.dropHandler); } } - private pasteHandler = async (event: ClipboardEvent, editor: Editor) => { + private pasteHandler = async (event: ClipboardEvent, editor: Editor) : Promise => { const { files } = event.clipboardData; await this.uploadFiles(files, event, editor); // to fix } - private dropHandler = async (event: DragEventInit, editor: Editor) => { + private dropHandler = async (event: DragEventInit, editor: Editor) : Promise => { const { files } = event.dataTransfer; await this.uploadFiles(files, event, editor); // to fix } @@ -145,7 +145,7 @@ export default class CloudinaryUploader extends Plugin { } // Set subfolder for upload - private setSubfolder(file: File) { + private setSubfolder(file: File) :string { if (file.type.startsWith("image")) { return `${this.settings.folder}/${this.settings.imageSubfolder}`; } else if (file.type.startsWith("audio")) { @@ -174,7 +174,7 @@ export default class CloudinaryUploader extends Plugin { } } - private uploadCurrentNoteFiles(){ + private uploadCurrentNoteFiles() : void{ let file = this.app.workspace.getActiveFile(); let data = this.app.vault.cachedRead(file).then((result)=>{ data = result; @@ -207,7 +207,7 @@ export default class CloudinaryUploader extends Plugin { } }); } - private generateResourceUrl(type,url){ + private generateResourceUrl(type:string,url:string) : string{ if(type == 'audio'){ return `\n`; }else if(type == 'video'){ From 6d2c1ec05b4d77191e7f225762e4ca5120d327a2 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:49:39 -0400 Subject: [PATCH 14/52] condense functions --- src/main.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main.ts b/src/main.ts index 27a1061..8d3a1aa 100755 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,8 @@ import { Notice, Plugin, Editor, - FileSystemAdapter + FileSystemAdapter, + TFile } from "obsidian"; // For API requests @@ -23,22 +24,26 @@ export default class CloudinaryUploader extends Plugin { id: "backup-files-cloudinary", name: "Backup media files to Cloudinary", callback: () => { - this.uploadVault(); + const files = this.app.vault.getMarkdownFiles() + for(let file of files){ + this.uploadNote(file) + } } }); this.addCommand({ id: "note-files-to-cloudinary", name: "Upload files in current note to Cloudinary", callback: () => { - this.uploadNote(); + let file = this.app.workspace.getActiveFile(); + this.uploadNote(file); } }) } - private uploadNote() { + private uploadNote(file:TFile) { new WarningModal(this.app, (result) :void => { if (result == 'true') { - this.uploadCurrentNoteFiles(); + this.uploadCurrentNoteFiles(file); return; } else { return; @@ -174,8 +179,7 @@ export default class CloudinaryUploader extends Plugin { } } - private uploadCurrentNoteFiles() : void{ - let file = this.app.workspace.getActiveFile(); + private uploadCurrentNoteFiles(file :TFile) : void{ let data = this.app.vault.cachedRead(file).then((result)=>{ data = result; }).then(()=>{ From 1f79267c189242bd7bb4aea2c73dad3a4f052b06 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 16:30:33 -0400 Subject: [PATCH 15/52] update search regex --- src/main.ts | 34 +++++++++++++++------------------- src/modal.ts | 8 ++++++++ 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8d3a1aa..4d99d76 100755 --- a/src/main.ts +++ b/src/main.ts @@ -114,25 +114,9 @@ export default class CloudinaryUploader extends Plugin { console.log(res); let url = objectPath.get(res.data, 'secure_url'); let resType = objectPath.get(res.data,'resource_type'); - let replaceMarkdownText = this.generateResourceUrl(resType,url); // Split URL to allow for appending transformations - if (this.settings.transformParams) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/" + this.settings.transformParams + "/" + splitURL[1]; - replaceMarkdownText = this.generateResourceUrl(resType,url); - } - if (this.settings.f_auto) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; - replaceMarkdownText = this.generateResourceUrl(resType,url); - // leave standard of no transformations added - } - // Change URL format based on content type - if (files[0].type.startsWith("audio")) { - replaceMarkdownText = `\n` - } else if (files[0].type.startsWith("video")) { - replaceMarkdownText = `\n` - } + url = this.generateTransformParams(url); + let replaceMarkdownText = this.generateResourceUrl(resType,url); // Show MD syntax using uploaded image URL, in Obsidian Editor this.replaceText(editor, pastePlaceText, replaceMarkdownText) }, err => { @@ -161,6 +145,18 @@ export default class CloudinaryUploader extends Plugin { return `${this.settings.folder}/${this.settings.rawSubfolder}`; } } + private generateTransformParams(url : string) : string{ + if (this.settings.transformParams) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/" + this.settings.transformParams + "/" + splitURL[1]; + } + if (this.settings.f_auto) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; + // leave standard of no transformations added + } + return url; + } // Function to replace text private replaceText(editor: Editor, target: string, replacement: string): void { target = target.trim(); @@ -183,7 +179,7 @@ export default class CloudinaryUploader extends Plugin { let data = this.app.vault.cachedRead(file).then((result)=>{ data = result; }).then(()=>{ - const found = data.match(/(?:\[\[(?!https?:\/\/).*?\]\])/g); + const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); for (let find of found) { let fileString = find.substring(2, find.length-2); let filePath; diff --git a/src/modal.ts b/src/modal.ts index 9ac496c..6a7f802 100644 --- a/src/modal.ts +++ b/src/modal.ts @@ -16,6 +16,14 @@ export class WarningModal extends Modal { " It is HIGHLY recommended that you backup this note elsewhere before performing this operation."+ " The media files in this note will attempt to be uploaded to Cloudinary"); contentEl.createEl("p", { text: textFragment }); + contentEl.createEl("h1", { text: "Other Information" }); + textFragment = document.createDocumentFragment(); + textFragment.append("The success of this action largely depends on the following:"); + textFragment = document.createDocumentFragment(); + textFragment.append('Your Cloudinary account subscription plan -- different plans have different upload limits'); + textFragment.append('The content you upload -- Certain files (example, .exe, .ps1) are not allowed to be uploaded to Cloudinary. If these are in your vault, the upload of these specific files will fail'); + contentEl.createEl("li", { text: textFragment }); + new Setting(contentEl) .addButton((btn) => btn.setButtonText("Continue action") From 41ded5d64a9940f789a12771e3f606fdcfc95164 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 16:38:17 -0400 Subject: [PATCH 16/52] refactor transformation functions --- src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.ts b/src/main.ts index 4d99d76..501e915 100755 --- a/src/main.ts +++ b/src/main.ts @@ -193,6 +193,7 @@ export default class CloudinaryUploader extends Plugin { console.log(res); let url = objectPath.get(res, 'secure_url'); let resType = objectPath.get(res,'resource_type'); + url = this.generateTransformParams(url); let replaceMarkdownText = this.generateResourceUrl(resType,url); data = data.replace(find,replaceMarkdownText); this.app.vault.process(file,(oldData)=>{ From 12632f7b88fbd92bdee428b29bf2f11a3f10eb67 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 18:22:42 -0400 Subject: [PATCH 17/52] fix folder reference for per-note uploads --- src/main.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 501e915..ff26de0 100755 --- a/src/main.ts +++ b/src/main.ts @@ -187,7 +187,7 @@ export default class CloudinaryUploader extends Plugin { if (adapter instanceof FileSystemAdapter) { filePath = adapter.getFullPath(fileString) cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, + folder: this.settings.folder, resource_type: 'auto' }).then(res => { console.log(res); @@ -196,7 +196,7 @@ export default class CloudinaryUploader extends Plugin { url = this.generateTransformParams(url); let replaceMarkdownText = this.generateResourceUrl(resType,url); data = data.replace(find,replaceMarkdownText); - this.app.vault.process(file,(oldData)=>{ + this.app.vault.process(file,()=>{ console.log('this is data '+data); return data; }) From d37fb1ad6d6f6559ec7d1d9a6cce2ab823945d93 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:00:38 -0400 Subject: [PATCH 18/52] change modal text --- src/main.ts | 100 +++++++++++++++++++++++++++++++++------------------ src/modal.ts | 6 +++- 2 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/main.ts b/src/main.ts index ff26de0..453225a 100755 --- a/src/main.ts +++ b/src/main.ts @@ -25,7 +25,7 @@ export default class CloudinaryUploader extends Plugin { name: "Backup media files to Cloudinary", callback: () => { const files = this.app.vault.getMarkdownFiles() - for(let file of files){ + for (let file of files) { this.uploadNote(file) } } @@ -40,8 +40,8 @@ export default class CloudinaryUploader extends Plugin { }) } - private uploadNote(file:TFile) { - new WarningModal(this.app, (result) :void => { + private uploadNote(file: TFile) { + new WarningModal(this.app, (result): void => { if (result == 'true') { this.uploadCurrentNoteFiles(file); return; @@ -50,12 +50,12 @@ export default class CloudinaryUploader extends Plugin { } }).open(); } - private clearHandlers() : void { + private clearHandlers(): void { this.app.workspace.off('editor-paste', this.pasteHandler); this.app.workspace.off('editor-drop', this.dropHandler); } - private setupHandlers() : void { + private setupHandlers(): void { if (this.settings.clipboardUpload) { this.registerEvent(this.app.workspace.on('editor-paste', this.pasteHandler)); } else { @@ -67,11 +67,11 @@ export default class CloudinaryUploader extends Plugin { this.app.workspace.off('editor-drop', this.dropHandler); } } - private pasteHandler = async (event: ClipboardEvent, editor: Editor) : Promise => { + private pasteHandler = async (event: ClipboardEvent, editor: Editor): Promise => { const { files } = event.clipboardData; await this.uploadFiles(files, event, editor); // to fix } - private dropHandler = async (event: DragEventInit, editor: Editor) : Promise => { + private dropHandler = async (event: DragEventInit, editor: Editor): Promise => { const { files } = event.dataTransfer; await this.uploadFiles(files, event, editor); // to fix } @@ -102,7 +102,7 @@ export default class CloudinaryUploader extends Plugin { const formData = new FormData(); formData.append('file', file); formData.append('upload_preset', this.settings.uploadPreset); - formData.append('folder', this.setSubfolder(file)); + formData.append('folder', this.setSubfolder(file,undefined)); // Make API call axios({ @@ -113,10 +113,10 @@ export default class CloudinaryUploader extends Plugin { // Get response public URL of uploaded image console.log(res); let url = objectPath.get(res.data, 'secure_url'); - let resType = objectPath.get(res.data,'resource_type'); + let resType = objectPath.get(res.data, 'resource_type'); // Split URL to allow for appending transformations url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType,url); + let replaceMarkdownText = this.generateResourceUrl(resType, url); // Show MD syntax using uploaded image URL, in Obsidian Editor this.replaceText(editor, pastePlaceText, replaceMarkdownText) }, err => { @@ -134,18 +134,31 @@ export default class CloudinaryUploader extends Plugin { } // Set subfolder for upload - private setSubfolder(file: File) :string { - if (file.type.startsWith("image")) { - return `${this.settings.folder}/${this.settings.imageSubfolder}`; - } else if (file.type.startsWith("audio")) { - return `${this.settings.folder}/${this.settings.audioSubfolder}`; - } else if (file.type.startsWith("video")) { - return `${this.settings.folder}/${this.settings.videoSubfolder}`; - } else { - return `${this.settings.folder}/${this.settings.rawSubfolder}`; + private setSubfolder(file?: File, resourceType?: string): string { + if (file) { + if (file.type && file.type.startsWith("image")) { + return `${this.settings.folder}/${this.settings.imageSubfolder}`; + } else if (file.type.startsWith("audio")) { + return `${this.settings.folder}/${this.settings.audioSubfolder}`; + } else if (file.type.startsWith("video")) { + return `${this.settings.folder}/${this.settings.videoSubfolder}`; + } else { + return `${this.settings.folder}/${this.settings.rawSubfolder}`; + } + }else if(resourceType){ + if (resourceType.startsWith("image")) { + return `${this.settings.folder}/${this.settings.imageSubfolder}`; + } else if (resourceType.startsWith("audio")) { + return `${this.settings.folder}/${this.settings.audioSubfolder}`; + } else if (resourceType.startsWith("video")) { + return `${this.settings.folder}/${this.settings.videoSubfolder}`; + } else { + return `${this.settings.folder}/${this.settings.rawSubfolder}`; + } } + } - private generateTransformParams(url : string) : string{ + private generateTransformParams(url: string): string { if (this.settings.transformParams) { const splitURL = url.split("/upload/", 2); url = splitURL[0] += "/upload/" + this.settings.transformParams + "/" + splitURL[1]; @@ -175,13 +188,13 @@ export default class CloudinaryUploader extends Plugin { } } - private uploadCurrentNoteFiles(file :TFile) : void{ - let data = this.app.vault.cachedRead(file).then((result)=>{ + private uploadCurrentNoteFiles(file: TFile): void { + let data = this.app.vault.cachedRead(file).then((result) => { data = result; - }).then(()=>{ + }).then(() => { const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); for (let find of found) { - let fileString = find.substring(2, find.length-2); + let fileString = find.substring(2, find.length - 2); let filePath; const adapter = this.app.vault.adapter; if (adapter instanceof FileSystemAdapter) { @@ -192,12 +205,31 @@ export default class CloudinaryUploader extends Plugin { }).then(res => { console.log(res); let url = objectPath.get(res, 'secure_url'); - let resType = objectPath.get(res,'resource_type'); + let resType = objectPath.get(res, 'resource_type'); + let pubId = objectPath.get(res,'public_id'); url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType,url); - data = data.replace(find,replaceMarkdownText); - this.app.vault.process(file,()=>{ - console.log('this is data '+data); + let replaceMarkdownText = this.generateResourceUrl(resType, url); + data = data.replace(find, replaceMarkdownText); + this.app.vault.process(file, () => { + console.log('this is data ' + data); + return data; + }) + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) + cloudinary.uploader.rename(pubId, this.settings.uploadPreset, { + folder: this.settings.folder, + resource_type: 'auto' + }).then(res => { + console.log(res); + let url = objectPath.get(res, 'secure_url'); + let resType = objectPath.get(res, 'resource_type'); + url = this.generateTransformParams(url); + let replaceMarkdownText = this.generateResourceUrl(resType, url); + data = data.replace(find, replaceMarkdownText); + this.app.vault.process(file, () => { + console.log('this is data ' + data); return data; }) }, err => { @@ -208,12 +240,12 @@ export default class CloudinaryUploader extends Plugin { } }); } - private generateResourceUrl(type:string,url:string) : string{ - if(type == 'audio'){ + private generateResourceUrl(type: string, url: string): string { + if (type == 'audio') { return `\n`; - }else if(type == 'video'){ + } else if (type == 'video') { return `\n`; - }else{ + } else { return `![](${url})`; } } @@ -248,4 +280,4 @@ export default class CloudinaryUploader extends Plugin { this.clearHandlers(); this.setupHandlers(); } -} +} \ No newline at end of file diff --git a/src/modal.ts b/src/modal.ts index 6a7f802..de1fe67 100644 --- a/src/modal.ts +++ b/src/modal.ts @@ -18,9 +18,13 @@ export class WarningModal extends Modal { contentEl.createEl("p", { text: textFragment }); contentEl.createEl("h1", { text: "Other Information" }); textFragment = document.createDocumentFragment(); - textFragment.append("The success of this action largely depends on the following:"); + textFragment.append("As a precaution, your local files in your vault will NOT be deleted, and will still remain in your vault "+ + " if you need to reference them. The success of this action largely depends on the following:"); + contentEl.createEl("p", { text: textFragment }); textFragment = document.createDocumentFragment(); textFragment.append('Your Cloudinary account subscription plan -- different plans have different upload limits'); + contentEl.createEl("li", { text: textFragment }); + textFragment = document.createDocumentFragment(); textFragment.append('The content you upload -- Certain files (example, .exe, .ps1) are not allowed to be uploaded to Cloudinary. If these are in your vault, the upload of these specific files will fail'); contentEl.createEl("li", { text: textFragment }); From 391f5c53036360bcf9ead0d7c56af6876c2f228c Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:07:16 -0400 Subject: [PATCH 19/52] allow for warning modal override --- src/main.ts | 13 +++++++++---- src/settings-tab.ts | 24 ++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main.ts b/src/main.ts index 453225a..bf62643 100755 --- a/src/main.ts +++ b/src/main.ts @@ -26,7 +26,7 @@ export default class CloudinaryUploader extends Plugin { callback: () => { const files = this.app.vault.getMarkdownFiles() for (let file of files) { - this.uploadNote(file) + this.uploadNoteModal(file) } } }); @@ -35,12 +35,17 @@ export default class CloudinaryUploader extends Plugin { name: "Upload files in current note to Cloudinary", callback: () => { let file = this.app.workspace.getActiveFile(); - this.uploadNote(file); + if(this.settings.ignoreWarnings){ + this.uploadCurrentNoteFiles(file); + }else{ + this.uploadNoteModal(file); + + } } }) } - private uploadNote(file: TFile) { + private uploadNoteModal(file: TFile) { new WarningModal(this.app, (result): void => { if (result == 'true') { this.uploadCurrentNoteFiles(file); @@ -193,7 +198,7 @@ export default class CloudinaryUploader extends Plugin { data = result; }).then(() => { const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); - for (let find of found) { + if(found && found.length > 0) for (let find of found) { let fileString = find.substring(2, find.length - 2); let filePath; const adapter = this.app.vault.adapter; diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 4b51104..06b306a 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -28,6 +28,7 @@ export interface CloudinarySettings { rawSubfolder: string; preserveBackupFilePath: boolean; backupFolder: string; + ignoreWarnings: boolean; } export const DEFAULT_SETTINGS: CloudinarySettings = { cloudName: "", @@ -46,7 +47,8 @@ export const DEFAULT_SETTINGS: CloudinarySettings = { videoSubfolder: "", rawSubfolder: "", preserveBackupFilePath: false, - backupFolder: "" + backupFolder: "", + ignoreWarnings: false }; export default class CloudinaryUploaderSettingTab extends PluginSettingTab { @@ -377,6 +379,24 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { " This may make it hard to identify. Additionally, file uploads will always be overwritten. You can use a combination of settings for unique file naming as found in "); textFragment.append(link); containerEl.createEl("p", { text: textFragment }); - + + + containerEl.createEl("h4", { text: "Warnings" }); + new Setting(containerEl) + .setName("Hide command palette mass upload warning") + .setDesc("Hides the warning modal and assumes that all mass actions are approved") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.ignoreWarnings) + .onChange(async (value) => { + try { + this.plugin.settings.ignoreWarnings = value; + await this.plugin.saveSettings(); + } + catch (e) { + console.log(e) + } + }) + }); } } \ No newline at end of file From 3f18411237da751d18d97437c7113908155f017a Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Sun, 9 Jun 2024 19:22:58 -0400 Subject: [PATCH 20/52] fix silent error for no regex matches --- src/main.ts | 2 +- src/settings-tab.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index bf62643..2c1e2fe 100755 --- a/src/main.ts +++ b/src/main.ts @@ -19,7 +19,7 @@ import { WarningModal } from "./modal"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; - private setCommands() { + private setCommands(): void { this.addCommand({ id: "backup-files-cloudinary", name: "Backup media files to Cloudinary", diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 06b306a..0504e83 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -7,7 +7,7 @@ import { Setting, } from 'obsidian'; -import CloudinaryUploader from './main' +import CloudinaryUploader from "./CloudinaryUploader"; //Define Cloudinary Settings export interface CloudinarySettings { From 48bc58d9e9ddfc7bd4f38468d9a4d052f463f1ff Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 06:52:50 -0400 Subject: [PATCH 21/52] url typing function --- src/formats.ts | 3 +++ src/main.ts | 15 ++++++++++++++- src/settings-tab.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/formats.ts diff --git a/src/formats.ts b/src/formats.ts new file mode 100644 index 0000000..4edb19c --- /dev/null +++ b/src/formats.ts @@ -0,0 +1,3 @@ +export const audioFormats : string[] = ['mp3','wav','m4a','aac','ogg', +'flac','wma','aiff','ape','opus','amr','pcm','au','ra','mka','ac3','mid','midi', +'mp2','wv','dts']; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 2c1e2fe..f1cd736 100755 --- a/src/main.ts +++ b/src/main.ts @@ -16,6 +16,7 @@ import { v2 as cloudinary } from 'cloudinary'; import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; import { WarningModal } from "./modal"; +import { audioFormats } from "./formats"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; @@ -245,8 +246,20 @@ export default class CloudinaryUploader extends Plugin { } }); } + // Required as Cloudinary doesn't have an 'audio' resource type. + // As we only know the file type after it's been uploaded (we don't know MIME type), + // we check if audio was uploaded based on the most-commonly used audio formats + private isAudio(url: string, formats:string[]) : boolean{ + let foundAudio = false; + for(let format of formats){ + if(url.endsWith(format)){ + foundAudio = true; + } + } + return foundAudio; + } private generateResourceUrl(type: string, url: string): string { - if (type == 'audio') { + if (type == 'audio' || this.isAudio(url,audioFormats)) { return `\n`; } else if (type == 'video') { return `\n`; diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 0504e83..06b306a 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -7,7 +7,7 @@ import { Setting, } from 'obsidian'; -import CloudinaryUploader from "./CloudinaryUploader"; +import CloudinaryUploader from './main' //Define Cloudinary Settings export interface CloudinarySettings { From 5ce3752e8e2e868b9c0b551460dbe77ca3a0deb6 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 06:55:16 -0400 Subject: [PATCH 22/52] fix url generation reference for clipboard uploads --- src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index f1cd736..f30d435 100755 --- a/src/main.ts +++ b/src/main.ts @@ -122,7 +122,7 @@ export default class CloudinaryUploader extends Plugin { let resType = objectPath.get(res.data, 'resource_type'); // Split URL to allow for appending transformations url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType, url); + let replaceMarkdownText = this.generateResourceUrl(file.type, url); // Show MD syntax using uploaded image URL, in Obsidian Editor this.replaceText(editor, pastePlaceText, replaceMarkdownText) }, err => { From 18a5cc4de816d7a6413abbf2083ee84775c5d0d8 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 07:59:29 -0400 Subject: [PATCH 23/52] update docs on single file mass upload --- docs/configuring-cloudinary.md | 6 +++-- docs/configuring-the-plugin.md | 7 +++--- docs/plugin-commands/plugin-commands.md | 14 +++++++++++ docs/plugin-commands/plugin-commands.yml | 4 +++ .../upload-single-note-cloudinary.md | 25 +++++++++++++++++++ 5 files changed, 51 insertions(+), 5 deletions(-) create mode 100644 docs/plugin-commands/plugin-commands.md create mode 100644 docs/plugin-commands/plugin-commands.yml create mode 100644 docs/plugin-commands/upload-single-note-cloudinary.md diff --git a/docs/configuring-cloudinary.md b/docs/configuring-cloudinary.md index c308ee6..d6ef5aa 100644 --- a/docs/configuring-cloudinary.md +++ b/docs/configuring-cloudinary.md @@ -5,7 +5,7 @@ order: 800 author: Jordan Handy icon: gear --- -## Cloudinary Confiuration Steps +## Cloudinary Configuration Steps 1. Log in to Cloudinary and find your Cloud Name here ![Cloudinary Dashboard](assets/cloudinary-dash.png) 2. Enable Unsigned Uploads @@ -21,4 +21,6 @@ When the preset is created, it will have a "name" associated with it. Use this !!! Note If you have a folder name already configured on Cloudinary under the settings for your specific upload preset (can be configured on Cloudinary itself), this folder setting will be ignored. -!!! \ No newline at end of file +!!! + +Continue to [Plugin Commands](plugin-commands.md) \ No newline at end of file diff --git a/docs/configuring-the-plugin.md b/docs/configuring-the-plugin.md index d7c3910..fdef922 100644 --- a/docs/configuring-the-plugin.md +++ b/docs/configuring-the-plugin.md @@ -11,15 +11,16 @@ To configure the Plugin, you'll need the following information: - Cloudinary Cloud Name - Cloudinary Upload Preset - Cloudinary Folder Name +- Configure the behaviour that triggers a Cloudinary upload + - Copy/paste from clipboard + - Drag and Drop - OPTIONAL: [Default Transformation Options](https://cloudinary.com/documentation/transformation_reference) - OPTIONAL: Configure which asset types you want to be uploaded to Cloudinary - Raw (non-media type files) - Audio - Video - Images -- OPTIONAL: Configure the behaviour that triggers a Cloudinary upload - - Copy/paste from clipboard - - Drag and Drop +- OPTIONAL: Configure specific folders that different media types should be placed into By default, only images are set to be uploaded to Cloudinary via a copy/paste from the clipboard, but this can be altered ![Cloudinary plugin configuration](https://res.cloudinary.com/dakfccuv5/image/upload/v1716510330/obsidian/wwhysme8vdrz6syd5skp.png) diff --git a/docs/plugin-commands/plugin-commands.md b/docs/plugin-commands/plugin-commands.md new file mode 100644 index 0000000..a6a0cf1 --- /dev/null +++ b/docs/plugin-commands/plugin-commands.md @@ -0,0 +1,14 @@ +--- +label: Plugin Commands +layout: default +order: 1000 +author: Jordan Handy +icon: gear +--- +## Plugin Commands + +The Cloudinary Uploader plugin has palette commands that can be run. Explore them below: +- Upload single note assets to Cloudinary +- Upload all note assets to Cloudinary +- Local asset file backup +Continue to [Upload single note assets to Cloudinary](upload-single-note-cloudinary.md) \ No newline at end of file diff --git a/docs/plugin-commands/plugin-commands.yml b/docs/plugin-commands/plugin-commands.yml new file mode 100644 index 0000000..b0df7f2 --- /dev/null +++ b/docs/plugin-commands/plugin-commands.yml @@ -0,0 +1,4 @@ +label: Plugin Commands +order: 700 +icon: gear +expanded: true \ No newline at end of file diff --git a/docs/plugin-commands/upload-single-note-cloudinary.md b/docs/plugin-commands/upload-single-note-cloudinary.md new file mode 100644 index 0000000..1363daa --- /dev/null +++ b/docs/plugin-commands/upload-single-note-cloudinary.md @@ -0,0 +1,25 @@ +--- +label: Upload single note assets to Cloudinary +layout: default +order: 900 +author: Jordan Handy +icon: cloud +--- +## Upload Single Note Assets to Cloudinary + +Within the command palette, run the "Upload files in current note to Cloudinary" option. This will take all local assets located in the current note and attempt to upload them to Cloudinary. + +!!!warning +It is **strongly** recommended to take a backup of your notes before you perform this action. +As part of the action, your local media assets are **not deleted** when the upload to Cloudinary happens, so you can still reference them if you wanted to. However, because of the nature of the upload, and the variability of syntax in how some users may choose to reference media, this may alter the formatting of your notes. Use with caution. +!!! + +The success / failure of this plugin largely depends on the following factors: +1. Your Cloudinary plan -- Certain Cloudinary plans have quotas applied to their accounts. Consult Cloudinary documentation to understand what your limits are +2. The file types being uploaded -- Cloudinary only accepts certain file types to be uploaded to their servers. If you try to upload an unsupported file type, the upload for that specific file will fail. Consult [this documentation on media types](https://support.cloudinary.com/hc/en-us/articles/202520642-What-type-of-image-video-and-audio-formats-do-you-support) and [this documentation on raw types](https://support.cloudinary.com/hc/en-us/articles/202520572-Using-Cloudinary-for-files-other-than-images-and-videos) for more information. + +## Warning +When you first use the option for mass note upload, you will be presented with a warning message explaining the potential dangers of your action. By default, this warning message will be displayed **every time** you invoke the action. If you would like to disable this message, toggle the "Hide command palette mass upload warning" option in plugin settings. +## Demo + +Continue to [Configuring Cloudinary](configuring-cloudinary.md) \ No newline at end of file From dd665cd2e727b520fe4f32627017f7475295973a Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:01:20 -0400 Subject: [PATCH 24/52] fix rename error --- src/main.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/main.ts b/src/main.ts index f30d435..b19bf65 100755 --- a/src/main.ts +++ b/src/main.ts @@ -224,24 +224,6 @@ export default class CloudinaryUploader extends Plugin { console.log(JSON.stringify(err)) new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); }) - cloudinary.uploader.rename(pubId, this.settings.uploadPreset, { - folder: this.settings.folder, - resource_type: 'auto' - }).then(res => { - console.log(res); - let url = objectPath.get(res, 'secure_url'); - let resType = objectPath.get(res, 'resource_type'); - url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType, url); - data = data.replace(find, replaceMarkdownText); - this.app.vault.process(file, () => { - console.log('this is data ' + data); - return data; - }) - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); - }) } } }); From 898b13113afe13890db60399b0052389d240a064 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:09:26 -0400 Subject: [PATCH 25/52] fix upload file reference error --- docs/plugin-commands/upload-single-note-cloudinary.md | 2 ++ src/main.ts | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/plugin-commands/upload-single-note-cloudinary.md b/docs/plugin-commands/upload-single-note-cloudinary.md index 1363daa..05c2b68 100644 --- a/docs/plugin-commands/upload-single-note-cloudinary.md +++ b/docs/plugin-commands/upload-single-note-cloudinary.md @@ -21,5 +21,7 @@ The success / failure of this plugin largely depends on the following factors: ## Warning When you first use the option for mass note upload, you will be presented with a warning message explaining the potential dangers of your action. By default, this warning message will be displayed **every time** you invoke the action. If you would like to disable this message, toggle the "Hide command palette mass upload warning" option in plugin settings. ## Demo +View a demo of the command below: +![Demo video - mass upload](https://res.cloudinary.com/dakfccuv5/video/upload/v1718021473/mass-note-upload_qobrpz.mp4) Continue to [Configuring Cloudinary](configuring-cloudinary.md) \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index b19bf65..a1d6580 100755 --- a/src/main.ts +++ b/src/main.ts @@ -200,7 +200,7 @@ export default class CloudinaryUploader extends Plugin { }).then(() => { const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); if(found && found.length > 0) for (let find of found) { - let fileString = find.substring(2, find.length - 2); + let fileString = find.substring(3, find.length - 2); let filePath; const adapter = this.app.vault.adapter; if (adapter instanceof FileSystemAdapter) { @@ -212,7 +212,6 @@ export default class CloudinaryUploader extends Plugin { console.log(res); let url = objectPath.get(res, 'secure_url'); let resType = objectPath.get(res, 'resource_type'); - let pubId = objectPath.get(res,'public_id'); url = this.generateTransformParams(url); let replaceMarkdownText = this.generateResourceUrl(resType, url); data = data.replace(find, replaceMarkdownText); From c4330f49659ce211c448da8830194fdafe062639 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:17:23 -0400 Subject: [PATCH 26/52] upload docs with demo video --- docs/plugin-commands/upload-single-note-cloudinary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/plugin-commands/upload-single-note-cloudinary.md b/docs/plugin-commands/upload-single-note-cloudinary.md index 05c2b68..36a62cd 100644 --- a/docs/plugin-commands/upload-single-note-cloudinary.md +++ b/docs/plugin-commands/upload-single-note-cloudinary.md @@ -22,6 +22,6 @@ The success / failure of this plugin largely depends on the following factors: When you first use the option for mass note upload, you will be presented with a warning message explaining the potential dangers of your action. By default, this warning message will be displayed **every time** you invoke the action. If you would like to disable this message, toggle the "Hide command palette mass upload warning" option in plugin settings. ## Demo View a demo of the command below: -![Demo video - mass upload](https://res.cloudinary.com/dakfccuv5/video/upload/v1718021473/mass-note-upload_qobrpz.mp4) +![Demo video - mass upload](https://res.cloudinary.com/dakfccuv5/video/upload/v1718021709/mass-note-upload_qnx5ar.mp4) Continue to [Configuring Cloudinary](configuring-cloudinary.md) \ No newline at end of file From 2f88ba75808c556e26cda086cca24baeb3ab6c5e Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 08:30:40 -0400 Subject: [PATCH 27/52] update docs on mass upload for notes --- .../upload-all-notes-cloudinary.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 docs/plugin-commands/upload-all-notes-cloudinary.md diff --git a/docs/plugin-commands/upload-all-notes-cloudinary.md b/docs/plugin-commands/upload-all-notes-cloudinary.md new file mode 100644 index 0000000..0d4a0c6 --- /dev/null +++ b/docs/plugin-commands/upload-all-notes-cloudinary.md @@ -0,0 +1,15 @@ +--- +label: Upload all note assets to Cloudinary +layout: default +order: 800 +author: Jordan Handy +icon: cloud +--- +## Upload All Note Assets to Cloudinary + +Within the command palette, run the "Upload all note files to Cloudinary" option. This will take all local assets located in the vault and attempt to upload them to Cloudinary. + +See below for information: +[!ref Upload single note files](upload-single-note-cloudinary.md) + +Continue to [Backup Vault Assets](backup-vault-assets.md) \ No newline at end of file From 260bbba40cb8ae2f2e4ecd2544c7568f3f40776f Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:31:45 -0400 Subject: [PATCH 28/52] add command for mass upload assets to cloudinary + update docs --- docs/plugin-commands/backup-vault-assets.md | 12 +++++ src/main.ts | 50 ++++++++++++++++----- 2 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 docs/plugin-commands/backup-vault-assets.md diff --git a/docs/plugin-commands/backup-vault-assets.md b/docs/plugin-commands/backup-vault-assets.md new file mode 100644 index 0000000..36d1f77 --- /dev/null +++ b/docs/plugin-commands/backup-vault-assets.md @@ -0,0 +1,12 @@ +--- +label: Backup Vault Assets to Cloudinary +layout: default +order: 700 +author: Jordan Handy +icon: cloud +--- +## Backup All Vault Assets to Cloudinary + +!!!warning +This is an experimental feature. Your mileage may vary as to the success of this command because of the issues already outlined in [uploading single note content](upload-single-note-cloudinary.md) and [upload all note contents](upload-all-notes-cloudinary.md). This command attempts to read all "non-markdown" files in your vault and upload a copy of them to Cloudinary. The "Backup Folder" option is where these files are stored. The "Preserve File Paths" option attempts to maintain the same foldering structure you have in your vault, prepended with your specified backup folder as the root. +!!! \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index a1d6580..fce05d7 100755 --- a/src/main.ts +++ b/src/main.ts @@ -22,8 +22,21 @@ export default class CloudinaryUploader extends Plugin { private setCommands(): void { this.addCommand({ - id: "backup-files-cloudinary", - name: "Backup media files to Cloudinary", + id: "upload-single-note-files-to-cloudinary", + name: "Upload files in current note to Cloudinary", + callback: () => { + let file = this.app.workspace.getActiveFile(); + if(this.settings.ignoreWarnings){ + this.uploadCurrentNoteFiles(file); + }else{ + this.uploadNoteModal(file); + + } + } + }); + this.addCommand({ + id: "upload-all-note-files-cloudinary", + name: "Upload all note files to Cloudinary", callback: () => { const files = this.app.vault.getMarkdownFiles() for (let file of files) { @@ -32,18 +45,15 @@ export default class CloudinaryUploader extends Plugin { } }); this.addCommand({ - id: "note-files-to-cloudinary", - name: "Upload files in current note to Cloudinary", + id: "upload-all-media-assets-cloudinary", + name: "Upload all vault media assets to Cloudinary", callback: () => { - let file = this.app.workspace.getActiveFile(); - if(this.settings.ignoreWarnings){ - this.uploadCurrentNoteFiles(file); - }else{ - this.uploadNoteModal(file); - + const files = this.app.vault.getMarkdownFiles() + for (let file of files) { + this.uploadNoteModal(file) } } - }) + }); } private uploadNoteModal(file: TFile) { @@ -194,6 +204,24 @@ export default class CloudinaryUploader extends Plugin { } } + private uploadVault() : void{ + const files = this.app.vault.getFiles() + for(let file of files){ + if(file.extension != 'md'){ + let path; + let filePath; + const adapter = this.app.vault.adapter; + if(adapter instanceof FileSystemAdapter){ + filePath = adapter.getFullPath(file.path); + console.log(path); + } + cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset,{ + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(file.path)) : this.settings.backupFolder, + resourceType: 'auto' + }); + } + } + } private uploadCurrentNoteFiles(file: TFile): void { let data = this.app.vault.cachedRead(file).then((result) => { data = result; From 3c45259d369fe2055d0c80e23dd1fc4fdd63c1d0 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:34:24 -0400 Subject: [PATCH 29/52] rearrange settings --- src/main.ts | 5 +--- src/settings-tab.ts | 57 +++++++++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/src/main.ts b/src/main.ts index fce05d7..7c851d2 100755 --- a/src/main.ts +++ b/src/main.ts @@ -48,10 +48,7 @@ export default class CloudinaryUploader extends Plugin { id: "upload-all-media-assets-cloudinary", name: "Upload all vault media assets to Cloudinary", callback: () => { - const files = this.app.vault.getMarkdownFiles() - for (let file of files) { - this.uploadNoteModal(file) - } + this.uploadVault(); } }); } diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 06b306a..0b466cf 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -336,6 +336,35 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); }) }); + containerEl.createEl("h5", { text: "File names, file conflicts, overwrite behaviour" }); + link = document.createElement("a"); + link.text="plugin documentation "; + link.href="https://google.ca"; + textFragment = document.createDocumentFragment(); + textFragment.append("Assuming all defaults in your Cloudinary Upload Preset settings, all file backups will receive a unique public ID (file name) within the Cloudinary console."+ + " This may make it hard to identify. Additionally, file uploads will always be overwritten. You can use a combination of settings for unique file naming as found in "); + textFragment.append(link); + containerEl.createEl("p", { text: textFragment }); + + + containerEl.createEl("h4", { text: "Warnings" }); + new Setting(containerEl) + .setName("Hide command palette mass upload warning") + .setDesc("Hides the warning modal and assumes that all mass actions are approved") + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.ignoreWarnings) + .onChange(async (value) => { + try { + this.plugin.settings.ignoreWarnings = value; + await this.plugin.saveSettings(); + } + catch (e) { + console.log(e) + } + }) + }); + containerEl.createEl("h3", { text: "EXPERIMENTAL FEATURES" }); containerEl.createEl("h4", { text: "Local File Backup" }); textFragment = document.createDocumentFragment(); textFragment.append("If you run the command to create a backup of vault local assets, these settings apply"); @@ -370,33 +399,5 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { } }) }); - containerEl.createEl("h5", { text: "File names, file conflicts, overwrite behaviour" }); - link = document.createElement("a"); - link.text="plugin documentation "; - link.href="https://google.ca"; - textFragment = document.createDocumentFragment(); - textFragment.append("Assuming all defaults in your Cloudinary Upload Preset settings, all file backups will receive a unique public ID (file name) within the Cloudinary console."+ - " This may make it hard to identify. Additionally, file uploads will always be overwritten. You can use a combination of settings for unique file naming as found in "); - textFragment.append(link); - containerEl.createEl("p", { text: textFragment }); - - - containerEl.createEl("h4", { text: "Warnings" }); - new Setting(containerEl) - .setName("Hide command palette mass upload warning") - .setDesc("Hides the warning modal and assumes that all mass actions are approved") - .addToggle((toggle) => { - toggle - .setValue(this.plugin.settings.ignoreWarnings) - .onChange(async (value) => { - try { - this.plugin.settings.ignoreWarnings = value; - await this.plugin.saveSettings(); - } - catch (e) { - console.log(e) - } - }) - }); } } \ No newline at end of file From b358fb94d9f608744af60adc8d75c574b55986ec Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:41:55 -0400 Subject: [PATCH 30/52] create modal for mass upload of assets --- src/main.ts | 23 ++++++++-- .../asset-warning-modal.ts} | 0 src/modals/note-warning-modal.ts | 46 +++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) rename src/{modal.ts => modals/asset-warning-modal.ts} (100%) create mode 100644 src/modals/note-warning-modal.ts diff --git a/src/main.ts b/src/main.ts index 7c851d2..801355c 100755 --- a/src/main.ts +++ b/src/main.ts @@ -15,7 +15,8 @@ import { v2 as cloudinary } from 'cloudinary'; // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; -import { WarningModal } from "./modal"; +import { NoteWarningModal } from "./modals/note-warning-modal"; +import { AssetWarningModal } from "./modals/asset-warning-modal"; import { audioFormats } from "./formats"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; @@ -48,13 +49,27 @@ export default class CloudinaryUploader extends Plugin { id: "upload-all-media-assets-cloudinary", name: "Upload all vault media assets to Cloudinary", callback: () => { - this.uploadVault(); + if(this.settings.ignoreWarnings){ + this.uploadVault(); + }else{ + this.uploadAssetModal() + } } }); } - private uploadNoteModal(file: TFile) { - new WarningModal(this.app, (result): void => { + private uploadNoteModal() : void { + new NoteWarningModal(this.app, (result): void => { + if (result == 'true') { + this.uploadVault(); + return; + } else { + return; + } + }).open(); + } + private uploadAssetModal(file: TFile) : void { + new AssetWarningModal(this.app, (result): void => { if (result == 'true') { this.uploadCurrentNoteFiles(file); return; diff --git a/src/modal.ts b/src/modals/asset-warning-modal.ts similarity index 100% rename from src/modal.ts rename to src/modals/asset-warning-modal.ts diff --git a/src/modals/note-warning-modal.ts b/src/modals/note-warning-modal.ts new file mode 100644 index 0000000..82edaf2 --- /dev/null +++ b/src/modals/note-warning-modal.ts @@ -0,0 +1,46 @@ +import { App, Modal,Setting } from "obsidian"; + +export class NoteWarningModal extends Modal { + result: string; + onSubmit: (result: string) => void; + constructor(app: App, onSubmit: (result:string)=> void) { + super(app); + this.onSubmit = onSubmit; + } + + onOpen() { + let { contentEl } = this; + contentEl.createEl("h1", { text: "Warning" }); + let textFragment = document.createDocumentFragment(); + textFragment.append("This is a potentially dangerous action."+ + " It is HIGHLY recommended that you backup this note elsewhere before performing this operation."+ + " The media files in this note will attempt to be uploaded to Cloudinary"); + contentEl.createEl("p", { text: textFragment }); + contentEl.createEl("h1", { text: "Other Information" }); + textFragment = document.createDocumentFragment(); + textFragment.append("As a precaution, your local files in your vault will NOT be deleted, and will still remain in your vault "+ + " if you need to reference them. The success of this action largely depends on the following:"); + contentEl.createEl("p", { text: textFragment }); + textFragment = document.createDocumentFragment(); + textFragment.append('Your Cloudinary account subscription plan -- different plans have different upload limits'); + contentEl.createEl("li", { text: textFragment }); + textFragment = document.createDocumentFragment(); + textFragment.append('The content you upload -- Certain files (example, .exe, .ps1) are not allowed to be uploaded to Cloudinary. If these are in your vault, the upload of these specific files will fail'); + contentEl.createEl("li", { text: textFragment }); + + new Setting(contentEl) + .addButton((btn) => + btn.setButtonText("Continue action") + .setCta() + .onClick(()=>{ + this.close(); + this.result = 'true'; + this.onSubmit(this.result) + })) + } + + onClose() { + let { contentEl } = this; + contentEl.empty(); + } +} \ No newline at end of file From 323e460de33cf97713b5559a26f9b996100c01f6 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:07:21 -0400 Subject: [PATCH 31/52] update modal to support multiple upload types --- src/main.ts | 23 ++++--------- src/modals/asset-warning-modal.ts | 46 -------------------------- src/{modals => }/note-warning-modal.ts | 17 ++++++++-- 3 files changed, 21 insertions(+), 65 deletions(-) delete mode 100644 src/modals/asset-warning-modal.ts rename src/{modals => }/note-warning-modal.ts (63%) diff --git a/src/main.ts b/src/main.ts index 801355c..d8d6b79 100755 --- a/src/main.ts +++ b/src/main.ts @@ -15,8 +15,7 @@ import { v2 as cloudinary } from 'cloudinary'; // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; -import { NoteWarningModal } from "./modals/note-warning-modal"; -import { AssetWarningModal } from "./modals/asset-warning-modal"; +import { NoteWarningModal } from "./note-warning-modal"; import { audioFormats } from "./formats"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; @@ -30,7 +29,7 @@ export default class CloudinaryUploader extends Plugin { if(this.settings.ignoreWarnings){ this.uploadCurrentNoteFiles(file); }else{ - this.uploadNoteModal(file); + this.uploadNoteModal(file, 'note'); } } @@ -41,7 +40,7 @@ export default class CloudinaryUploader extends Plugin { callback: () => { const files = this.app.vault.getMarkdownFiles() for (let file of files) { - this.uploadNoteModal(file) + this.uploadNoteModal(file, 'note') } } }); @@ -52,24 +51,14 @@ export default class CloudinaryUploader extends Plugin { if(this.settings.ignoreWarnings){ this.uploadVault(); }else{ - this.uploadAssetModal() + this.uploadNoteModal(undefined,'asset'); } } }); } - private uploadNoteModal() : void { - new NoteWarningModal(this.app, (result): void => { - if (result == 'true') { - this.uploadVault(); - return; - } else { - return; - } - }).open(); - } - private uploadAssetModal(file: TFile) : void { - new AssetWarningModal(this.app, (result): void => { + private uploadNoteModal(file?: TFile, type?:string) : void { + new NoteWarningModal(this.app, type, (result): void => { if (result == 'true') { this.uploadCurrentNoteFiles(file); return; diff --git a/src/modals/asset-warning-modal.ts b/src/modals/asset-warning-modal.ts deleted file mode 100644 index de1fe67..0000000 --- a/src/modals/asset-warning-modal.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { App, Modal,Setting } from "obsidian"; - -export class WarningModal extends Modal { - result: string; - onSubmit: (result: string) => void; - constructor(app: App, onSubmit: (result:string)=> void) { - super(app); - this.onSubmit = onSubmit; - } - - onOpen() { - let { contentEl } = this; - contentEl.createEl("h1", { text: "Warning" }); - let textFragment = document.createDocumentFragment(); - textFragment.append("This is a potentially dangerous action."+ - " It is HIGHLY recommended that you backup this note elsewhere before performing this operation."+ - " The media files in this note will attempt to be uploaded to Cloudinary"); - contentEl.createEl("p", { text: textFragment }); - contentEl.createEl("h1", { text: "Other Information" }); - textFragment = document.createDocumentFragment(); - textFragment.append("As a precaution, your local files in your vault will NOT be deleted, and will still remain in your vault "+ - " if you need to reference them. The success of this action largely depends on the following:"); - contentEl.createEl("p", { text: textFragment }); - textFragment = document.createDocumentFragment(); - textFragment.append('Your Cloudinary account subscription plan -- different plans have different upload limits'); - contentEl.createEl("li", { text: textFragment }); - textFragment = document.createDocumentFragment(); - textFragment.append('The content you upload -- Certain files (example, .exe, .ps1) are not allowed to be uploaded to Cloudinary. If these are in your vault, the upload of these specific files will fail'); - contentEl.createEl("li", { text: textFragment }); - - new Setting(contentEl) - .addButton((btn) => - btn.setButtonText("Continue action") - .setCta() - .onClick(()=>{ - this.close(); - this.result = 'true'; - this.onSubmit(this.result) - })) - } - - onClose() { - let { contentEl } = this; - contentEl.empty(); - } -} \ No newline at end of file diff --git a/src/modals/note-warning-modal.ts b/src/note-warning-modal.ts similarity index 63% rename from src/modals/note-warning-modal.ts rename to src/note-warning-modal.ts index 82edaf2..29bc6be 100644 --- a/src/modals/note-warning-modal.ts +++ b/src/note-warning-modal.ts @@ -2,15 +2,21 @@ import { App, Modal,Setting } from "obsidian"; export class NoteWarningModal extends Modal { result: string; + type: string; onSubmit: (result: string) => void; - constructor(app: App, onSubmit: (result:string)=> void) { + constructor(app: App, type:string,onSubmit: (result:string)=> void) { super(app); this.onSubmit = onSubmit; + this.type = type; } onOpen() { let { contentEl } = this; - contentEl.createEl("h1", { text: "Warning" }); + if(this.type == 'note'){ + contentEl.createEl("h1", { text: "Note Media Upload - Warning" }); + }else{ + contentEl.createEl("h1", { text: "Mass Asset Backup - Warning" }); + } let textFragment = document.createDocumentFragment(); textFragment.append("This is a potentially dangerous action."+ " It is HIGHLY recommended that you backup this note elsewhere before performing this operation."+ @@ -27,6 +33,13 @@ export class NoteWarningModal extends Modal { textFragment = document.createDocumentFragment(); textFragment.append('The content you upload -- Certain files (example, .exe, .ps1) are not allowed to be uploaded to Cloudinary. If these are in your vault, the upload of these specific files will fail'); contentEl.createEl("li", { text: textFragment }); + if(this.type == 'note'){ + textFragment.append('Should this process fail, or timeout, you can attempt to run the same command again as assets already uploaded to Cloudinary should not be reuploaded because of how the file search is completed for initial upload.') + }else{ + textFragment.append('Should this process fail, trying again will start fresh from the beginning. This is because there is currently no flag to denote if an upload has been completed from a previous attempt.' + + ' Additionally, depending on your Cloudinary settings (within the upload preset in the Cloudinary account, pre-existing files may be overwritten OR duplicated depending on these settings.'); + } + contentEl.createEl("p",{ text : textFragment }); new Setting(contentEl) .addButton((btn) => From 6335733da44d72b1aad9f4600808939985968c11 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:13:54 -0400 Subject: [PATCH 32/52] update common file formats --- src/formats.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++++--- src/main.ts | 2 +- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/formats.ts b/src/formats.ts index 4edb19c..d40f56d 100644 --- a/src/formats.ts +++ b/src/formats.ts @@ -1,3 +1,48 @@ -export const audioFormats : string[] = ['mp3','wav','m4a','aac','ogg', -'flac','wma','aiff','ape','opus','amr','pcm','au','ra','mka','ac3','mid','midi', -'mp2','wv','dts']; \ No newline at end of file +export const audioFormats: string[] = ["mp3", "wav", "m4a", "aac", "ogg", + "flac", "wma", "aiff", "ape", "opus", "amr", "pcm", "au", "ra", "mka", "ac3", "mid", "midi", + "mp2", "wv", "dts"]; + +export const imageFormats: string[] = [ + "jpg", + "jpeg", + "png", + "gif", + "tiff", + "bmp", + "svg", + "raw", + "pdf", + "psd", + "eps", + "jp2", + "webp", + "heic", + "ico", + "tga", + "pict", + "pcx", + "wmf", + "exif" +]; +export const videoFormats : string[] = [ + "mp4", + "mov", + "avi", + "mkv", + "wmv", + "flv", + "webm", + "m4v", + "mpeg", + "mpg", + "3gp", + "ogg", + "qt", + "asf", + "rm", + "rmvb", + "m2ts", + "ts", + "vob", + "divx" + ]; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index d8d6b79..167bb80 100755 --- a/src/main.ts +++ b/src/main.ts @@ -16,7 +16,7 @@ import { v2 as cloudinary } from 'cloudinary'; import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; import { NoteWarningModal } from "./note-warning-modal"; -import { audioFormats } from "./formats"; +import { audioFormats, imageFormats, videoFormats } from "./formats"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; From f8b919e26e98d152c82f861194d8fa4281190cc9 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:24:41 -0400 Subject: [PATCH 33/52] fix subfoldering for sdk uploads --- src/main.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main.ts b/src/main.ts index 167bb80..7469631 100755 --- a/src/main.ts +++ b/src/main.ts @@ -151,7 +151,7 @@ export default class CloudinaryUploader extends Plugin { } // Set subfolder for upload - private setSubfolder(file?: File, resourceType?: string): string { + private setSubfolder(file?: File, resourceUrl?: string): string { if (file) { if (file.type && file.type.startsWith("image")) { return `${this.settings.folder}/${this.settings.imageSubfolder}`; @@ -162,12 +162,12 @@ export default class CloudinaryUploader extends Plugin { } else { return `${this.settings.folder}/${this.settings.rawSubfolder}`; } - }else if(resourceType){ - if (resourceType.startsWith("image")) { + }else if(resourceUrl){ + if (this.isType(resourceUrl,imageFormats)) { return `${this.settings.folder}/${this.settings.imageSubfolder}`; - } else if (resourceType.startsWith("audio")) { + } else if (this.isType(resourceUrl,audioFormats)) { return `${this.settings.folder}/${this.settings.audioSubfolder}`; - } else if (resourceType.startsWith("video")) { + } else if (this.isType(resourceUrl,videoFormats)) { return `${this.settings.folder}/${this.settings.videoSubfolder}`; } else { return `${this.settings.folder}/${this.settings.rawSubfolder}`; @@ -235,7 +235,7 @@ export default class CloudinaryUploader extends Plugin { if (adapter instanceof FileSystemAdapter) { filePath = adapter.getFullPath(fileString) cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.settings.folder, + folder: this.setSubfolder(undefined,filePath), resource_type: 'auto' }).then(res => { console.log(res); @@ -259,19 +259,19 @@ export default class CloudinaryUploader extends Plugin { // Required as Cloudinary doesn't have an 'audio' resource type. // As we only know the file type after it's been uploaded (we don't know MIME type), // we check if audio was uploaded based on the most-commonly used audio formats - private isAudio(url: string, formats:string[]) : boolean{ - let foundAudio = false; + private isType(url: string, formats:string[]) : boolean{ + let foundTypeMatch = false; for(let format of formats){ if(url.endsWith(format)){ - foundAudio = true; + foundTypeMatch = true; } } - return foundAudio; + return foundTypeMatch; } private generateResourceUrl(type: string, url: string): string { - if (type == 'audio' || this.isAudio(url,audioFormats)) { + if (type == 'audio' || this.isType(url,audioFormats)) { return `\n`; - } else if (type == 'video') { + } else if (type == 'video' || this.isType(url,videoFormats)) { return `\n`; } else { return `![](${url})`; From e06b31fef05d1add8324d7fca2acde7768790555 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 06:50:20 -0400 Subject: [PATCH 34/52] fix multi popup for bulk action --- src/main.ts | 84 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/src/main.ts b/src/main.ts index 7469631..3b773ba 100755 --- a/src/main.ts +++ b/src/main.ts @@ -26,9 +26,9 @@ export default class CloudinaryUploader extends Plugin { name: "Upload files in current note to Cloudinary", callback: () => { let file = this.app.workspace.getActiveFile(); - if(this.settings.ignoreWarnings){ + if (this.settings.ignoreWarnings) { this.uploadCurrentNoteFiles(file); - }else{ + } else { this.uploadNoteModal(file, 'note'); } @@ -39,31 +39,43 @@ export default class CloudinaryUploader extends Plugin { name: "Upload all note files to Cloudinary", callback: () => { const files = this.app.vault.getMarkdownFiles() - for (let file of files) { - this.uploadNoteModal(file, 'note') + if (this.settings.ignoreWarnings) { + for (let file of files) { + this.uploadCurrentNoteFiles(file); + } + + } else { + this.uploadNoteModal(file, 'note'); } + } }); this.addCommand({ id: "upload-all-media-assets-cloudinary", name: "Upload all vault media assets to Cloudinary", callback: () => { - if(this.settings.ignoreWarnings){ + if (this.settings.ignoreWarnings) { this.uploadVault(); - }else{ - this.uploadNoteModal(undefined,'asset'); + } else { + this.uploadNoteModal(undefined, 'asset'); } } }); } - private uploadNoteModal(file?: TFile, type?:string) : void { + private uploadNoteModal(file?: TFile, type?: string): void { new NoteWarningModal(this.app, type, (result): void => { - if (result == 'true') { - this.uploadCurrentNoteFiles(file); - return; + if (file) { + if (result == 'true') { + this.uploadCurrentNoteFiles(file); + return; + } else { + return; + } } else { - return; + if (type == 'asset') { + this.uploadVault(); + } } }).open(); } @@ -119,7 +131,7 @@ export default class CloudinaryUploader extends Plugin { const formData = new FormData(); formData.append('file', file); formData.append('upload_preset', this.settings.uploadPreset); - formData.append('folder', this.setSubfolder(file,undefined)); + formData.append('folder', this.settings.folder != '' ? this.setSubfolder(file, undefined) : ''); // Make API call axios({ @@ -162,12 +174,12 @@ export default class CloudinaryUploader extends Plugin { } else { return `${this.settings.folder}/${this.settings.rawSubfolder}`; } - }else if(resourceUrl){ - if (this.isType(resourceUrl,imageFormats)) { + } else if (resourceUrl) { + if (this.isType(resourceUrl, imageFormats)) { return `${this.settings.folder}/${this.settings.imageSubfolder}`; - } else if (this.isType(resourceUrl,audioFormats)) { + } else if (this.isType(resourceUrl, audioFormats)) { return `${this.settings.folder}/${this.settings.audioSubfolder}`; - } else if (this.isType(resourceUrl,videoFormats)) { + } else if (this.isType(resourceUrl, videoFormats)) { return `${this.settings.folder}/${this.settings.videoSubfolder}`; } else { return `${this.settings.folder}/${this.settings.rawSubfolder}`; @@ -205,22 +217,22 @@ export default class CloudinaryUploader extends Plugin { } } - private uploadVault() : void{ + private uploadVault(): void { const files = this.app.vault.getFiles() - for(let file of files){ - if(file.extension != 'md'){ + for (let file of files) { + if (file.extension != 'md') { let path; let filePath; - const adapter = this.app.vault.adapter; - if(adapter instanceof FileSystemAdapter){ - filePath = adapter.getFullPath(file.path); - console.log(path); - } - cloudinary.uploader.unsigned_upload(path,this.settings.uploadPreset,{ - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder,path.dirname(file.path)) : this.settings.backupFolder, - resourceType: 'auto' - }); + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(file.path); + console.log(path); } + cloudinary.uploader.unsigned_upload(path, this.settings.uploadPreset, { + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, + resourceType: 'auto' + }); + } } } private uploadCurrentNoteFiles(file: TFile): void { @@ -228,14 +240,14 @@ export default class CloudinaryUploader extends Plugin { data = result; }).then(() => { const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); - if(found && found.length > 0) for (let find of found) { + if (found && found.length > 0) for (let find of found) { let fileString = find.substring(3, find.length - 2); let filePath; const adapter = this.app.vault.adapter; if (adapter instanceof FileSystemAdapter) { filePath = adapter.getFullPath(fileString) cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.setSubfolder(undefined,filePath), + folder: this.setSubfolder(undefined, filePath), resource_type: 'auto' }).then(res => { console.log(res); @@ -259,19 +271,19 @@ export default class CloudinaryUploader extends Plugin { // Required as Cloudinary doesn't have an 'audio' resource type. // As we only know the file type after it's been uploaded (we don't know MIME type), // we check if audio was uploaded based on the most-commonly used audio formats - private isType(url: string, formats:string[]) : boolean{ + private isType(url: string, formats: string[]): boolean { let foundTypeMatch = false; - for(let format of formats){ - if(url.endsWith(format)){ + for (let format of formats) { + if (url.endsWith(format)) { foundTypeMatch = true; } } return foundTypeMatch; } private generateResourceUrl(type: string, url: string): string { - if (type == 'audio' || this.isType(url,audioFormats)) { + if (type == 'audio' || this.isType(url, audioFormats)) { return `\n`; - } else if (type == 'video' || this.isType(url,videoFormats)) { + } else if (type == 'video' || this.isType(url, videoFormats)) { return `\n`; } else { return `![](${url})`; From 449c1b645777b78482dda89ca3c5dcfb86ff9aea Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 07:04:41 -0400 Subject: [PATCH 35/52] refactor modal actions --- src/main.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 3b773ba..2bae5bd 100755 --- a/src/main.ts +++ b/src/main.ts @@ -45,7 +45,7 @@ export default class CloudinaryUploader extends Plugin { } } else { - this.uploadNoteModal(file, 'note'); + this.uploadNoteModal(undefined, 'note'); } } @@ -75,6 +75,13 @@ export default class CloudinaryUploader extends Plugin { } else { if (type == 'asset') { this.uploadVault(); + return; + } else if (type == 'note') { + const files = this.app.vault.getMarkdownFiles() + for (let file of files) { + this.uploadCurrentNoteFiles(file); + } + } } }).open(); @@ -257,7 +264,6 @@ export default class CloudinaryUploader extends Plugin { let replaceMarkdownText = this.generateResourceUrl(resType, url); data = data.replace(find, replaceMarkdownText); this.app.vault.process(file, () => { - console.log('this is data ' + data); return data; }) }, err => { From 8d9d29762810f89c0ec7fec8b52ab84aefd5d829 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 07:57:23 -0400 Subject: [PATCH 36/52] remove extra files --- src/main.ts | 130 ++++++++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/src/main.ts b/src/main.ts index 2bae5bd..f276a8e 100755 --- a/src/main.ts +++ b/src/main.ts @@ -12,6 +12,8 @@ import axios from "axios" import objectPath from 'object-path' import path from 'path' import { v2 as cloudinary } from 'cloudinary'; + + // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; @@ -65,26 +67,76 @@ export default class CloudinaryUploader extends Plugin { private uploadNoteModal(file?: TFile, type?: string): void { new NoteWarningModal(this.app, type, (result): void => { - if (file) { - if (result == 'true') { + if (result == 'true') { + if (file) { this.uploadCurrentNoteFiles(file); return; } else { - return; + if (type == 'asset') { + this.uploadVault(); + return; + } else if (type == 'note') { + const files = this.app.vault.getMarkdownFiles() + for (let file of files) { + this.uploadCurrentNoteFiles(file); + } + + } } } else { - if (type == 'asset') { - this.uploadVault(); - return; - } else if (type == 'note') { - const files = this.app.vault.getMarkdownFiles() - for (let file of files) { - this.uploadCurrentNoteFiles(file); - } + return; + } + }).open(); + } + private uploadVault(): void { + const files = this.app.vault.getFiles() + for (let file of files) { + if (file.extension != 'md') { + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(file.path); + console.log(filePath); } + cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { + folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, + resourceType: 'auto' + }); } - }).open(); + } + } + private uploadCurrentNoteFiles(file: TFile): void { + let data = this.app.vault.cachedRead(file).then((result) => { + data = result; + }).then(() => { + const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); + if (found && found.length > 0) for (let find of found) { + let fileString = find.substring(3, find.length - 2); + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(fileString) + cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { + folder: this.setSubfolder(undefined, filePath), + resource_type: 'auto' + }).then(res => { + console.log(res); + let url = objectPath.get(res, 'secure_url'); + let resType = objectPath.get(res, 'resource_type'); + url = this.generateTransformParams(url); + let replaceMarkdownText = this.generateResourceUrl(resType, url); + data = data.replace(find, replaceMarkdownText); + this.app.vault.process(file, () => { + return data; + }) + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) + } + } + }); } private clearHandlers(): void { this.app.workspace.off('editor-paste', this.pasteHandler); @@ -223,57 +275,6 @@ export default class CloudinaryUploader extends Plugin { } } } - - private uploadVault(): void { - const files = this.app.vault.getFiles() - for (let file of files) { - if (file.extension != 'md') { - let path; - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(file.path); - console.log(path); - } - cloudinary.uploader.unsigned_upload(path, this.settings.uploadPreset, { - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, - resourceType: 'auto' - }); - } - } - } - private uploadCurrentNoteFiles(file: TFile): void { - let data = this.app.vault.cachedRead(file).then((result) => { - data = result; - }).then(() => { - const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); - if (found && found.length > 0) for (let find of found) { - let fileString = find.substring(3, find.length - 2); - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(fileString) - cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.setSubfolder(undefined, filePath), - resource_type: 'auto' - }).then(res => { - console.log(res); - let url = objectPath.get(res, 'secure_url'); - let resType = objectPath.get(res, 'resource_type'); - url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType, url); - data = data.replace(find, replaceMarkdownText); - this.app.vault.process(file, () => { - return data; - }) - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); - }) - } - } - }); - } // Required as Cloudinary doesn't have an 'audio' resource type. // As we only know the file type after it's been uploaded (we don't know MIME type), // we check if audio was uploaded based on the most-commonly used audio formats @@ -295,7 +296,6 @@ export default class CloudinaryUploader extends Plugin { return `![](${url})`; } } - // Plugin load steps async onload(): Promise { console.log("loading Cloudinary Uploader"); @@ -306,7 +306,7 @@ export default class CloudinaryUploader extends Plugin { cloudinary.config({ cloud_name: this.settings.cloudName }); - this.setCommands(); + this.setCommands() } // Plugin shutdown steps From 6f2e9459537f2ee8707b1ca9c7fc4f1a5ed66e94 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 07:59:52 -0400 Subject: [PATCH 37/52] add messaging --- src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.ts b/src/main.ts index f276a8e..532afd0 100755 --- a/src/main.ts +++ b/src/main.ts @@ -130,6 +130,7 @@ export default class CloudinaryUploader extends Plugin { this.app.vault.process(file, () => { return data; }) + new Notice("Upload of note files was completed"); }, err => { console.log(JSON.stringify(err)) new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); From b025f24b499eb9b7543be0a5a1b2e2e33db4b819 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 08:26:07 -0400 Subject: [PATCH 38/52] update documentation --- docs/cloudinary-duplication.md | 24 ++++++++++++++++++++++++ docs/configuring-cloudinary.md | 4 ++++ docs/plugin-commands/plugin-commands.md | 7 ++++--- src/settings-tab.ts | 2 +- 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 docs/cloudinary-duplication.md diff --git a/docs/cloudinary-duplication.md b/docs/cloudinary-duplication.md new file mode 100644 index 0000000..2299d0c --- /dev/null +++ b/docs/cloudinary-duplication.md @@ -0,0 +1,24 @@ +--- +label: Cloudinary Duplication +layout: default +order: 600 +author: Jordan Handy +icon: gear +--- +## Duplication in Cloudinary +Without proper configuration, it is highly likely that Cloudinary will not upload items as you expect. I recommend reading [Cloudinary documentation on Upload Presets](https://support.cloudinary.com/hc/en-us/articles/360016481620-What-are-Upload-presets-and-how-to-use-them). + +This doc will explain some of the most important pieces of information. + +### Use filename or externally-defined public ID +In your Upload Preset Settings under the Storage and Access menu, this option allows you to preserve filenames in Cloudinary. Normally, when you upload an item to Cloudinary, it is renamed with a unique public ID. If you would like to preserve filename, use this setting to allow for you to keep original filenames. The Obsidian plugin is configured such that filenames will be used if this setting is enabled. +![Externally defined public ID](https://res.cloudinary.com/dakfccuv5/image/upload/v1718108147/457f3fb2-f4c6-4235-8708-981ed138485d.png) + +## Unique Filename +If **disabled**, the Unique Filename option will guard against duplication. With this enabled, Cloudinary will append random characters to end string of every upload. This means that even if you preserve file names, no two uploads of the same file will match and every upload will be unique. This could lead to mass duplication. + +Keep this **disabled** so that if a file with the same filename already exists duplication is less-likely to occur +![Unique Filename](https://res.cloudinary.com/dakfccuv5/image/upload/v1718108164/46971520-e89b-4ae3-ad31-83a7790419d3.png) + + +Continue to [Plugin Commands](plugin-commands.md) \ No newline at end of file diff --git a/docs/configuring-cloudinary.md b/docs/configuring-cloudinary.md index d6ef5aa..b564c0c 100644 --- a/docs/configuring-cloudinary.md +++ b/docs/configuring-cloudinary.md @@ -23,4 +23,8 @@ When the preset is created, it will have a "name" associated with it. Use this If you have a folder name already configured on Cloudinary under the settings for your specific upload preset (can be configured on Cloudinary itself), this folder setting will be ignored. !!! +## Cloudinary and Potential Duplicate Uploads +I recommend double-checking all of your Cloudinary Upload Preset settings before you begin to use the plugin. +[!ref Cloudinary Duplication](cloudinary-duplication.md) + Continue to [Plugin Commands](plugin-commands.md) \ No newline at end of file diff --git a/docs/plugin-commands/plugin-commands.md b/docs/plugin-commands/plugin-commands.md index a6a0cf1..c677eab 100644 --- a/docs/plugin-commands/plugin-commands.md +++ b/docs/plugin-commands/plugin-commands.md @@ -8,7 +8,8 @@ icon: gear ## Plugin Commands The Cloudinary Uploader plugin has palette commands that can be run. Explore them below: -- Upload single note assets to Cloudinary -- Upload all note assets to Cloudinary -- Local asset file backup +- [Upload single note assets to Cloudinary](upload-single-note-cloudinary.md) +- [Upload all note assets to Cloudinary](upload-all-notes-cloudinary.md) +- [Local asset file backup](backup-vault-assets.md) + Continue to [Upload single note assets to Cloudinary](upload-single-note-cloudinary.md) \ No newline at end of file diff --git a/src/settings-tab.ts b/src/settings-tab.ts index 0b466cf..af39eaa 100755 --- a/src/settings-tab.ts +++ b/src/settings-tab.ts @@ -339,7 +339,7 @@ export default class CloudinaryUploaderSettingTab extends PluginSettingTab { containerEl.createEl("h5", { text: "File names, file conflicts, overwrite behaviour" }); link = document.createElement("a"); link.text="plugin documentation "; - link.href="https://google.ca"; + link.href="https://jordanhandy.github.io/obsidian-cloudinary-uploader/cloudinary-duplication/"; textFragment = document.createDocumentFragment(); textFragment.append("Assuming all defaults in your Cloudinary Upload Preset settings, all file backups will receive a unique public ID (file name) within the Cloudinary console."+ " This may make it hard to identify. Additionally, file uploads will always be overwritten. You can use a combination of settings for unique file naming as found in "); From f809d259b6e85d0d28067b130e3cb19214cc8419 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Tue, 11 Jun 2024 08:30:00 -0400 Subject: [PATCH 39/52] update documentation --- docs/plugin-commands/backup-vault-assets.md | 4 +++- docs/plugin-commands/upload-single-note-cloudinary.md | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/plugin-commands/backup-vault-assets.md b/docs/plugin-commands/backup-vault-assets.md index 36d1f77..4608849 100644 --- a/docs/plugin-commands/backup-vault-assets.md +++ b/docs/plugin-commands/backup-vault-assets.md @@ -9,4 +9,6 @@ icon: cloud !!!warning This is an experimental feature. Your mileage may vary as to the success of this command because of the issues already outlined in [uploading single note content](upload-single-note-cloudinary.md) and [upload all note contents](upload-all-notes-cloudinary.md). This command attempts to read all "non-markdown" files in your vault and upload a copy of them to Cloudinary. The "Backup Folder" option is where these files are stored. The "Preserve File Paths" option attempts to maintain the same foldering structure you have in your vault, prepended with your specified backup folder as the root. -!!! \ No newline at end of file +!!! + +[See note on Cloudinary Duplication](../cloudinary-duplication.md) \ No newline at end of file diff --git a/docs/plugin-commands/upload-single-note-cloudinary.md b/docs/plugin-commands/upload-single-note-cloudinary.md index 36a62cd..ae4a722 100644 --- a/docs/plugin-commands/upload-single-note-cloudinary.md +++ b/docs/plugin-commands/upload-single-note-cloudinary.md @@ -24,4 +24,4 @@ When you first use the option for mass note upload, you will be presented with a View a demo of the command below: ![Demo video - mass upload](https://res.cloudinary.com/dakfccuv5/video/upload/v1718021709/mass-note-upload_qnx5ar.mp4) -Continue to [Configuring Cloudinary](configuring-cloudinary.md) \ No newline at end of file +Continue to [Uploading all note assets to Cloudinary](upload-all-notes-cloudinary.md) \ No newline at end of file From 45a3d1df1766eca0bc8fa35ace61e319f50a3d58 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:07:50 -0400 Subject: [PATCH 40/52] func refactoring --- src/commands/utils.ts | 143 +++++++++++++++++++++++++++++++++++++ src/main.ts | 161 ++++-------------------------------------- 2 files changed, 157 insertions(+), 147 deletions(-) create mode 100644 src/commands/utils.ts diff --git a/src/commands/utils.ts b/src/commands/utils.ts new file mode 100644 index 0000000..e629f0b --- /dev/null +++ b/src/commands/utils.ts @@ -0,0 +1,143 @@ +import {Notice,FileSystemAdapter, TFile} from 'obsidian'; +import path from 'path'; +import objectPath from 'object-path' +import { NoteWarningModal } from '../note-warning-modal'; +import { v2 as cloudinary } from 'cloudinary'; +import { audioFormats, videoFormats, imageFormats } from '../formats'; +import CloudinaryUploader from '../main'; +import { CloudinarySettings } from '../settings-tab'; + +export function uploadNoteModal(file?: TFile, type?: string,plugin:CloudinaryUploader): void { + new NoteWarningModal(this.app, type, (result): void => { + if (result == 'true') { + if (file) { + uploadCurrentNoteFiles(file,plugin); + return; + } else { + if (type == 'asset') { + uploadVault(plugin); + return; + } else if (type == 'note') { + const files = this.app.vault.getMarkdownFiles() + for (let file of files) { + uploadCurrentNoteFiles(file,plugin); + } + + } + } + } else { + return; + } + + }).open(); + } + +export function uploadVault(plugin:CloudinaryUploader): void { + + const files = this.app.vault.getFiles() + for (let file of files) { + if (file.extension != 'md') { + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(file.path); + console.log(filePath); + } + cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { + folder: settings.preserveBackupFilePath ? path.join(settings.backupFolder, path.dirname(file.path)) : settings.backupFolder, + resourceType: 'auto' + }); + } + } + } + export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { + let data = this.app.vault.cachedRead(file).then((result) => { + data = result; + }).then(() => { + const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); + if (found && found.length > 0) for (let find of found) { + let fileString = find.substring(3, find.length - 2); + let filePath; + const adapter = this.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(fileString) + cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { + folder: setSubfolder(undefined, filePath,plugin), + resource_type: 'auto' + }).then(res => { + console.log(res); + let url = objectPath.get(res, 'secure_url'); + let resType = objectPath.get(res, 'resource_type'); + url = generateTransformParams(url,plugin); + let replaceMarkdownText = generateResourceUrl(resType, url); + data = data.replace(find, replaceMarkdownText); + this.app.vault.process(file, () => { + return data; + }) + new Notice("Upload of note files was completed"); + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) + } + } + }); + } + export function generateTransformParams(url: string, plugin:CloudinaryUploader): string { + if (plugin.settings.transformParams) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/" + plugin.settings.transformParams + "/" + splitURL[1]; + } + if (plugin.settings.f_auto) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; + // leave standard of no transformations added + } + return url; + } + export function generateResourceUrl(type: string, url: string): string { + if (type == 'audio' || isType(url, audioFormats)) { + return `\n`; + } else if (type == 'video' || isType(url, videoFormats)) { + return `\n`; + } else { + return `![](${url})`; + } + } + // Required as Cloudinary doesn't have an 'audio' resource type. + // As we only know the file type after it's been uploaded (we don't know MIME type), + // we check if audio was uploaded based on the most-commonly used audio formats + export function isType(url: string, formats: string[]): boolean { + let foundTypeMatch = false; + for (let format of formats) { + if (url.endsWith(format)) { + foundTypeMatch = true; + } + } + return foundTypeMatch; + } + export function setSubfolder(file: File, resourceUrl: string,settings:CloudinaryUploader): string { + if (file) { + if (file.type && file.type.startsWith("image")) { + return `${settings.folder}/${settings.imageSubfolder}`; + } else if (file.type.startsWith("audio")) { + return `${settings.folder}/${settings.audioSubfolder}`; + } else if (file.type.startsWith("video")) { + return `${settings.folder}/${settings.videoSubfolder}`; + } else { + return `${settings.folder}/${settings.rawSubfolder}`; + } + } else if (resourceUrl) { + if (isType(resourceUrl, imageFormats)) { + return `${settings.folder}/${settings.imageSubfolder}`; + } else if (isType(resourceUrl, audioFormats)) { + return `${settings.folder}/${settings.audioSubfolder}`; + } else if (isType(resourceUrl, videoFormats)) { + return `${settings.folder}/${settings.videoSubfolder}`; + } else { + return `${settings.folder}/${settings.rawSubfolder}`; + } + } + + } + \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 532afd0..8dc9b8a 100755 --- a/src/main.ts +++ b/src/main.ts @@ -3,15 +3,13 @@ import { Notice, Plugin, Editor, - FileSystemAdapter, - TFile } from "obsidian"; // For API requests import axios from "axios" import objectPath from 'object-path' -import path from 'path' import { v2 as cloudinary } from 'cloudinary'; +import { uploadVault, uploadNoteModal, uploadCurrentNoteFiles, setSubfolder, generateResourceUrl,generateTransformParams } from "./commands/utils"; // Settings tab import @@ -29,9 +27,9 @@ export default class CloudinaryUploader extends Plugin { callback: () => { let file = this.app.workspace.getActiveFile(); if (this.settings.ignoreWarnings) { - this.uploadCurrentNoteFiles(file); + uploadCurrentNoteFiles(file,this); } else { - this.uploadNoteModal(file, 'note'); + uploadNoteModal(file, 'note',this); } } @@ -43,11 +41,11 @@ export default class CloudinaryUploader extends Plugin { const files = this.app.vault.getMarkdownFiles() if (this.settings.ignoreWarnings) { for (let file of files) { - this.uploadCurrentNoteFiles(file); + uploadCurrentNoteFiles(file,this); } } else { - this.uploadNoteModal(undefined, 'note'); + uploadNoteModal(undefined, 'note',this); } } @@ -57,84 +55,9 @@ export default class CloudinaryUploader extends Plugin { name: "Upload all vault media assets to Cloudinary", callback: () => { if (this.settings.ignoreWarnings) { - this.uploadVault(); + uploadVault(this); } else { - this.uploadNoteModal(undefined, 'asset'); - } - } - }); - } - - private uploadNoteModal(file?: TFile, type?: string): void { - new NoteWarningModal(this.app, type, (result): void => { - if (result == 'true') { - if (file) { - this.uploadCurrentNoteFiles(file); - return; - } else { - if (type == 'asset') { - this.uploadVault(); - return; - } else if (type == 'note') { - const files = this.app.vault.getMarkdownFiles() - for (let file of files) { - this.uploadCurrentNoteFiles(file); - } - - } - } - } else { - return; - } - - }).open(); - } - private uploadVault(): void { - const files = this.app.vault.getFiles() - for (let file of files) { - if (file.extension != 'md') { - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(file.path); - console.log(filePath); - } - cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.settings.preserveBackupFilePath ? path.join(this.settings.backupFolder, path.dirname(file.path)) : this.settings.backupFolder, - resourceType: 'auto' - }); - } - } - } - private uploadCurrentNoteFiles(file: TFile): void { - let data = this.app.vault.cachedRead(file).then((result) => { - data = result; - }).then(() => { - const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); - if (found && found.length > 0) for (let find of found) { - let fileString = find.substring(3, find.length - 2); - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(fileString) - cloudinary.uploader.unsigned_upload(filePath, this.settings.uploadPreset, { - folder: this.setSubfolder(undefined, filePath), - resource_type: 'auto' - }).then(res => { - console.log(res); - let url = objectPath.get(res, 'secure_url'); - let resType = objectPath.get(res, 'resource_type'); - url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(resType, url); - data = data.replace(find, replaceMarkdownText); - this.app.vault.process(file, () => { - return data; - }) - new Notice("Upload of note files was completed"); - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); - }) + uploadNoteModal(undefined, 'asset',this); } } }); @@ -191,7 +114,7 @@ export default class CloudinaryUploader extends Plugin { const formData = new FormData(); formData.append('file', file); formData.append('upload_preset', this.settings.uploadPreset); - formData.append('folder', this.settings.folder != '' ? this.setSubfolder(file, undefined) : ''); + formData.append('folder', this.settings.folder != '' ? setSubfolder(file, undefined,this) : ''); // Make API call axios({ @@ -204,8 +127,8 @@ export default class CloudinaryUploader extends Plugin { let url = objectPath.get(res.data, 'secure_url'); let resType = objectPath.get(res.data, 'resource_type'); // Split URL to allow for appending transformations - url = this.generateTransformParams(url); - let replaceMarkdownText = this.generateResourceUrl(file.type, url); + url = generateTransformParams(url); + let replaceMarkdownText = generateResourceUrl(file.type, url); // Show MD syntax using uploaded image URL, in Obsidian Editor this.replaceText(editor, pastePlaceText, replaceMarkdownText) }, err => { @@ -222,43 +145,7 @@ export default class CloudinaryUploader extends Plugin { } } - // Set subfolder for upload - private setSubfolder(file?: File, resourceUrl?: string): string { - if (file) { - if (file.type && file.type.startsWith("image")) { - return `${this.settings.folder}/${this.settings.imageSubfolder}`; - } else if (file.type.startsWith("audio")) { - return `${this.settings.folder}/${this.settings.audioSubfolder}`; - } else if (file.type.startsWith("video")) { - return `${this.settings.folder}/${this.settings.videoSubfolder}`; - } else { - return `${this.settings.folder}/${this.settings.rawSubfolder}`; - } - } else if (resourceUrl) { - if (this.isType(resourceUrl, imageFormats)) { - return `${this.settings.folder}/${this.settings.imageSubfolder}`; - } else if (this.isType(resourceUrl, audioFormats)) { - return `${this.settings.folder}/${this.settings.audioSubfolder}`; - } else if (this.isType(resourceUrl, videoFormats)) { - return `${this.settings.folder}/${this.settings.videoSubfolder}`; - } else { - return `${this.settings.folder}/${this.settings.rawSubfolder}`; - } - } - } - private generateTransformParams(url: string): string { - if (this.settings.transformParams) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/" + this.settings.transformParams + "/" + splitURL[1]; - } - if (this.settings.f_auto) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; - // leave standard of no transformations added - } - return url; - } // Function to replace text private replaceText(editor: Editor, target: string, replacement: string): void { target = target.trim(); @@ -276,39 +163,19 @@ export default class CloudinaryUploader extends Plugin { } } } - // Required as Cloudinary doesn't have an 'audio' resource type. - // As we only know the file type after it's been uploaded (we don't know MIME type), - // we check if audio was uploaded based on the most-commonly used audio formats - private isType(url: string, formats: string[]): boolean { - let foundTypeMatch = false; - for (let format of formats) { - if (url.endsWith(format)) { - foundTypeMatch = true; - } - } - return foundTypeMatch; - } - private generateResourceUrl(type: string, url: string): string { - if (type == 'audio' || this.isType(url, audioFormats)) { - return `\n`; - } else if (type == 'video' || this.isType(url, videoFormats)) { - return `\n`; - } else { - return `![](${url})`; - } - } + // Plugin load steps async onload(): Promise { console.log("loading Cloudinary Uploader"); await this.loadSettings(); this.clearHandlers(); this.setupHandlers(); - this.addSettingTab(new CloudinaryUploaderSettingTab(this.app, this)); + this.addSettingTab(new CloudinaryUploaderSettingTab(this.app,this)); cloudinary.config({ cloud_name: this.settings.cloudName }); - this.setCommands() - } + this.setCommands(); + } // Plugin shutdown steps onunload(): void { From 7edf8f29cf52eeb02189a2cebf0efa5f793d8cd3 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:08:54 -0400 Subject: [PATCH 41/52] fix imports --- src/main.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main.ts b/src/main.ts index 8dc9b8a..2705d60 100755 --- a/src/main.ts +++ b/src/main.ts @@ -15,8 +15,6 @@ import { uploadVault, uploadNoteModal, uploadCurrentNoteFiles, setSubfolder, gen // Settings tab import import CloudinaryUploaderSettingTab from './settings-tab' import { DEFAULT_SETTINGS, CloudinarySettings } from "./settings-tab"; -import { NoteWarningModal } from "./note-warning-modal"; -import { audioFormats, imageFormats, videoFormats } from "./formats"; export default class CloudinaryUploader extends Plugin { settings: CloudinarySettings; From 0e5116c90dfd2ebfae606a31b595ebba626883b6 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:18:31 -0400 Subject: [PATCH 42/52] references fix --- src/commands/utils.ts | 235 +++++++++++++++++++++--------------------- 1 file changed, 119 insertions(+), 116 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index e629f0b..d645e99 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -1,143 +1,146 @@ -import {Notice,FileSystemAdapter, TFile} from 'obsidian'; +/*--------- Utilities for Commands and async uploads -----------*/ + +import { Notice, FileSystemAdapter, TFile } from 'obsidian'; import path from 'path'; import objectPath from 'object-path' import { NoteWarningModal } from '../note-warning-modal'; import { v2 as cloudinary } from 'cloudinary'; import { audioFormats, videoFormats, imageFormats } from '../formats'; import CloudinaryUploader from '../main'; -import { CloudinarySettings } from '../settings-tab'; -export function uploadNoteModal(file?: TFile, type?: string,plugin:CloudinaryUploader): void { - new NoteWarningModal(this.app, type, (result): void => { - if (result == 'true') { - if (file) { - uploadCurrentNoteFiles(file,plugin); +// Generates a modal when command is invoked +// References 'plugin' as we need access to settings on Cloudinary Uploader +export function uploadNoteModal(file?: TFile, type: string, plugin: CloudinaryUploader): void { + new NoteWarningModal(plugin.app, type, (result): void => { + if (result == 'true') { + if (file) { + uploadCurrentNoteFiles(file, plugin); // If a file was passed and modal agreed + return; + } else { + // If no file passed, but assets were to be uploaded + if (type == 'asset') { + uploadVault(plugin); // Upload vault function return; - } else { - if (type == 'asset') { - uploadVault(plugin); - return; - } else if (type == 'note') { - const files = this.app.vault.getMarkdownFiles() - for (let file of files) { - uploadCurrentNoteFiles(file,plugin); - } - + } else if (type == 'note') { //! If no file passed, but 'notes' to be uploaded, this means all notes are requested. + const files = plugin.app.vault.getMarkdownFiles() + for (let file of files) { + uploadCurrentNoteFiles(file, plugin); } + } - } else { - return; } + } else { + return; + } - }).open(); - } + }).open(); +} -export function uploadVault(plugin:CloudinaryUploader): void { +export function uploadVault(plugin: CloudinaryUploader): void { - const files = this.app.vault.getFiles() - for (let file of files) { - if (file.extension != 'md') { - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(file.path); - console.log(filePath); - } - cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { - folder: settings.preserveBackupFilePath ? path.join(settings.backupFolder, path.dirname(file.path)) : settings.backupFolder, - resourceType: 'auto' - }); + const files = plugin.app.vault.getFiles() + for (let file of files) { + if (file.extension != 'md') { + let filePath; + const adapter = plugin.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(file.path); + console.log(filePath); } + cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { + folder: plugin.settings.preserveBackupFilePath ? path.join(plugin.settings.backupFolder, path.dirname(file.path)) : plugin.settings.backupFolder, + resourceType: 'auto' + }); } } - export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { - let data = this.app.vault.cachedRead(file).then((result) => { - data = result; - }).then(() => { - const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); - if (found && found.length > 0) for (let find of found) { - let fileString = find.substring(3, find.length - 2); - let filePath; - const adapter = this.app.vault.adapter; - if (adapter instanceof FileSystemAdapter) { - filePath = adapter.getFullPath(fileString) - cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { - folder: setSubfolder(undefined, filePath,plugin), - resource_type: 'auto' - }).then(res => { - console.log(res); - let url = objectPath.get(res, 'secure_url'); - let resType = objectPath.get(res, 'resource_type'); - url = generateTransformParams(url,plugin); - let replaceMarkdownText = generateResourceUrl(resType, url); - data = data.replace(find, replaceMarkdownText); - this.app.vault.process(file, () => { - return data; - }) - new Notice("Upload of note files was completed"); - }, err => { - console.log(JSON.stringify(err)) - new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); +} +export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { + let data = plugin.app.vault.cachedRead(file).then((result) => { + data = result; + }).then(() => { + const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); + if (found && found.length > 0) for (let find of found) { + let fileString = find.substring(3, find.length - 2); + let filePath; + const adapter = plugin.app.vault.adapter; + if (adapter instanceof FileSystemAdapter) { + filePath = adapter.getFullPath(fileString) + cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { + folder: setSubfolder(undefined, filePath, plugin), + resource_type: 'auto' + }).then(res => { + console.log(res); + let url = objectPath.get(res, 'secure_url'); + let resType = objectPath.get(res, 'resource_type'); + url = generateTransformParams(url, plugin); + let replaceMarkdownText = generateResourceUrl(resType, url); + data = data.replace(find, replaceMarkdownText); + plugin.app.vault.process(file, () => { + return data; }) - } + new Notice("Upload of note files was completed"); + }, err => { + console.log(JSON.stringify(err)) + new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); + }) } - }); - } - export function generateTransformParams(url: string, plugin:CloudinaryUploader): string { - if (plugin.settings.transformParams) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/" + plugin.settings.transformParams + "/" + splitURL[1]; } - if (plugin.settings.f_auto) { - const splitURL = url.split("/upload/", 2); - url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; - // leave standard of no transformations added + }); +} +export function generateTransformParams(url: string, plugin: CloudinaryUploader): string { + if (plugin.settings.transformParams) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/" + plugin.settings.transformParams + "/" + splitURL[1]; + } + if (plugin.settings.f_auto) { + const splitURL = url.split("/upload/", 2); + url = splitURL[0] += "/upload/f_auto/" + splitURL[1]; + // leave standard of no transformations added + } + return url; +} +export function generateResourceUrl(type: string, url: string): string { + if (type == 'audio' || isType(url, audioFormats)) { + return `\n`; + } else if (type == 'video' || isType(url, videoFormats)) { + return `\n`; + } else { + return `![](${url})`; + } +} +// Required as Cloudinary doesn't have an 'audio' resource type. +// As we only know the file type after it's been uploaded (we don't know MIME type), +// we check if audio was uploaded based on the most-commonly used audio formats +export function isType(url: string, formats: string[]): boolean { + let foundTypeMatch = false; + for (let format of formats) { + if (url.endsWith(format)) { + foundTypeMatch = true; } - return url; } - export function generateResourceUrl(type: string, url: string): string { - if (type == 'audio' || isType(url, audioFormats)) { - return `\n`; - } else if (type == 'video' || isType(url, videoFormats)) { - return `\n`; + return foundTypeMatch; +} +export function setSubfolder(file: File, resourceUrl: string, settings: CloudinaryUploader): string { + if (file) { + if (file.type && file.type.startsWith("image")) { + return `${settings.folder}/${settings.imageSubfolder}`; + } else if (file.type.startsWith("audio")) { + return `${settings.folder}/${settings.audioSubfolder}`; + } else if (file.type.startsWith("video")) { + return `${settings.folder}/${settings.videoSubfolder}`; } else { - return `![](${url})`; + return `${settings.folder}/${settings.rawSubfolder}`; } - } - // Required as Cloudinary doesn't have an 'audio' resource type. - // As we only know the file type after it's been uploaded (we don't know MIME type), - // we check if audio was uploaded based on the most-commonly used audio formats - export function isType(url: string, formats: string[]): boolean { - let foundTypeMatch = false; - for (let format of formats) { - if (url.endsWith(format)) { - foundTypeMatch = true; - } + } else if (resourceUrl) { + if (isType(resourceUrl, imageFormats)) { + return `${settings.folder}/${settings.imageSubfolder}`; + } else if (isType(resourceUrl, audioFormats)) { + return `${settings.folder}/${settings.audioSubfolder}`; + } else if (isType(resourceUrl, videoFormats)) { + return `${settings.folder}/${settings.videoSubfolder}`; + } else { + return `${settings.folder}/${settings.rawSubfolder}`; } - return foundTypeMatch; } - export function setSubfolder(file: File, resourceUrl: string,settings:CloudinaryUploader): string { - if (file) { - if (file.type && file.type.startsWith("image")) { - return `${settings.folder}/${settings.imageSubfolder}`; - } else if (file.type.startsWith("audio")) { - return `${settings.folder}/${settings.audioSubfolder}`; - } else if (file.type.startsWith("video")) { - return `${settings.folder}/${settings.videoSubfolder}`; - } else { - return `${settings.folder}/${settings.rawSubfolder}`; - } - } else if (resourceUrl) { - if (isType(resourceUrl, imageFormats)) { - return `${settings.folder}/${settings.imageSubfolder}`; - } else if (isType(resourceUrl, audioFormats)) { - return `${settings.folder}/${settings.audioSubfolder}`; - } else if (isType(resourceUrl, videoFormats)) { - return `${settings.folder}/${settings.videoSubfolder}`; - } else { - return `${settings.folder}/${settings.rawSubfolder}`; - } - } - } - \ No newline at end of file +} From a778761ca585348d579f3f1789fb8214ba14fcc3 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:40:23 -0400 Subject: [PATCH 43/52] comments - refactoring --- src/commands/utils.ts | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index d645e99..48f32b5 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -37,7 +37,8 @@ export function uploadNoteModal(file?: TFile, type: string, plugin: CloudinaryUp } export function uploadVault(plugin: CloudinaryUploader): void { - + //* Get all files in vault that are not + //* MD files, so they may be uploaded const files = plugin.app.vault.getFiles() for (let file of files) { if (file.extension != 'md') { @@ -55,6 +56,14 @@ export function uploadVault(plugin: CloudinaryUploader): void { } } export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { + //! Read a cached version of the file, then: + /* + * Find a RegEx match for [[]] file refs + * Get file name and find full path of file + * Pass full path to Cloudinary for upload + * Based on answer returned, determine subfolder, then: + * Replace current strings with Cloudinary URLs + */ let data = plugin.app.vault.cachedRead(file).then((result) => { data = result; }).then(() => { @@ -78,15 +87,17 @@ export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): plugin.app.vault.process(file, () => { return data; }) - new Notice("Upload of note files was completed"); + new Notice("Upload of note files was completed"); // Success }, err => { - console.log(JSON.stringify(err)) + // Failure new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); }) } } }); } +// Called to generate the output of the transformation parameters +// that are set on uploads export function generateTransformParams(url: string, plugin: CloudinaryUploader): string { if (plugin.settings.transformParams) { const splitURL = url.split("/upload/", 2); @@ -99,6 +110,8 @@ export function generateTransformParams(url: string, plugin: CloudinaryUploader) } return url; } +// Once upload completes, generate the resulting getMarkdown +// that will be written to file export function generateResourceUrl(type: string, url: string): string { if (type == 'audio' || isType(url, audioFormats)) { return `\n`; @@ -120,26 +133,28 @@ export function isType(url: string, formats: string[]): boolean { } return foundTypeMatch; } -export function setSubfolder(file: File, resourceUrl: string, settings: CloudinaryUploader): string { +// Determine the subfolder to place the uploaded +// file in, based on file type uploaded +export function setSubfolder(file: File, resourceUrl: string, plugin: CloudinaryUploader): string { if (file) { if (file.type && file.type.startsWith("image")) { - return `${settings.folder}/${settings.imageSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.imageSubfolder}`; } else if (file.type.startsWith("audio")) { - return `${settings.folder}/${settings.audioSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.audioSubfolder}`; } else if (file.type.startsWith("video")) { - return `${settings.folder}/${settings.videoSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.videoSubfolder}`; } else { - return `${settings.folder}/${settings.rawSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.rawSubfolder}`; } } else if (resourceUrl) { if (isType(resourceUrl, imageFormats)) { - return `${settings.folder}/${settings.imageSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.imageSubfolder}`; } else if (isType(resourceUrl, audioFormats)) { - return `${settings.folder}/${settings.audioSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.audioSubfolder}`; } else if (isType(resourceUrl, videoFormats)) { - return `${settings.folder}/${settings.videoSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.videoSubfolder}`; } else { - return `${settings.folder}/${settings.rawSubfolder}`; + return `${plugin.settings.folder}/${plugin.settings.rawSubfolder}`; } } From 689fded88c5eeaf0917ac20cfab03e5f6cd39aa9 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:48:19 -0400 Subject: [PATCH 44/52] vault upload completion messaging --- src/commands/utils.ts | 4 ++++ src/main.ts | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index 48f32b5..d6a7a0b 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -51,6 +51,10 @@ export function uploadVault(plugin: CloudinaryUploader): void { cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { folder: plugin.settings.preserveBackupFilePath ? path.join(plugin.settings.backupFolder, path.dirname(file.path)) : plugin.settings.backupFolder, resourceType: 'auto' + }).then(res=>{ + new Notice("Vault upload completed",0); + },err=>{ + new Notice("There was an error somewhere uploading your files "+err.message); }); } } diff --git a/src/main.ts b/src/main.ts index 2705d60..3795bb7 100755 --- a/src/main.ts +++ b/src/main.ts @@ -125,7 +125,7 @@ export default class CloudinaryUploader extends Plugin { let url = objectPath.get(res.data, 'secure_url'); let resType = objectPath.get(res.data, 'resource_type'); // Split URL to allow for appending transformations - url = generateTransformParams(url); + url = generateTransformParams(url,this); let replaceMarkdownText = generateResourceUrl(file.type, url); // Show MD syntax using uploaded image URL, in Obsidian Editor this.replaceText(editor, pastePlaceText, replaceMarkdownText) @@ -169,6 +169,8 @@ export default class CloudinaryUploader extends Plugin { this.clearHandlers(); this.setupHandlers(); this.addSettingTab(new CloudinaryUploaderSettingTab(this.app,this)); + + // Set cloudinary cloud name config for node module cloudinary.config({ cloud_name: this.settings.cloudName }); From 8e8d8e27c7255a19197ce6afc0a4720129772d95 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:56:29 -0400 Subject: [PATCH 45/52] fix vault upload completion messaging --- src/commands/utils.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index d6a7a0b..74fef98 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -40,6 +40,8 @@ export function uploadVault(plugin: CloudinaryUploader): void { //* Get all files in vault that are not //* MD files, so they may be uploaded const files = plugin.app.vault.getFiles() + let successMessages = []; + let failureMessages = []; for (let file of files) { if (file.extension != 'md') { let filePath; @@ -52,11 +54,20 @@ export function uploadVault(plugin: CloudinaryUploader): void { folder: plugin.settings.preserveBackupFilePath ? path.join(plugin.settings.backupFolder, path.dirname(file.path)) : plugin.settings.backupFolder, resourceType: 'auto' }).then(res=>{ - new Notice("Vault upload completed",0); + successMessages.push('success') },err=>{ - new Notice("There was an error somewhere uploading your files "+err.message); + failureMessages.push(err.message); }); } + }if(successMessages.length > 0 && failureMessages.length > 0){ + new Notice("There was some success in uploading your vault media to Cloudinary. Look for error notices to discover what needs to be fixed",0); + }else if(successMessages.length > 0 && failureMessages.length < 1){ + new Notice("Vault complete backup was successful. No error messages to report",0); + }if(failureMessages.length > 0){ + new Notice("There was some failure in uploading some files. An array of all failure messages has been printed to console as well as this Notice in the case the notice is too difficult to read "+failureMessages,0); + for(let msg of failureMessages){ + console.warn("Vault upload failure: "+msg); + } } } export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { From 6332d11bf9ca495acdff2fe4e7fd0d6d34a43528 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 00:57:16 -0400 Subject: [PATCH 46/52] comments --- src/commands/utils.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index 74fef98..bf90aef 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -54,12 +54,14 @@ export function uploadVault(plugin: CloudinaryUploader): void { folder: plugin.settings.preserveBackupFilePath ? path.join(plugin.settings.backupFolder, path.dirname(file.path)) : plugin.settings.backupFolder, resourceType: 'auto' }).then(res=>{ - successMessages.push('success') + successMessages.push('success'); // tag success },err=>{ - failureMessages.push(err.message); + failureMessages.push(err.message); // tag failure }); } - }if(successMessages.length > 0 && failureMessages.length > 0){ + } + // Display messaging + if(successMessages.length > 0 && failureMessages.length > 0){ new Notice("There was some success in uploading your vault media to Cloudinary. Look for error notices to discover what needs to be fixed",0); }else if(successMessages.length > 0 && failureMessages.length < 1){ new Notice("Vault complete backup was successful. No error messages to report",0); From 231c003cec2dc46ed8b18b08993970f601bf1b56 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 01:05:30 -0400 Subject: [PATCH 47/52] bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 725eec8..27d67b4 100755 --- a/manifest.json +++ b/manifest.json @@ -5,5 +5,5 @@ "author": "Jordan Handy", "isDesktopOnly": true, "minAppVersion": "0.11.0", - "version": "0.3.1" + "version": "0.4.0" } From 4273549093883f37f347532045ad7a50881089b9 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 01:16:56 -0400 Subject: [PATCH 48/52] add readme video --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index b7f6ff5..95cae0e 100755 --- a/README.md +++ b/README.md @@ -25,7 +25,10 @@ Released under [MIT](/LICENSE) by [@jordanhandy](https://github.com/jordanhandy) This plugin allows you to automatically upload images, video, audio and raw files pasted to Obsidian directly into your Cloudinary account (instead of stored locally). Note: There is functionality for media manipulation in this plugin using Cloudinary's custom parameters ## How it Works +### Single File Upload ![Action GIF](https://res.cloudinary.com/dakfccuv5/image/upload/v1636859613/Nov-13-2021_22-11-40_bpei0d.gif) +### Multi-file Upload +![Multi File](https://res.cloudinary.com/dakfccuv5/video/upload/v1718021709/mass-note-upload_qnx5ar.mp4) ## Configuration 1. Disable Obsidian Safe Mode 2. Install the Plugin From 9f6d7561b4a645e49a3ca3e7b3a868855f197516 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 07:17:04 -0400 Subject: [PATCH 49/52] fix mass asset uploading --- src/commands/utils.ts | 46 +++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index bf90aef..19a3e36 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -19,7 +19,8 @@ export function uploadNoteModal(file?: TFile, type: string, plugin: CloudinaryUp } else { // If no file passed, but assets were to be uploaded if (type == 'asset') { - uploadVault(plugin); // Upload vault function + fetchMessages(plugin); + //uploadVault(plugin); // Upload vault function return; } else if (type == 'note') { //! If no file passed, but 'notes' to be uploaded, this means all notes are requested. const files = plugin.app.vault.getMarkdownFiles() @@ -36,41 +37,48 @@ export function uploadNoteModal(file?: TFile, type: string, plugin: CloudinaryUp }).open(); } -export function uploadVault(plugin: CloudinaryUploader): void { +export async function uploadVault(plugin: CloudinaryUploader): Promise { + let successMessages = []; + let failureMessages = []; //* Get all files in vault that are not //* MD files, so they may be uploaded const files = plugin.app.vault.getFiles() - let successMessages = []; - let failureMessages = []; + new Notice("Upload of vault files started. Depending on your vault size, this could take a long while. Watch for error or success notices",0); for (let file of files) { if (file.extension != 'md') { let filePath; const adapter = plugin.app.vault.adapter; if (adapter instanceof FileSystemAdapter) { filePath = adapter.getFullPath(file.path); - console.log(filePath); + //console.log(filePath); } - cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { + await cloudinary.uploader.unsigned_upload(filePath, plugin.settings.uploadPreset, { folder: plugin.settings.preserveBackupFilePath ? path.join(plugin.settings.backupFolder, path.dirname(file.path)) : plugin.settings.backupFolder, - resourceType: 'auto' + resource_type: 'auto' }).then(res=>{ - successMessages.push('success'); // tag success + successMessages.push('success') + //new Notice("Vault upload completed",0); },err=>{ - failureMessages.push(err.message); // tag failure + failureMessages.push(err.message); + // new Notice("There was an error somewhere uploading your files "+err.message); }); } } - // Display messaging - if(successMessages.length > 0 && failureMessages.length > 0){ - new Notice("There was some success in uploading your vault media to Cloudinary. Look for error notices to discover what needs to be fixed",0); - }else if(successMessages.length > 0 && failureMessages.length < 1){ - new Notice("Vault complete backup was successful. No error messages to report",0); - }if(failureMessages.length > 0){ - new Notice("There was some failure in uploading some files. An array of all failure messages has been printed to console as well as this Notice in the case the notice is too difficult to read "+failureMessages,0); - for(let msg of failureMessages){ - console.warn("Vault upload failure: "+msg); + return [successMessages,failureMessages]; +} +async function fetchMessages(plugin:CloudinaryUploader){ + uploadVault(plugin).then((data)=>{ + if(data[0].length > 0 && data[1].length > 0){ + new Notice("Cloudinary vault asset backup: There was some success in uploading vault files, as well as some errors. Open Developer Tools for error information in Console",0); + for(let msg of data[1]){ + console.warn(msg); + } + }else if(data[0].length >0){ + new Notice("Cloudinary vault asset backup: This was successfully completed. No errors to report",0); + }else if(data[1].length > 0){ + new Notice("Cloudinary vault asset backup: This operation failed. Please try again",0); } - } + }); } export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): void { //! Read a cached version of the file, then: From ea384925c19f4476a4f86f29c24da2efdc3a168c Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 07:20:54 -0400 Subject: [PATCH 50/52] comments --- src/commands/utils.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index 19a3e36..d7db7e8 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -56,17 +56,23 @@ export async function uploadVault(plugin: CloudinaryUploader): Promise{ + // add to success messages array successMessages.push('success') - //new Notice("Vault upload completed",0); },err=>{ + // add to failure messages array failureMessages.push(err.message); - // new Notice("There was an error somewhere uploading your files "+err.message); }); } } + // send messages return [successMessages,failureMessages]; } -async function fetchMessages(plugin:CloudinaryUploader){ + +//* This function fetches messages from the mass upload job +//* as this could take a while if the vault is larger +async function fetchMessages(plugin:CloudinaryUploader) : Promise{ + // After the upload action completes then + // retrieve data and display messages based on results. uploadVault(plugin).then((data)=>{ if(data[0].length > 0 && data[1].length > 0){ new Notice("Cloudinary vault asset backup: There was some success in uploading vault files, as well as some errors. Open Developer Tools for error information in Console",0); From 489d4476d1f15d1a14e26fbe7c88b9c002e13099 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 07:27:50 -0400 Subject: [PATCH 51/52] refactor --- src/commands/utils.ts | 9 +++------ src/main.ts | 5 +++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/commands/utils.ts b/src/commands/utils.ts index d7db7e8..fc698d9 100644 --- a/src/commands/utils.ts +++ b/src/commands/utils.ts @@ -50,7 +50,6 @@ export async function uploadVault(plugin: CloudinaryUploader): Promise{ +export async function fetchMessages(plugin:CloudinaryUploader) : Promise{ // After the upload action completes then // retrieve data and display messages based on results. uploadVault(plugin).then((data)=>{ @@ -95,9 +94,7 @@ export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): * Based on answer returned, determine subfolder, then: * Replace current strings with Cloudinary URLs */ - let data = plugin.app.vault.cachedRead(file).then((result) => { - data = result; - }).then(() => { + let data = plugin.app.vault.cachedRead(file).then(() => { const found = data.match(/\!\[\[(?!https?:\/\/).*?\]\]/g); if (found && found.length > 0) for (let find of found) { let fileString = find.substring(3, find.length - 2); @@ -118,7 +115,7 @@ export function uploadCurrentNoteFiles(file: TFile, plugin: CloudinaryUploader): plugin.app.vault.process(file, () => { return data; }) - new Notice("Upload of note files was completed"); // Success + new Notice("Upload of note file was completed"); // Success }, err => { // Failure new Notice("There was something wrong with your upload. Please try again. " + file.name + '. ' + err.message, 0); diff --git a/src/main.ts b/src/main.ts index 3795bb7..aa51848 100755 --- a/src/main.ts +++ b/src/main.ts @@ -9,7 +9,7 @@ import { import axios from "axios" import objectPath from 'object-path' import { v2 as cloudinary } from 'cloudinary'; -import { uploadVault, uploadNoteModal, uploadCurrentNoteFiles, setSubfolder, generateResourceUrl,generateTransformParams } from "./commands/utils"; +import { uploadVault, uploadNoteModal, uploadCurrentNoteFiles, setSubfolder, generateResourceUrl,generateTransformParams,fetchMessages } from "./commands/utils"; // Settings tab import @@ -53,7 +53,8 @@ export default class CloudinaryUploader extends Plugin { name: "Upload all vault media assets to Cloudinary", callback: () => { if (this.settings.ignoreWarnings) { - uploadVault(this); + //async fetch messages after upload of vault assets + fetchMessages(this); } else { uploadNoteModal(undefined, 'asset',this); } From 4878c1312c308a17520e3fd139410c8a8909c315 Mon Sep 17 00:00:00 2001 From: Jordan Handy <6423379+jordanhandy@users.noreply.github.com> Date: Wed, 12 Jun 2024 07:28:35 -0400 Subject: [PATCH 52/52] bump version --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 27d67b4..b321456 100755 --- a/manifest.json +++ b/manifest.json @@ -5,5 +5,5 @@ "author": "Jordan Handy", "isDesktopOnly": true, "minAppVersion": "0.11.0", - "version": "0.4.0" + "version": "1.0.0" }