diff --git a/.gitignore b/.gitignore index 2fd7c5e..75dd4c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,13 @@ -# Intellij -*.iml -.idea - -# npm -node_modules -package-lock.json - -# build -main.js -*.js.map - -# obsidian -data.json +# Intellij +*.iml +.idea +# npm +node_modules +package-lock.json +# build +main.js +*.js.map +# obsidian +data.json +/Folder.DotSettings.user +.vs \ No newline at end of file diff --git a/constants.ts b/constants.ts new file mode 100644 index 0000000..351b06d --- /dev/null +++ b/constants.ts @@ -0,0 +1,3 @@ +export const defaultArchiveText = "(Archived)"; +export const waybackUrl = "https://web.archive.org/web/"; +export const waybackSaveUrl = "https://web.archive.org/save/"; \ No newline at end of file diff --git a/main.ts b/main.ts index 9b98fe0..470443c 100644 --- a/main.ts +++ b/main.ts @@ -1,114 +1,118 @@ -import { Console } from 'console'; -import { App, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, request, Setting } from 'obsidian'; -import { Url } from 'url'; - -export const enum ArchiveOptions { - wayback, - archiveis, - both -} - -interface LinkArchivePluginSettings { - archiveOption: ArchiveOptions; -} - -const DEFAULT_SETTINGS: LinkArchivePluginSettings = { - archiveOption: ArchiveOptions.archiveis -} +import { MarkdownView, Notice, Plugin, request } from "obsidian"; +import { waybackSaveUrl, waybackUrl } from "./constants"; +import { defaultSettings as DEFAULT_SETTINGS, LinkArchivePluginSettings as LinkArchivePluginSettings, LinkArchiveSettingTab } from "./settings"; const urlRegex =/(\b(https?|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig; -const waybackUrl = 'https://web.archive.org/web/'; -const waybackSaveUrl = 'https://web.archive.org/save/'; - -const archiveText = '[(Archived)]'; - export default class ObsidianLinkArchivePlugin extends Plugin { settings: LinkArchivePluginSettings; async onload() { - console.log('Loading Link Archive plugin...'); + console.log("Loading Link Archive plugin..."); await this.loadSettings(); - this.addRibbonIcon('restore-file-glyph', 'Archive Links', async () => { - + this.addRibbonIcon("restore-file-glyph", "Archive Links", async () => { + // test save //new Notice('Archive option: ' + this.settings.archiveOption.toString()); + const archiveText = `[${this.settings.archiveText}]`; + const dateLinkPart = new Date().toISOString().slice(0, 10).replace(/-/g, ""); + const view = this.app.workspace.getActiveViewOfType(MarkdownView); if(view) { const viewData = view.getViewData(); - let reverseArray: Array<[string, number]> = []; + const reverseArray: Array<[string, number]> = []; - let linkArray; + let linkArray: any; while ((linkArray = urlRegex.exec(viewData)) !== null) { console.log(`Found ${linkArray[0]}. Next starts at ${urlRegex.lastIndex}.`); - - if(linkArray[0].startsWith(waybackUrl)) continue; - if(viewData.substring(urlRegex.lastIndex, urlRegex.lastIndex + 14).contains(archiveText)) continue; + //if(linkArray[0].startsWith(waybackUrl)) continue; + + //if(viewData.substring(urlRegex.lastIndex, urlRegex.lastIndex + 14).contains(archiveText)) continue; - // replace clean logic with + // replace clean logic with // IF next link is the same except with archiveorg in front of it, skip it reverseArray.unshift([linkArray[0], urlRegex.lastIndex]); } - - console.log(reverseArray); - if(reverseArray.length == 0) { - new Notice('No (new) links to archive.'); + console.log(reverseArray); + + // ReSharper marks the "some" call as an error, but it's actually correct... + const cleanedList = reverseArray.filter(x => + !x[0].startsWith(waybackUrl) + && !reverseArray.some(y => y[0].startsWith(waybackUrl) && y[0].endsWith(x[0]))); + + console.log(cleanedList); + + if (cleanedList.length === 0) { + this.popNotice("No (new) links to archive."); return; } - new Notice(`Preparing to archive ${reverseArray.length} link(s), this might take a while - please be patient...`, 10 * 1000); - - for (const tuple of reverseArray) { - let currentLink = tuple[0]; - let saveLink = `${waybackSaveUrl}${currentLink}`; - let archiveLink = ` ${archiveText}(${waybackUrl}${currentLink})`; - let offset = view.editor.offsetToPos(tuple[1]); - let message = `Successfully archived ${currentLink}!`; - - await request({ - url: saveLink - }); - - view.editor.replaceRange(archiveLink, offset); - console.log(message); - new Notice(message); - } - - // reverseArray.forEach(async function (tuple) { - // let currentLink = tuple[0]; - // let saveLink = `${waybackSaveUrl}${currentLink}`; - // let archiveLink = ` ${archiveText}(${waybackUrl}${currentLink})`; - // let offset = view.editor.offsetToPos(tuple[1]); - // let message = `Successfully archived ${currentLink}!`; - - // request({ - // url: saveLink - // }).then(function(pageRes) { - // view.editor.replaceRange(archiveLink, offset); - // console.log(message); - // new Notice(message); - // }); - // }); - - new Notice('Link archiving done!'); + const processingNotice = new Notice(`Archiving ${cleanedList.length} link(s), this might take a while - please be patient...`, 0); + + let i = 1; + const totalLinks = cleanedList.length; + + for (const tuple of cleanedList) { + const currentLink = tuple[0]; + const saveLink = `${waybackSaveUrl}${currentLink}`; + const archiveLink = ` ${archiveText}(${waybackUrl}${dateLinkPart}/${currentLink})`; + const extraOffset = viewData.charAt(tuple[1]) === ")" ? 1 : 0; + const offset = view.editor.offsetToPos(tuple[1] + extraOffset); + const message = `(${i}/${totalLinks}) Successfully archived ${currentLink}!`; + const failMessage = `(${i}/${totalLinks}) Failed to archive ${currentLink}!`; + i += 1; + + await this.delay(400); + + try { + await request({ + url: saveLink + }); + + view.editor.replaceRange(archiveLink, offset); + console.log(message); + this.popNotice(message); + } + catch (exception) { + this.popNotice(failMessage); + } + } + + this.popNotice("Link archiving done!"); + + processingNotice.hide(); } else { - new Notice('Link archiving only works if you have a note open.'); + this.popNotice("Link archiving only works if you have a note open."); } - }); + }); this.addSettingTab(new LinkArchiveSettingTab(this.app, this)); } + popNotice(message: string, timeInSeconds?: number) { + // ReSharper disable WrongExpressionStatement + if (arguments.length === 1) { + new Notice(message); + } else { + new Notice(message, timeInSeconds * 1000); + } + // ReSharper restore WrongExpressionStatement + } + + delay(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + onunload() { - console.log('Unloading Link Archive plugin...'); + console.log("Unloading Link Archive plugin..."); } async loadSettings() { @@ -119,53 +123,3 @@ export default class ObsidianLinkArchivePlugin extends Plugin { await this.saveData(this.settings); } } - -class LinkArchiveSettingTab extends PluginSettingTab { - plugin: ObsidianLinkArchivePlugin; - - constructor(app: App, plugin: ObsidianLinkArchivePlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const plugin: ObsidianLinkArchivePlugin = (this as any).plugin; - - let {containerEl} = this; - - containerEl.empty(); - - // add archive link text customization option - - // containerEl.createEl('h2', {text: 'Archive Settings'}); - - // new Setting(containerEl) - // .setName('Archive Provider') - // .setDesc('Choose a provider for the link archive') - // .addDropdown((dropdown) => { - // const options: Record = { - // 0: "Internet Archive", - // 1: "archive.is", - // 2: "Both" - // }; - - // dropdown - // .addOptions(options) - // .setValue(plugin.settings.archiveOption.toString()) - // .onChange(async (value) => { - // console.log('Archive option: ' + value); - // plugin.settings.archiveOption = +value; - // await plugin.saveSettings(); - // this.display(); - // }) - // }); - - containerEl.createEl('h2', {text: 'About Link Archive'}); - - containerEl.createEl('p', {text: 'This plugin archives links in your note so they\'re available to you even if the original site goes down or gets removed.'}); - - containerEl.createEl('a', {text: 'Open GitHub repository', href: 'https://github.com/tomzorz/obsidian-link-archive'}); - - // TODO github support and ko-fi - } -} diff --git a/manifest.json b/manifest.json index 85dda27..d1c8fee 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-link-archive", "name": "Link Archive", - "version": "0.1.1", + "version": "0.2.0", "minAppVersion": "0.9.12", "description": "This plugin archives links in your note so they're available to you even if the original site goes down or gets removed.", "author": "Tamás Deme - @tomzorz_", diff --git a/settings.ts b/settings.ts new file mode 100644 index 0000000..cb09745 --- /dev/null +++ b/settings.ts @@ -0,0 +1,83 @@ +import { App, PluginSettingTab, Setting } from "obsidian"; +import ObsidianLinkArchivePlugin from "./main"; +import { defaultArchiveText } from "./constants"; + +export const enum ArchiveOptions { + Wayback, + Archiveis, + Both +} + +// ReSharper disable once InconsistentNaming +export interface LinkArchivePluginSettings { + archiveOption: ArchiveOptions; + archiveText: string; +} + +export const defaultSettings: LinkArchivePluginSettings = { + archiveOption: ArchiveOptions.Archiveis, + archiveText: defaultArchiveText +} + + +export class LinkArchiveSettingTab extends PluginSettingTab { + plugin: ObsidianLinkArchivePlugin; + + constructor(app: App, plugin: ObsidianLinkArchivePlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const plugin: ObsidianLinkArchivePlugin = (this as any).plugin; + + const { containerEl } = this; + + containerEl.empty(); + + // add archive link text customization option + + containerEl.createEl("h2", {text: "Archive Settings"}); + + new Setting(containerEl) + .setName("Link text") + .setDesc("The text of the archive links") + .addText(text => + text + .setValue(plugin.settings.archiveText) + .onChange(async value => { + console.log(`Link text: ${value}`); + plugin.settings.archiveText = value; + await plugin.saveSettings(); + })); + + // new Setting(containerEl) + // .setName('Archive Provider') + // .setDesc('Choose a provider for the link archive') + // .addDropdown((dropdown) => { + // const options: Record = { + // 0: "Internet Archive", + // 1: "archive.is", + // 2: "Both" + // }; + + // dropdown + // .addOptions(options) + // .setValue(plugin.settings.archiveOption.toString()) + // .onChange(async (value) => { + // console.log('Archive option: ' + value); + // plugin.settings.archiveOption = +value; + // await plugin.saveSettings(); + // this.display(); + // }) + // }); + + containerEl.createEl("h2", {text: "About Link Archive"}); + + containerEl.createEl("p", {text: "This plugin archives links in your note so they're available to you even if the original site goes down or gets removed."}); + + containerEl.createEl("a", {text: "Open GitHub repository", href: "https://github.com/tomzorz/obsidian-link-archive"}); + + // TODO github support and ko-fi + } +} diff --git a/styles.css b/styles.css index e69de29..03e2dbe 100644 --- a/styles.css +++ b/styles.css @@ -0,0 +1 @@ +/* nothing to see here for now */ \ No newline at end of file