diff --git a/main.ts b/main.ts index f3c366d..503647a 100644 --- a/main.ts +++ b/main.ts @@ -17,6 +17,7 @@ import { DoubanPluginSetting } from "@App/setting/model/DoubanPluginSetting"; import {DEFAULT_SETTINGS} from "./src/constant/DefaultSettings"; import UserComponent from "@App/user/UserComponent"; import SettingsManager from "@App/setting/SettingsManager"; +import NetFileHandler from "./src/net/NetFileHandler"; export default class DoubanPlugin extends Plugin { public settings: DoubanPluginSetting; @@ -25,6 +26,7 @@ export default class DoubanPlugin extends Plugin { public fileHandler: FileHandler; public userComponent: UserComponent; public settingsManager: SettingsManager; + public netFileHandler: NetFileHandler; async putToObsidian(context: HandleContext, extract: DoubanSubject) { @@ -111,28 +113,39 @@ export default class DoubanPlugin extends Plugin { id: "search-douban-import-and-create-file", name: i18nHelper.getMessage("110101"), callback: () => - this.getDoubanTextForCreateNewNote({mode: SearchHandleMode.FOR_CREATE, settings: this.settings, userComponent: this.userComponent}), + this.getDoubanTextForCreateNewNote({mode: SearchHandleMode.FOR_CREATE, + settings: this.settings, + userComponent: this.userComponent, + netFileHandler: this.netFileHandler}), + }); + + 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, + userComponent: this.userComponent, + netFileHandler: this.netFileHandler}), }); 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, userComponent: this.userComponent}), + this.getDoubanTextForActiveFile({mode: SearchHandleMode.FOR_REPLACE, + settings: this.settings, + editor: editor, + userComponent: this.userComponent, + netFileHandler: this.netFileHandler}), }); - - 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, userComponent: this.userComponent}), - }); this.settingsManager = new SettingsManager(app, this); this.userComponent = new UserComponent(this.settingsManager); - + this.netFileHandler = new NetFileHandler(this.fileHandler); if (this.userComponent.needLogin()) { - this.userComponent.loginByCookie(); + await this.userComponent.loginByCookie(); } this.addSettingTab(new DoubanSettingTab(this.app, this)); diff --git a/src/constant/DefaultSettings.ts b/src/constant/DefaultSettings.ts index 0a7a146..5910ba6 100644 --- a/src/constant/DefaultSettings.ts +++ b/src/constant/DefaultSettings.ts @@ -27,4 +27,6 @@ export const DEFAULT_SETTINGS: DoubanPluginSetting = { {name: 'myType', value: 'teleplay', field: SupportType.TELEPLAY}, ], loginCookiesContent: '', + cacheImage: true, + attachmentPath: 'assets', } diff --git a/src/douban/component/DoubanLoginModel.ts b/src/douban/component/DoubanLoginModel.ts index 9e44cb4..f79da70 100644 --- a/src/douban/component/DoubanLoginModel.ts +++ b/src/douban/component/DoubanLoginModel.ts @@ -57,7 +57,7 @@ export default class DoubanLoginModel { try { await this.modal.loadURL('https://accounts.douban.com/passport/login'); } catch (error) { - log.error('加载豆瓣登录页面失败') + log.error(i18nHelper.getMessage('100101'), error) } } diff --git a/src/douban/data/handler/DoubanAbstractLoadHandler.ts b/src/douban/data/handler/DoubanAbstractLoadHandler.ts index ebf759c..73b12c4 100644 --- a/src/douban/data/handler/DoubanAbstractLoadHandler.ts +++ b/src/douban/data/handler/DoubanAbstractLoadHandler.ts @@ -33,6 +33,7 @@ export default abstract class DoubanAbstractLoadHandler async parse(extract: T, context: HandleContext): Promise { let template: string = await this.getTemplate(extract, context); + await this.saveImage(extract, 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 = ''; @@ -388,4 +389,20 @@ export default abstract class DoubanAbstractLoadHandler return v.UNKNOWN; } } + + private async saveImage(extract: T, context: HandleContext) { + if (!extract.image || !context.settings.cacheImage) { + return; + } + let image = extract.image; + const filename = image.split('/').pop(); + let folder = context.settings.attachmentPath; + if (!folder) { + folder = DEFAULT_SETTINGS.attachmentPath; + } + const {success, filepath} = await context.netFileHandler.downloadFile(image, folder, filename); + if (success) { + extract.image = filepath; + } + } } diff --git a/src/douban/data/model/HandleContext.ts b/src/douban/data/model/HandleContext.ts index 673368c..4a446a8 100644 --- a/src/douban/data/model/HandleContext.ts +++ b/src/douban/data/model/HandleContext.ts @@ -2,10 +2,12 @@ import {SearchHandleMode} from "../../../constant/Constsant"; import {Editor} from "obsidian"; import { DoubanPluginSetting } from "@App/setting/model/DoubanPluginSetting"; import UserComponent from "@App/user/UserComponent"; +import NetFileHandler from "src/net/NetFileHandler"; export default interface HandleContext { mode:SearchHandleMode; settings: DoubanPluginSetting; editor?:Editor; userComponent: UserComponent; + netFileHandler: NetFileHandler; } diff --git a/src/douban/setting/BasicSettingsHelper.ts b/src/douban/setting/BasicSettingsHelper.ts index e6560be..ad68b35 100644 --- a/src/douban/setting/BasicSettingsHelper.ts +++ b/src/douban/setting/BasicSettingsHelper.ts @@ -5,6 +5,7 @@ import SettingsManager from "@App/setting/SettingsManager"; import DoubanLoginModel from "@App/component/DoubanLoginModel"; import DoubanLogoutModel from "@App/component/DoubanLogoutModel"; import User from "@App/user/User"; +import {createFolderSelectionSetting} from "@App/setting/TemplateSettingHelper"; export function constructBasicUI(containerEl: HTMLElement, manager: SettingsManager) { containerEl.createEl('h3', { text: i18nHelper.getMessage('1210') }); @@ -94,6 +95,8 @@ export function constructBasicUI(containerEl: HTMLElement, manager: SettingsMana await manager.plugin.saveSettings(); }); }); + + } export function constructDoubanTokenSettingsUI(containerEl: HTMLElement, manager: SettingsManager) { @@ -165,10 +168,10 @@ function showMobileLogout(containerEl: HTMLElement, manager: SettingsManager) { const user: User = manager.plugin.userComponent.getUser(); let userDom = new DocumentFragment(); userDom.createDiv().innerHTML = - `已登录
-豆瓣ID: ${user.id}
- 昵称: ${user.name}
-登录后导入参数可使用你的评分以及阅读状态等,具体可用参数见最后.`; + `${i18nHelper.getMessage('100120')}
+${i18nHelper.getMessage('100123')}: ${user.id}
+ ${i18nHelper.getMessage('100124')}: ${user.name}
+${i18nHelper.getMessage('100125')}`; new Setting(containerEl) .setName(i18nHelper.getMessage('100126')) .setDesc(userDom) diff --git a/src/douban/setting/OutputSettingsHelper.ts b/src/douban/setting/OutputSettingsHelper.ts index faa5d02..a72d916 100644 --- a/src/douban/setting/OutputSettingsHelper.ts +++ b/src/douban/setting/OutputSettingsHelper.ts @@ -7,8 +7,14 @@ import SettingsManager from "@App/setting/SettingsManager"; export function constructOutUI(containerEl: HTMLElement, manager: SettingsManager) { containerEl.createEl('h3', { text: i18nHelper.getMessage('1220') }); + + new Setting(containerEl); + let attachmentFileSetting = containerEl.createDiv({ cls: 'settings-item-attachment' }); + constructAttachmentFileSettingsUI(attachmentFileSetting, manager); + new Setting(containerEl).then(createFolderSelectionSetting({name: '121501', desc: '121502', placeholder: '121503', key: 'dataFilePath', manager: manager})); let outfolder = containerEl.createDiv({ cls: 'settings-item' }); + constructOutFolderUI(outfolder, manager); new Setting(containerEl) @@ -76,3 +82,25 @@ export function constructOutFolderUI(containerEl: HTMLElement, manager: Settings }); }) } + + +export function constructAttachmentFileSettingsUI(containerEl: HTMLElement, manager: SettingsManager) { + containerEl.empty(); + new Setting(containerEl) + .setName(i18nHelper.getMessage('121430')) + .setDesc(i18nHelper.getMessage('121431')) + .addToggle((toggleComponent) => { + toggleComponent + // .setTooltip(i18nHelper.getMessage('121403')) + .setValue(manager.plugin.settings.cacheImage) + .onChange(async (value) => { + manager.plugin.settings.cacheImage = value; + await manager.plugin.saveSettings(); + constructAttachmentFileSettingsUI(containerEl, manager); + }); + }); + + if(manager.plugin.settings.cacheImage) { + new Setting(containerEl).then(createFolderSelectionSetting({name: '121432', desc: '121433', placeholder: '121434', key: 'attachmentPath', manager: manager})); + } +} diff --git a/src/douban/setting/TemplateVariableSettingsHelper.ts b/src/douban/setting/TemplateVariableSettingsHelper.ts index b67d2d1..fc97ce8 100644 --- a/src/douban/setting/TemplateVariableSettingsHelper.ts +++ b/src/douban/setting/TemplateVariableSettingsHelper.ts @@ -269,6 +269,6 @@ ${i18nHelper.getMessage('160225')} ` ; new Setting(containerEl) - .setName(i18nHelper.getMessage('122002')) + .setName(i18nHelper.getMessage('122010')) .setDesc(userInfoVariables); } diff --git a/src/douban/setting/model/DoubanPluginSetting.ts b/src/douban/setting/model/DoubanPluginSetting.ts index adfba85..150a073 100644 --- a/src/douban/setting/model/DoubanPluginSetting.ts +++ b/src/douban/setting/model/DoubanPluginSetting.ts @@ -18,5 +18,6 @@ export interface DoubanPluginSetting { statusBar: boolean, customProperties: CustomProperty[], loginCookiesContent: string, - + cacheImage: boolean, + attachmentPath: string, } diff --git a/src/file/FileHandler.ts b/src/file/FileHandler.ts index 402d55f..922bfe5 100644 --- a/src/file/FileHandler.ts +++ b/src/file/FileHandler.ts @@ -68,6 +68,36 @@ export default class FileHandler { } + /** + * 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 creatAttachmentWithData(originalFilePath: string, data:ArrayBuffer): 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}`); + + try { + const fileExists = await adapter.exists(filePath); + if (fileExists) { + // If the file already exists, respond with error + // throw new Error(i18nHelper.getMessage('110201').replace('{0}', filePath??'')); + return ; + } + if (dir !== '') { + // If `input` includes a directory part, create it + await this.createDirectory(dir); + } + await vault.createBinary(filePath, data); + // Create the file and open it in the active leaf + } catch (error) { + log.error(error.toString(), error); + } + } + /** * Handles creating the new note * A new markdown file will be created at the given file path (`input`) diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index 905378a..dbf30d7 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -37,6 +37,7 @@ export default { '100129': `You have not login Douban, please login in computer first! After login, you can use your rating and reading status, see the last for specific variables.`, '100130': `Login`, '100131': `Login Douban`, + '100132': `Load Douban Login Page Failed`, '1210': `Basic Setting`, @@ -130,6 +131,13 @@ export default { '121401': `Status Bar`, '121402': `Display status bar when import data ?`, + '121430': `Save Attachment File`, + '121431': `Save attachment file to local disk, such as image ?`, + '121432': `Attachment folder`, + '121433': `Attachment file created from Obsidian-Douban will be placed in this folder, If blank, they will be placed in the default location for this vault.`, + '121434': `assets`, + + '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`, @@ -175,6 +183,7 @@ export default { '122002': `Extra Variables`, '122003': `Basic Variables must has value, Extra Variables can be empty`, '122004': `To use the template variables, you need to wrap them in double curly brackets. For example, {{title}} will be replaced with the title of the note.`, + '122010': `My State Variables`, diff --git a/src/lang/locale/zh-cn.ts b/src/lang/locale/zh-cn.ts index f808d63..d789da6 100644 --- a/src/lang/locale/zh-cn.ts +++ b/src/lang/locale/zh-cn.ts @@ -28,7 +28,7 @@ export default { '100129': `豆瓣未登录,请先在电脑端登录!登录后导入参数可使用你的评分以及阅读状态等,具体可用参数见最后.`, '100130': `登录`, '100131': `登录豆瓣`, - + '100132': `加载豆瓣登录页面失败`, @@ -138,8 +138,15 @@ export default { '121401': `状态栏`, '121402': `当在导入数据时, 是否需要在状态栏显示处理状态? `, + '121430': `保存图片附件`, + '121431': `导入数据会同步保存图片附件到本地文件夹, 如电影封面,书籍封面 `, + '121432': `附件存放位置`, + '121433': `保存的附件将会存放至该文件夹中. 如果为空, 笔记将会存放到默认位置(assets) `, + '121434': `assets`, + + '121501': `笔记存放位置`, - '121502': `创建的笔记将会存放导该文件夹中. 如果为空, 笔记将会存放到Obsidian的默认位置`, + '121502': `创建的笔记将会存放至该文件夹中. 如果为空, 笔记将会存放到Obsidian的默认位置`, '121601': `笔记名称`, '121602': `创建的笔记将会使用此名称作为模板, 支持所有'通用'的参数作为名称(如:{{type}}/{{title}}), 且支持路径, 比如: 'MyData/{{title}}'. 如果为空, 笔记将会使用默认名称. `, @@ -197,7 +204,7 @@ export default { '122002': `扩展参数`, '122003': `通用参数一定有值不为空, 扩展参数可能为空`, '122004': `以下参数使用时请用'{{}}'包裹, 举例: 参数title, 则使用时为{{title}}. `, - + '122010': `我的状态参数`, '410101': `其他`, '410102': `未知`, diff --git a/src/net/NetFileHandler.ts b/src/net/NetFileHandler.ts new file mode 100644 index 0000000..49a5889 --- /dev/null +++ b/src/net/NetFileHandler.ts @@ -0,0 +1,37 @@ +import { requestUrl, RequestUrlParam} from "obsidian"; +import {log} from "../utils/Logutil"; +import {i18nHelper} from "../lang/helper"; +import FileHandler from "../file/FileHandler"; +import {FileUtil} from "../utils/FileUtil"; + +export default class NetFileHandler { + private fileHandler: FileHandler; + + constructor(fileHandler: FileHandler) { + this.fileHandler = fileHandler; + } + + async downloadFile(url: string, folder:string, filename: string): Promise<{ success: boolean, error:string, filepath: string }> { + const requestUrlParam: RequestUrlParam = { + url: url, + method: "GET", + throw: true, + headers: {} + }; + const filePath:string = FileUtil.join(folder, filename); + return requestUrl(requestUrlParam) + .then((response) => { + this.fileHandler.creatAttachmentWithData(filePath, response.arrayBuffer); + }).then(() => { + return {success: true, error: '', filepath: filePath}; + }) + .catch(e => log + .error( + i18nHelper.getMessage('130101') + .replace('{0}', e.toString()) + , e)); + ; + } +} + +