diff --git a/main.ts b/main.ts index b032977..97ebb63 100644 --- a/main.ts +++ b/main.ts @@ -1,112 +1,147 @@ -import { DEFAULT_SETTINGS, DoubanPluginSettings } from "./src/douban/Douban"; -import { Editor, Plugin } from "obsidian"; +import {Editor, Plugin} from "obsidian"; -import { DoubanFuzzySuggester } from "src/douban/data/search/DoubanSearchFuzzySuggestModal"; -import { DoubanSearchChooseItemHandler } from "src/douban/data/handler/DoubanSearchChooseItemHandler"; -import { DoubanSearchModal } from "src/douban/data/search/DoubanSearchModal"; -import { DoubanSettingTab } from "src/douban/DoubanSettingTab"; +import {DoubanFuzzySuggester} from "src/douban/data/search/DoubanSearchFuzzySuggestModal"; +import {DoubanSearchChooseItemHandler} from "src/douban/data/handler/DoubanSearchChooseItemHandler"; +import {DoubanSearchModal} from "src/douban/data/search/DoubanSearchModal"; +import {DoubanSettingTab} from "@App/setting/DoubanSettingTab"; import DoubanSubject from "src/douban/data/model/DoubanSubject"; import Searcher from "src/douban/data/search/Search"; -import { i18nHelper } from './src/lang/helper'; -import { log } from "src/utils/Logutil"; -import {BasicConst} from "./src/constant/Constsant"; +import {i18nHelper} from './src/lang/helper'; +import {log} from "src/utils/Logutil"; +import {BasicConst, SearchHandleMode} from "./src/constant/Constsant"; +import FileHandler from "./src/file/FileHandler"; +import HandleContext from "@App/data/model/HandleContext"; +import HandleResult from "@App/data/model/HandleResult"; +import {FileUtil} from "./src/utils/FileUtil"; +import { DoubanPluginSetting } from "@App/setting/model/DoubanPluginSetting"; +import {DEFAULT_SETTINGS} from "./src/constant/DefaultSettings"; export default class DoubanPlugin extends Plugin { - public settings: DoubanPluginSettings; + public settings: DoubanPluginSetting; public doubanExtractHandler: DoubanSearchChooseItemHandler; public doubanStatusBar: HTMLElement; + public fileHandler: FileHandler; - async putToEditor(editor:Editor, extract:DoubanSubject) { + + async putToObsidian(context: HandleContext, extract: DoubanSubject) { try { - if (!editor || !extract) { + if (!extract) { log.warn(i18nHelper.getMessage('140101')); return; } this.showStatus('140204', extract.title); - let content: string = this.doubanExtractHandler.parseText(extract, this.settings) + const content = await this.doubanExtractHandler.parseText(extract, context) if (content) { - editor.replaceSelection(content); + this.putContentToObsidian(context, content); } this.showStatus('140205', extract.title); - }catch (e) { + } catch (e) { this.showStatus('140206', e.message); - }finally { + } finally { setTimeout(() => this.doubanStatusBar.empty(), BasicConst.CLEAN_STATUS_BAR_DELAY) } } + async putContentToObsidian(context: HandleContext, content: HandleResult) { + const {mode} = context; + switch (mode) { + case SearchHandleMode.FOR_CREATE: + this.createFile(context, content); + break; + case SearchHandleMode.FOR_REPLACE: + this.putToEditor(context.editor, content.content); + break; + } + } - async search(searchTerm:string, editor: Editor) { + async putToEditor(editor: Editor, content: string) { + editor.replaceSelection(content); + } + + async createFile(context: HandleContext, content: HandleResult) { + this.fileHandler.createNewNoteWithData(content.fileName,content.content); + } + + async search(searchTerm: string, context: HandleContext) { try { this.showStatus('140201', searchTerm); const resultList = await Searcher.search(searchTerm, this.settings); this.showStatus('140202', resultList.length.toString()); - new DoubanFuzzySuggester(this, editor).showSearchList(resultList); - }catch (e) { + new DoubanFuzzySuggester(this, context).showSearchList(resultList); + } catch (e) { this.showStatus('140206', e.message); - }finally { + } finally { setTimeout(() => this.doubanStatusBar.empty(), BasicConst.CLEAN_STATUS_BAR_DELAY) } } - async getDoubanTextForActiveFile(editor: Editor) { + async getDoubanTextForActiveFile(context: HandleContext) { const activeFile = await this.app.workspace.getActiveFile(); if (activeFile) { - const searchTerm = activeFile.basename; - if (searchTerm) { - await this.search(searchTerm, editor); - } + const searchTerm = activeFile.basename; + if (searchTerm) { + await this.search(searchTerm, context); + } } - } - - async getDoubanTextForSearchTerm(editor: Editor) { - new DoubanSearchModal(this.app, this, editor).open(); } - + + async getDoubanTextForCreateNewNote(context: HandleContext) { + new DoubanSearchModal(this.app, this, context).open(); + } + + async getDoubanTextForSearchTerm(context: HandleContext) { + new DoubanSearchModal(this.app, this, context).open(); + } + async onload() { - await this.loadSettings(); + await this.loadSettings(); this.doubanStatusBar = this.addStatusBarItem(); this.addCommand({ - id: "search-douban-by-current-file-name", - name: i18nHelper.getMessage("110001"), - editorCallback: (editor: Editor) => - this.getDoubanTextForActiveFile(editor), - }); - - - this.addCommand({ - id: "search-douban-and-input-current-file", - name: i18nHelper.getMessage("110002"), - editorCallback: (editor: Editor) => - this.getDoubanTextForSearchTerm(editor), - }); -//TODO will support in future - // this.addCommand({ - // id: "sync-douban-broadcast-by-user-id", - // name: i18nHelper.getMessage('110006'), - // editorCallback: (editor: Editor) => - // this.geDoubanTextForSearchTerm(editor), - // }); - - this.addSettingTab(new DoubanSettingTab(this.app, this)); - } - - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); - this.doubanExtractHandler = new DoubanSearchChooseItemHandler(this.app, this); - } - - async saveSettings() { - await this.saveData(this.settings); + id: "search-douban-import-and-create-file", + name: i18nHelper.getMessage("110101"), + callback: () => + this.getDoubanTextForCreateNewNote({mode: SearchHandleMode.FOR_CREATE, settings: this.settings}), + }); + + this.addCommand({ + id: "search-douban-by-current-file-name", + name: i18nHelper.getMessage("110001"), + editorCallback: (editor: Editor) => + this.getDoubanTextForActiveFile({mode: SearchHandleMode.FOR_REPLACE, settings: this.settings, editor: editor}), + }); + + + this.addCommand({ + id: "search-douban-and-input-current-file", + name: i18nHelper.getMessage("110002"), + editorCallback: (editor: Editor) => + this.getDoubanTextForSearchTerm({mode: SearchHandleMode.FOR_REPLACE, settings: this.settings, editor: editor}), + }); + + this.addSettingTab(new DoubanSettingTab(this.app, this)); } - showStatus(origin:string, message:string) { + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + this.doubanExtractHandler = new DoubanSearchChooseItemHandler(this.app, this); + this.fileHandler = new FileHandler(this.app); + } + + async saveSettings() { + await this.saveData(this.settings); + } + + + + + + showStatus(origin: string, message: string) { this.doubanStatusBar.empty(); // @ts-ignore this.doubanStatusBar.setText(i18nHelper.getMessage(origin).replace('{0}', message)); } - } +} diff --git a/package-lock.json b/package-lock.json index 065b103..c9cfd18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "obsidian-sample-plugin", - "version": "1.0.1", + "name": "obsidian-douban-plugin", + "version": "v1.5.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -781,6 +781,11 @@ "mime2ext": "^1.0.1" } }, + "@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + }, "@sinclair/typebox": { "version": "0.23.5", "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.23.5.tgz", @@ -1096,20 +1101,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "axios": { - "version": "0.27.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-0.27.2.tgz", - "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", - "requires": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" - } - }, "babel-jest": { "version": "28.1.2", "resolved": "https://registry.npmmirror.com/babel-jest/-/babel-jest-28.1.2.tgz", @@ -1371,14 +1362,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", @@ -1422,11 +1405,6 @@ "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" }, - "cuint": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/cuint/-/cuint-0.2.2.tgz", - "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==" - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", @@ -1448,11 +1426,6 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz", @@ -1507,14 +1480,6 @@ "domhandler": "^5.0.1" } }, - "douban-search-crack": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/douban-search-crack/-/douban-search-crack-1.0.6.tgz", - "integrity": "sha512-IchjtqsemiJH0x7xo/7/QjEVa0khKMfY0KMtA9GMXXSQRJFPtiedi5Z0htB/xq0Thpo6SsbLXYJIkdVX3n3Qng==", - "requires": { - "xxhashjs": "^0.2.2" - } - }, "electron-to-chromium": { "version": "1.4.185", "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.185.tgz", @@ -1857,21 +1822,6 @@ "path-exists": "^4.0.0" } }, - "follow-redirects": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.0.tgz", - "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2722,19 +2672,6 @@ "picomatch": "^2.3.1" } }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, "mime2ext": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/mime2ext/-/mime2ext-1.0.1.tgz", @@ -2773,11 +2710,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz", @@ -3261,11 +3193,6 @@ "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", "dev": true }, - "tiny-network": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/tiny-network/-/tiny-network-0.0.6.tgz", - "integrity": "sha512-fYNn6nnbKZFch5nd1aWtwVwJG7zJc5dcyypKx1PgJvv1EFr/tUhQPSFx5Tw6lKTVqemxZystG8mkbQuu/P/R5g==" - }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz", @@ -3416,14 +3343,6 @@ "signal-exit": "^3.0.7" } }, - "xxhashjs": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/xxhashjs/-/xxhashjs-0.2.2.tgz", - "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", - "requires": { - "cuint": "^0.2.2" - } - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 04c0aa9..3dacfdf 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@notable/html2markdown": "^1.1.3", + "@popperjs/core": "^2.11.6", "cheerio": "^1.0.0-rc.11", "schema-dts": "^1.1.0" } diff --git a/src/constant/Constsant.ts b/src/constant/Constsant.ts index da946dd..714341b 100644 --- a/src/constant/Constsant.ts +++ b/src/constant/Constsant.ts @@ -16,6 +16,20 @@ export enum TemplateTextMode { YAML, } +/** + * 搜索结果处理模式 + */ +export enum SearchHandleMode { + /** + * 为了替换当前文档 + */ + FOR_REPLACE, + /** + * 为了创建文档 + */ + FOR_CREATE, +} + /** * 名称模式 */ @@ -25,6 +39,19 @@ export enum PersonNameMode { CH_EN_NAME = "CH_EN", } +/** + * 模板的key + * + */ +export enum TemplateKey { + movieTemplateFile= 'movieTemplateFile', + bookTemplateFile = 'bookTemplateFile', + musicTemplateFile = 'musicTemplateFile', + noteTemplateFile = 'noteTemplateFile', + gameTemplateFile = 'gameTemplateFile', + teleplayTemplateFile = 'teleplayTemplateFile', +} + /** * 名称模式选项 */ diff --git a/src/constant/DefaultSettings.ts b/src/constant/DefaultSettings.ts new file mode 100644 index 0000000..1a33a5b --- /dev/null +++ b/src/constant/DefaultSettings.ts @@ -0,0 +1,20 @@ +import {DoubanPluginSetting} from "@App/setting/model/DoubanPluginSetting"; +import {PersonNameMode} from "./Constsant"; +import {doubanHeaders} from "./Douban"; + +export const DEFAULT_SETTINGS: DoubanPluginSetting = { + movieTemplateFile: ``, + bookTemplateFile: ``, + musicTemplateFile: ``, + noteTemplateFile: ``, + gameTemplateFile: ``, + teleplayTemplateFile: ``, + searchUrl: 'https://www.douban.com/search?q=', + searchHeaders: JSON.stringify(doubanHeaders), + dateFormat: "yyyy-MM-DD", + timeFormat: "HH:mm:ss", + arraySpilt: ", ", + personNameMode: PersonNameMode.CH_NAME, + dataFilePath: "", + dataFileNamePath: "/{{type}}/{{title}}" +} diff --git a/src/douban/Douban.ts b/src/constant/DefaultTemplateContent.ts similarity index 57% rename from src/douban/Douban.ts rename to src/constant/DefaultTemplateContent.ts index 8ceb876..4d17eb6 100644 --- a/src/douban/Douban.ts +++ b/src/constant/DefaultTemplateContent.ts @@ -1,29 +1,5 @@ -import {PersonNameMode} from "../constant/Constsant"; - -export interface DoubanPluginSettings { - movieTemplate: string, - bookTemplate: string, - musicTemplate: string, - noteTemplate: string, - gameTemplate: string, - dateFormat: string, - timeFormat: string, - searchUrl: string, - arraySpilt: string, - searchHeaders?: string, - personNameMode: PersonNameMode, -} - - -export const doubanHeaders = { - "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", - "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36", -}; - -export const DEFAULT_SETTINGS: DoubanPluginSettings = { - movieTemplate: - `--- +export const DEFAULT_TEMPLATE_CONTENT = { + movieTemplateFileContent: `--- doubanId: {{id}} title: {{title}} type: {{type}} @@ -36,13 +12,13 @@ actor: {{actor}} author: {{author}} tags: {{type}} url: {{url}} +dataTime: {{currentDate}} {{currentTime}} desc: {{desc}} --- ![image]({{image}}) `, - bookTemplate: - `--- + bookTemplateFileContent: `--- doubanId: {{id}} title: {{title}} subTitle: {{subTitle}} @@ -60,6 +36,7 @@ totalPage: {{totalPage}} price: {{price}} tags: Book binding: {{binding}} +dataTime: {{currentDate}} {{currentTime}} desc: {{desc}} --- @@ -67,8 +44,7 @@ desc: {{desc}} {{menu}} `, - musicTemplate: - `--- + musicTemplateFileContent: `--- doubanId: {{id}} title: {{title}} type: {{type}} @@ -83,13 +59,13 @@ barcode: {{barcode}} url: {{url}} records: {{records}} tags: Music +dataTime: {{currentDate}} {{currentTime}} desc: {{desc}} --- ![image|150]({{image}}) `, - noteTemplate: - `--- + noteTemplateFileContent: `--- doubanId: {{id}} title: {{title}} type: {{type}} @@ -98,13 +74,13 @@ authorUrl: {{authorUrl}} dateTimePublished: {{datePublished}} {{timePublished}} url: {{url}} tags: Article +dataTime: {{currentDate}} {{currentTime}} desc: {{desc}} --- {{content}} `, - gameTemplate: - `--- + gameTemplateFileContent: `--- doubanId: {{id}} title: {{title}} aliases: {{aliases}} @@ -117,19 +93,28 @@ developer: {{developer}} platform: {{platform}} url: {{url}} tags: Game +dataTime: {{currentDate}} {{currentTime}} desc: {{desc}} --- ![image]({{image}}) +`,teleplayTemplateFileContent: `--- +doubanId: {{id}} +title: {{title}} +type: {{type}} +score: {{score}} +originalTitle: {{originalTitle}} +genre: {{genre}} +datePublished: {{datePublished}} +director: {{director}} +actor: {{actor}} +author: {{author}} +tags: {{type}} +url: {{url}} +dataTime: {{currentDate}} {{currentTime}} +desc: {{desc}} +--- + +![image]({{image}}) `, -// totalWord: {{totalWord}} - - searchUrl: 'https://www.douban.com/search?q=', - searchHeaders: JSON.stringify(doubanHeaders), - dateFormat: "yyyy-MM-DD", - timeFormat: "HH:mm:ss", - arraySpilt: ", ", - personNameMode: PersonNameMode.CH_NAME - } - diff --git a/src/constant/Douban.ts b/src/constant/Douban.ts new file mode 100644 index 0000000..3090d00 --- /dev/null +++ b/src/constant/Douban.ts @@ -0,0 +1,12 @@ +import {PersonNameMode} from "./Constsant"; +import {DoubanPluginSetting} from "@App/setting/model/DoubanPluginSetting"; + + + +export const doubanHeaders = { + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", + "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7", + "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.61 Safari/537.36", +}; + + diff --git a/src/douban/DoubanSettingTab.ts b/src/douban/DoubanSettingTab.ts deleted file mode 100644 index 86c5504..0000000 --- a/src/douban/DoubanSettingTab.ts +++ /dev/null @@ -1,350 +0,0 @@ -import {App, PluginSettingTab, Setting} from "obsidian"; -import {DEFAULT_SETTINGS} from "./Douban"; - -import DoubanPlugin from "main"; -import {i18nHelper} from "src/lang/helper"; -import {PersonNameMode, PersonNameModeRecords} from "../constant/Constsant"; - -export class DoubanSettingTab extends PluginSettingTab { - plugin: DoubanPlugin; - - constructor(app: App, plugin: DoubanPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - containerEl.createEl("h2", {text: 'Obsidian Douban'}); - - new Setting(containerEl).setName(i18nHelper.getMessage('120001')) - .then((setting) => { - setting.addText((textField) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('120002')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120003')); - frag.createEl( - 'a', - { - text: i18nHelper.getMessage('120901'), - href: 'https://www.douban.com', - }, - (a) => { - a.setAttr('target', '_blank'); - } - ); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120004')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120005')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120006')); - frag.createEl('br'); - }) - ); - textField.inputEl.addClass("obsidian_douban_settings_textField"); - textField - .setPlaceholder(DEFAULT_SETTINGS.searchUrl) - .setValue(this.plugin.settings.searchUrl) - .onChange(async (value) => { - this.plugin.settings.searchUrl = value; - await this.plugin.saveSettings(); - }); - - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage('120501')).then((setting) => { - setting.addMomentFormat((mf) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText( - i18nHelper.getMessage('120503') - ); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120506') + ' '); - frag.createEl( - 'a', - { - text: i18nHelper.getMessage('120508'), - href: 'https://momentjs.com/docs/#/displaying/format/', - }, - (a) => { - a.setAttr('target', '_blank'); - } - ); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120507') + ': '); - mf.setSampleEl(frag.createEl('b', {cls: 'u-pop'})); - frag.createEl('br'); - }) - ); - mf.setPlaceholder(DEFAULT_SETTINGS.dateFormat); - mf.setValue(this.plugin.settings.dateFormat) - mf.onChange(async (value) => { - this.plugin.settings.dateFormat = value; - await this.plugin.saveSettings(); - }); - - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage('120502')).then((setting) => { - setting.addMomentFormat((mf) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText( - i18nHelper.getMessage('120504') - ); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120506') + ' '); - frag.createEl( - 'a', - { - text: i18nHelper.getMessage('120508'), - href: 'https://momentjs.com/docs/#/displaying/format/', - }, - (a) => { - a.setAttr('target', '_blank'); - } - ); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120507') + ': '); - mf.setSampleEl(frag.createEl('b', {cls: 'u-pop'})); - frag.createEl('br'); - }) - ); - mf.setPlaceholder(DEFAULT_SETTINGS.timeFormat); - mf.setValue(this.plugin.settings.timeFormat) - mf.onChange(async (value) => { - this.plugin.settings.timeFormat = value; - await this.plugin.saveSettings(); - }); - - }); - }); - - - new Setting(containerEl) - .setName(i18nHelper.getMessage('120601')) - .setDesc(i18nHelper.getMessage('120602')) - .addText((textField) => { - textField.setPlaceholder(DEFAULT_SETTINGS.arraySpilt) - .setValue(this.plugin.settings.arraySpilt) - .onChange(async (value) => { - this.plugin.settings.arraySpilt = value; - await this.plugin.saveSettings(); - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage('121201')).then((setting) => { - setting.addDropdown((dropdwon) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('121202')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121203')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121204')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121205')); - frag.createEl('br'); - }) - ); - // dropdwon.inputEl.addClass("settings_area"); - // dropdwon.inputEl.setAttr("rows", 10); - dropdwon.addOption(PersonNameMode.CH_NAME, PersonNameModeRecords.CH) - dropdwon.addOption(PersonNameMode.EN_NAME, PersonNameModeRecords.EN) - dropdwon.addOption(PersonNameMode.CH_EN_NAME, PersonNameModeRecords.CH_EN) - dropdwon.setValue(this.plugin.settings.personNameMode) - .onChange(async (value: string) => { - this.plugin.settings.personNameMode = value as PersonNameMode; - await this.plugin.saveSettings(); - }); - }); - }); - - - new Setting(containerEl).setName(i18nHelper.getMessage('120101')).then((setting) => { - setting.addTextArea((textarea) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('120102')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120103')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120104')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120105')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120106')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120107')); - frag.createEl('br'); - }) - ); - textarea.inputEl.addClass("obsidian_douban_settings_area"); - textarea.inputEl.setAttr("rows", 20); - textarea.setPlaceholder(DEFAULT_SETTINGS.movieTemplate) - .setValue(this.plugin.settings.movieTemplate) - .onChange(async (value) => { - this.plugin.settings.movieTemplate = value; - await this.plugin.saveSettings(); - }); - }); - }); - - // new Setting(containerEl) - // .setName(i18nHelper.getMessage('120101') + 111) - // .then((setting) => { - // setting.addTextArea((textarea) => { - // setting.descEl.appendChild( - // createEl('table', 'table', (table) => { - // table.createEl('thead', 'thread', (thead) => { - // thead.createEl('tr', 'tr',(tr) => { - // tr.createEl('th', { text: i18nHelper.getMessage('120108') }); - // tr.createEl('th', { text: i18nHelper.getMessage('120109') }); - // tr.createEl('th', { text: i18nHelper.getMessage('120110') }); - // }); - // }); - // table.createEl('tbody', 'tbody', (tbody) => { - // personNameModeRecords.forEach((record) => { - // tbody.createEl('tr', (tr) => { - // tr.createEl('td', { text: record.name }); - // tr.createEl('td', { text: record.key }); - // tr.createEl('td', { text: record.desc }); - // }); - // }); - // }); - // } - // - // ); - // })); - - - new Setting(containerEl).setName(i18nHelper.getMessage('120201')).then((setting) => { - setting.addTextArea((textarea) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('120202')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120203')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120204')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120205')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120206')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120207')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120208')); - frag.createEl('br'); - }) - ); - textarea.inputEl.addClass("obsidian_douban_settings_area"); - textarea.inputEl.setAttr("rows", 20); - textarea.setPlaceholder(DEFAULT_SETTINGS.bookTemplate) - .setValue(this.plugin.settings.bookTemplate) - .onChange(async (value) => { - this.plugin.settings.bookTemplate = value; - await this.plugin.saveSettings(); - }); - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage('120301')).then((setting) => { - setting.addTextArea((textarea) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('120302')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120303')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120304')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120305')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120306')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120307')); - frag.createEl('br'); - }) - ); - textarea.inputEl.addClass("obsidian_douban_settings_area"); - textarea.inputEl.setAttr("rows", 20); - textarea.setPlaceholder(DEFAULT_SETTINGS.musicTemplate) - .setValue(this.plugin.settings.musicTemplate) - .onChange(async (value) => { - this.plugin.settings.musicTemplate = value; - await this.plugin.saveSettings(); - }); - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage("120401")).then((setting) => { - setting.addTextArea((textarea) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('120402')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120403')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120404')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120405')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120406')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('120407')); - frag.createEl('br'); - - }) - ); - textarea.inputEl.addClass("obsidian_douban_settings_area"); - textarea.inputEl.setAttr("rows", 20); - textarea.setPlaceholder(DEFAULT_SETTINGS.noteTemplate) - .setValue(this.plugin.settings.noteTemplate) - .onChange(async (value) => { - this.plugin.settings.noteTemplate = value; - await this.plugin.saveSettings(); - }); - }); - }); - - new Setting(containerEl).setName(i18nHelper.getMessage('121301')).then((setting) => { - setting.addTextArea((textarea) => { - setting.descEl.appendChild( - createFragment((frag) => { - frag.appendText(i18nHelper.getMessage('121302')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121303')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121304')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121305')); - frag.createEl('br'); - frag.appendText(i18nHelper.getMessage('121306')); - frag.createEl('br'); - }) - ); - textarea.inputEl.addClass("obsidian_douban_settings_area"); - textarea.inputEl.setAttr("rows", 20); - textarea.setPlaceholder(DEFAULT_SETTINGS.gameTemplate) - .setValue(this.plugin.settings.gameTemplate) - .onChange(async (value) => { - this.plugin.settings.gameTemplate = value; - await this.plugin.saveSettings(); - }); - }); - }) - - - - } -} diff --git a/src/douban/broadcast/handler/DoubanPageBroadcatLoadHandler.ts b/src/douban/broadcast/handler/DoubanPageBroadcatLoadHandler.ts index 5c6b44d..4513aed 100644 --- a/src/douban/broadcast/handler/DoubanPageBroadcatLoadHandler.ts +++ b/src/douban/broadcast/handler/DoubanPageBroadcatLoadHandler.ts @@ -3,34 +3,23 @@ import DoubanAbstractLoadHandler from 'src/douban/data/handler/DoubanAbstractLoa import DoubanNoteSubject from '../model/DoubanPageBroadcastSubject'; import DoubanPageBroadcastSubject from '../model/DoubanPageBroadcastSubject'; import DoubanPlugin from "main"; -import {DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from 'src/douban/data/model/DoubanSubject'; +import HandleContext from "@App/data/model/HandleContext"; +import {TemplateKey} from "../../../constant/Constsant"; //TODO will support in future version -export default class DoubanPageBroadcatLoadHandler extends DoubanAbstractLoadHandler { +class DoubanPageBroadcatLoadHandler extends DoubanAbstractLoadHandler { constructor(doubanPlugin: DoubanPlugin) { super(doubanPlugin); } - getTemplate(settings: DoubanPluginSettings): string { - return settings.bookTemplate; + getTemplateKey(context: HandleContext): TemplateKey { + return null; } - parseText(beforeContent: string, extract: DoubanNoteSubject, settings: DoubanPluginSettings): string { - return settings.bookTemplate ? null : ""; - // settings.noteTemplate - // .replaceAll("{{id}}", extract.id) - // .replaceAll("{{type}}", extract.type ? extract.type : "") - // .replaceAll("{{title}}", extract.title ? extract.title : "") - // .replaceAll("{{desc}}", extract.desc ? extract.desc : "") - // .replaceAll("{{image}}", extract.image ? extract.image : "") - // .replaceAll("{{timePublished}}", extract.timePublished ? moment(extract.timePublished).format(settings.dateTimeFormat) : "") - // .replaceAll("{{url}}", extract.url ? extract.url : "") - // .replaceAll("{{content}}", extract.content ? extract.content : "") - // .replaceAll("{{url}}", extract.url ? extract.url : "") - // .replaceAll("{{authorUrl}}", extract.authorUrl ? extract.authorUrl : "") - // .replaceAll("{{author}}", extract.author ? extract.author : "") + parseText(beforeContent: string, extract: DoubanNoteSubject, context: HandleContext): string { + return null; } support(extract: DoubanSubject): boolean { @@ -38,37 +27,6 @@ export default class DoubanPageBroadcatLoadHandler extends DoubanAbstractLoadHan } parseSubjectFromHtml(html: CheerioAPI): DoubanPageBroadcastSubject { - // return html('.new-status .status-wrapper') - // .get() - // .map(i -> { - // let div = html(i); - // div.find('') - // }); - - let title = html(html("head > meta[property= 'og:title']").get(0)).attr("content"); - let desc = html(html("head > meta[property= 'og:description']").get(0)).attr("content"); - let url = html(html("head > meta[property= 'og:url']").get(0)).attr("content"); - let image = html(html("head > meta[property= 'og:image']").get(0)).attr("content"); - let type = html(html("head > meta[property= 'og:type']").get(0)).attr("content"); - let authorA = html(html("a.note-author").get(0)); - let timePublished = html(html(".pub-date").get(0)).text(); - let content = html(html(".note").get(1)); - let idPattern = /(\d){5,10}/g; - let id = idPattern.exec(url); - - // const result:DoubanNoteSubject = { - // image: image, - // timePublished: timePublished ? new Date(timePublished) : null, - // content: content ? html2markdown(content.toString()): "", - // id: id ? id[0] : "", - // type: "Article", - // title: title, - // desc: desc, - // url: url, - // author: authorA ? authorA.text() : null, - // authorUrl: authorA ? authorA.attr("href") : null, - // }; - // return result; return null; } diff --git a/src/douban/data/handler/DoubanAbstractLoadHandler.ts b/src/douban/data/handler/DoubanAbstractLoadHandler.ts index 290f6c6..2e64628 100644 --- a/src/douban/data/handler/DoubanAbstractLoadHandler.ts +++ b/src/douban/data/handler/DoubanAbstractLoadHandler.ts @@ -1,14 +1,17 @@ -import {DoubanPluginSettings} from "src/douban/Douban"; import DoubanPlugin from "main"; import DoubanSubject, {DoubanParameter} from '../model/DoubanSubject'; import DoubanSubjectLoadHandler from "./DoubanSubjectLoadHandler"; -import {Editor, moment, request, RequestUrlParam} from "obsidian"; +import {moment, request, RequestUrlParam, TFile} from "obsidian"; import {i18nHelper} from 'src/lang/helper'; import {log} from "src/utils/Logutil"; import {CheerioAPI, load} from "cheerio"; import YamlUtil from "../../../utils/YamlUtil"; -import {BasicConst, PersonNameMode, TemplateTextMode} from "../../../constant/Constsant"; +import {BasicConst, PersonNameMode, SearchHandleMode, TemplateKey, TemplateTextMode} from "../../../constant/Constsant"; +import HandleContext from "@App/data/model/HandleContext"; +import HandleResult from "@App/data/model/HandleResult"; +import {DEFAULT_TEMPLATE_CONTENT} from "../../../constant/DefaultTemplateContent"; +import FileHandler from "../../../file/FileHandler"; export default abstract class DoubanAbstractLoadHandler implements DoubanSubjectLoadHandler { @@ -19,30 +22,37 @@ export default abstract class DoubanAbstractLoadHandler this.doubanPlugin = doubanPlugin; } - parse(extract: T, settings: DoubanPluginSettings): string { - let template: string = this.getTemplate(settings); + async parse(extract: T, context: HandleContext): Promise { + let template: string = await this.getTemplate(context); let frontMatterStart: number = template.indexOf(BasicConst.YAML_FRONT_MATTER_SYMBOL, 0); let frontMatterEnd: number = template.indexOf(BasicConst.YAML_FRONT_MATTER_SYMBOL, frontMatterStart + 1); let frontMatter: string = ''; let frontMatterBefore: string = ''; let frontMatterAfter: string = ''; + let result:string = ''; if (frontMatterStart > -1 && frontMatterEnd > -1) { frontMatterBefore = template.substring(0, frontMatterStart); frontMatter = template.substring(frontMatterStart, frontMatterEnd + 3); frontMatterAfter = template.substring(frontMatterEnd + 3); if (frontMatterBefore.length > 0) { - frontMatterBefore = this.parsePartText(frontMatterBefore, extract, settings); + frontMatterBefore = this.parsePartText(frontMatterBefore, extract, context); } if (frontMatterAfter.length > 0) { - frontMatterAfter = this.parsePartText(frontMatterAfter, extract, settings); + frontMatterAfter = this.parsePartText(frontMatterAfter, extract, context); } if (frontMatter.length > 0) { - frontMatter = this.parsePartText(frontMatter, extract, settings, TemplateTextMode.YAML); + frontMatter = this.parsePartText(frontMatter, extract, context, TemplateTextMode.YAML); } - return frontMatterBefore + frontMatter + frontMatterAfter; + result = frontMatterBefore + frontMatter + frontMatterAfter; } else { - return this.parsePartText(template, extract, settings); + result = this.parsePartText(template, extract, context); } + let fileName:string = ''; + if (SearchHandleMode.FOR_CREATE == context.mode) { + fileName = this.parsePartText(template, extract, context); + } + + return {content: result, fileName: fileName}; } /** @@ -66,14 +76,14 @@ export default abstract class DoubanAbstractLoadHandler * @param settings * @param textMode */ - handleContentArray(array: any[], settings: DoubanPluginSettings, textMode: TemplateTextMode): string { + handleContentArray(array: any[], context: HandleContext, textMode: TemplateTextMode): string { let result; switch (textMode) { case TemplateTextMode.YAML: result = array.map(YamlUtil.handleText).join(', '); break; default: - result = array.join(settings.arraySpilt); + result = array.join(context.settings.arraySpilt); } return result; } @@ -84,10 +94,10 @@ export default abstract class DoubanAbstractLoadHandler * @param textMode * @param settings */ - handleSpecialContent(value: any, textMode: TemplateTextMode = TemplateTextMode.NORMAL, settings: DoubanPluginSettings = null): string { + handleSpecialContent(value: any, textMode: TemplateTextMode = TemplateTextMode.NORMAL, context: HandleContext = null): string { let result; if (value instanceof Array) { - result = this.handleContentArray(value, settings, textMode); + result = this.handleContentArray(value, context, textMode); } else if (value instanceof Number) { result = value ? value.toString() : ''; } else { @@ -96,13 +106,13 @@ export default abstract class DoubanAbstractLoadHandler return result; } - abstract getTemplate(settings: DoubanPluginSettings): string; + abstract getTemplateKey(context: HandleContext): TemplateKey; - abstract parseText(beforeContent: string, extract: T, settings: DoubanPluginSettings, textMode: TemplateTextMode): string; + abstract parseText(beforeContent: string, extract: T, context: HandleContext, textMode: TemplateTextMode): string; abstract support(extract: DoubanSubject): boolean; - handle(url: string, editor: Editor): void { + handle(url: string, context: HandleContext): void { const requestUrlParam: RequestUrlParam = { url: url, method: "GET", @@ -112,7 +122,7 @@ export default abstract class DoubanAbstractLoadHandler request(requestUrlParam) .then(load) .then(this.parseSubjectFromHtml) - .then(content => this.toEditor(editor, content)) + .then(content => this.toEditor(context, content)) // .then(content => content ? editor.replaceSelection(content) : content) .catch(e => log.error(i18nHelper.getMessage('130101'))) ; @@ -121,12 +131,13 @@ export default abstract class DoubanAbstractLoadHandler abstract parseSubjectFromHtml(data: CheerioAPI): T | undefined; - toEditor(editor: Editor, extract: T): T { - this.doubanPlugin.putToEditor(editor, extract); + toEditor(context: HandleContext, extract: T): T { + this.doubanPlugin.putToObsidian(context, extract); return extract; } - getPersonName(name: string, settings: DoubanPluginSettings): string { + getPersonName(name: string, context: HandleContext): string { + const {settings} = context; if (!name || !settings || !settings.personNameMode) { return ""; } @@ -173,7 +184,7 @@ export default abstract class DoubanAbstractLoadHandler return s; } - private parsePartText(template: string, extract: T, settings: DoubanPluginSettings, textMode: TemplateTextMode = TemplateTextMode.NORMAL): string { + private parsePartText(template: string, extract: T, context: HandleContext, textMode: TemplateTextMode = TemplateTextMode.NORMAL): string { const resultContent = template .replaceAll(DoubanParameter.ID, extract.id) .replaceAll(DoubanParameter.TITLE, this.handleSpecialContent(extract.title, textMode)) @@ -183,11 +194,29 @@ export default abstract class DoubanAbstractLoadHandler .replaceAll(DoubanParameter.URL, extract.url) .replaceAll(DoubanParameter.DESC, this.handleSpecialContent(extract.desc, textMode)) .replaceAll(DoubanParameter.PUBLISHER, extract.publisher) - .replaceAll(DoubanParameter.DATE_PUBLISHED, extract.datePublished ? moment(extract.datePublished).format(settings.dateFormat) : '') - .replaceAll(DoubanParameter.TIME_PUBLISHED, extract.datePublished ? moment(extract.datePublished).format(settings.timeFormat) : '') - .replaceAll(DoubanParameter.GENRE, this.handleSpecialContent(extract.genre, textMode, settings)) + .replaceAll(DoubanParameter.DATE_PUBLISHED, extract.datePublished ? moment(extract.datePublished).format(context.settings.dateFormat) : '') + .replaceAll(DoubanParameter.TIME_PUBLISHED, extract.datePublished ? moment(extract.datePublished).format(context.settings.timeFormat) : '') + .replaceAll(DoubanParameter.CURRENT_DATE, moment(new Date()).format(context.settings.dateFormat)) + .replaceAll(DoubanParameter.CURRENT_TIME, moment(new Date()).format(context.settings.timeFormat)) + .replaceAll(DoubanParameter.GENRE, this.handleSpecialContent(extract.genre, textMode, context)) ; - return this.parseText(resultContent, extract, settings, textMode); + return this.parseText(resultContent, extract, context, textMode); } + private async getTemplate(context: HandleContext):Promise { + const tempKey:TemplateKey = this.getTemplateKey(context); + const templatePath:string = context.settings[tempKey]; + let firstLinkpathDest:TFile = this.doubanPlugin.app.metadataCache.getFirstLinkpathDest(templatePath, ''); + + // @ts-ignore + const defaultContent = DEFAULT_TEMPLATE_CONTENT[tempKey + 'Content']; + + if (!firstLinkpathDest) { + return defaultContent; + }else { + // return firstLinkpathDest. + const val = await new FileHandler(this.doubanPlugin.app).getFileContent(firstLinkpathDest.path); + return val?val:defaultContent; + } + } } diff --git a/src/douban/data/handler/DoubanBookLoadHandler.ts b/src/douban/data/handler/DoubanBookLoadHandler.ts index d4cd047..2e19d0f 100644 --- a/src/douban/data/handler/DoubanBookLoadHandler.ts +++ b/src/douban/data/handler/DoubanBookLoadHandler.ts @@ -2,10 +2,10 @@ import {CheerioAPI} from 'cheerio'; import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler"; import DoubanBookSubject, {DoubanBookParameter} from "../model/DoubanBookSubject"; import DoubanPlugin from "main"; -import {DEFAULT_SETTINGS, DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from "../model/DoubanSubject"; -import {TemplateTextMode} from "../../../constant/Constsant"; +import {TemplateKey, TemplateTextMode} from "../../../constant/Constsant"; import StringUtil from "../../../utils/StringUtil"; +import HandleContext from "@App/data/model/HandleContext"; export default class DoubanBookLoadHandler extends DoubanAbstractLoadHandler { @@ -13,16 +13,16 @@ export default class DoubanBookLoadHandler extends DoubanAbstractLoadHandler { @@ -12,16 +13,12 @@ export default class DoubanGameLoadHandler extends DoubanAbstractLoadHandler { @@ -13,16 +14,17 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") - .replaceAll("{{actor}}", extract.actor ? extract.actor.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") - .replaceAll("{{author}}", extract.author ? extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{director}}", extract.director ? extract.director.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{actor}}", extract.actor ? extract.actor.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{author}}", extract.author ? extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") ; } diff --git a/src/douban/data/handler/DoubanMusicLoadHandler.ts b/src/douban/data/handler/DoubanMusicLoadHandler.ts index d371ff3..c16e142 100644 --- a/src/douban/data/handler/DoubanMusicLoadHandler.ts +++ b/src/douban/data/handler/DoubanMusicLoadHandler.ts @@ -2,9 +2,9 @@ import {CheerioAPI} from 'cheerio'; import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler"; import DoubanMusicSubject from '../model/DoubanMusicSubject'; import DoubanPlugin from "main"; -import {DEFAULT_SETTINGS, DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from '../model/DoubanSubject'; -import StringUtil from "../../../utils/StringUtil"; +import HandleContext from "@App/data/model/HandleContext"; +import {TemplateKey} from "../../../constant/Constsant"; export default class DoubanMusicLoadHandler extends DoubanAbstractLoadHandler { @@ -12,11 +12,12 @@ export default class DoubanMusicLoadHandler extends DoubanAbstractLoadHandler { let key = html(info).text().trim(); - let value = '' + let value = ''; if (key.indexOf('表演者') >= 0) { // value = html(info.next.next).text().trim(); let vas: string[] = key.split("\n \n "); diff --git a/src/douban/data/handler/DoubanNoteLoadHandler.ts b/src/douban/data/handler/DoubanNoteLoadHandler.ts index 2be36cb..3e726c5 100644 --- a/src/douban/data/handler/DoubanNoteLoadHandler.ts +++ b/src/douban/data/handler/DoubanNoteLoadHandler.ts @@ -2,10 +2,10 @@ import {CheerioAPI} from 'cheerio'; import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler"; import DoubanNoteSubject from '../model/DoubanNoteSubject'; import DoubanPlugin from "main"; -import {DEFAULT_SETTINGS, DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from '../model/DoubanSubject'; import html2markdown from '@notable/html2markdown'; -import StringUtil from "../../../utils/StringUtil"; +import HandleContext from "@App/data/model/HandleContext"; +import {TemplateKey} from "../../../constant/Constsant"; export default class DoubanNoteLoadHandler extends DoubanAbstractLoadHandler { @@ -13,11 +13,11 @@ export default class DoubanNoteLoadHandler extends DoubanAbstractLoadHandler { - getTemplate(settings: DoubanPluginSettings): string { - return ""; + getTemplateKey(context: HandleContext): TemplateKey { + return TemplateKey.bookTemplate } - parseText(beforeContent: string, extract: DoubanSubject, settings: DoubanPluginSettings): string { + parseText(beforeContent: string, extract: DoubanSubject, context: HandleContext): string { log.warn(i18nHelper.getMessage('140101')); return ""; } diff --git a/src/douban/data/handler/DoubanSearchChooseItemHandler.ts b/src/douban/data/handler/DoubanSearchChooseItemHandler.ts index e20ebae..1c0d075 100644 --- a/src/douban/data/handler/DoubanSearchChooseItemHandler.ts +++ b/src/douban/data/handler/DoubanSearchChooseItemHandler.ts @@ -1,4 +1,4 @@ -import {App, Editor} from "obsidian"; +import {App} from "obsidian"; import DoubanBookLoadHandler from "./DoubanBookLoadHandler"; import DoubanMovieLoadHandler from "./DoubanMovieLoadHandler"; @@ -6,11 +6,12 @@ import DoubanMusicLoadHandler from "./DoubanMusicLoadHandler"; import DoubanNoteLoadHandler from "./DoubanNoteLoadHandler"; import DoubanOtherLoadHandler from "./DoubanOtherLoadHandler"; import DoubanPlugin from "main"; -import {DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from "../model/DoubanSubject"; import DoubanSubjectLoadHandler from "./DoubanSubjectLoadHandler"; import {DoubanTeleplayLoadHandler} from "./DoubanTeleplayLoadHandler"; import DoubanGameLoadHandler from "./DoubanGameLoadHandler"; +import HandleContext from "@App/data/model/HandleContext"; +import HandleResult from "@App/data/model/HandleResult"; export class DoubanSearchChooseItemHandler { @@ -33,34 +34,32 @@ export class DoubanSearchChooseItemHandler { this._doubanSubjectHandlerDefault]; } - public handle(searchExtract: DoubanSubject, editor: Editor): void { + public handle(searchExtract: DoubanSubject, context: HandleContext): void { if (!searchExtract) { return; } let doubanSubjectHandlers: DoubanSubjectLoadHandler[] = this._doubanSubjectHandlers .filter(h => h.support(searchExtract)); if (doubanSubjectHandlers && doubanSubjectHandlers.length > 0) { - doubanSubjectHandlers[0].handle(searchExtract.url, editor); + doubanSubjectHandlers[0].handle(searchExtract.url, context); } else { - this._doubanSubjectHandlerDefault.handle(searchExtract.url, editor); + this._doubanSubjectHandlerDefault.handle(searchExtract.url, context); } } - public parseText(extract: DoubanSubject, settings: DoubanPluginSettings): string { - if (!settings) { - return ""; - } + public async parseText(extract: DoubanSubject, context: HandleContext): Promise { let doubanSubjectHandlers: DoubanSubjectLoadHandler[] = this._doubanSubjectHandlers .filter(h => h.support(extract)); + let result:string=''; if (doubanSubjectHandlers && doubanSubjectHandlers.length > 0) { - let result = doubanSubjectHandlers.map(h => h.parse(extract, settings)); + let result = await doubanSubjectHandlers.map(h => h.parse(extract, context)); if (result && result.length > 0) { return result[0]; } else { - return ""; + return {content: ''}; } } else { - return this._doubanSubjectHandlerDefault.parse(extract, settings); + return this._doubanSubjectHandlerDefault.parse(extract, context); } } diff --git a/src/douban/data/handler/DoubanSubjectLoadHandler.ts b/src/douban/data/handler/DoubanSubjectLoadHandler.ts index 670ba9b..6122989 100644 --- a/src/douban/data/handler/DoubanSubjectLoadHandler.ts +++ b/src/douban/data/handler/DoubanSubjectLoadHandler.ts @@ -1,14 +1,14 @@ -import {DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from "../model/DoubanSubject"; -import {Editor} from "obsidian"; +import HandleContext from "@App/data/model/HandleContext"; +import HandleResult from "@App/data/model/HandleResult"; export default interface DoubanSubjectLoadHandler { - parse(extract: T, settings: DoubanPluginSettings): string; + parse(extract: T, context: HandleContext): Promise; support(extract: DoubanSubject): boolean; - handle(url: string, editor: Editor): void; + handle(url: string, context: HandleContext): void; } diff --git a/src/douban/data/handler/DoubanTeleplayLoadHandler.ts b/src/douban/data/handler/DoubanTeleplayLoadHandler.ts index 478ac64..72d0672 100644 --- a/src/douban/data/handler/DoubanTeleplayLoadHandler.ts +++ b/src/douban/data/handler/DoubanTeleplayLoadHandler.ts @@ -1,11 +1,11 @@ import {CheerioAPI} from "cheerio"; import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler"; import DoubanPlugin from "main"; -import {DEFAULT_SETTINGS, DoubanPluginSettings} from "src/douban/Douban"; import DoubanSubject from "../model/DoubanSubject"; import DoubanTeleplaySubject from "../model/DoubanTeleplaySubject"; import SchemaOrg from "src/utils/SchemaOrg"; -import StringUtil from "../../../utils/StringUtil"; +import HandleContext from "@App/data/model/HandleContext"; +import {TemplateKey} from "../../../constant/Constsant"; /** * teleplay @@ -16,16 +16,17 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") - .replaceAll("{{actor}}", extract.actor ? extract.actor.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") - .replaceAll("{{author}}", extract.author ? extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, settings)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{director}}", extract.director ? extract.director.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{actor}}", extract.actor ? extract.actor.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") + .replaceAll("{{author}}", extract.author ? extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c).join(settings.arraySpilt) : "") } support(extract: DoubanSubject): boolean { diff --git a/src/douban/data/model/DoubanSubject.ts b/src/douban/data/model/DoubanSubject.ts index 68a0fd3..6a64416 100644 --- a/src/douban/data/model/DoubanSubject.ts +++ b/src/douban/data/model/DoubanSubject.ts @@ -27,4 +27,6 @@ export const DoubanParameter = { DATE_PUBLISHED: '{{datePublished}}', TIME_PUBLISHED: '{{timePublished}}', GENRE: '{{genre}}', + CURRENT_DATE: '{{currentDate}}', + CURRENT_TIME: '{{currentTime}}', } diff --git a/src/douban/data/model/HandleContext.ts b/src/douban/data/model/HandleContext.ts new file mode 100644 index 0000000..5654526 --- /dev/null +++ b/src/douban/data/model/HandleContext.ts @@ -0,0 +1,9 @@ +import {SearchHandleMode} from "../../../constant/Constsant"; +import {Editor} from "obsidian"; +import { DoubanPluginSetting } from "@App/setting/model/DoubanPluginSetting"; + +export default interface HandleContext { + mode:SearchHandleMode; + settings: DoubanPluginSetting; + editor?:Editor; +} diff --git a/src/douban/data/model/HandleResult.ts b/src/douban/data/model/HandleResult.ts new file mode 100644 index 0000000..4de4a92 --- /dev/null +++ b/src/douban/data/model/HandleResult.ts @@ -0,0 +1,4 @@ +export default interface HandleResult { + content:string + fileName?:string +} diff --git a/src/douban/data/search/DoubanSearchFuzzySuggestModal.ts b/src/douban/data/search/DoubanSearchFuzzySuggestModal.ts index fb652c0..aa9e7ce 100644 --- a/src/douban/data/search/DoubanSearchFuzzySuggestModal.ts +++ b/src/douban/data/search/DoubanSearchFuzzySuggestModal.ts @@ -1,25 +1,25 @@ -import {Editor, FuzzySuggestModal} from "obsidian"; +import {FuzzySuggestModal} from "obsidian"; import DoubanPlugin from "main"; import DoubanSearchResultSubject from "../model/DoubanSearchResultSubject"; import {log} from "src/utils/Logutil"; import {i18nHelper} from "../../../lang/helper"; +import HandleContext from "@App/data/model/HandleContext"; export {DoubanFuzzySuggester} class DoubanFuzzySuggester extends FuzzySuggestModal { - public editor: Editor; private plugin: DoubanPlugin; - private doubanSearchResultExtract: DoubanSearchResultSubject[] + private doubanSearchResultExtract: DoubanSearchResultSubject[]; + private context: HandleContext; - constructor(plugin: DoubanPlugin, editor: Editor) { + constructor(plugin: DoubanPlugin, context: HandleContext) { super(app); - this.editor = editor; this.plugin = plugin; + this.context = context; this.setPlaceholder(i18nHelper.getMessage('150101')); - } @@ -34,7 +34,7 @@ class DoubanFuzzySuggester extends FuzzySuggestModal onChooseItem(item: DoubanSearchResultSubject, evt: MouseEvent | KeyboardEvent): void { this.plugin.showStatus('140204', item.title); - this.plugin.doubanExtractHandler.handle(item, this.editor); + this.plugin.doubanExtractHandler.handle(item, this.context); } public showSearchList(doubanSearchResultExtractList: DoubanSearchResultSubject[]) { diff --git a/src/douban/data/search/DoubanSearchModal.ts b/src/douban/data/search/DoubanSearchModal.ts index 4bdcd44..39227d8 100644 --- a/src/douban/data/search/DoubanSearchModal.ts +++ b/src/douban/data/search/DoubanSearchModal.ts @@ -1,17 +1,18 @@ -import {App, Editor, Modal, TextComponent} from "obsidian"; +import {App, Modal, TextComponent} from "obsidian"; import DoubanPlugin from "main"; import {i18nHelper} from "src/lang/helper"; +import HandleContext from "@App/data/model/HandleContext"; export class DoubanSearchModal extends Modal { searchTerm: string; plugin: DoubanPlugin; - editor: Editor; + context: HandleContext - constructor(app: App, plugin: DoubanPlugin, editor: Editor) { + constructor(app: App, plugin: DoubanPlugin, context: HandleContext) { super(app); this.plugin = plugin; - this.editor = editor; + this.context = context; } onOpen() { @@ -55,7 +56,7 @@ export class DoubanSearchModal extends Modal { let {contentEl} = this; contentEl.empty(); if (this.searchTerm) { - await this.plugin.search(this.searchTerm, this.editor); + await this.plugin.search(this.searchTerm, this.context); } } diff --git a/src/douban/data/search/Search.ts b/src/douban/data/search/Search.ts index d6fb9f5..a3b52c8 100644 --- a/src/douban/data/search/Search.ts +++ b/src/douban/data/search/Search.ts @@ -1,4 +1,3 @@ -import {DoubanPluginSettings} from 'src/douban/Douban'; import DoubanSearchResultSubject from '../model/DoubanSearchResultSubject'; import SearchParserHandler from './SearchParser'; @@ -6,9 +5,10 @@ import {log} from 'src/utils/Logutil'; import {request, RequestUrlParam} from "obsidian"; import {i18nHelper} from "../../../lang/helper"; import {load} from 'cheerio'; +import {DoubanPluginSetting} from "@App/setting/model/DoubanPluginSetting"; export default class Searcher { - static search(searchItem: string, doubanSettings: DoubanPluginSettings): Promise { + static search(searchItem: string, doubanSettings: DoubanPluginSetting): Promise { let requestUrlParam: RequestUrlParam = { url: doubanSettings.searchUrl + searchItem, method: "GET", diff --git a/src/douban/setting/DoubanSettingTab.ts b/src/douban/setting/DoubanSettingTab.ts new file mode 100644 index 0000000..de717ab --- /dev/null +++ b/src/douban/setting/DoubanSettingTab.ts @@ -0,0 +1,186 @@ +import {App, PluginSettingTab, SearchComponent, Setting} from "obsidian"; + +import DoubanPlugin from "main"; +import {i18nHelper} from "src/lang/helper"; +import {PersonNameMode, PersonNameModeRecords} from "../../constant/Constsant"; +import SettingsManager from "@App/setting/SettingsManager"; +import {FolderSuggest} from "@App/setting/model/FolderSuggest"; +import { DEFAULT_SETTINGS } from "src/constant/DefaultSettings"; + +/** + * 部分逻辑参考以下项目 + * obsidian-kanban + */ +export class DoubanSettingTab extends PluginSettingTab { + plugin: DoubanPlugin; + settingsManager: SettingsManager; + constructor(app: App, plugin: DoubanPlugin) { + super(app, plugin); + this.plugin = plugin; + this.settingsManager = new SettingsManager(app, plugin); + } + + display(): void { + const {containerEl} = this; + + + containerEl.empty(); + + containerEl.createEl("h2", {text: 'Obsidian Douban'}); + + new Setting(containerEl).setName(i18nHelper.getMessage('120001')) + .then((setting) => { + setting.addText((textField) => { + setting.descEl.appendChild( + createFragment((frag) => { + frag.appendText(i18nHelper.getMessage('120002')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120003')); + frag.createEl( + 'a', + { + text: i18nHelper.getMessage('120901'), + href: 'https://www.douban.com', + }, + (a) => { + a.setAttr('target', '_blank'); + } + ); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120004')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120005')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120006')); + frag.createEl('br'); + }) + ); + textField.inputEl.addClass("obsidian_douban_settings_textField"); + textField + .setPlaceholder(DEFAULT_SETTINGS.searchUrl) + .setValue(this.plugin.settings.searchUrl) + .onChange(async (value) => { + this.plugin.settings.searchUrl = value; + await this.plugin.saveSettings(); + }); + + }); + }); + + new Setting(containerEl).setName(i18nHelper.getMessage('120501')).then((setting) => { + setting.addMomentFormat((mf) => { + setting.descEl.appendChild( + createFragment((frag) => { + frag.appendText( + i18nHelper.getMessage('120503') + ); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120506') + ' '); + frag.createEl( + 'a', + { + text: i18nHelper.getMessage('120508'), + href: 'https://momentjs.com/docs/#/displaying/format/', + }, + (a) => { + a.setAttr('target', '_blank'); + } + ); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120507') + ': '); + mf.setSampleEl(frag.createEl('b', {cls: 'u-pop'})); + frag.createEl('br'); + }) + ); + mf.setPlaceholder(DEFAULT_SETTINGS.dateFormat); + mf.setValue(this.plugin.settings.dateFormat) + mf.onChange(async (value) => { + this.plugin.settings.dateFormat = value; + await this.plugin.saveSettings(); + }); + + }); + }); + + new Setting(containerEl).setName(i18nHelper.getMessage('120502')).then((setting) => { + setting.addMomentFormat((mf) => { + setting.descEl.appendChild( + createFragment((frag) => { + frag.appendText( + i18nHelper.getMessage('120504') + ); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120506') + ' '); + frag.createEl( + 'a', + { + text: i18nHelper.getMessage('120508'), + href: 'https://momentjs.com/docs/#/displaying/format/', + }, + (a) => { + a.setAttr('target', '_blank'); + } + ); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('120507') + ': '); + mf.setSampleEl(frag.createEl('b', {cls: 'u-pop'})); + frag.createEl('br'); + }) + ); + mf.setPlaceholder(DEFAULT_SETTINGS.timeFormat); + mf.setValue(this.plugin.settings.timeFormat) + mf.onChange(async (value) => { + this.plugin.settings.timeFormat = value; + await this.plugin.saveSettings(); + }); + + }); + }); + + + new Setting(containerEl) + .setName(i18nHelper.getMessage('120601')) + .setDesc(i18nHelper.getMessage('120602')) + .addText((textField) => { + textField.setPlaceholder(DEFAULT_SETTINGS.arraySpilt) + .setValue(this.plugin.settings.arraySpilt) + .onChange(async (value) => { + this.plugin.settings.arraySpilt = value; + await this.plugin.saveSettings(); + }); + }); + + this.settingsManager.constructUI(containerEl); + + new Setting(containerEl).setName(i18nHelper.getMessage('121201')).then((setting) => { + setting.addDropdown((dropdwon) => { + setting.descEl.appendChild( + createFragment((frag) => { + frag.appendText(i18nHelper.getMessage('121202')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('121203')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('121204')); + frag.createEl('br'); + frag.appendText(i18nHelper.getMessage('121205')); + frag.createEl('br'); + }) + ); + // dropdwon.inputEl.addClass("settings_area"); + // dropdwon.inputEl.setAttr("rows", 10); + dropdwon.addOption(PersonNameMode.CH_NAME, PersonNameModeRecords.CH) + dropdwon.addOption(PersonNameMode.EN_NAME, PersonNameModeRecords.EN) + dropdwon.addOption(PersonNameMode.CH_EN_NAME, PersonNameModeRecords.CH_EN) + dropdwon.setValue(this.plugin.settings.personNameMode) + .onChange(async (value: string) => { + this.plugin.settings.personNameMode = value as PersonNameMode; + await this.plugin.saveSettings(); + }); + }); + }); + + + + + } +} diff --git a/src/douban/setting/SettingHelper.ts b/src/douban/setting/SettingHelper.ts new file mode 100644 index 0000000..8f1d9dd --- /dev/null +++ b/src/douban/setting/SettingHelper.ts @@ -0,0 +1,61 @@ +import {i18nHelper} from "../../lang/helper"; +import {CreateTemplateSelectParams} from "@App/setting/model/CreateTemplateSelectParams"; +import { FileSuggest } from "./model/FileSuggest"; +import {SearchComponent, Setting} from "obsidian"; + + +export function createFileSelectionSetting({ + name, desc, placeholder, key, manager, + }: CreateTemplateSelectParams) { + return (setting: Setting) => { + // @ts-ignore + setting.setName( i18nHelper.getMessage(name)); + // @ts-ignore + setting.setDesc( i18nHelper.getMessage(desc)); + setting.addSearch(async (search: SearchComponent) => { + const [oldValue, defaultVal] = manager.getSetting(key); + let v = defaultVal; + if (oldValue) { + v = oldValue; + } + new FileSuggest(manager.app, search.inputEl); + search.setValue(v); + // @ts-ignore + search.setPlaceholder(i18nHelper.getMessage()); + search.onChange(async (value: string) => { + manager.updateSetting(key, value); + + }); + }); + }; +} + +export function createFolderSelectionSetting({ + name, desc, placeholder, key, manager, + }: CreateTemplateSelectParams) { + return (setting: Setting) => { + // @ts-ignore + setting.setName( i18nHelper.getMessage(name)); + // @ts-ignore + setting.setDesc( i18nHelper.getMessage(desc)); + setting.addSearch(async (search: SearchComponent) => { + const [oldValue, defaultVal] = manager.getSetting(key); + let v = defaultVal; + if (oldValue) { + v = oldValue; + } + new FileSuggest(manager.app, search.inputEl); + search + .setValue(v) + // @ts-ignore + .setPlaceholder(i18nHelper.getMessage(placeholder)) + .onChange(async (value: string) => { + manager.updateSetting(key, value); + + }); + }); + }; +} + + + diff --git a/src/douban/setting/SettingsManager.ts b/src/douban/setting/SettingsManager.ts new file mode 100644 index 0000000..c234d86 --- /dev/null +++ b/src/douban/setting/SettingsManager.ts @@ -0,0 +1,40 @@ +import {App, Setting} from "obsidian"; +import { DEFAULT_SETTINGS } from "src/constant/DefaultSettings"; +import DoubanPlugin from "../../../main"; +import { DoubanPluginSetting } from "./model/DoubanPluginSetting"; +import {createFileSelectionSetting, createFolderSelectionSetting} from "@App/setting/SettingHelper"; + +export default class SettingsManager { + app: App; + plugin: DoubanPlugin; + settings: DoubanPluginSetting; + cleanupFns: Array<() => void> = []; + + constructor(app: App, plugin: DoubanPlugin) { + this.app = app; + this.plugin = plugin; + this.settings = plugin.settings; + } + + getSetting(key: keyof DoubanPluginSetting) { + return [this.settings[key], DEFAULT_SETTINGS[key]]; + } + + + + async updateSetting(key: keyof DoubanPluginSetting, value:any) { + this.settings[key] = value; + await this.plugin.saveSettings(); + } + + constructUI(contenterEl: HTMLElement) { + new Setting(contenterEl).then(createFileSelectionSetting({name: '120101', desc: '120102', placeholder: '121701', key: 'movieTemplateFile', manager: this})); + new Setting(contenterEl).then(createFileSelectionSetting({name: '120201', desc: '120202', placeholder: '121701', key: 'bookTemplateFile', manager: this})); + new Setting(contenterEl).then(createFileSelectionSetting({name: '120301', desc: '120302', placeholder: '121701', key: 'musicTemplateFile', manager: this})); + new Setting(contenterEl).then(createFileSelectionSetting({name: '120401', desc: '120402', placeholder: '121701', key: 'noteTemplateFile', manager: this})); + new Setting(contenterEl).then(createFileSelectionSetting({name: '121301', desc: '121302', placeholder: '121701', key: 'gameTemplateFile', manager: this})); + new Setting(contenterEl).then(createFileSelectionSetting({name: '121301', desc: '121302', placeholder: '121701', key: 'teleplayTemplateFile', manager: this})); + new Setting(contenterEl).then(createFolderSelectionSetting({name: '121301', desc: '121302', placeholder: '121701', key: 'dataFilePath', manager: this})); + } + +} diff --git a/src/douban/setting/model/CreateTemplateSelectParams.ts b/src/douban/setting/model/CreateTemplateSelectParams.ts new file mode 100644 index 0000000..574473c --- /dev/null +++ b/src/douban/setting/model/CreateTemplateSelectParams.ts @@ -0,0 +1,11 @@ +import SettingsManager from "@App/setting/SettingsManager"; +import { DoubanPluginSetting } from "./DoubanPluginSetting"; + +export interface CreateTemplateSelectParams { + // @ts-ignore + name:string, + desc:string, + placeholder:string, + key: keyof DoubanPluginSetting; + manager: SettingsManager; +} diff --git a/src/douban/setting/model/DoubanPluginSetting.ts b/src/douban/setting/model/DoubanPluginSetting.ts new file mode 100644 index 0000000..04bfbb6 --- /dev/null +++ b/src/douban/setting/model/DoubanPluginSetting.ts @@ -0,0 +1,18 @@ + +export interface DoubanPluginSetting { + movieTemplateFile: string, + bookTemplateFile: string, + musicTemplateFile: string, + noteTemplateFile: string, + gameTemplateFile: string, + teleplayTemplateFile: string, + dateFormat: string, + timeFormat: string, + searchUrl: string, + arraySpilt: string, + searchHeaders?: string, + personNameMode: string, + dataFilePath: string, + dataFileNamePath: string, + +} diff --git a/src/douban/setting/model/FileSuggest.ts b/src/douban/setting/model/FileSuggest.ts new file mode 100644 index 0000000..b9dafe4 --- /dev/null +++ b/src/douban/setting/model/FileSuggest.ts @@ -0,0 +1,33 @@ +import {TAbstractFile, TFile, TFolder} from "obsidian"; +import {TextInputSuggest} from "@App/setting/model/TextInputSuggest"; +// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes + +export class FileSuggest extends TextInputSuggest { + getSuggestions(inputStr: string): TFile[] { + const abstractFiles = this.app.vault.getAllLoadedFiles(); + const files: TFile[] = []; + const lowerCaseInputStr = inputStr.toLowerCase(); + + abstractFiles.forEach((file: TAbstractFile) => { + if ( + file instanceof TFile && + file.extension === "md" && + file.path.toLowerCase().contains(lowerCaseInputStr) + ) { + files.push(file); + } + }); + + return files; + } + + renderSuggestion(file: TFile, el: HTMLElement): void { + el.setText(file.path); + } + + selectSuggestion(file: TFile): void { + this.inputEl.value = file.path; + this.inputEl.trigger("input"); + this.close(); + } +} diff --git a/src/douban/setting/model/FolderSuggest.ts b/src/douban/setting/model/FolderSuggest.ts new file mode 100644 index 0000000..e8d9164 --- /dev/null +++ b/src/douban/setting/model/FolderSuggest.ts @@ -0,0 +1,32 @@ +import { TAbstractFile, TFolder } from "obsidian"; +import {TextInputSuggest} from "@App/setting/model/TextInputSuggest"; +// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes + +export class FolderSuggest extends TextInputSuggest { + getSuggestions(inputStr: string): TFolder[] { + const abstractFiles = this.app.vault.getAllLoadedFiles(); + const folders: TFolder[] = []; + const lowerCaseInputStr = inputStr.toLowerCase(); + + abstractFiles.forEach((folder: TAbstractFile) => { + if ( + folder instanceof TFolder && + folder.path.toLowerCase().contains(lowerCaseInputStr) + ) { + folders.push(folder); + } + }); + + return folders; + } + + renderSuggestion(file: TFolder, el: HTMLElement): void { + el.setText(file.path); + } + + selectSuggestion(file: TFolder): void { + this.inputEl.value = file.path; + this.inputEl.trigger("input"); + this.close(); + } +} diff --git a/src/douban/setting/model/Suggest.ts b/src/douban/setting/model/Suggest.ts new file mode 100644 index 0000000..df0f950 --- /dev/null +++ b/src/douban/setting/model/Suggest.ts @@ -0,0 +1,98 @@ +import { App, ISuggestOwner, Scope } from "obsidian"; +import { createPopper, Instance as PopperInstance } from "@popperjs/core"; + + +const wrapAround = (value: number, size: number): number => { + return ((value % size) + size) % size; +}; + +export default class Suggest { + private owner: ISuggestOwner; + private values: T[]; + private suggestions: HTMLDivElement[]; + private selectedItem: number; + private containerEl: HTMLElement; + + constructor(owner: ISuggestOwner, containerEl: HTMLElement, scope: Scope) { + this.owner = owner; + this.containerEl = containerEl; + + containerEl.on("click", ".suggestion-item", this.onSuggestionClick.bind(this)); + containerEl.on( + "mousemove", + ".suggestion-item", + this.onSuggestionMouseover.bind(this) + ); + + scope.register([], "ArrowUp", (event) => { + if (!event.isComposing) { + this.setSelectedItem(this.selectedItem - 1, true); + return false; + } + }); + + scope.register([], "ArrowDown", (event) => { + if (!event.isComposing) { + this.setSelectedItem(this.selectedItem + 1, true); + return false; + } + }); + + scope.register([], "Enter", (event) => { + if (!event.isComposing) { + this.useSelectedItem(event); + return false; + } + }); + } + + onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void { + event.preventDefault(); + + const item = this.suggestions.indexOf(el); + this.setSelectedItem(item, false); + this.useSelectedItem(event); + } + + onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void { + const item = this.suggestions.indexOf(el); + this.setSelectedItem(item, false); + } + + setSuggestions(values: T[]) { + this.containerEl.empty(); + const suggestionEls: HTMLDivElement[] = []; + + values.forEach((value) => { + const suggestionEl = this.containerEl.createDiv("suggestion-item"); + this.owner.renderSuggestion(value, suggestionEl); + suggestionEls.push(suggestionEl); + }); + + this.values = values; + this.suggestions = suggestionEls; + this.setSelectedItem(0, false); + } + + useSelectedItem(event: MouseEvent | KeyboardEvent) { + const currentValue = this.values[this.selectedItem]; + if (currentValue) { + this.owner.selectSuggestion(currentValue, event); + } + } + + setSelectedItem(selectedIndex: number, scrollIntoView: boolean) { + const normalizedIndex = wrapAround(selectedIndex, this.suggestions.length); + const prevSelectedSuggestion = this.suggestions[this.selectedItem]; + const selectedSuggestion = this.suggestions[normalizedIndex]; + + prevSelectedSuggestion?.removeClass("is-selected"); + selectedSuggestion?.addClass("is-selected"); + + this.selectedItem = normalizedIndex; + + if (scrollIntoView) { + selectedSuggestion.scrollIntoView(false); + } + } +} diff --git a/src/douban/setting/model/TextInputSuggest.ts b/src/douban/setting/model/TextInputSuggest.ts new file mode 100644 index 0000000..425a183 --- /dev/null +++ b/src/douban/setting/model/TextInputSuggest.ts @@ -0,0 +1,89 @@ +import {App, ISuggestOwner, Scope} from "obsidian"; +import Suggest from "./Suggest"; +import { createPopper, type Instance as PopperInstance } from "@popperjs/core"; + + +// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes + +export abstract class TextInputSuggest implements ISuggestOwner { + protected app: App; + protected inputEl: HTMLInputElement; + + private popper: PopperInstance; + private scope: Scope; + private suggestEl: HTMLElement; + private suggest: Suggest; + + constructor(app: App, inputEl: HTMLInputElement) { + this.app = app; + this.inputEl = inputEl; + this.scope = new Scope(); + + this.suggestEl = createDiv("suggestion-container"); + const suggestion = this.suggestEl.createDiv("suggestion"); + this.suggest = new Suggest(this, suggestion, this.scope); + + this.scope.register([], "Escape", this.close.bind(this)); + + this.inputEl.addEventListener("input", this.onInputChanged.bind(this)); + this.inputEl.addEventListener("focus", this.onInputChanged.bind(this)); + this.inputEl.addEventListener("blur", this.close.bind(this)); + this.suggestEl.on("mousedown", ".suggestion-container", (event: MouseEvent) => { + event.preventDefault(); + }); + } + + onInputChanged(): void { + const inputStr = this.inputEl.value; + const suggestions = this.getSuggestions(inputStr); + + if (suggestions.length > 0) { + this.suggest.setSuggestions(suggestions); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.open((this.app).dom.appContainerEl, this.inputEl); + } + } + + open(container: HTMLElement, inputEl: HTMLElement): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (this.app).keymap.pushScope(this.scope); + + container.appendChild(this.suggestEl); + this.popper = createPopper(inputEl, this.suggestEl, { + placement: "bottom-start", + modifiers: [ + { + name: "sameWidth", + enabled: true, + fn: ({ state, instance }) => { + // Note: positioning needs to be calculated twice - + // first pass - positioning it according to the width of the popper + // second pass - position it with the width bound to the reference element + // we need to early exit to avoid an infinite loop + const targetWidth = `${state.rects.reference.width}px`; + if (state.styles.popper.width === targetWidth) { + return; + } + state.styles.popper.width = targetWidth; + instance.update(); + }, + phase: "beforeWrite", + requires: ["computeStyles"], + }, + ], + }); + } + + close(): void { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (this.app).keymap.popScope(this.scope); + + this.suggest.setSuggestions([]); + this.popper.destroy(); + this.suggestEl.detach(); + } + + abstract getSuggestions(inputStr: string): T[]; + abstract renderSuggestion(item: T, el: HTMLElement): void; + abstract selectSuggestion(item: T): void; +} diff --git a/src/file/FileHandler.ts b/src/file/FileHandler.ts new file mode 100644 index 0000000..1676dbe --- /dev/null +++ b/src/file/FileHandler.ts @@ -0,0 +1,133 @@ +import { DoubanPluginSetting } from "@App/setting/model/DoubanPluginSetting"; +import {App, normalizePath, Platform} from "obsidian"; +import { log } from "src/utils/Logutil"; +import DoubanPlugin from "../../main"; +import {FileUtil} from "../utils/FileUtil"; + +/** + * 文件处理 + * 此段代码逻辑参考了obsidian-advanced-new-file插件 + * @auther: wanxuping + * @date: 2022/11/01 23:32 + */ +export default class FileHandler { + + private _app: App; + + + constructor(app:App) { + this._app = app; + } + + /** + * 创建文件路径如果不存在的话 + */ + /** + * Creates a directory (recursive) if it does not already exist. + * This is a helper function that includes a workaround for a bug in the + * Obsidian mobile app. + */ + private async createDirectory(dir: string): Promise { + const {vault} = this._app; + const {adapter} = vault; + const root = vault.getRoot().path; + const directoryPath = FileUtil.join(dir); + const directoryExists = await adapter.exists(directoryPath); + // =============================================================== + // -> Desktop App + // =============================================================== + if (!Platform.isIosApp) { + if (!directoryExists) { + return adapter.mkdir(normalizePath(directoryPath)); + } + } + // =============================================================== + // -> Mobile App (IOS) + // =============================================================== + // This is a workaround for a bug in the mobile app: + // To get the file explorer view to update correctly, we have to create + // each directory in the path one at time. + + // Split the path into an array of sub paths + // Note: `normalizePath` converts path separators to '/' on all platforms + // @example '/one/two/three/' ==> ['one', 'one/two', 'one/two/three'] + // @example 'one\two\three' ==> ['one', 'one/two', 'one/two/three'] + const subPaths: string[] = normalizePath(directoryPath) + .split('/') + .filter((part) => part.trim() !== '') + .map((_, index, arr) => arr.slice(0, index + 1).join('/')); + + // Create each directory if it does not exist + for (const subPath of subPaths) { + const directoryExists = await adapter.exists(FileUtil.join(root, subPath)); + if (!directoryExists) { + await adapter.mkdir(FileUtil.join(root, subPath)); + } + } + + } + + /** + * Handles creating the new note + * A new markdown file will be created at the given file path (`input`) + * in the specified parent folder (`this.folder`) + */ + async createNewNote(originalFilePath: string): Promise { + this.createNewNoteWithData(originalFilePath, ''); + } + + /** + * Handles creating the new note + * A new markdown file will be created at the given file path (`input`) + * in the specified parent folder (`this.folder`) + */ + async createNewNoteWithData(originalFilePath: string, data:string): Promise { + const {vault} = this._app; + const {adapter} = vault; + const prependDirInput = FileUtil.join("", originalFilePath); + const {dir, name} = FileUtil.parse(prependDirInput); + const filePath = FileUtil.join(dir, `${name}.md`); + + try { + const fileExists = await adapter.exists(filePath); + if (fileExists) { + // If the file already exists, respond with error + throw new Error(`${filePath} already exists`); + } + if (dir !== '') { + // If `input` includes a directory part, create it + await this.createDirectory(dir); + } + const File = await vault.create(filePath, data); + // Create the file and open it in the active leaf + const leaf = this._app.workspace.splitLeafOrActive(); + await leaf.openFile(File); + } catch (error) { + log.error(error.toString()); + } + } + + async getFileContent(filePath: string | undefined): Promise { + const { metadataCache, vault } = this._app; + const normalizedTemplatePath = normalizePath(filePath ?? ""); + if (filePath === "/") { + return Promise.resolve(""); + } + + try { + const file = metadataCache.getFirstLinkpathDest(normalizedTemplatePath, ""); + return file ? vault.cachedRead(file) : ""; + } catch (err) { + console.error( + `Failed to read the daily note template '${normalizedTemplatePath}'`, + err + ); + //TODO i18n + log.error('Failed to read the template') + return ""; + } + } + + + +} diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index a7c2665..529afd8 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -32,7 +32,7 @@ export default { '120131': `Create New File Name`, - '120201': `Book Content Template`, + '120201': `Book Template File`, '120202': `Set markdown Book template for extract to be inserted.`, '120203': `Available Book template variables are :`, '120204': `{{id}}, {{title}}, {{type}}, {{score}}, {{image}},`, @@ -41,7 +41,7 @@ export default { '120207': `{{translator}}, {{isbn}}, {{price}}, {{totalPage}}`, '120208': `{{series}}, {{binding}}, {{menu}}`, - '120301': `Music Content Template`, + '120301': `Music Template File`, '120302': `Set markdown Music template for extract to be inserted.`, '120303': `Available Music template variables are :`, '120304': `{{id}}, {{title}}, {{type}}, {{score}}, {{image}},`, @@ -49,7 +49,7 @@ export default { '120306': `{{actor}}, {{medium}}, {{albumType}},`, '120307': `{{barcode}}, {{records}}`, - '120401': `Article Content Template`, + '120401': `Article Template File`, '120402': `Set markdown Article template for extract to be inserted.`, '120403': `Available Article template variables are :`, '120404': `{{id}}, {{title}}, {{type}}, {{image}},`, @@ -57,7 +57,7 @@ export default { '120406': `{{author}}, {{authorUrl}}, {{content}}`, '120407': `{{timePublished}}`, - '121301': `Game Content Template`, + '121301': `Game Template File`, '121302': `Set markdown Game template for extract to be inserted.`, '121303': `Available Game template variables are :`, '121304': `{{id}}, {{title}}, {{type}}, {{score}}, {{image}},`, @@ -88,8 +88,20 @@ export default { '121207': `English Name`, '121208': `Chinese And English Name`, + '121401': `Status Bar`, + '121402': `Display status bar when import data ?`, + + '121501': `Note folder`, + '121502': `Nodes created from Obsidian-Douban will be placed in this folder, If blank, they will be placed in the default location for this vault. `, + '121503': `Default Folder`, + '121601': `Note Name`, + '121602': `Nodes created from Obsidian-Douban will use this fileName(also support filePath), If blank, they will be created by default name. `, + + '121701': `Input And Search Template`, + '121702': `Movie created from Obsidian-Douban will be placed in this folder, If blank, they will be placed in the default location for this vault. `, + '121703': `Default`, //error @@ -110,4 +122,8 @@ export default { //content '200101': `. `, + '210101': `Default`, + '210201': `Search...` + + } diff --git a/src/lang/locale/zh-cn.ts b/src/lang/locale/zh-cn.ts index ae92ad4..4b6458b 100644 --- a/src/lang/locale/zh-cn.ts +++ b/src/lang/locale/zh-cn.ts @@ -90,6 +90,15 @@ export default { '121207': `英文名`, '121208': `中文名和英文名`, + '121401': `状态栏`, + '121402': `当在导入数据时, 是否需要在状态栏显示处理状态? `, + + '121501': `笔记存放位置`, + '121502': `从Obsidian-Douban创建的笔记将会存放导该文件夹中. 如果为空,笔记将会存放到Obsidian的默认文件存放位置`, + + '121601': `笔记名称`, + '121602': `从Obsidian-Douban创建的笔记将会使用次名称,支持所有通用的参数作为名称(如:{{type}},{{title}}等等). 如果为空, 笔记将会使用默认名称`, + '130101': `获取数据失败,您如有需要请至Github提交Issues`, '130102': `Obsidian Douban插件错误提示:`, '130103': `Obsidian Douban插件异常提示:`, diff --git a/src/utils/FileUtil.ts b/src/utils/FileUtil.ts new file mode 100644 index 0000000..1e91333 --- /dev/null +++ b/src/utils/FileUtil.ts @@ -0,0 +1,55 @@ +import {normalizePath} from "obsidian"; + +interface ParsedPath { + /** + * The full directory path such as '/home/user/dir' or 'folder/sub' + * */ + dir: string; + /** + * The file name without extension + * */ + name: string; + /** + * The file extension without the dot. + */ + extension:string; +} + +export const FileUtil = { + /** + * Parses the file path into a directory and file name. + * If the path string does not include a file name, it will default to + * 'Untitled'. + * + * @example + * parse('/one/two/file name') + * // ==> { dir: '/one/two', name: 'file name' } + * + * parse('\\one\\two\\file name') + * // ==> { dir: '/one/two', name: 'file name' } + * + * parse('') + * // ==> { dir: '', name: 'Untitled' } + * + * parse('/one/two/') + * // ==> { dir: '/one/two/', name: 'Untitled' } + */ + parse(pathString: string): ParsedPath { + const regex = /(?([^/\\]+[/\\])*)(?[^/\\]*$)/; + const match = String(pathString).match(regex); + const { dir, name } = match && match.groups; + return { dir, name: name || 'Untitled', extension: 'md'}; + }, + + /** + * Joins multiple strings into a path using Obsidian's preferred format. + * The resulting path is normalized with Obsidian's `normalizePath` func. + * - Converts path separators to '/' on all platforms + * - Removes duplicate separators + * - Removes trailing slash + */ + join(...strings: string[]): string { + const parts = strings.map((s) => String(s).trim()).filter((s) => s != null); + return normalizePath(parts.join('/')); + }, +};