mirror of
https://github.com/woodchen-ink/obsidian-publish-to-discourse.git
synced 2025-07-17 21:32:05 +08:00
新增远程图片URL替换功能,允许用户在发布后将本地图片链接替换为Discourse上的远程URL。
新增支持markdown格式引用本地图片的上传.
This commit is contained in:
parent
4900f26222
commit
e56a479a45
22
src/api.ts
22
src/api.ts
@ -15,7 +15,7 @@ export class DiscourseAPI {
|
||||
) {}
|
||||
|
||||
// 上传图片到Discourse
|
||||
async uploadImage(file: TFile): Promise<string | null> {
|
||||
async uploadImage(file: TFile): Promise<{shortUrl: string, fullUrl?: string} | null> {
|
||||
try {
|
||||
const imgfile = await this.app.vault.readBinary(file);
|
||||
const boundary = genBoundary();
|
||||
@ -53,7 +53,25 @@ export class DiscourseAPI {
|
||||
|
||||
if (response.status == 200) {
|
||||
const jsonResponse = response.json;
|
||||
return jsonResponse.short_url;
|
||||
let fullUrl: string | undefined;
|
||||
|
||||
// 处理完整URL的拼接
|
||||
if (jsonResponse.url) {
|
||||
// 如果返回的url已经是完整URL(包含http/https),直接使用
|
||||
if (jsonResponse.url.startsWith('http://') || jsonResponse.url.startsWith('https://')) {
|
||||
fullUrl = jsonResponse.url;
|
||||
} else {
|
||||
// 如果是相对路径,需要与baseUrl拼接
|
||||
const baseUrl = this.settings.baseUrl.replace(/\/$/, ''); // 移除尾部斜杠
|
||||
const urlPath = jsonResponse.url.startsWith('/') ? jsonResponse.url : `/${jsonResponse.url}`;
|
||||
fullUrl = `${baseUrl}${urlPath}`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
shortUrl: jsonResponse.short_url,
|
||||
fullUrl: fullUrl
|
||||
};
|
||||
} else {
|
||||
new NotifyUser(this.app, `Error uploading image: ${response.status}`).open();
|
||||
return null;
|
||||
|
@ -6,6 +6,7 @@ export interface DiscourseSyncSettings {
|
||||
baseUrl: string;
|
||||
category: number;
|
||||
skipH1: boolean;
|
||||
useRemoteImageUrl: boolean;
|
||||
userApiKey: string;
|
||||
lastNotifiedVersion?: string; // 记录上次显示更新通知的版本
|
||||
}
|
||||
@ -14,6 +15,7 @@ export const DEFAULT_SETTINGS: DiscourseSyncSettings = {
|
||||
baseUrl: "https://yourforum.example.com",
|
||||
category: 1,
|
||||
skipH1: false,
|
||||
useRemoteImageUrl: false,
|
||||
userApiKey: ""
|
||||
};
|
||||
|
||||
@ -230,5 +232,17 @@ export class DiscourseSyncSettingsTab extends PluginSettingTab {
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(publishSection)
|
||||
.setName(t('USE_REMOTE_IMAGE_URL'))
|
||||
.setDesc(t('USE_REMOTE_IMAGE_URL_DESC'))
|
||||
.addToggle((toggle) =>
|
||||
toggle
|
||||
.setValue(this.plugin.settings.useRemoteImageUrl)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.useRemoteImageUrl = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,30 @@ export class EmbedHandler {
|
||||
|
||||
// 提取嵌入引用
|
||||
extractEmbedReferences(content: string): string[] {
|
||||
const regex = /!\[\[(.*?)\]\]/g;
|
||||
const matches = [];
|
||||
const references: string[] = [];
|
||||
|
||||
// 匹配 ![[...]] 格式 (Wiki格式)
|
||||
const wikiRegex = /!\[\[(.*?)\]\]/g;
|
||||
let match;
|
||||
while ((match = regex.exec(content)) !== null) {
|
||||
matches.push(match[1]);
|
||||
while ((match = wikiRegex.exec(content)) !== null) {
|
||||
references.push(match[1]);
|
||||
}
|
||||
return matches;
|
||||
|
||||
// 匹配  格式 (Markdown格式)
|
||||
const markdownRegex = /!\[.*?\]\(([^)]+)\)/g;
|
||||
while ((match = markdownRegex.exec(content)) !== null) {
|
||||
// 过滤掉网络URL,只处理本地文件路径
|
||||
const path = match[1];
|
||||
if (!path.startsWith('http://') && !path.startsWith('https://') && !path.startsWith('upload://')) {
|
||||
references.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
return references;
|
||||
}
|
||||
|
||||
// 处理嵌入内容
|
||||
async processEmbeds(embedReferences: string[], activeFileName: string): Promise<string[]> {
|
||||
async processEmbeds(embedReferences: string[], activeFileName: string, useRemoteUrl = false): Promise<string[]> {
|
||||
const uploadedUrls: string[] = [];
|
||||
for (const ref of embedReferences) {
|
||||
// 处理带有#的文件路径,分离文件名和标题部分
|
||||
@ -37,8 +50,14 @@ export class EmbedHandler {
|
||||
if (abstractFile instanceof TFile) {
|
||||
// 检查是否为图片或PDF文件
|
||||
if (isImageFile(abstractFile)) {
|
||||
const imageUrl = await this.api.uploadImage(abstractFile);
|
||||
uploadedUrls.push(imageUrl || "");
|
||||
const imageResult = await this.api.uploadImage(abstractFile);
|
||||
if (imageResult) {
|
||||
// 根据配置选择使用短URL还是完整URL
|
||||
const urlToUse = useRemoteUrl && imageResult.fullUrl ? imageResult.fullUrl : imageResult.shortUrl;
|
||||
uploadedUrls.push(urlToUse);
|
||||
} else {
|
||||
uploadedUrls.push("");
|
||||
}
|
||||
} else {
|
||||
// 非图片文件,返回空字符串
|
||||
uploadedUrls.push("");
|
||||
@ -58,14 +77,23 @@ export class EmbedHandler {
|
||||
// 替换内容中的嵌入引用为Markdown格式
|
||||
replaceEmbedReferences(content: string, embedReferences: string[], uploadedUrls: string[]): string {
|
||||
let processedContent = content;
|
||||
|
||||
embedReferences.forEach((ref, index) => {
|
||||
const obsRef = `![[${ref}]]`;
|
||||
// 只有当上传URL不为空时(即为图片)才替换为Markdown格式的图片链接
|
||||
if (uploadedUrls[index]) {
|
||||
const discoRef = ``;
|
||||
processedContent = processedContent.replace(obsRef, discoRef);
|
||||
// 处理 ![[...]] 格式 (Wiki格式)
|
||||
const wikiRef = `![[${ref}]]`;
|
||||
const wikiReplacement = ``;
|
||||
processedContent = processedContent.replace(wikiRef, wikiReplacement);
|
||||
|
||||
// 处理  格式 (Markdown格式)
|
||||
// 创建正则表达式来匹配具体的路径
|
||||
const escapedRef = ref.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const markdownRegex = new RegExp(`!\\[([^\\]]*)\\]\\(${escapedRef}\\)`, 'g');
|
||||
const markdownReplacement = ``;
|
||||
processedContent = processedContent.replace(markdownRegex, markdownReplacement);
|
||||
}
|
||||
});
|
||||
|
||||
return processedContent;
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ export default {
|
||||
|
||||
'SKIP_H1': 'Skip First Heading',
|
||||
'SKIP_H1_DESC': 'Skip the first heading (H1) when publishing to Discourse',
|
||||
'USE_REMOTE_IMAGE_URL': 'Use Remote Image URLs',
|
||||
'USE_REMOTE_IMAGE_URL_DESC': 'Replace local image links with remote URLs from Discourse after publishing',
|
||||
'TEST_API_KEY': 'Test Connection',
|
||||
'TESTING': 'Testing...',
|
||||
'API_TEST_SUCCESS': 'Connection successful! API key is valid',
|
||||
@ -17,7 +19,7 @@ export default {
|
||||
'CATEGORY': 'Category',
|
||||
'TAGS': 'Tags',
|
||||
'ENTER_TAG': 'Enter tag name (press Enter to add)',
|
||||
'ENTER_TAG_WITH_CREATE': 'Enter tag name (can create new tags)',
|
||||
'ENTER_TAG_WITH_CREATE': 'Enter tag name (press Enter to add) (can create new tags)',
|
||||
'PUBLISHING': 'Publishing...',
|
||||
'UPDATING': 'Updating...',
|
||||
'PUBLISH': 'Publish',
|
||||
|
@ -5,6 +5,8 @@ export default {
|
||||
|
||||
'SKIP_H1': '跳过一级标题',
|
||||
'SKIP_H1_DESC': '发布到 Discourse 时跳过笔记中的一级标题',
|
||||
'USE_REMOTE_IMAGE_URL': '使用远程图片URL',
|
||||
'USE_REMOTE_IMAGE_URL_DESC': '发布后用Discourse上的远程图片URL替换本地文章中的图片链接',
|
||||
'TEST_API_KEY': '测试连接',
|
||||
'TESTING': '测试中...',
|
||||
'API_TEST_SUCCESS': '连接成功!API密钥有效',
|
||||
@ -17,7 +19,7 @@ export default {
|
||||
'CATEGORY': '分类',
|
||||
'TAGS': '标签',
|
||||
'ENTER_TAG': '输入标签名称(回车添加)',
|
||||
'ENTER_TAG_WITH_CREATE': '输入标签名称(可创建新标签)',
|
||||
'ENTER_TAG_WITH_CREATE': '输入标签名称(回车添加)(可创建新标签)',
|
||||
'PUBLISHING': '发布中...',
|
||||
'UPDATING': '更新中...',
|
||||
'PUBLISH': '发布',
|
||||
|
53
src/main.ts
53
src/main.ts
@ -239,7 +239,7 @@ export default class PublishToDiscourse extends Plugin implements PluginInterfac
|
||||
const embedReferences = this.embedHandler.extractEmbedReferences(content);
|
||||
|
||||
// 处理嵌入内容
|
||||
const uploadedUrls = await this.embedHandler.processEmbeds(embedReferences, this.activeFile.name);
|
||||
const uploadedUrls = await this.embedHandler.processEmbeds(embedReferences, this.activeFile.name, this.settings.useRemoteImageUrl);
|
||||
|
||||
// 替换嵌入引用为Markdown格式
|
||||
content = this.embedHandler.replaceEmbedReferences(content, embedReferences, uploadedUrls);
|
||||
@ -276,6 +276,11 @@ export default class PublishToDiscourse extends Plugin implements PluginInterfac
|
||||
// 如果更新成功,更新Front Matter
|
||||
if (result.success) {
|
||||
await this.updateFrontMatter(postId, topicId, currentTags);
|
||||
|
||||
// 如果启用了远程URL替换,更新本地文件中的图片链接
|
||||
if (this.settings.useRemoteImageUrl) {
|
||||
await this.updateLocalImageLinks(embedReferences, uploadedUrls);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 创建新帖子
|
||||
@ -289,6 +294,11 @@ export default class PublishToDiscourse extends Plugin implements PluginInterfac
|
||||
// 如果创建成功,更新Front Matter
|
||||
if (result.success && result.postId && result.topicId) {
|
||||
await this.updateFrontMatter(result.postId, result.topicId, currentTags);
|
||||
|
||||
// 如果启用了远程URL替换,更新本地文件中的图片链接
|
||||
if (this.settings.useRemoteImageUrl) {
|
||||
await this.updateLocalImageLinks(embedReferences, uploadedUrls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,6 +372,47 @@ export default class PublishToDiscourse extends Plugin implements PluginInterfac
|
||||
}
|
||||
}
|
||||
|
||||
// 更新本地文件中的图片链接为远程URL
|
||||
private async updateLocalImageLinks(embedReferences: string[], uploadedUrls: string[]) {
|
||||
try {
|
||||
const activeFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
let content = await this.app.vault.read(activeFile);
|
||||
let hasChanges = false;
|
||||
|
||||
embedReferences.forEach((ref, index) => {
|
||||
if (uploadedUrls[index]) {
|
||||
// 替换 ![[...]] 格式 (Wiki格式)
|
||||
const wikiRef = `![[${ref}]]`;
|
||||
const wikiReplacement = ``;
|
||||
if (content.includes(wikiRef)) {
|
||||
content = content.replace(wikiRef, wikiReplacement);
|
||||
hasChanges = true;
|
||||
}
|
||||
|
||||
// 替换  格式 (Markdown格式)
|
||||
const escapedRef = ref.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const markdownRegex = new RegExp(`!\\[([^\\]]*)\\]\\(${escapedRef}\\)`, 'g');
|
||||
const markdownReplacement = ``;
|
||||
if (markdownRegex.test(content)) {
|
||||
content = content.replace(markdownRegex, markdownReplacement);
|
||||
hasChanges = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 只有在有变更时才保存文件
|
||||
if (hasChanges) {
|
||||
await this.app.vault.modify(activeFile, content);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update local image links:', error);
|
||||
}
|
||||
}
|
||||
|
||||
onunload() {}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user