Skip to content

Commit

Permalink
major rework, v0.2.0
Browse files Browse the repository at this point in the history
- added error handling
- plugin properly handles markdown links
- added better progress reporting
- refactored plugin into multiple files
- added a setting for a custom link text
- improved logic for detecting already-archived links
  • Loading branch information
tomzorz committed Oct 25, 2021
1 parent 1dad0c9 commit fd490cb
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 138 deletions.
27 changes: 13 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const defaultArchiveText = "(Archived)";
export const waybackUrl = "https://web.archive.org/web/";
export const waybackSaveUrl = "https://web.archive.org/save/";
200 changes: 77 additions & 123 deletions main.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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<ArchiveOptions, string> = {
// 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
}
}
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -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_",
Expand Down
83 changes: 83 additions & 0 deletions settings.ts
Original file line number Diff line number Diff line change
@@ -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<ArchiveOptions, string> = {
// 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
}
}
1 change: 1 addition & 0 deletions styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* nothing to see here for now */

0 comments on commit fd490cb

Please sign in to comment.