Skip to content

Commit

Permalink
load pdf.js from local http server
Browse files Browse the repository at this point in the history
  • Loading branch information
oylbin committed Dec 21, 2024
1 parent 7077ee5 commit efeb02f
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
node_modules/

assets/
main.js
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:
gh release create "$tag" \
--title="$tag" \
--draft \
main.js manifest.json styles.css
main.js manifest.json styles.css assets/
22 changes: 22 additions & 0 deletions assets/pdf.js/2.16.105/pdf.min.js

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions assets/pdf.js/2.16.105/pdf.worker.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions assets/pdf.js/2.16.105/pdf_viewer.min.css

Large diffs are not rendered by default.

72 changes: 42 additions & 30 deletions main.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import { Editor, MarkdownView, Notice, Plugin, MarkdownPostProcessorContext, Platform } from 'obsidian';
import * as http from 'http';
import * as path from 'path';
import { httpRequestHandler, findAvailablePort } from './server';
import { httpRequestHandler, findAvailablePort, CrossComputerLinkContext } from './server';
import { openFileWithDefaultProgram, getRelativePath } from './utils';
import { CrossComputerLinkPluginSettings, DEFAULT_SETTINGS, CrossComputerLinkSettingTab, DragAction } from './settings';
import { parseEmbedArgumentWidthHeight, parseEmbedData, parseEmbedPdfArguments } from 'embedProcessor';


export default class CrossComputerLinkPlugin extends Plugin {
settings: CrossComputerLinkPluginSettings;
homeDirectory: string;
vaultDirectory: string;
// homeDirectory: string;
// vaultDirectory: string;
server: http.Server | null;
private cleanupDropHandler: (() => void) | null = null;
context: CrossComputerLinkContext;


insertText(editor: Editor, text: string) {
Expand Down Expand Up @@ -92,42 +93,42 @@ export default class CrossComputerLinkPlugin extends Plugin {
// @ts-ignore Property 'path' exists at runtime but is not typed
const fullpath = files[i].path;
if (action === DragAction.EmbedRelativeToHome) {
const relativePath = getRelativePath(this.homeDirectory, fullpath);
const relativePath = getRelativePath(this.context.homeDirectory, fullpath);
this.createEmbedRelativeToHome(editor, relativePath);
} else if (action === DragAction.EmbedRelativeToVault) {
const relativePath = getRelativePath(this.vaultDirectory, fullpath);
const relativePath = getRelativePath(this.context.vaultDirectory, fullpath);
this.createEmbedRelativeToVault(editor, relativePath);
} else if (action === DragAction.LinkRelativeToHome) {
const relativePath = getRelativePath(this.homeDirectory, fullpath);
const relativePath = getRelativePath(this.context.homeDirectory, fullpath);
this.createLinkRelativeToHome(editor, relativePath);
} else if (action === DragAction.LinkRelativeToVault) {
const relativePath = getRelativePath(this.vaultDirectory, fullpath);
const relativePath = getRelativePath(this.context.vaultDirectory, fullpath);
this.createLinkRelativeToVault(editor, relativePath);
}else if(action === DragAction.InlineLinkRelativeToHome){
const relativePath = getRelativePath(this.homeDirectory, fullpath);
const relativePath = getRelativePath(this.context.homeDirectory, fullpath);
this.createInlineLinkRelativeToHome(editor, relativePath);
}else if(action === DragAction.InlineLinkRelativeToVault){
const relativePath = getRelativePath(this.vaultDirectory, fullpath);
const relativePath = getRelativePath(this.context.vaultDirectory, fullpath);
this.createInlineLinkRelativeToVault(editor, relativePath);
}
}
}
}
private startHttpServer() {
findAvailablePort(this.settings.httpServerPort)
findAvailablePort(this.context.port)
.then((port: number) => {
if (port !== this.settings.httpServerPort) {
new Notice(`Port ${this.settings.httpServerPort} was in use. Using port ${port} instead.`);
this.settings.httpServerPort = port;
this.saveSettings();
if (port !== this.context.port) {
//new Notice(`Port ${this.context.port} was in use. Using port ${port} instead.`);
this.context.port = port;
//this.saveSettings();
}

const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {
httpRequestHandler(req, res, this.homeDirectory, this.vaultDirectory);
httpRequestHandler(req, res, this.context);
});

server.listen(port, "127.0.0.1", () => {
console.log(`HTTP server is running on port ${port}`);
//console.log(`HTTP server is running on port ${port}`);
});
server.on('error', (e: NodeJS.ErrnoException) => {
if (e.code === 'EADDRINUSE') {
Expand All @@ -147,11 +148,22 @@ export default class CrossComputerLinkPlugin extends Plugin {
}
async onload() {
await this.loadSettings();
this.homeDirectory = process.env.HOME || process.env.USERPROFILE || '';
this.context = new CrossComputerLinkContext();
this.context.homeDirectory = process.env.HOME || process.env.USERPROFILE || '';
// @ts-ignore Property 'basePath' exists at runtime but is not typed
this.vaultDirectory = this.app.vault.adapter.basePath;
this.context.vaultDirectory = this.app.vault.adapter.basePath;
this.context.port = 11411;
if(this.manifest.dir){
this.context.pluginDirectory = this.manifest.dir;
}else{
this.context.pluginDirectory = this.app.vault.configDir + '/plugins/' + this.manifest.id;
}
// console.log("vaultDirectory", this.vaultDirectory);
// console.log("homeDirectory", this.homeDirectory);
// @ts-ignore Property 'manifest' exists at runtime but is not typed
//const pluginDirectory = this.app.vault.configDir + '/plugins/' + this.manifest.id;
// console.log("vault object", this.app.vault);
// console.log("manifest", this.manifest);

this.startHttpServer();

Expand All @@ -166,7 +178,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(
editor,
this.homeDirectory,
this.context.homeDirectory,
this.createEmbedRelativeToHome.bind(this)
);
}
Expand All @@ -175,14 +187,14 @@ export default class CrossComputerLinkPlugin extends Plugin {
id: 'add-external-link-relative-to-home',
name: 'Add external link relative to home',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(editor, this.homeDirectory, this.createLinkRelativeToHome.bind(this));
this.showFilePickerAndCreateEmbed(editor, this.context.homeDirectory, this.createLinkRelativeToHome.bind(this));
}
});
this.addCommand({
id: 'add-external-inline-link-relative-to-home',
name: 'Add external inline link relative to home',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(editor, this.homeDirectory, this.createInlineLinkRelativeToHome.bind(this));
this.showFilePickerAndCreateEmbed(editor, this.context.homeDirectory, this.createInlineLinkRelativeToHome.bind(this));
}
});

Expand All @@ -192,7 +204,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(
editor,
this.vaultDirectory,
this.context.vaultDirectory,
this.createEmbedRelativeToVault.bind(this)
);
}
Expand All @@ -201,14 +213,14 @@ export default class CrossComputerLinkPlugin extends Plugin {
id: 'add-external-link-relative-to-vault',
name: 'Add external link relative to vault',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(editor, this.vaultDirectory, this.createLinkRelativeToVault.bind(this));
this.showFilePickerAndCreateEmbed(editor, this.context.vaultDirectory, this.createLinkRelativeToVault.bind(this));
}
});
this.addCommand({
id: 'add-external-inline-link-relative-to-vault',
name: 'Add external inline link relative to vault',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.showFilePickerAndCreateEmbed(editor, this.vaultDirectory, this.createInlineLinkRelativeToVault.bind(this));
this.showFilePickerAndCreateEmbed(editor, this.context.vaultDirectory, this.createInlineLinkRelativeToVault.bind(this));
}
});

Expand Down Expand Up @@ -345,7 +357,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
// Find elements with LinkRelativeToHome class
element.querySelectorAll('.LinkRelativeToHome').forEach((el) => {
const relativePath = el.textContent?.trim();
const fullPath = this.homeDirectory + "/" + relativePath;
const fullPath = this.context.homeDirectory + "/" + relativePath;
el.textContent = path.basename(fullPath);

// Modifying href here is not effective, clicking will cause Not allowed to load local resource error
Expand All @@ -360,7 +372,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
});
element.querySelectorAll('.LinkRelativeToVault').forEach((el) => {
const relativePath = el.textContent?.trim();
const fullPath = this.vaultDirectory + "/" + relativePath;
const fullPath = this.context.vaultDirectory + "/" + relativePath;
el.textContent = path.basename(fullPath);

// Modifying href here is not effective, clicking will cause Not allowed to load local resource error
Expand All @@ -381,7 +393,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
if (filePath.startsWith("/")) {
filePath = filePath.substring(1);
}
const fullPath = `${relativeTo === "home" ? this.homeDirectory : this.vaultDirectory}/${filePath}`;
const fullPath = `${relativeTo === "home" ? this.context.homeDirectory : this.context.vaultDirectory}/${filePath}`;

// Extract file name
const fileName = filePath.split("/").pop();
Expand Down Expand Up @@ -422,10 +434,10 @@ export default class CrossComputerLinkPlugin extends Plugin {
const embedData = parseEmbedData(filePath);
// console.log("embedData", embedData);
// check if filename contains '|', the text after '|' is the embed arguments
const fileUrl = `http://127.0.0.1:${this.settings.httpServerPort}/download/${relativeTo === "home" ? "home" : "vault"}?p=${embedData.embedFilePath}`;
const fileUrl = `http://127.0.0.1:${this.context.port}/download/${relativeTo === "home" ? "home" : "vault"}?p=${embedData.embedFilePath}`;
if (embedData.embedType === 'pdf') {
// this.embedPdf(fileUrl, embedData.embedArguments, element, context);
const embedUrl = `http://127.0.0.1:${this.settings.httpServerPort}/embed/${relativeTo === "home" ? "home" : "vault"}?p=${embedData.embedFilePath}`;
const embedUrl = `http://127.0.0.1:${this.context.port}/embed/${relativeTo === "home" ? "home" : "vault"}?p=${embedData.embedFilePath}`;
this.embedPdfWithIframe(embedUrl, embedData.embedArguments, element, context);
} else if (embedData.embedType === 'image') {
this.embedImage(fileUrl, embedData.embedArguments, element, context);
Expand All @@ -436,7 +448,7 @@ export default class CrossComputerLinkPlugin extends Plugin {
} else if (embedData.embedType === 'markdown') {
//this.embedMarkdown(fileUrl, embedData.embedArguments, element, context);
} else {
const fullPath = `${relativeTo === "home" ? this.homeDirectory : this.vaultDirectory}/${filePath}`;
const fullPath = `${relativeTo === "home" ? this.context.homeDirectory : this.context.vaultDirectory}/${filePath}`;
this.embedOther(fullPath, element, context);
}
}
Expand Down
47 changes: 32 additions & 15 deletions server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const PDF_HTML_TEMPLATE = `
<html>
<head>
<title>PDF Viewer</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf_viewer.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.min.js"></script>
<link rel="stylesheet" href="http://127.0.0.1:PORT_TO_REPLACE/assets/pdf.js/2.16.105/pdf_viewer.min.css">
<script src="http://127.0.0.1:PORT_TO_REPLACE/assets/pdf.js/2.16.105/pdf.min.js"></script>
<style>
#toolbar {
background-color: #474747;
Expand Down Expand Up @@ -55,7 +55,7 @@ const PDF_HTML_TEMPLATE = `
<div id="viewer"></div>
</div>
<script>
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.16.105/pdf.worker.min.js';
pdfjsLib.GlobalWorkerOptions.workerSrc = 'http://127.0.0.1:PORT_TO_REPLACE/assets/pdf.js/2.16.105/pdf.worker.min.js';
let pdfDoc = null;
let pageNum = PAGE_TO_REPLACE;
Expand Down Expand Up @@ -140,6 +140,12 @@ const UNSUPPORTED_FILE_TEMPLATE = `
</html>
`

export class CrossComputerLinkContext {
homeDirectory: string;
vaultDirectory: string;
port: number;
pluginDirectory: string;
}

export function getTemplate(extname: string) {
extname = extname.toLowerCase();
Expand Down Expand Up @@ -181,24 +187,24 @@ export function findAvailablePort(startPort: number): Promise<number> {



function getFilePathFromUrl(url: string, homeDirectory: string, vaultDirectory: string) {
function getFilePathFromUrl(url: string, context: CrossComputerLinkContext) {
// console.log("getFilePathFromUrl", url);
const [urlWithoutParams, params] = url.split('?');
const parsedParams = parseUrlParams(params);
// console.log("urlWithoutParams", urlWithoutParams);
// console.log("parsedParams", parsedParams);
if(urlWithoutParams.startsWith("/download/home") || urlWithoutParams.startsWith("/open/home") || urlWithoutParams.startsWith("/embed/home")) {
const decodedPath = decodeURIComponent(parsedParams.p);
const filePath = path.join(homeDirectory, decodedPath);
const filePath = path.join(context.homeDirectory, decodedPath);
return filePath;
} else if(urlWithoutParams.startsWith("/download/vault") || urlWithoutParams.startsWith("/open/vault") || urlWithoutParams.startsWith("/embed/vault")) {
const decodedPath = decodeURIComponent(parsedParams.p);
const filePath = path.join(vaultDirectory, decodedPath);
const filePath = path.join(context.vaultDirectory, decodedPath);
return filePath;
}
throw new Error("Invalid url: " + url);
}
export function embedRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, homeDirectory: string, vaultDirectory: string) {
export function embedRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, context: CrossComputerLinkContext) {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
// url may contain ? followed by parameters, split url and parameters
const [, params] = url.split('?');
Expand All @@ -215,6 +221,7 @@ export function embedRequestHandler(url: string, req: http.IncomingMessage, res:
parsedParams.page = "1";
}
multiLineStr = multiLineStr.replace("PAGE_TO_REPLACE", parsedParams.page);
multiLineStr = multiLineStr.replace(/PORT_TO_REPLACE/g, context.port.toString());
}
res.end(multiLineStr);
} else {
Expand All @@ -223,8 +230,8 @@ export function embedRequestHandler(url: string, req: http.IncomingMessage, res:
res.end(multiLineStr);
}
}
export function downloadRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, homeDirectory: string, vaultDirectory: string) {
const filePath = getFilePathFromUrl(url, homeDirectory, vaultDirectory);
export function downloadRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, context: CrossComputerLinkContext) {
const filePath = getFilePathFromUrl(url, context);
const extname = path.extname(filePath).toLowerCase();
const contentType = getContentType(extname);
if (!fs.existsSync(filePath)) {
Expand Down Expand Up @@ -285,8 +292,8 @@ export function downloadRequestHandler(url: string, req: http.IncomingMessage, r
stream.pipe(res);
}
}
function openRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, homeDirectory: string, vaultDirectory: string) {
const filePath = getFilePathFromUrl(url, homeDirectory, vaultDirectory);
function openRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, context: CrossComputerLinkContext) {
const filePath = getFilePathFromUrl(url, context);
// console.log("filePath", filePath);
openFileWithDefaultProgram(filePath, (error: Error) => {
if(error){
Expand All @@ -310,7 +317,14 @@ function openRequestHandler(url: string, req: http.IncomingMessage, res: http.Se
`;
res.end(multiLineStr);
}
export function httpRequestHandler(req: http.IncomingMessage, res: http.ServerResponse, homeDirectory: string, vaultDirectory: string) {
function assetRequestHandler(url: string, req: http.IncomingMessage, res: http.ServerResponse, context: CrossComputerLinkContext) {
const filePath = path.join(context.vaultDirectory,context.pluginDirectory, url);
const contentType = getContentType(filePath);
res.setHeader('Content-Type', contentType);
const stream = fs.createReadStream(filePath);
stream.pipe(res);
}
export function httpRequestHandler(req: http.IncomingMessage, res: http.ServerResponse, context: CrossComputerLinkContext) {
// console.log("httpRequestHandler", req.url);
// Read file content and return it via http response
const url = req.url;
Expand All @@ -320,13 +334,16 @@ export function httpRequestHandler(req: http.IncomingMessage, res: http.ServerRe
return;
}
if(url.startsWith("/embed/")) {
embedRequestHandler(url, req, res, homeDirectory, vaultDirectory);
embedRequestHandler(url, req, res, context);
return;
}else if(url.startsWith("/download/")) {
downloadRequestHandler(url, req, res, homeDirectory, vaultDirectory);
downloadRequestHandler(url, req, res, context);
return;
}else if(url.startsWith("/open/")) {
openRequestHandler(url, req, res, homeDirectory, vaultDirectory);
openRequestHandler(url, req, res, context);
return;
}else if(url.startsWith("/assets/")) {
assetRequestHandler(url, req, res, context);
return;
}
res.writeHead(404);
Expand Down
2 changes: 0 additions & 2 deletions settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,13 @@ export enum DragAction {
}

export interface CrossComputerLinkPluginSettings {
httpServerPort: number;
dragWithCtrl: DragAction;
dragWithShift: DragAction;
dragWithCtrlShift: DragAction;
enableDragAndDrop: boolean;
}

export const DEFAULT_SETTINGS: CrossComputerLinkPluginSettings = {
httpServerPort: 11411,
dragWithCtrl: DragAction.Default,
dragWithShift: DragAction.InlineLinkRelativeToHome,
dragWithCtrlShift: DragAction.EmbedRelativeToHome,
Expand Down

0 comments on commit efeb02f

Please sign in to comment.