From 4d3dc61d33a345b0c1ef22e382ba0c90a1d04db2 Mon Sep 17 00:00:00 2001 From: Wanxp <977741432@qq.com> Date: Thu, 6 Mar 2025 18:44:31 +0800 Subject: [PATCH] =?UTF-8?q?fix=20#139=201.=20=E4=BC=98=E5=8C=96=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E9=85=8D=E7=BD=AE,=E5=A2=9E=E5=8A=A0=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E5=AF=BC=E5=85=A5=E6=97=B6=E7=9A=84=E9=80=89=E9=A1=B9?= =?UTF-8?q?=202.=20=E4=BC=98=E5=8C=96=E5=AF=BC=E5=85=A5=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=9A=84=E6=98=BE=E7=A4=BA=EF=BC=8C=E7=8E=B0=E5=9C=A8=E4=BC=9A?= =?UTF-8?q?=E5=9C=A8=E7=BB=93=E6=9E=9C=E6=96=87=E6=9C=AC=E4=B8=AD=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=85=B7=E4=BD=93=E7=9A=84=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/org/wanxp/constant/Constsant.ts | 19 +- src/org/wanxp/constant/DefaultSettings.ts | 1 + src/org/wanxp/constant/DoubanUserState.ts | 15 +- .../wanxp/douban/component/DoubanSyncModal.ts | 80 ++- .../wanxp/douban/data/model/HandleContext.ts | 1 + src/org/wanxp/douban/data/model/SearchPage.ts | 25 +- .../wanxp/douban/data/model/SearchPageInfo.ts | 4 + .../douban/data/model/SearchPageTypeOf.ts | 29 ++ .../douban/data/model/SubjectListItem.ts | 1 + .../douban/setting/TemplateSettingHelper.ts | 3 +- .../setting/model/DoubanPluginSetting.ts | 3 +- .../setting/model/FileTreeSelectSuggest.ts | 13 +- .../sync/handler/DoubanAbstractSyncHandler.ts | 460 ++++++++++++++++-- .../wanxp/douban/sync/handler/SyncHandler.ts | 47 +- .../handler/list/DoubanAbstractListHandler.ts | 156 +++--- .../sync/handler/list/DoubanListHandler.ts | 4 +- src/org/wanxp/douban/sync/model/SyncConfig.ts | 8 +- .../wanxp/douban/sync/model/SyncItemResult.ts | 8 +- .../douban/sync/model/SyncStatusHolder.ts | 56 ++- src/org/wanxp/lang/helper.ts | 21 +- src/org/wanxp/lang/locale/en.ts | 24 +- src/org/wanxp/lang/locale/zh-cn.ts | 30 +- src/org/wanxp/main.ts | 7 +- styles.css | 20 +- 24 files changed, 855 insertions(+), 180 deletions(-) create mode 100644 src/org/wanxp/douban/data/model/SearchPageTypeOf.ts diff --git a/src/org/wanxp/constant/Constsant.ts b/src/org/wanxp/constant/Constsant.ts index 4912e92..dc45f67 100644 --- a/src/org/wanxp/constant/Constsant.ts +++ b/src/org/wanxp/constant/Constsant.ts @@ -223,7 +223,7 @@ export const SyncTypeUrlDomain: Map = new Map([ * 同步模式选项 */ // @ts-ignore -export const SyncTypeRecords: { [key in SyncType]: string } = { +export const SyncTypeRecords: { [key in SyncType | string]: string } = { [SyncType.movie]: i18nHelper.getMessage('504103'), [SyncType.teleplay]: i18nHelper.getMessage('504107'), [SyncType.book]: i18nHelper.getMessage('504102'), @@ -257,6 +257,7 @@ export enum SyncItemStatus { replace = 'replace', create = 'create', fail = 'fail', + failByDiffType = 'failByDiffType', unHandle = 'unHandle', } @@ -459,15 +460,15 @@ export enum SyncConditionType { * 最近新变动 */ ALL = "all", - /** - * 最近新变动 - */ - LAST_UPDATE = "lastUpdate", + // /** + // * 最近新变动 + // */ + // LAST_UPDATE = "lastUpdate", /** * 最近10条 */ - LAST_TEN = "lastTen", + LAST_THIRTY = "lastThirty", /** * 自定义时间 @@ -485,10 +486,10 @@ export enum SyncConditionType { * 名称模式选项 */ // @ts-ignore -export const SyncConditionTypeRecords: { [key in SyncConditionType]: string } = { +export const SyncConditionTypeRecords: { [key in SyncConditionType|string]: string } = { [SyncConditionType.ALL]: i18nHelper.getMessage('110071'), - [SyncConditionType.LAST_UPDATE]: i18nHelper.getMessage('110072'), - [SyncConditionType.LAST_TEN]: i18nHelper.getMessage('110075'), + // [SyncConditionType.LAST_UPDATE]: i18nHelper.getMessage('110072'), + [SyncConditionType.LAST_THIRTY]: i18nHelper.getMessage('110075'), [SyncConditionType.CUSTOM_ITEM]: i18nHelper.getMessage('110076'), [SyncConditionType.CUSTOM_TIME]: i18nHelper.getMessage('110074'), diff --git a/src/org/wanxp/constant/DefaultSettings.ts b/src/org/wanxp/constant/DefaultSettings.ts index 94af101..e84eaa4 100644 --- a/src/org/wanxp/constant/DefaultSettings.ts +++ b/src/org/wanxp/constant/DefaultSettings.ts @@ -51,6 +51,7 @@ export const DEFAULT_SETTINGS: DoubanPluginSetting = { cacheHighQuantityImage: true, attachmentPath: 'assets', syncHandledDataArray: [], + // syncLastUpdateTime: new Map(), scoreSetting: { starFull: '⭐', starEmpty: '☆', diff --git a/src/org/wanxp/constant/DoubanUserState.ts b/src/org/wanxp/constant/DoubanUserState.ts index e9a1e1d..16c598f 100644 --- a/src/org/wanxp/constant/DoubanUserState.ts +++ b/src/org/wanxp/constant/DoubanUserState.ts @@ -1,5 +1,5 @@ import {i18nHelper} from "../lang/helper"; -import {SupportType} from "./Constsant"; +import {SupportType, SyncType} from "./Constsant"; export enum DoubanSubjectState { not = 'not', @@ -123,6 +123,19 @@ export const DoubanSubjectStateRecords_MUSIC_SYNC: { [key in DoubanSubjectState] [DoubanSubjectState.collect]: i18nHelper.getMessage('500404'), } +// @ts-ignore +export const DoubanSubjectStateRecords_SYNC: { [key in SyncType]: Record } = { + [SyncType.movie]:DoubanSubjectStateRecords_MOVIE_SYNC, + [SyncType.book]:DoubanSubjectStateRecords_BOOK_SYNC, + [SyncType.music]:DoubanSubjectStateRecords_MUSIC_SYNC, + // [SyncType.note]:DoubanSubjectStateRecords_NOTE_SYNC, + // [SyncType.game]:DoubanSubjectStateRecords_GAME_SYNC, + [SyncType.teleplay]:DoubanSubjectStateRecords_TELEPLAY_SYNC, + // [SyncType.theater]:DoubanSubjectStateRecords_THEATER_SYNC, +} + + + export const DoubanSubjectStateRecords_KEY_WORD_TYPE: Map = new Map ( [['我看过这部电视剧', SupportType.TELEPLAY], ['我最近看过这部电视剧', SupportType.TELEPLAY], diff --git a/src/org/wanxp/douban/component/DoubanSyncModal.ts b/src/org/wanxp/douban/component/DoubanSyncModal.ts index abcbbab..7f199bc 100644 --- a/src/org/wanxp/douban/component/DoubanSyncModal.ts +++ b/src/org/wanxp/douban/component/DoubanSyncModal.ts @@ -15,7 +15,7 @@ import { SyncTypeRecords } from "../../constant/Constsant"; import { - ALL, + ALL, DoubanSubjectState, DoubanSubjectStateRecords, DoubanSubjectStateRecords_BOOK_SYNC, DoubanSubjectStateRecords_BROADCAST_SYNC, DoubanSubjectStateRecords_MOVIE_SYNC, @@ -36,6 +36,7 @@ import {ArraySetting, DEFAULT_SETTINGS_ARRAY_NAME} from "../setting/model/ArrayS import {arraySettingDisplay} from "../setting/ArrayDisplayTypeSettingsHelper"; import {DatePickComponent} from "./DatePickComponent"; import {NumberComponent} from "./NumberComponent"; +import {log} from "../../utils/Logutil"; export class DoubanSyncModal extends Modal { plugin: DoubanPlugin; @@ -107,7 +108,16 @@ export class DoubanSyncModal extends Modal { progress.innerHTML = `

${syncStatus.getHasHandle()}/${syncStatus.getTotal()}:${i18nHelper.getMessage('110036')} -

` +

+

+ +${i18nHelper.getMessage('110090', syncStatus.getTypeName(), syncStatus.getScopeName(), syncStatus.getAllTotal(), syncStatus.getTotal())} +

+

+ +${syncStatus.getMessage()} +

+` backgroundButton.setDisabled(true); stopButton.setButtonText(i18nHelper.getMessage('110036')) return; @@ -116,7 +126,16 @@ export class DoubanSyncModal extends Modal { ${syncStatus.getTotal() == 0 ? i18nHelper.getMessage('110043') : syncStatus.getHasHandle() + '/' + syncStatus.getTotal()} ${syncStatus.getHandle() == 0? '...' : i18nHelper.getMessage('110042') + ':' + TimeUtil.estimateTimeMsg(syncStatus.getNeedHandled()-syncStatus.getHandle(), syncStatus.getOverSize())} -

`} +

+

+ +${i18nHelper.getMessage('110090', syncStatus.getTypeName(), syncStatus.getScopeName(), syncStatus.getAllTotal(), syncStatus.getTotal())} +

+

+ +${syncStatus.getMessage()} +

+`} private showSyncConfig(contentEl: HTMLElement) { if (this.timer != null) { @@ -133,11 +152,11 @@ ${syncStatus.getHandle() == 0? '...' : i18nHelper.getMessage('110042') + ':' + T attachmentPath: (settings.attachmentPath == '' || settings.attachmentPath == null) ? DEFAULT_SETTINGS.attachmentPath : settings.attachmentPath, templateFile: (settings.movieTemplateFile == '' || settings.movieTemplateFile == null) ? DEFAULT_SETTINGS.movieTemplateFile : settings.movieTemplateFile, incrementalUpdate: true, - syncConditionType: SyncConditionType.LAST_UPDATE, - syncConditionDateFromValue: null, - syncConditionDateToValue: null, - syncConditionCountFromValue: null, - syncConditionCountToValue: null + syncConditionType: SyncConditionType.ALL, + syncConditionDateFromValue: TimeUtil.getLastMonth(), + syncConditionDateToValue: new Date(), + syncConditionCountFromValue: 1, + syncConditionCountToValue: 30 }; this.showConfigPan(contentEl.createDiv('config'), syncConfig, false); const controls = contentEl.createDiv("controls"); @@ -149,6 +168,9 @@ ${syncStatus.getHandle() == 0? '...' : i18nHelper.getMessage('110042') + ':' + T const syncButton = new ButtonComponent(controls) .setButtonText(i18nHelper.getMessage('110007')) .onClick(async () => { + if (!this.plugin.userComponent.isLogin()) { + await this.plugin.userComponent.login(); + } if(!await this.plugin.checkLogin(this.context)) { return; } @@ -461,12 +483,17 @@ function showCustomInputCount(containerEl: HTMLElement, config: SyncConfig, disa containerEl.createEl('span', { text: i18nHelper.getMessage('110078') }) const fromField = new TextComponent(containerEl); fromField.setPlaceholder(i18nHelper.getMessage('110080')) - .setValue(config.syncConditionCountFromValue) + .setValue(config.syncConditionCountFromValue + '') .onChange(async (value) => { if (!value) { + config.syncConditionCountFromValue = 1; return; } - config.syncConditionCountFromValue = value; + try { + config.syncConditionCountFromValue = parseInt(value); + }catch (e) { + log.notice(i18nHelper.getMessage('112080')) + } }).setDisabled(disable); let fromEl = fromField.inputEl; fromEl.addClass('obsidian_douban_settings_input') @@ -481,12 +508,17 @@ function showCustomInputCount(containerEl: HTMLElement, config: SyncConfig, disa containerEl.createEl('span', { text: i18nHelper.getMessage('110078') }) const toField = new TextComponent(containerEl); toField.setPlaceholder(i18nHelper.getMessage('110080')) - .setValue(config.syncConditionCountToValue) + .setValue(config.syncConditionCountToValue + '') .onChange(async (value) => { if (!value) { + config.syncConditionCountToValue = 30; return; } - config.syncConditionCountToValue = value; + try { + config.syncConditionCountToValue = parseInt(value); + }catch (e) { + log.notice(i18nHelper.getMessage('112080')) + } }).setDisabled(disable); let toEl = toField.inputEl; toEl.addClass('obsidian_douban_settings_input') @@ -495,6 +527,9 @@ function showCustomInputCount(containerEl: HTMLElement, config: SyncConfig, disa if (lang == 'zh') { containerEl.createEl('span', {text: i18nHelper.getMessage('110073')}) } + containerEl.createEl('span', {text: ' '}) + const buttopn = new ButtonComponent(containerEl).setIcon('help').setTooltip(i18nHelper.getMessage('110095')) + containerEl.appendChild(buttopn.buttonEl); } function showCustomInputTime(containerEl: HTMLElement, config: SyncConfig, disable: boolean) { @@ -502,14 +537,18 @@ function showCustomInputTime(containerEl: HTMLElement, config: SyncConfig, disab const fromDateField = new TextComponent(containerEl); const fromDateEl = fromDateField.inputEl; fromDateEl.type = 'date'; - fromDateEl.value = config.syncConditionDateFromValue??TimeUtil.getLastMonth().toISOString().substring(0, 10); + fromDateEl.value = config.syncConditionDateFromValue ? config.syncConditionDateFromValue.toISOString().substring(0, 10) : TimeUtil.getLastMonth().toISOString().substring(0, 10); fromDateField.setPlaceholder(i18nHelper.getMessage('110075')) - .setValue(config.syncConditionDateFromValue) + .setValue(config.syncConditionDateFromValue ? config.syncConditionDateFromValue.toISOString().substring(0, 10) : TimeUtil.getLastMonth().toISOString().substring(0, 10)) .onChange(async (value) => { if (!value) { return; } - config.syncConditionDateFromValue = value; + try { + config.syncConditionDateFromValue = new Date(value); + }catch (e) { + log.notice(i18nHelper.getMessage('110082')) + } }).setDisabled(disable); fromDateEl.addClass('obsidian_douban_settings_input') containerEl.appendChild(fromDateEl); @@ -518,16 +557,21 @@ function showCustomInputTime(containerEl: HTMLElement, config: SyncConfig, disab const toDateField = new TextComponent(containerEl); let toDateEl = toDateField.inputEl; toDateEl.type = 'date'; - toDateEl.value = config.syncConditionDateToValue??new Date().toISOString().substring(0, 10); + toDateEl.value = config.syncConditionDateToValue ? config.syncConditionDateToValue.toISOString().substring(0, 10) : new Date().toISOString().substring(0, 10); toDateField.setPlaceholder(i18nHelper.getMessage('110075')) - .setValue(config.syncConditionDateFromValue) + .setValue(config.syncConditionDateToValue ? config.syncConditionDateToValue.toISOString().substring(0, 10) : new Date().toISOString().substring(0, 10)) .onChange(async (value) => { if (!value) { return; } - config.syncConditionDateFromValue = value; + try { + config.syncConditionDateToValue = new Date(value); + }catch (e) { + log.notice(i18nHelper.getMessage('110082')) + } }).setDisabled(disable); toDateEl.addClass('obsidian_douban_settings_input') containerEl.appendChild(toDateEl); + new ButtonComponent(containerEl).setIcon('help').setTooltip(i18nHelper.getMessage('110095')) } \ No newline at end of file diff --git a/src/org/wanxp/douban/data/model/HandleContext.ts b/src/org/wanxp/douban/data/model/HandleContext.ts index eea65c0..8a3996d 100644 --- a/src/org/wanxp/douban/data/model/HandleContext.ts +++ b/src/org/wanxp/douban/data/model/HandleContext.ts @@ -26,5 +26,6 @@ export default interface HandleContext { syncActive?:boolean; searchPage?:SearchPageInfo; + syncOffset?:number; } diff --git a/src/org/wanxp/douban/data/model/SearchPage.ts b/src/org/wanxp/douban/data/model/SearchPage.ts index 1f4aee8..6a27f6f 100644 --- a/src/org/wanxp/douban/data/model/SearchPage.ts +++ b/src/org/wanxp/douban/data/model/SearchPage.ts @@ -1,21 +1,14 @@ -import {SearchPageInfo} from "./SearchPageInfo"; -import {SupportType} from "../../../constant/Constsant"; +import { SearchPageInfo } from "./SearchPageInfo"; +import { SupportType } from "../../../constant/Constsant"; +import {SearchPageTypeOf} from "./SearchPageTypeOf"; -export class SearchPage extends SearchPageInfo{ +export class SearchPage extends SearchPageTypeOf { - private _list:any[]; - - - constructor(total: number, pageNum: number, pageSize: number, type:SupportType, list: any[]) { - super(total, pageNum, pageSize, type); - this._list = list; - } - - public get list() { - return this._list; - } - - public static empty(type:SupportType):SearchPage { + public static empty(type: SupportType): SearchPage { return new SearchPage(0, 0, 0, type, []); } + + static emptyWithNoType() { + return new SearchPage(0, 0, 0, null, []); + } } diff --git a/src/org/wanxp/douban/data/model/SearchPageInfo.ts b/src/org/wanxp/douban/data/model/SearchPageInfo.ts index 1224023..9a1dd8a 100644 --- a/src/org/wanxp/douban/data/model/SearchPageInfo.ts +++ b/src/org/wanxp/douban/data/model/SearchPageInfo.ts @@ -56,6 +56,10 @@ export class SearchPageInfo { return this._total; } + public set total(total: number) { + this._total = total; + } + get pageSize(): number { return this._pageSize; } diff --git a/src/org/wanxp/douban/data/model/SearchPageTypeOf.ts b/src/org/wanxp/douban/data/model/SearchPageTypeOf.ts new file mode 100644 index 0000000..3d3391b --- /dev/null +++ b/src/org/wanxp/douban/data/model/SearchPageTypeOf.ts @@ -0,0 +1,29 @@ +import { SearchPageInfo } from "./SearchPageInfo"; +import { SupportType } from "../../../constant/Constsant"; + +export class SearchPageTypeOf extends SearchPageInfo { + private _list: T[]; + + constructor( + total: number, + pageNum: number, + pageSize: number, + type: SupportType, + list: T[], + ) { + super(total, pageNum, pageSize, type); + this._list = list; + } + + public get list() { + return this._list; + } + + public static empty(type: SupportType): SearchPageTypeOf { + return new SearchPageTypeOf(0, 0, 0, type, []); + } + + static emptyWithNoType() { + return new SearchPageTypeOf(0, 0, 0, null, []); + } +} diff --git a/src/org/wanxp/douban/data/model/SubjectListItem.ts b/src/org/wanxp/douban/data/model/SubjectListItem.ts index 8c9bea2..1c96dc6 100644 --- a/src/org/wanxp/douban/data/model/SubjectListItem.ts +++ b/src/org/wanxp/douban/data/model/SubjectListItem.ts @@ -2,4 +2,5 @@ export interface SubjectListItem { id:string; url:string; title:string; + updateDate: Date | null; } diff --git a/src/org/wanxp/douban/setting/TemplateSettingHelper.ts b/src/org/wanxp/douban/setting/TemplateSettingHelper.ts index b2a7dde..769589e 100644 --- a/src/org/wanxp/douban/setting/TemplateSettingHelper.ts +++ b/src/org/wanxp/douban/setting/TemplateSettingHelper.ts @@ -38,7 +38,7 @@ export function createFileSelectionSetting({containerEl, name, desc, placeholder if (oldValue) { v = oldValue; } - new FileTreeSelectSuggest(manager.app, search.inputEl); + const fileTreeSelectSuggest = new FileTreeSelectSuggest(manager.app, search.inputEl, manager, key); // @ts-ignore search.setValue(v); // @ts-ignore @@ -48,6 +48,7 @@ export function createFileSelectionSetting({containerEl, name, desc, placeholder search.onChange(async (value: string) => { manager.updateSetting(key, value); }); + }); setting.addExtraButton((button) => { diff --git a/src/org/wanxp/douban/setting/model/DoubanPluginSetting.ts b/src/org/wanxp/douban/setting/model/DoubanPluginSetting.ts index fd6c4eb..90cde78 100644 --- a/src/org/wanxp/douban/setting/model/DoubanPluginSetting.ts +++ b/src/org/wanxp/douban/setting/model/DoubanPluginSetting.ts @@ -36,6 +36,7 @@ export interface DoubanPluginSetting { pictureBedType: string; pictureBedSetting: PictureBedSetting; syncHandledDataArray: SyncHandledData[], + // syncLastUpdateTime: Map, arraySettings: ArraySetting[], - scoreSetting: ScoreSetting, + scoreSetting: ScoreSetting, } diff --git a/src/org/wanxp/douban/setting/model/FileTreeSelectSuggest.ts b/src/org/wanxp/douban/setting/model/FileTreeSelectSuggest.ts index df453db..be0befc 100644 --- a/src/org/wanxp/douban/setting/model/FileTreeSelectSuggest.ts +++ b/src/org/wanxp/douban/setting/model/FileTreeSelectSuggest.ts @@ -1,8 +1,17 @@ -import {TAbstractFile, TFile, TFolder} from "obsidian"; +import {App, TAbstractFile, TFile, TFolder} from "obsidian"; import {TextInputSuggest} from "./TextInputSuggest"; +import SettingsManager from "../SettingsManager"; export class FileTreeSelectSuggest extends TextInputSuggest { parentPath: string = "/"; + settingKey: string = ""; + manager: SettingsManager; + + constructor(app: App, inputEl: HTMLInputElement, manager: SettingsManager, settingKey:string) { + super(app, inputEl); + this.manager = manager; + this.settingKey = settingKey; + } getSuggestions(inputStr: string): TAbstractFile[] { const files: TAbstractFile[] = []; @@ -71,6 +80,8 @@ export class FileTreeSelectSuggest extends TextInputSuggest { this.inputEl.value += "/"; this.inputEl.trigger("input"); }else { + //@ts-ignore + this.manager.updateSetting(this.settingKey, file.path); this.close(); } diff --git a/src/org/wanxp/douban/sync/handler/DoubanAbstractSyncHandler.ts b/src/org/wanxp/douban/sync/handler/DoubanAbstractSyncHandler.ts index 494e2a7..ab4c419 100644 --- a/src/org/wanxp/douban/sync/handler/DoubanAbstractSyncHandler.ts +++ b/src/org/wanxp/douban/sync/handler/DoubanAbstractSyncHandler.ts @@ -1,25 +1,67 @@ import DoubanPlugin from "../../../main"; -import {BasicConst, SubjectHandledStatus, SyncType} from "../../../constant/Constsant"; -import {DoubanSyncHandler} from "./DoubanSyncHandler"; -import {SyncConfig} from "../model/SyncConfig"; +import { + BasicConst, + PAGE_SIZE, + SyncConditionType, + SyncType, +} from "../../../constant/Constsant"; +import { DoubanSyncHandler } from "./DoubanSyncHandler"; +import { SyncConfig } from "../model/SyncConfig"; import HandleContext from "../../data/model/HandleContext"; -import {SubjectListItem} from "../../data/model/SubjectListItem"; -import {sleepRange} from "../../../utils/TimeUtil"; +import { SubjectListItem } from "../../data/model/SubjectListItem"; +import { sleepRange } from "../../../utils/TimeUtil"; import DoubanSubjectLoadHandler from "../../data/handler/DoubanSubjectLoadHandler"; -import {DoubanListHandler} from "./list/DoubanListHandler"; +import { DoubanListHandler } from "./list/DoubanListHandler"; import DoubanSubject from "../../data/model/DoubanSubject"; -import {log} from "../../../utils/Logutil"; -import {i18nHelper} from "../../../lang/helper"; +import { log } from "../../../utils/Logutil"; +import { i18nHelper } from "../../../lang/helper"; +import { SearchPage } from "../../data/model/SearchPage"; +import {SearchPageTypeOf} from "../../data/model/SearchPageTypeOf"; -export abstract class DoubanAbstractSyncHandler implements DoubanSyncHandler{ +function toDateList(dataList: SubjectListItem[]): Date[] { + const dateList = dataList + .map((item) => item.updateDate) + .sort((a, b) => { + try { + return a.getTime() - b.getTime(); + } catch (e) { + } + return 0; + }); + return dateList; +} +function testTouchEndCondition(searchPage: SearchPage, context: HandleContext) { + const { syncConfig } = context; + if (!syncConfig) { + return false; + } + switch (syncConfig.syncConditionType) { + case SyncConditionType.ALL: + return false; + case SyncConditionType.LAST_THIRTY: + return searchPage.pageNum >= 0; + case SyncConditionType.CUSTOM_ITEM: + const syncConditionCountToValue = syncConfig.syncConditionCountToValue? syncConfig.syncConditionCountToValue : searchPage.total; + return searchPage.start + PAGE_SIZE - 1 >= syncConditionCountToValue; + case SyncConditionType.CUSTOM_TIME: + return true; + } + return false; +} + +export abstract class DoubanAbstractSyncHandler + implements DoubanSyncHandler +{ private plugin: DoubanPlugin; - private doubanSubjectLoadHandler:DoubanSubjectLoadHandler; - private doubanListHandlers:DoubanListHandler[]; + private doubanSubjectLoadHandler: DoubanSubjectLoadHandler; + private doubanListHandlers: DoubanListHandler[]; - constructor(plugin: DoubanPlugin, - doubanSubjectLoadHandler:DoubanSubjectLoadHandler, - doubanListHandlers:DoubanListHandler[]) { + constructor( + plugin: DoubanPlugin, + doubanSubjectLoadHandler: DoubanSubjectLoadHandler, + doubanListHandlers: DoubanListHandler[], + ) { this.plugin = plugin; this.doubanSubjectLoadHandler = doubanSubjectLoadHandler; this.doubanListHandlers = doubanListHandlers; @@ -31,61 +73,387 @@ export abstract class DoubanAbstractSyncHandler implem abstract getSyncType(): SyncType; - async sync(syncConfig: SyncConfig, context: HandleContext): Promise{ - return Promise.resolve() - .then(() => this.getItems(syncConfig, context)) - .then(items => this.removeExists(items , syncConfig, context)) - .then(items => this.handleItems(items, context)); + async sync(syncConfig: SyncConfig, context: HandleContext): Promise { + if (syncConfig.syncConditionType == SyncConditionType.CUSTOM_TIME) { + await this.syncByTimeLimit(syncConfig, context); + } else if (syncConfig.syncConditionType == SyncConditionType.CUSTOM_ITEM) { + await this.syncByCountLimit(syncConfig, context); + }else if (syncConfig.syncConditionType == SyncConditionType.ALL) { + await this.syncAll(syncConfig, context); + }else if (syncConfig.syncConditionType == SyncConditionType.LAST_THIRTY) { + await this.syncLastThirty(syncConfig, context); + }else { + log.warn(i18nHelper.getMessage("110083")); + } } + async getByTimeLimit(syncConfig: SyncConfig, context: HandleContext): Promise { + let startDate = syncConfig.syncConditionDateFromValue + ? new Date(syncConfig.syncConditionDateFromValue) + : null; + let endDate = syncConfig.syncConditionDateToValue + ? new Date(syncConfig.syncConditionDateToValue) + : null; + if (!startDate && !endDate) { + log.warn(i18nHelper.getMessage("110081")); + return; + } + const cacheList = new Map>(); + let searchPage = await this.getItems(syncConfig, context); + if (!searchPage) { + return; + } + const total = searchPage.total; + const lastPage = total / PAGE_SIZE + 1; + if (lastPage == 1) { + return searchPage.list; + } + let leftPage = 1; + let startPage = 1; + let rightPage = lastPage; + let endPage = lastPage; + let currentPage = 1; + cacheList.set(currentPage, searchPage); + if (startDate != null) { + do { + if (!context.plugin.statusHolder.syncing()) { + break; + } + let page = cacheList.get(currentPage); + if (!page) { + page = await this.getItems(syncConfig, context); + if (!page) { + break; + } + cacheList.set(currentPage, page); + } + const pageItems = page.list; + const pageDateList = toDateList(pageItems); + if (pageDateList[pageDateList.length - 1] >= startDate) { + leftPage = currentPage; + endPage = currentPage; + currentPage = Math.ceil((leftPage + rightPage) / 2); + }else { + rightPage = currentPage; + endPage = currentPage; + currentPage = Math.floor((leftPage + rightPage) / 2); + } + if (currentPage == leftPage || currentPage == rightPage) { + break; + } + } while (currentPage < lastPage); + } + leftPage = 1; + rightPage = lastPage; + currentPage = 1; + if (endDate != null) { + do { + if (!context.plugin.statusHolder.syncing()) { + break; + } + let page = cacheList.get(currentPage); + if (!page) { + page = await this.getItems(syncConfig, context); + if (!page) { + break; + } + cacheList.set(currentPage, page); + } + const pageItems = page.list; + const pageDateList = toDateList(pageItems); + if (pageDateList[0] <= endDate) { + rightPage = currentPage; + startPage = currentPage; + currentPage = Math.ceil((leftPage + rightPage) / 2); + }else { + leftPage = currentPage; + startPage = currentPage; + currentPage = Math.floor((leftPage + rightPage) / 2); + } + if (currentPage == leftPage || currentPage == rightPage) { + break; + } + } while (currentPage < lastPage); + } + let needHandleItems:SubjectListItem[] = []; + for (let pageNum = startPage; pageNum <= endPage; pageNum++) { + if (!context.plugin.statusHolder.syncing()) { + break; + } + let page = cacheList.get(pageNum); + if (!page) { + page = await this.getItems(syncConfig, context); + if (!page) { + break; + } + cacheList.set(pageNum, page); + } + const pageItems = page.list; + needHandleItems = needHandleItems.concat(pageItems + .filter((item) => { + const itemDate = item.updateDate; + return (!startDate || itemDate >= startDate) && (!endDate || itemDate <= endDate); + } + )); + } + return needHandleItems; + + } + async syncByTimeLimit(syncConfig: SyncConfig, context: HandleContext) { + const items = await this.getByTimeLimit(syncConfig, context); + if (!items || items.length == 0) { + return; + } + + let subjectListItems = await this.removeExists( + items, + syncConfig, + context, + ); + + const searchPage = new SearchPageTypeOf(subjectListItems.length, + 1, + subjectListItems.length, + null,subjectListItems); + await this.handleItems(searchPage, subjectListItems, context); - private async getItems(syncConfig:SyncConfig, context:HandleContext):Promise { - const supportHandlers:DoubanListHandler[] = this.doubanListHandlers.filter((h) => h.support(syncConfig)); - let items:SubjectListItem[] = []; - for(const handler of supportHandlers) { - if (!context.plugin.statusHolder.syncing()) { - return []; - } - const item = await handler.getAllPageList(context); - if (item) { - items = items.concat(item); - } - } - return items; } - private async removeExists(items:SubjectListItem[], syncConfig: SyncConfig, context: HandleContext):Promise { + private async getItems( + syncConfig: SyncConfig, + context: HandleContext, + ): Promise> { + const supportHandlers: DoubanListHandler[] = + this.doubanListHandlers.filter((h) => h.support(syncConfig)); + const handler = supportHandlers[0]; + if (!context.plugin.statusHolder.syncing()) { + return SearchPage.emptyWithNoType(); + } + const item = await handler.getPageData(context); + if (!context.plugin.statusHolder.syncing()) { + return SearchPage.emptyWithNoType(); + } + return item; + } + + private async removeExists( + items: SubjectListItem[], + syncConfig: SyncConfig, + context: HandleContext, + ): Promise { if (!context.plugin.statusHolder.syncing()) { return []; } return items; } - private async handleItems(items:SubjectListItem[], context:HandleContext):Promise { + private async handleItems( + searchPage: SearchPage, + items: SubjectListItem[], + context: HandleContext, + ): Promise { if (!items || items.length == 0) { - return ; + return; } - const {syncStatus} = context.syncStatusHolder; - syncStatus.totalNum(items.length); - const needHandled:number = items.filter(item => syncStatus.shouldSync(item.id)).length; + const { syncStatus } = context.syncStatusHolder; + syncStatus.totalNum(searchPage.total); + const needHandled: number = + syncStatus.getTotal() - syncStatus.getHasHandle(); syncStatus.setNeedHandled(needHandled); for (const item of items) { if (!context.plugin.statusHolder.syncing()) { return; } try { - if(syncStatus.shouldSync(item.id)) { - let subject: DoubanSubject = await this.doubanSubjectLoadHandler.handle(item.id, context); - await sleepRange(BasicConst.CALL_DOUBAN_DELAY, BasicConst.CALL_DOUBAN_DELAY + BasicConst.CALL_DOUBAN_DELAY_RANGE); - }else { + if (syncStatus.shouldSync(item.id)) { + let subject: DoubanSubject = + await this.doubanSubjectLoadHandler.handle( + item.id, + context, + ); + + await sleepRange( + BasicConst.CALL_DOUBAN_DELAY, + BasicConst.CALL_DOUBAN_DELAY + + BasicConst.CALL_DOUBAN_DELAY_RANGE, + ); + } else { syncStatus.unHandle(item.id, item.title); } - }catch (e) { - log.notice(i18nHelper.getMessage('130120')) + } catch (e) { + log.notice(i18nHelper.getMessage("130120")); } } } + private async syncByCountLimit(syncConfig: SyncConfig, context: HandleContext) { + const {syncConditionCountFromValue, syncConditionCountToValue} = syncConfig; + const startOffset = Math.floor((syncConditionCountFromValue - 1)/ PAGE_SIZE) * PAGE_SIZE; + context.syncOffset = startOffset; + //结束点是第几条 + let endOffsetNumberForCustom = 0; + let needHandleTotalCustomItem = 0; + let isFirstStep = true; + let handleCount = 0; + do { + let searchPage = await this.getItems(syncConfig, context); + if (!context.plugin.statusHolder.syncing()) { + break; + } + const {list, total} = searchPage; + if ( + !searchPage || + !list || + list.length == 0 + ) { + break; + } + if (syncConditionCountFromValue > total) { + context.syncStatusHolder.syncStatus.setMessage(i18nHelper.getMessage("130121", total)); + break; + } + if (endOffsetNumberForCustom == 0) { + endOffsetNumberForCustom = Math.min(syncConditionCountToValue?syncConditionCountToValue:searchPage.total, searchPage.total); + needHandleTotalCustomItem = endOffsetNumberForCustom - syncConditionCountFromValue + 1; + + } + let subjectListItems = []; + + //在开始和结束同一页 + if (Math.floor((syncConditionCountFromValue - 1) / PAGE_SIZE) == Math.floor((endOffsetNumberForCustom - 1) / PAGE_SIZE)) { + const startIndex = Math.floor((syncConditionCountFromValue - 1) % PAGE_SIZE); + const endIndex = Math.floor((endOffsetNumberForCustom - 1) % PAGE_SIZE); + subjectListItems = await this.removeExists( + list.slice(startIndex, endIndex + 1), + syncConfig, + context, + ); + handleCount += (endIndex - startIndex + 1); + //第一页 + } else if (isFirstStep) { + const startIndex = (syncConditionCountFromValue - 1) % PAGE_SIZE; + handleCount += (list.length - startIndex); + subjectListItems = await this.removeExists( + list.slice(startIndex), + syncConfig, + context, + ); + isFirstStep = false; + } + //最后一页 + else if (needHandleTotalCustomItem - handleCount <= PAGE_SIZE) { + const endIndex = needHandleTotalCustomItem - handleCount; + subjectListItems = await this.removeExists( + list.slice(0, endIndex), + syncConfig, + context, + ); + handleCount += endIndex; + //中间页 + } else { + subjectListItems = await this.removeExists( + list, + syncConfig, + context, + ); + handleCount += PAGE_SIZE; + } + + + if (!subjectListItems || subjectListItems.length == 0) { + await sleepRange( + BasicConst.CALL_DOUBAN_DELAY, + BasicConst.CALL_DOUBAN_DELAY + + BasicConst.CALL_DOUBAN_DELAY_RANGE, + ); + continue; + } + + searchPage.total = needHandleTotalCustomItem; + //处理 + await this.handleItems(searchPage, subjectListItems, context); + + context.syncOffset = context.syncOffset + PAGE_SIZE; + await sleepRange( + BasicConst.CALL_DOUBAN_DELAY, + BasicConst.CALL_DOUBAN_DELAY + + BasicConst.CALL_DOUBAN_DELAY_RANGE, + ); + } while (handleCount < needHandleTotalCustomItem); + } + + private async syncAll(syncConfig: SyncConfig, context: HandleContext) { + //最多100000条 + context.syncOffset = 0; + let handleCount = 0; + let totalForHandle = 0; + let isFirstStep = true; + do { + let searchPage = await this.getItems(syncConfig, context); + if (!context.plugin.statusHolder.syncing()) { + break; + } + + const {list, total} = searchPage; + if ( + !searchPage || + !list || + list.length == 0 + ) { + break; + } + if (isFirstStep) { + totalForHandle = total; + isFirstStep = false; + } + handleCount += list.length; + let subjectListItems = await this.removeExists( + list, + syncConfig, + context, + ); + if (!subjectListItems || subjectListItems.length == 0) { + await sleepRange( + BasicConst.CALL_DOUBAN_DELAY, + BasicConst.CALL_DOUBAN_DELAY + + BasicConst.CALL_DOUBAN_DELAY_RANGE, + ); + continue; + } + await this.handleItems(searchPage, subjectListItems, context); + context.syncOffset = context.syncOffset + PAGE_SIZE; + await sleepRange( + BasicConst.CALL_DOUBAN_DELAY, + BasicConst.CALL_DOUBAN_DELAY + + BasicConst.CALL_DOUBAN_DELAY_RANGE, + ); + } while (handleCount <= totalForHandle); + } + + private async syncLastThirty(syncConfig: SyncConfig, context: HandleContext) { + context.syncOffset = 0; + let searchPage = await this.getItems(syncConfig, context); + if (!context.plugin.statusHolder.syncing()) { + return; + } + const {list, total} = searchPage; + if ( + !searchPage || + !list || + list.length == 0 + ) { + return; + } + + let subjectListItems = await this.removeExists( + list, + syncConfig, + context, + ); + if (!subjectListItems || subjectListItems.length == 0) { + return; + } + searchPage.total = Math.min(list.length, total); + await this.handleItems(searchPage, subjectListItems, context); + } } - - diff --git a/src/org/wanxp/douban/sync/handler/SyncHandler.ts b/src/org/wanxp/douban/sync/handler/SyncHandler.ts index 4cb419e..90cbdf3 100644 --- a/src/org/wanxp/douban/sync/handler/SyncHandler.ts +++ b/src/org/wanxp/douban/sync/handler/SyncHandler.ts @@ -9,6 +9,7 @@ import { DoubanMusicSyncHandler } from "./DoubanMusicSyncHandler"; import { DoubanBookSyncHandler } from "./DoubanBookSyncHandler"; import {i18nHelper} from "../../../lang/helper"; import {DoubanTeleplaySyncHandler} from "./DoubanTeleplaySyncHandler"; +import {SyncConditionType} from "../../../constant/Constsant"; export default class SyncHandler { private app: App; @@ -18,6 +19,7 @@ export default class SyncHandler { private syncHandlers: DoubanSyncHandler[]; private defaultSyncHandler: DoubanSyncHandler; + constructor(app: App, plugin: DoubanPlugin, syncConfig: SyncConfig, context: HandleContext) { this.app = app; this.plugin = plugin; @@ -38,6 +40,10 @@ export default class SyncHandler { async sync() { if (this.syncConfig && this.syncConfig.syncType && this.syncConfig.scope) { + if(this.checkSyncConfig()) { + this.context.syncStatusHolder.syncStatus.setMessage(this.checkSyncConfig()); + return; + } let syncHandler = this.syncHandlers.find(handler => handler.support(this.syncConfig.syncType)); if (syncHandler) { await syncHandler.sync(this.syncConfig, this.context); @@ -53,6 +59,25 @@ export default class SyncHandler { const {syncStatus} = syncStatusHolder; const {statusHandleMap} = syncStatus; const {syncResultMap} = syncStatus; + const {syncConfig} = this; + + let extendCondition = ''; + if (syncConfig.syncConditionType == SyncConditionType.CUSTOM_ITEM) { + extendCondition = `${syncConfig.syncConditionCountFromValue} - ${syncConfig.syncConditionCountToValue}`; + } else if (syncConfig.syncConditionType == SyncConditionType.CUSTOM_TIME) { + extendCondition = `${syncConfig.syncConditionDateFromValue.toISOString().substring(0, 10)} - ${syncConfig.syncConditionDateToValue.toISOString().substring(0, 10)}`; + } + + + const condition = ` +1. ${i18nHelper.getMessage('110030')} \`${syncStatus.getTypeName()}\` +2. ${i18nHelper.getMessage('110032')} \`${syncStatus.getScopeName()}\` +3. ${i18nHelper.getMessage('110070')} \`${syncStatus.getSyncConditionName()}\`${extendCondition? (' => ' + extendCondition) : ''} +4. ${i18nHelper.getMessage('110039')} \`${syncConfig.incrementalUpdate?i18nHelper.getMessage('110055'):i18nHelper.getMessage('110056')}\` +5. ${i18nHelper.getMessage('110031')} \`${syncConfig.force?i18nHelper.getMessage('110055'):i18nHelper.getMessage('110056')}\` +` + + let summary:string = `${i18nHelper.getMessage('110053', i18nHelper.getMessage('110050'), i18nHelper.getMessage('110051'), i18nHelper.getMessage('110052'))} |-----|----|----------------------------------| @@ -79,8 +104,28 @@ export default class SyncHandler { } } - const result : string = i18nHelper.getMessage('110037', summary, details); + const result : string = i18nHelper.getMessage('110037', condition, summary, details); const resultFileName = `${i18nHelper.getMessage('110038')}_${moment(new Date()).format('YYYYMMDDHHmmss')}` await this.plugin.fileHandler.createNewNoteWithData(`${this.syncConfig.dataFilePath}/${resultFileName}`, result, true); } + + private checkSyncConfig() { + const {syncConfig} = this; + switch (syncConfig.syncConditionType) { + case SyncConditionType.CUSTOM_ITEM: + if (syncConfig.syncConditionCountFromValue && syncConfig.syncConditionCountToValue) { + if (syncConfig.syncConditionCountFromValue > syncConfig.syncConditionCountToValue) { + return i18nHelper.getMessage('110044'); + } + } + break; + case SyncConditionType.CUSTOM_TIME: + if (syncConfig.syncConditionDateToValue && syncConfig.syncConditionDateFromValue) { + if (syncConfig.syncConditionDateFromValue > syncConfig.syncConditionDateToValue) { + return i18nHelper.getMessage('110045'); + } + } + + } + } } diff --git a/src/org/wanxp/douban/sync/handler/list/DoubanAbstractListHandler.ts b/src/org/wanxp/douban/sync/handler/list/DoubanAbstractListHandler.ts index 5b461a4..a56cbba 100644 --- a/src/org/wanxp/douban/sync/handler/list/DoubanAbstractListHandler.ts +++ b/src/org/wanxp/douban/sync/handler/list/DoubanAbstractListHandler.ts @@ -1,88 +1,124 @@ -import { request, RequestUrlParam} from "obsidian"; -import {i18nHelper} from 'src/org/wanxp/lang/helper'; -import {log} from "src/org/wanxp/utils/Logutil"; -import {CheerioAPI, load} from "cheerio"; +import { request, RequestUrlParam } from "obsidian"; +import { i18nHelper } from "src/org/wanxp/lang/helper"; +import { log } from "src/org/wanxp/utils/Logutil"; +import { CheerioAPI, load } from "cheerio"; import HandleContext from "../../../data/model/HandleContext"; -import {doubanSubjectSyncListUrl} from "../../../../constant/Douban"; -import {BasicConst, PAGE_SIZE, SyncType, SyncTypeUrlDomain} from "../../../../constant/Constsant"; -import {SubjectListItem} from "../../../data/model/SubjectListItem"; -import {DoubanListHandler} from "./DoubanListHandler"; -import {SyncConfig} from "../../model/SyncConfig"; -import { sleepRange} from "../../../../utils/TimeUtil"; -import {ALL} from "../../../../constant/DoubanUserState"; +import { doubanSubjectSyncListUrl } from "../../../../constant/Douban"; +import { + BasicConst, + PAGE_SIZE, + SyncType, + SyncTypeUrlDomain, +} from "../../../../constant/Constsant"; +import { SubjectListItem } from "../../../data/model/SubjectListItem"; +import { DoubanListHandler } from "./DoubanListHandler"; +import { SyncConfig } from "../../model/SyncConfig"; +import { sleepRange } from "../../../../utils/TimeUtil"; +import { ALL } from "../../../../constant/DoubanUserState"; import HttpUtil from "../../../../utils/HttpUtil"; -import {DoubanHttpUtil} from "../../../../utils/DoubanHttpUtil"; +import { DoubanHttpUtil } from "../../../../utils/DoubanHttpUtil"; +import { SearchPage } from "../../../data/model/SearchPage"; +import {SearchPageTypeOf} from "../../../data/model/SearchPageTypeOf"; -export default abstract class DoubanAbstractListHandler implements DoubanListHandler{ +export default abstract class DoubanAbstractListHandler + implements DoubanListHandler +{ + async getPageData(context: HandleContext): Promise { + let all: SubjectListItem[] = []; + let pages: SearchPage = SearchPage.emptyWithNoType(); - async getAllPageList(context: HandleContext):Promise{ - let all:SubjectListItem[] = []; - let pages:SubjectListItem[] = []; - let start = 0; - do { - await sleepRange(BasicConst.CALL_DOUBAN_DELAY, - BasicConst.CALL_DOUBAN_DELAY + BasicConst.CALL_DOUBAN_DELAY_RANGE); - const url:string = this.getUrl(context, start); - if (!context.plugin.statusHolder.syncing()) { - return []; - } - pages = await this.getPageList(url, context); - if (pages) { - all = all.concat(pages); - } - start = start + PAGE_SIZE; - } while (pages && pages.length > 0) - return all; + const url: string = this.getUrl(context, context.syncOffset); + if (!context.plugin.statusHolder.syncing()) { + return SearchPage.emptyWithNoType(); + } + let subjectListItemSearchPageTypeOf = await this.getPageList(url, context); + if (subjectListItemSearchPageTypeOf) { + context.plugin.statusHolder.syncStatus.setAllTotal(subjectListItemSearchPageTypeOf.total) + } + return subjectListItemSearchPageTypeOf; } - async delay(ms: number) { + async delay(ms: number) {} + + private getUrl(context: HandleContext, start: number) { + return doubanSubjectSyncListUrl( + this.getSyncTypeDomain(), + context.userComponent.getUserId(), + this.getDoType(), + start, + ); } - private getUrl(context: HandleContext, start:number) { - return doubanSubjectSyncListUrl(this.getSyncTypeDomain(), context.userComponent.getUserId(), this.getDoType(), start); - } + abstract getDoType(): string; - abstract getDoType():string; + abstract getSyncType(): SyncType; - abstract getSyncType():SyncType; - - getSyncTypeDomain():string { + getSyncTypeDomain(): string { return SyncTypeUrlDomain.get(this.getSyncType()); } - async getPageList(url: string, context: HandleContext):Promise { - return DoubanHttpUtil.httpRequestGet(url, context.plugin.settingsManager.getHeaders(), context.plugin.settingsManager) + async getPageList( + url: string, + context: HandleContext, + ): Promise> { + return DoubanHttpUtil.httpRequestGet( + url, + context.plugin.settingsManager.getHeaders(), + context.plugin.settingsManager, + ) .then(load) - .then(data => this.parseSubjectFromHtml(data, context)) - .catch(e => log - .error( - i18nHelper.getMessage('130101') - .replace('{0}', e.toString()) - , e)); - ; - + .then((data) => this.parseSubjectFromHtml(data, context)) + .catch((e) => + log.error( + i18nHelper + .getMessage("130101") + .replace("{0}", e.toString()), + e, + ), + ); } - - - - - parseSubjectFromHtml(dataHtml: CheerioAPI, context: HandleContext):SubjectListItem[] { - return dataHtml('.item-show') + parseSubjectFromHtml( + dataHtml: CheerioAPI, + context: HandleContext, + ): SearchPageTypeOf { + const items = dataHtml(".item-show") .get() .map((i: any) => { const item = dataHtml(i); - const linkValue:string = item.find('div.title > a').attr('href'); - const titleValue:string = item.find('div.title > a').text().trim(); + const linkValue: string = item + .find("div.title > a") + .attr("href"); + const titleValue: string = item + .find("div.title > a") + .text() + .trim(); + const updateDateStr: string = item.find("div.date").text().trim(); + let updateDate = null; + try { + updateDate = new Date(updateDateStr); + }catch (e) { + console.error(e); + log.info("parse date error:" + titleValue); + } let idPattern = /(\d){5,10}/g; let ececResult = idPattern.exec(linkValue); - return ececResult?{id: ececResult[0], url: linkValue, title: titleValue}:null; + return !ececResult ? null : {id: ececResult[0], url: linkValue, title: titleValue, updateDate: updateDate}; // return linkValue; - }) + }); + const subjectNumText = dataHtml(".subject-num").text().trim(); + const totalNumMatch = subjectNumText.match(/\/\s*(\d+)/); + const totalNum = totalNumMatch ? parseInt(totalNumMatch[1], 10) : 0; + return new SearchPage( + totalNum, + Math.floor(context.syncOffset / PAGE_SIZE) + 1, + PAGE_SIZE, + null, + items, + ); } support(config: SyncConfig): boolean { return this.getDoType() == config.scope || ALL == config.scope; } } - diff --git a/src/org/wanxp/douban/sync/handler/list/DoubanListHandler.ts b/src/org/wanxp/douban/sync/handler/list/DoubanListHandler.ts index 252cc62..1d35c7c 100644 --- a/src/org/wanxp/douban/sync/handler/list/DoubanListHandler.ts +++ b/src/org/wanxp/douban/sync/handler/list/DoubanListHandler.ts @@ -1,10 +1,10 @@ import HandleContext from "../../../data/model/HandleContext"; -import {SubjectListItem} from "../../../data/model/SubjectListItem"; import {SyncConfig} from "../../model/SyncConfig"; +import {SearchPage} from "../../../data/model/SearchPage"; export interface DoubanListHandler { - getAllPageList(context: HandleContext):Promise; + getPageData(context: HandleContext):Promise; support(config:SyncConfig):boolean; } diff --git a/src/org/wanxp/douban/sync/model/SyncConfig.ts b/src/org/wanxp/douban/sync/model/SyncConfig.ts index 03b89d6..22c7fe6 100644 --- a/src/org/wanxp/douban/sync/model/SyncConfig.ts +++ b/src/org/wanxp/douban/sync/model/SyncConfig.ts @@ -1,10 +1,10 @@ export interface SyncConfig { syncType: string, syncConditionType: string, - syncConditionCountFromValue: string, - syncConditionCountToValue: string, - syncConditionDateFromValue: string, - syncConditionDateToValue: string, + syncConditionCountFromValue: number, + syncConditionCountToValue: number, + syncConditionDateFromValue: Date, + syncConditionDateToValue: Date, scope: string, force: boolean, dataFilePath: string; diff --git a/src/org/wanxp/douban/sync/model/SyncItemResult.ts b/src/org/wanxp/douban/sync/model/SyncItemResult.ts index e26580f..f55ec67 100644 --- a/src/org/wanxp/douban/sync/model/SyncItemResult.ts +++ b/src/org/wanxp/douban/sync/model/SyncItemResult.ts @@ -1,7 +1,7 @@ -import {SyncItemStatus} from "../../../constant/Constsant"; +import { SyncItemStatus } from "../../../constant/Constsant"; export interface SyncItemResult { - id:string, - title:string, - status:SyncItemStatus, + id: string; + title: string; + status: SyncItemStatus; } diff --git a/src/org/wanxp/douban/sync/model/SyncStatusHolder.ts b/src/org/wanxp/douban/sync/model/SyncStatusHolder.ts index 5d7c0cf..aca69ef 100644 --- a/src/org/wanxp/douban/sync/model/SyncStatusHolder.ts +++ b/src/org/wanxp/douban/sync/model/SyncStatusHolder.ts @@ -1,7 +1,18 @@ import {SyncConfig} from "./SyncConfig"; import {SyncItemResult} from "./SyncItemResult"; -import {BasicConst, SyncItemStatus} from "../../../constant/Constsant"; +import { + BasicConst, + SupportType, + SyncConditionTypeRecords, + SyncItemStatus, + SyncTypeRecords +} from "../../../constant/Constsant"; import {SyncHandledData} from "../../setting/model/SyncHandledData"; +import { + DoubanSubjectState, + DoubanSubjectStateRecords, + DoubanSubjectStateRecords_SYNC +} from "../../../constant/DoubanUserState"; export default class SyncStatusHolder { @@ -12,15 +23,20 @@ export default class SyncStatusHolder { [SyncItemStatus.replace, 0], [SyncItemStatus.create, 0], [SyncItemStatus.fail, 0], - [SyncItemStatus.unHandle, 0], + [SyncItemStatus.failByDiffType, 0], + [SyncItemStatus.unHandle, 0], ]); private key: string; + //不管处不处理的总数,比如会包含已经存在的,或者条件没覆盖的部分,比如过滤条件选择了1-2条,但总共其实有10条 + private allTotal: number; public syncConfig: SyncConfig; + //处理的总数 private total:number; private handle:number; private needHandled:number; + private message:string = ''; constructor(syncConfig: SyncConfig) { this.syncConfig = syncConfig; @@ -46,6 +62,16 @@ export default class SyncStatusHolder { return this.handle; } + getAllTotal():number { + return this.allTotal; + } + + setAllTotal(allTotal:number) { + this.allTotal = allTotal; + } + + + /** * 已处理总数,包含已经存在不需要同步的部分 */ @@ -86,6 +112,10 @@ export default class SyncStatusHolder { this.updateResult(id, title, SyncItemStatus.fail); } + public failByDiffType(id:string, title:string) { + this.updateResult(id, title, SyncItemStatus.failByDiffType); + } + private updateResult(id:string, title:string, status:SyncItemStatus) { this.syncResultMap.set(id, {id: id,title:title,status:status}); this.statusHandleMap.set(status, this.statusHandleMap.get(status) + 1); @@ -161,4 +191,26 @@ export default class SyncStatusHolder { return false; } + public setMessage(s: string) { + this.message = s; + } + + public getMessage() { + return this.message; + } + + public getScopeName():string { + //@ts-ignore + return DoubanSubjectStateRecords_SYNC[this.syncConfig.syncType][this.syncConfig.scope]; + } + + public getTypeName():string { + return SyncTypeRecords[this.syncConfig.syncType]; + + + } + + public getSyncConditionName() { + return SyncConditionTypeRecords[this.syncConfig.syncConditionType]; + } } diff --git a/src/org/wanxp/lang/helper.ts b/src/org/wanxp/lang/helper.ts index 11b310d..782e907 100644 --- a/src/org/wanxp/lang/helper.ts +++ b/src/org/wanxp/lang/helper.ts @@ -11,19 +11,34 @@ const locale = localeMap[lang || 'en']; export default class I18nHelper { - public getMessage(str: keyof typeof en, ...params: any[]): string { + public getMessage(str: keyof typeof en | string, ...params: any[]): string { if (!locale) { console.error('Error: obsidian douban locale not found', lang); } - + // @ts-ignore let val:string = (locale && locale[str]) || en[str]; if (params) { for (let i:number = 0;i < params.length;i++) { - val = val.replaceAll(`{${i}}`, params[i]) + val = this.replaceAll(i, val, params[i]) } } return val; } + + private replaceAll(index: number, message: string, replace: string): string { + const placeholderRegex = new RegExp(`\\{${index}:([^}]+)\\}`); + const match = message.match(placeholderRegex); + if (!match) { + return message.replaceAll(`{${index}}`, replace); + } + const defaultValue = match ? match[1] : ''; + + // If replace is undefined or null, use the default value + const replacement = (replace === undefined || replace === null) ? defaultValue : replace; + + // Replace the specific placeholder with the replacement value + return message.replace(placeholderRegex, replacement); + } } export const i18nHelper: I18nHelper = new I18nHelper(); diff --git a/src/org/wanxp/lang/locale/en.ts b/src/org/wanxp/lang/locale/en.ts index 78d3293..949fcb4 100644 --- a/src/org/wanxp/lang/locale/en.ts +++ b/src/org/wanxp/lang/locale/en.ts @@ -23,12 +23,15 @@ export default { '110035': `FileName: (Tip:Support Variables And Path)`, '110036': `Complete`, '110037': ` -### Summary +### Condition {0} -### Details +### Summary {1} +### Details +{2} + --- PS: This file could be delete if you want to. `, @@ -38,16 +41,20 @@ PS: This file could be delete if you want to. '110041': `IncrementalSync`, '110042': `EstimateTime`, '110043': `Loading Menu`, + '110044': `Custom Count Input End Must Bigger Than Start`, + '110045': `Custom Time Input End Must Bigger Than Start`, '110050': `Type`, '110051': `Number`, '110052': `Description`, '110152': `Confirm`, + '110055': `Enable`, + '110056': `Disable`, '110070': `Sync Scope:`, '110071': `All`, '110072': `Recent Changes`, - '110075': `Last 10 Items`, + '110075': `Last 30 Items`, '110074': `Custom Time`, '110076': `Custom Count`, '110077': `From`, @@ -55,12 +62,21 @@ PS: This file could be delete if you want to. '110079': `To`, '110073': `Items`, '110080': `Number`, + '112080': `Custom Count Input not a number`, + '110081': `Custom Time must input both start and end or only one`, + '110082': `Custom Time must input format: YYYY-MM-DD`, + '110083': `Not Support`, + '110090': `The total number of {0} for status {1} is {2}, and the number that needs to be synchronized is {3}`, + '110091': `Error`, + '110095': `The smaller the number, the closer to now. For example, the latest update on Douban is the first one, and the earliest update is the last one.`, + '110096': `This is the update time of our entry on Douban`, 'exists':`[exists]`, 'unHandle':`[unHandle]`, 'replace':`[replace]`, 'create':`[create]`, 'fail':`[fail]`, + 'failByDiffType': `[fail by diff type]`, 'syncall':`[summary]`, 'notsync':`[notsync]`, @@ -69,6 +85,7 @@ PS: This file could be delete if you want to. 'replace_desc': `replace`, 'create_desc': `create`, 'fail_desc': `fail`, + 'failByDiffType_desc': `fail because of different type`, 'notsync_desc': `notsync`, 'syncall_desc': `syncall`, @@ -309,6 +326,7 @@ PS: This file could be delete if you want to. '130107': `Can not find array setting for {1} in {0} , Please add it in array settings`, '130108': `Redirect times too much, please check your network or proxy`, '130120': `An error occurred during Sync, but Sync will continue. Error item is {}`, + '130121': `An error occurred during Sync, Begin number is bigger than total number {0}, will not sync this item`, '140201': `[OB-Douban]: searching '{0}'...`, '140202': `[OB-Douban]: result {0} rows`, diff --git a/src/org/wanxp/lang/locale/zh-cn.ts b/src/org/wanxp/lang/locale/zh-cn.ts index a3a3c24..ec219ec 100644 --- a/src/org/wanxp/lang/locale/zh-cn.ts +++ b/src/org/wanxp/lang/locale/zh-cn.ts @@ -28,22 +28,38 @@ export default { '110070': `同步范围:`, '110071': `全部`, '110072': `最新变动`, - '110075': `最近10条`, + '110075': `最近30条`, '110074': `自定义时间`, '110076': `自定义条数`, '110077': `从`, '110078': `第`, '110079': `到`, '110073': `条`, + '110095': `数字越小代表越接近现在,比如在豆瓣上我们最新更新一条内容为第一条,反之最早更新的就是最后一条了`, + '110096': `这是我们在豆瓣上条目的更新时间`, + '110080': `数字`, + '112080': `自定义的数量输入不是个数字`, + '110081': `开始时间和结束时间必须填写一个或都填写`, + '110082': `时间录入格式必须是YYYY-MM-DD`, + '110083': `不支持当前同步条件`, + '110090': `[{0:未知}]状态为[{1:未知}]的总数为{2:未知},当前需要同步数为{3::未知}`, + '110091': `异常:`, + '110092': `情况:`, + + + '110037': ` -### 同步结果汇总 +### 同步条件 {0} +### 同步结果汇总 +{1} + ### 同步结果明细 {1} @@ -52,20 +68,26 @@ export default { `, '110038': `豆瓣同步结果`, '110039': `增量同步:`, - '110040': `仅同步上面'笔记存放位置'目录中最近新增/同步失败/未同步的部分,不同步已同步的内容.若关闭则会全量同步.增量同步会更快,适合之前同步中途失败停止或最近有豆瓣新增了内容,全量同步适合修改了模板或修改了存放路径情况下进行.`, + '110040': `仅同步最近新增/同步失败/未同步的部分,增量适合之前同步中途失败停止或最近有豆瓣新增了内容,全量同步适合修改了模板或修改了存放路径情况下进行.`, '110041': `使用增量`, '110042': `预计处理时间`, '110043': `正在获取需要同步的列表, 请稍后`, + '110044': `自定义条数的录入结束数量不能比开始数量小`, + '110045': `自定义时间的录入结束时间不能比开始时间小`, + '110050': `类型`, '110051': `数量`, '110052': `说明`, + '110055': `开启`, + '110056': `关闭`, 'unHandle':`[已忽略]`, 'exists': `[未替换]`, 'replace': `[已替换]`, 'create': `[已创建]`, 'fail': `[已失败]`, + 'failByDiffType': `[类型不匹配]`, 'syncall': `[总数]`, 'notsync': `[未进行]`, @@ -74,6 +96,7 @@ export default { 'replace_desc': `已经存在的同名文档,这次已被替换`, 'create_desc': `之前不存在的文档,这次直接新增`, 'fail_desc': `处理过程钟出现错误,未能成功`, + 'failByDiffType_desc': `失败,类型不匹配,比如想要同步的是电影,但是实际获取到的是电视剧`, 'notsync_desc': `因异常中断或提前终止导致还未处理`, 'syncall_desc': `您此次同步条件在豆瓣中的条目总数`, @@ -308,6 +331,7 @@ export default { '130106': `请尝试在Douban插件中登录后操作. 若还是无效果则尝试于12小时或24小时后再重试,或重置你的网络(如重新拨号或更换网络) `, '130107': `参数{0}中指定的数组输出类型{1}不存在,请前往配置进行设置`, '130120': `同步时发生错误,但同步将会继续。错误项目是 {}。`, + '130121': `总数只有{0}, 选择的开始比总数还要大,将不会同步。`, '140201': `[OB-Douban]: 开始搜索'{0}'...`, '140202': `[OB-Douban]: 搜索条数{0}条`, diff --git a/src/org/wanxp/main.ts b/src/org/wanxp/main.ts index 078b53e..4dc6ffb 100644 --- a/src/org/wanxp/main.ts +++ b/src/org/wanxp/main.ts @@ -57,7 +57,12 @@ export default class DoubanPlugin extends Plugin { } if (context.syncActive && extract.guessType && extract.guessType != extract.type) { extract.handledStatus = SubjectHandledStatus.syncTypeDiffAbort; - console.log(i18nHelper.getMessage('140102', extract.type, extract.title, extract.guessType)); + if (Action.Sync == context.action) { + this.showStatus(i18nHelper.getMessage('140207', syncStatus.getHasHandle(), syncStatus.getTotal(), extract.title)); + syncStatus.failByDiffType(extract.id, extract.title); + }else { + console.log(i18nHelper.getMessage('140102', extract.type, extract.title, extract.guessType)); + } return; } if (Action.Sync == context.action) { diff --git a/styles.css b/styles.css index d0c620b..6974aa2 100644 --- a/styles.css +++ b/styles.css @@ -1,3 +1,15 @@ +/* 获取主题颜色并定义新的颜色 */ +:root { + --obsidian-primary-color: var(--text-normal); + --obsidian-secondary-color: var(--background-primary); + + /* 定义新的颜色,稍微调整亮度或饱和度 */ + --plugin-primary-color: hsl(var(--obsidian-primary-color-h), var(--obsidian-primary-color-s), calc(var(--obsidian-primary-color-l) + 10%)); + --plugin-secondary-color: hsl(var(--obsidian-secondary-color-h), var(--obsidian-secondary-color-s), calc(var(--obsidian-secondary-color-l) - 10%)); +} + + + .obsidian_douban_settings_area { margin-left: 5px; margin-right: 5px; @@ -140,17 +152,17 @@ } .obsidian_douban_settings_tab_header.active { - background-color: #282828; + background-color: var(--plugin-primary-color); font-weight: bold; - border: 1px solid #323232; + border: 1px solid var(--plugin-secondary-color); border-radius: 8px 8px 0 0; /* Rounded corners for the top */ } .obsidian_douban_settings_tab_content.active { - background-color: #282828; + background-color: var(--plugin-primary-color); display: block; padding: 10px; - border: 1px solid #323232; + border: 1px solid var(--plugin-secondary-color); border-radius: 0 8px 8px 8px; /* Rounded corners for the bottom */ }