From a0eccf7370b64934b7feb71186cd83f82392cb10 Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:24:07 +0800 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20handle()=20=E6=96=B9=E6=B3=95=20catc?= =?UTF-8?q?h=20=E5=9D=97=E8=BF=94=E5=9B=9E=20undefined=20=E8=80=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E9=94=99=E8=AF=AF=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当 HTTP 请求失败时,返回错误对象会导致上层代码尝试访问错误对象的属性, 造成 TypeError。改为返回 undefined 让上层代码正确处理空值情况。 --- .../douban/data/handler/DoubanAbstractLoadHandler.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts index 10e27a4..a4ac748 100644 --- a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts @@ -142,7 +142,7 @@ export default abstract class DoubanAbstractLoadHandler }else { context.syncStatusHolder?context.syncStatusHolder.syncStatus.handled(1):null; } - return e; + return undefined; }); @@ -608,12 +608,20 @@ export default abstract class DoubanAbstractLoadHandler handlePersonNameByMeta(html: CheerioAPI, movie: DoubanSubject, context: HandleContext, metaProperty:string, objectProperty:string) { + if (!movie) { + return; + } const metaProperties: string[] = html(`head > meta[property='${metaProperty}']`).get() .map((e) => { return html(e).attr('content'); }); // @ts-ignore - movie[objectProperty] + const currentArray = movie[objectProperty]; + if (!Array.isArray(currentArray)) { + return; + } + // @ts-ignore + currentArray // @ts-ignore .filter((p:Person) => p.name) // @ts-ignore From 297ccd33cfe4c71aca96e62bec23b22527b6f8a2 Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:25:56 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=E5=BD=93=20JSON-LD=20=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=A4=B1=E8=B4=A5=E6=97=B6=E6=B7=BB=E5=8A=A0=E5=9B=9E?= =?UTF-8?q?=E9=80=80=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 豆瓣现在对未登录请求返回反爬虫验证页面,导致 JSON-LD 解析返回 undefined。 添加从 OG meta 标签提取基本信息的回退机制,防止代码崩溃。 同时给所有数组字段添加默认值 || []。 --- .../data/handler/DoubanMovieLoadHandler.ts | 82 ++++++++++++++----- .../data/handler/DoubanTeleplayLoadHandler.ts | 58 +++++++++++-- 2 files changed, 111 insertions(+), 29 deletions(-) diff --git a/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts index 1ff1ea0..908ca42 100644 --- a/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanMovieLoadHandler.ts @@ -35,24 +35,24 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ @@ -98,7 +98,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler "application/ld+json" == html(scd).attr("type")) .map(i => { @@ -108,8 +108,8 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler meta[property='og:title']").get(0)).attr("content") || ''; + const image = html(html("head > meta[property='og:image']").get(0)).attr("content") || ''; + const urlMeta = html(html("head > meta[property='og:url']").get(0)).attr("content") || ''; + const desc = html(html("head > meta[property='og:description']").get(0)).attr("content") || ''; + + // Extract ID from URL + const idPattern = /(\d){5,10}/g; + const idMatch = idPattern.exec(urlMeta); + const id = idMatch ? idMatch[0] : ''; + + // Extract score from HTML + const scoreText = html("#interest_sectl strong[property='v:average']").text(); + const score = scoreText ? parseFloat(scoreText) : undefined; + + movie = { + id, + title, + type: this.getSupportType(), + score, + originalTitle: title, + desc, + url: urlMeta || (id ? `https://movie.douban.com/subject/${id}/` : ''), + director: [], + author: [], + actor: [], + aggregateRating: undefined, + datePublished: undefined, + image, + imageUrl: image, + genre: [], + publisher: '', + aliases: [], + language: [], + country: [], + time: null, + IMDb: null, + }; + } + + this.handlePersonNameByMeta(html, movie, context, 'video:actor', 'actor'); + this.handlePersonNameByMeta(html, movie, context, 'video:director', 'director'); + + const desc: string = html("span[property='v:summary']").text(); if (desc) { movie.desc = desc; } @@ -156,7 +198,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler v.trim()); - } else if(key.indexOf('片长') >= 0) { + } else if (key.indexOf('片长') >= 0) { value = html(info.next.next).text().trim() } else { value = html(info.next).text().trim(); @@ -164,11 +206,11 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanTeleplaySubject, context: HandleContext): void { - variableMap.set("director", new DataField("director", DataValueType.array, extract.director,extract.director.map(SchemaOrg.getPersonName).filter(c => c))); + variableMap.set("director", new DataField("director", DataValueType.array, extract.director,(extract.director || []).map(SchemaOrg.getPersonName).filter(c => c))); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ @@ -84,7 +84,7 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler "application/ld+json" == html(scd).attr("type")) .map(i => { @@ -104,14 +104,14 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler meta[property='og:title']").get(0)).attr("content") || ''; + const image = html(html("head > meta[property='og:image']").get(0)).attr("content") || ''; + const urlMeta = html(html("head > meta[property='og:url']").get(0)).attr("content") || ''; + const desc = html(html("head > meta[property='og:description']").get(0)).attr("content") || ''; + + const idPattern = /(\d){5,10}/g; + const idMatch = idPattern.exec(urlMeta); + const id = idMatch ? idMatch[0] : ''; + + const scoreText = html("#interest_sectl strong[property='v:average']").text(); + const score = scoreText ? parseFloat(scoreText) : undefined; + + teleplay = { + id, + title, + type: this.getSupportType(), + score, + originalTitle: title, + desc, + url: urlMeta || (id ? `https://movie.douban.com/subject/${id}/` : ''), + director: [], + author: [], + actor: [], + aggregateRating: undefined, + datePublished: undefined, + image, + imageUrl: image, + genre: [], + publisher: '', + aliases: [], + language: [], + country: [], + episode: null, + time: null, + IMDb: null, + }; + } + this.handlePersonNameByMeta(html, teleplay, context, 'video:actor', 'actor'); this.handlePersonNameByMeta(html, teleplay, context, 'video:director', 'director'); const desc:string = html("span[property='v:summary']").text(); From 99d417062691e49a86b756e87334d0d70e73cacf Mon Sep 17 00:00:00 2001 From: YuBai Date: Mon, 2 Feb 2026 11:26:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=E4=B8=BA=E5=85=B6=E4=BB=96=20handle?= =?UTF-8?q?r=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E7=BB=84=E7=A9=BA=E5=80=BC?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 parseVariable 方法中为所有数组字段的 .map() 调用添加 (field || []) 保护, 防止当字段为 undefined 时调用 .map() 报错。 --- .../wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts | 8 ++++---- .../wanxp/douban/data/handler/DoubanBookLoadHandler.ts | 4 ++-- .../wanxp/douban/data/handler/DoubanGameLoadHandler.ts | 2 +- .../wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts b/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts index 3d04057..64f4283 100644 --- a/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts +++ b/src/org/wanxp/douban/ai/handler/DoubanTheaterAiLoadHandler.ts @@ -33,24 +33,24 @@ export default class DoubanTheaterAiLoadHandler extends DoubanAbstractLoadHandle "director", DataValueType.array, extract.director, - extract.director.map(SchemaOrg.getPersonName).filter(c => c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ diff --git a/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts index 899ba04..64e2a36 100644 --- a/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanBookLoadHandler.ts @@ -30,9 +30,9 @@ export default class DoubanBookLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanBookSubject, context: HandleContext): void { variableMap.set(DoubanBookParameter.author, new DataField(DoubanBookParameter.author, - DataValueType.array, extract.author, extract.author.map(this.handleSpecialAuthorName))); + DataValueType.array, extract.author, (extract.author || []).map(this.handleSpecialAuthorName))); variableMap.set(DoubanBookParameter.translator, new DataField(DoubanBookParameter.translator, - DataValueType.array, extract.translator, extract.translator.map(this.handleSpecialAuthorName))); + DataValueType.array, extract.translator, (extract.translator || []).map(this.handleSpecialAuthorName))); } support(extract: DoubanSubject): boolean { diff --git a/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts index ca40aed..036d334 100644 --- a/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanGameLoadHandler.ts @@ -31,7 +31,7 @@ export default class DoubanGameLoadHandler extends DoubanAbstractLoadHandler, extract: DoubanGameSubject, context: HandleContext): void { // super.parseAliases(beforeContent, variableMap, extract, context); variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a=>a + (extract.aliases || []).map(a=>a .trim() // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // //replase multiple _ to single _ diff --git a/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts index a27f8bb..2f3a25c 100644 --- a/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanTheaterLoadHandler.ts @@ -33,28 +33,28 @@ export default class DoubanTheaterLoadHandler extends DoubanAbstractLoadHandler< "director", DataValueType.array, extract.director, - extract.director.map(SchemaOrg.getPersonName).filter(c => c) + (extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("actor", new DataField( "actor", DataValueType.array, extract.actor, - extract.actor.map(SchemaOrg.getPersonName).filter(c => c) + (extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) )); variableMap.set("author", new DataField( "author", DataValueType.array, extract.author, - extract.author.map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) + (extract.author || []).map(SchemaOrg.getPersonName).map(name => super.getPersonName(name, context)).filter(c => c) )); variableMap.set("aliases", new DataField( "aliases", DataValueType.array, extract.aliases, - extract.aliases.map(a => a + (extract.aliases || []).map(a => a .trim() .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') //replace multiple _ to single _ From c37551290321f38ac75357682e9dda6833ec7355 Mon Sep 17 00:00:00 2001 From: YuBai Date: Wed, 4 Mar 2026 18:20:58 +0800 Subject: [PATCH 4/5] fix: restore All search type and initialize login state on startup - Switch AllPageSearchPageFetcher from m.douban.com/rexxar/api/v2/search (returns 403 need_login) to www.douban.com/j/search (works with cookies) - Update AllFirstPageSearchResultPageParser and OtherAllPageSearchResultPageParser to use SearchParserHandler.parseSearchJson() matching the j/search response format, replacing the old Rexxar-specific SearchParserHandlerV2 parsing logic - Add await this.userComponent.login() in onload() so login state is initialized on startup, fixing false isLogin()=false causing "need login for next page" errors Co-Authored-By: Claude Sonnet 4.6 --- .../AllFirstPageSearchResultPageParser.ts | 22 +++++-------------- .../OtherAllPageSearchResultPageParser.ts | 9 +++----- .../searcher/AllPageSearchPageFetcher.ts | 4 ++-- src/org/wanxp/main.ts | 1 + 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/org/wanxp/douban/data/search/parser/AllFirstPageSearchResultPageParser.ts b/src/org/wanxp/douban/data/search/parser/AllFirstPageSearchResultPageParser.ts index 2d9fd6e..2695130 100644 --- a/src/org/wanxp/douban/data/search/parser/AllFirstPageSearchResultPageParser.ts +++ b/src/org/wanxp/douban/data/search/parser/AllFirstPageSearchResultPageParser.ts @@ -3,8 +3,7 @@ import { } from "../../../../constant/Constsant"; import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface"; import {SearchPage} from "../../model/SearchPage"; -import SearchParserHandlerV2 from "../SearchParserV2"; -import StringUtil from "../../../../utils/StringUtil"; +import SearchParserHandler from "../SearchParser"; import {log} from "../../../../utils/Logutil"; export class AllFirstPageSearchResultPageParser implements SearchResultPageParserInterface { @@ -12,22 +11,11 @@ export class AllFirstPageSearchResultPageParser implements SearchResultPageParse return pageNum == 1 && type == SupportType.all; } parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage { - if (!source || StringUtil.notJsonString(source)) { - //TODO 国际化 - log.notice("Obsidian-Douban:查询结果为空,无匹配结果,请尝试登录获取获取更多数据(已登录则忽略)"); - return SearchPage.empty(type); + log.debug("解析给多页面结果"); + if (!source) { + return new SearchPage(0, 0, 0, type, []); } - - const {subjects} = JSON.parse(source); - if (!subjects) { - return SearchPage.empty(type); - } - const {items} = subjects; - if (!items ||items.length == 0) { - return SearchPage.empty(type); - } - const doubanSearchResultSubjects = SearchParserHandlerV2.itemMapToSearchResult(items); - return new SearchPage(2000, pageNum, pageSize, type, doubanSearchResultSubjects); + return SearchParserHandler.parseSearchJson(source, type, pageNum); } diff --git a/src/org/wanxp/douban/data/search/parser/OtherAllPageSearchResultPageParser.ts b/src/org/wanxp/douban/data/search/parser/OtherAllPageSearchResultPageParser.ts index 4a6970a..1c276e6 100644 --- a/src/org/wanxp/douban/data/search/parser/OtherAllPageSearchResultPageParser.ts +++ b/src/org/wanxp/douban/data/search/parser/OtherAllPageSearchResultPageParser.ts @@ -2,7 +2,7 @@ import {SupportType} from "../../../../constant/Constsant"; import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface"; import {log} from "../../../../utils/Logutil"; import {SearchPage} from "../../model/SearchPage"; -import SearchParserHandlerV2 from "../SearchParserV2"; +import SearchParserHandler from "../SearchParser"; export class OtherAllPageSearchResultPageParser implements SearchResultPageParserInterface { support(type:SupportType, pageNum:number):boolean { @@ -10,13 +10,10 @@ export class OtherAllPageSearchResultPageParser implements SearchResultPageParse } parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage { log.debug("解析给多页面结果"); - const {contents} = JSON.parse(source); - if (!contents) { + if (!source) { return new SearchPage(0, 0, 0, type, []); } - const data:{total:number, start:number, count:number, items:any[]} = contents; - const doubanSearchResultSubjects = SearchParserHandlerV2.itemMapToSearchResult(data.items); - return new SearchPage(data.total, pageNum, pageSize, type, doubanSearchResultSubjects); + return SearchParserHandler.parseSearchJson(source, type, pageNum); } } diff --git a/src/org/wanxp/douban/data/search/searcher/AllPageSearchPageFetcher.ts b/src/org/wanxp/douban/data/search/searcher/AllPageSearchPageFetcher.ts index 66dfe89..816cb66 100644 --- a/src/org/wanxp/douban/data/search/searcher/AllPageSearchPageFetcher.ts +++ b/src/org/wanxp/douban/data/search/searcher/AllPageSearchPageFetcher.ts @@ -2,8 +2,8 @@ import {AbstractSearchPageFetcher} from "./AbstractSearchPageFetcher"; import { SupportType } from "src/org/wanxp/constant/Constsant"; export class AllPageSearchPageFetcher extends AbstractSearchPageFetcher { - getUrl(keyword: string, pageNum: number, pageSize: number): string { - return `https://m.douban.com/rexxar/api/v2/search?q=${keyword}&start=${pageNum}&count=${pageSize}`; + getUrl(keyword: string, start: number, pageSize: number): string { + return `https://www.douban.com/j/search?q=${keyword}&start=${start}`; } support(type: SupportType): boolean { return type == SupportType.all; diff --git a/src/org/wanxp/main.ts b/src/org/wanxp/main.ts index 3f8e9bf..33ee153 100644 --- a/src/org/wanxp/main.ts +++ b/src/org/wanxp/main.ts @@ -285,6 +285,7 @@ export default class DoubanPlugin extends Plugin { this.settingsManager = new SettingsManager(this.app, this); // this.fetchOnlineData(this.settingsManager); this.userComponent = new UserComponent(this.settingsManager); + await this.userComponent.login(); this.netFileHandler = new NetFileHandler(this.fileHandler); this.settingTab = new DoubanSettingTab(this.app, this); From 7ba1a7be0c884a813e00ac6e374cdf8ee18bf021 Mon Sep 17 00:00:00 2001 From: YuBai Date: Thu, 5 Mar 2026 20:38:48 +0800 Subject: [PATCH 5/5] feat: implement lazy login to eliminate startup network request Replace eager login verification on startup with assumeLoggedIn(), which sets login state from saved credentials without any network call. Real verification is deferred until sync (which needs the actual user ID) or when the settings page is opened. - UserComponent: add verified flag, assumeLoggedIn(), isVerified() - main.ts: replace onLayoutReady(login) with assumeLoggedIn(); fix inverted condition bug in checkLogin() (!needLogin -> needLogin) - DoubanSyncModal: remove redundant login check before checkLogin() - LoginSettingsHelper: handle assumed-but-unverified state in constructLoginUI() Co-Authored-By: Claude Sonnet 4.6 --- .../wanxp/douban/component/DoubanSyncModal.ts | 3 --- .../douban/setting/LoginSettingsHelper.ts | 22 +++++++++---------- src/org/wanxp/douban/user/UserComponent.ts | 20 +++++++++++++++++ src/org/wanxp/main.ts | 15 ++++++++----- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/org/wanxp/douban/component/DoubanSyncModal.ts b/src/org/wanxp/douban/component/DoubanSyncModal.ts index 16f00a7..b58fe94 100644 --- a/src/org/wanxp/douban/component/DoubanSyncModal.ts +++ b/src/org/wanxp/douban/component/DoubanSyncModal.ts @@ -169,9 +169,6 @@ ${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; } diff --git a/src/org/wanxp/douban/setting/LoginSettingsHelper.ts b/src/org/wanxp/douban/setting/LoginSettingsHelper.ts index c1a3405..768fa54 100644 --- a/src/org/wanxp/douban/setting/LoginSettingsHelper.ts +++ b/src/org/wanxp/douban/setting/LoginSettingsHelper.ts @@ -10,17 +10,17 @@ export function constructLoginUI(containerEl: HTMLElement, manager: SettingsMana // containerEl.createEl('h3', { text: i18nHelper.getMessage('1210') }); const userComponent = manager.plugin.userComponent; - if (userComponent.needLogin()) { - try { - userComponent.login() - .then(() => { - constructDoubanLoginSettingsUI(containerEl, manager); - }); - }catch (e) { - log.debug(i18nHelper.getMessage('100101')); - constructDoubanLoginSettingsUI(containerEl, manager); - } - }else { + if (userComponent.isLogin() && !userComponent.isVerified()) { + // Assumed login — verify to get user ID/name for settings display + userComponent.login() + .then(() => constructDoubanLoginSettingsUI(containerEl, manager)) + .catch(() => constructDoubanLoginSettingsUI(containerEl, manager)); + } else if (userComponent.needLogin()) { + // Has credentials but not yet logged in + userComponent.login() + .then(() => constructDoubanLoginSettingsUI(containerEl, manager)) + .catch(() => constructDoubanLoginSettingsUI(containerEl, manager)); + } else { constructDoubanLoginSettingsUI(containerEl, manager); } diff --git a/src/org/wanxp/douban/user/UserComponent.ts b/src/org/wanxp/douban/user/UserComponent.ts index 236f124..a79e6bb 100644 --- a/src/org/wanxp/douban/user/UserComponent.ts +++ b/src/org/wanxp/douban/user/UserComponent.ts @@ -15,6 +15,7 @@ import {DoubanHttpUtil} from "../../utils/DoubanHttpUtil"; export default class UserComponent { private settingsManager: SettingsManager; private user: User; + private verified: boolean = false; constructor(settingsManager: SettingsManager) { this.settingsManager = settingsManager; @@ -39,11 +40,26 @@ export default class UserComponent { this.user.login = false; } this.user = null; + this.verified = false; this.settingsManager.updateSetting('loginCookiesContent', ''); this.settingsManager.updateSetting('loginHeadersContent', ''); } + assumeLoggedIn(): void { + const headers: any = this.settingsManager.getSetting('loginHeadersContent'); + const cookies: any = this.settingsManager.getSetting('loginCookiesContent'); + if (headers || cookies) { + this.user = new User(); + this.user.login = true; + this.verified = false; + } + } + + isVerified(): boolean { + return this.verified; + } + needLogin() { @@ -68,6 +84,7 @@ export default class UserComponent { this.settingsManager.debug(`配置界面:loginCookie:豆瓣headers信息正常,${user&&user.id?'获取用户信息成功id:'+ StringUtil.confuse(user.id) + ',用户名:'+ StringUtil.confuse(user.name) :'获取用户信息失败'}`); }); if(this.user) { + this.verified = true; this.settingsManager.updateSetting('loginHeadersContent', JSON.stringify(headers)); } return this.user; @@ -139,6 +156,9 @@ export default class UserComponent { this.user = user; this.settingsManager.debug(`主界面:loginByCookie:豆瓣cookies信息正常,${user&&user.id?'获取用户信息成功id:'+ StringUtil.confuse(user.id) + ',用户名:'+ StringUtil.confuse(user.name) :'获取用户信息失败'}`); }); + if (this.user && this.user.id) { + this.verified = true; + } return this.user; } } diff --git a/src/org/wanxp/main.ts b/src/org/wanxp/main.ts index 33ee153..88565b7 100644 --- a/src/org/wanxp/main.ts +++ b/src/org/wanxp/main.ts @@ -285,8 +285,8 @@ export default class DoubanPlugin extends Plugin { this.settingsManager = new SettingsManager(this.app, this); // this.fetchOnlineData(this.settingsManager); this.userComponent = new UserComponent(this.settingsManager); - await this.userComponent.login(); this.netFileHandler = new NetFileHandler(this.fileHandler); + this.userComponent.assumeLoggedIn(); this.settingTab = new DoubanSettingTab(this.app, this); this.addSettingTab(this.settingTab); @@ -356,11 +356,16 @@ export default class DoubanPlugin extends Plugin { async checkLogin(context: HandleContext):Promise { this.settingsManager.debug('主界面:同步时的登录状态检测'); - if (!context.userComponent.needLogin()) { - this.settingsManager.debug('主界面:同步时的登录状态检测完成: 无用户信息, 尝试获取用户信息'); - await context.userComponent.login(); + const uc = context.userComponent; + // If assumed-logged-in but not verified, verify now (sync needs real user ID) + if (uc.isLogin() && !uc.isVerified()) { + await uc.login(); } - if (!context.userComponent.isLogin()) { + // If has saved credentials but not logged in, try login + if (uc.needLogin()) { + await uc.login(); + } + if (!uc.isLogin()) { this.settingsManager.debug('主界面:同步时的登录状态检测完成: 尝试获取用户信息失败'); new Notice(i18nHelper.getMessage('140303')); return false;