Compare commits

..

No commits in common. "master" and "2.3.1" have entirely different histories.

22 changed files with 116 additions and 341 deletions

@ -1,7 +1,7 @@
{ {
"id": "obsidian-douban-plugin", "id": "obsidian-douban-plugin",
"name": "Douban", "name": "Douban",
"version": "2.3.2", "version": "2.3.1",
"minAppVersion": "0.12.0", "minAppVersion": "0.12.0",
"description": "This is a plugin that can import movies/books/musics/notes/games info data from Douban for Obsidian .", "description": "This is a plugin that can import movies/books/musics/notes/games info data from Douban for Obsidian .",
"author": "Wanxp", "author": "Wanxp",

@ -1,6 +1,6 @@
{ {
"name": "obsidian-douban-plugin", "name": "obsidian-douban-plugin",
"version": "2.3.2", "version": "2.3.1",
"description": "This is a plugin for Obsidian (https://obsidian.md) that can import data from Douban (https://www.douban.com/).", "description": "This is a plugin for Obsidian (https://obsidian.md) that can import data from Douban (https://www.douban.com/).",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {

@ -33,24 +33,24 @@ export default class DoubanTheaterAiLoadHandler extends DoubanAbstractLoadHandle
"director", "director",
DataValueType.array, DataValueType.array,
extract.director, extract.director,
(extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) extract.director.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("actor", new DataField( variableMap.set("actor", new DataField(
"actor", "actor",
DataValueType.array, DataValueType.array,
extract.actor, extract.actor,
(extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) extract.actor.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("author", new DataField( variableMap.set("author", new DataField(
"author", "author",
DataValueType.array, DataValueType.array,
extract.author, 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, variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases,
(extract.aliases || []).map(a=>a extract.aliases.map(a=>a
.trim() .trim()
// .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_')
// //replase multiple _ to single _ // //replase multiple _ to single _

@ -169,6 +169,9 @@ ${syncStatus.getHandle() == 0? '...' : i18nHelper.getMessage('110042') + ':' + T
const syncButton = new ButtonComponent(controls) const syncButton = new ButtonComponent(controls)
.setButtonText(i18nHelper.getMessage('110007')) .setButtonText(i18nHelper.getMessage('110007'))
.onClick(async () => { .onClick(async () => {
if (!this.plugin.userComponent.isLogin()) {
await this.plugin.userComponent.login();
}
if(!await this.plugin.checkLogin(this.context)) { if(!await this.plugin.checkLogin(this.context)) {
return; return;
} }

@ -142,7 +142,7 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
}else { }else {
context.syncStatusHolder?context.syncStatusHolder.syncStatus.handled(1):null; context.syncStatusHolder?context.syncStatusHolder.syncStatus.handled(1):null;
} }
return undefined; return e;
}); });
@ -543,20 +543,15 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
} }
fileName = this.parsePartPath(fileName, extract, context, variableMap) fileName = this.parsePartPath(fileName, extract, context, variableMap)
fileName = fileName + fileNameSuffix; fileName = fileName + fileNameSuffix;
const imageReferer = (extract.id ? this.getSubjectUrl(extract.id) : '') || extract.url; // const referHeaders = {'referer': image};
const referHeaders = HttpUtil.buildImageRequestHeaders( const referHeaders = context.settings.loginHeadersContent ? JSON.parse(context.settings.loginHeadersContent) : {};
context.plugin.settingsManager.getHeaders() as Record<string, any>,
imageReferer
);
if ((syncConfig ? syncConfig.cacheHighQuantityImage : context.settings.cacheHighQuantityImage) && context.userComponent.isLogin()) { if ((syncConfig ? syncConfig.cacheHighQuantityImage : context.settings.cacheHighQuantityImage) && context.userComponent.isLogin()) {
try { try {
const highImageFilename = this.getImageFilename(image); const fileNameSpilt = fileName.split('.');
const highImage = this.getHighQuantityImageUrl(highImageFilename); const highFilename = fileNameSpilt.first() + '.jpg';
const highImageHeaders = HttpUtil.buildImageRequestHeaders(
context.plugin.settingsManager.getHeaders() as Record<string, any>, const highImage = this.getHighQuantityImageUrl(highFilename);
imageReferer const resultValue = await this.handleImage(highImage, folder, highFilename, context, false, referHeaders);
);
const resultValue = await this.handleImage(highImage, folder, fileName, context, false, highImageHeaders);
if (resultValue && resultValue.success) { if (resultValue && resultValue.success) {
extract.image = resultValue.filepath; extract.image = resultValue.filepath;
this.initImageVariableMap(extract, context, variableMap); this.initImageVariableMap(extract, context, variableMap);
@ -574,14 +569,6 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
} }
} }
private getImageFilename(image: string): string {
if (!image) {
return '';
}
const imageUrl = image.split('?').first() || image;
return imageUrl.substring(imageUrl.lastIndexOf('/') + 1);
}
private initImageVariableMap(extract: T, context: HandleContext, variableMap : Map<string, DataField>) { private initImageVariableMap(extract: T, context: HandleContext, variableMap : Map<string, DataField>) {
variableMap.set(DoubanParameterName.IMAGE_URL, new DataField( variableMap.set(DoubanParameterName.IMAGE_URL, new DataField(
DoubanParameterName.IMAGE_URL, DoubanParameterName.IMAGE_URL,
@ -621,20 +608,12 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
handlePersonNameByMeta(html: CheerioAPI, movie: DoubanSubject, context: HandleContext, handlePersonNameByMeta(html: CheerioAPI, movie: DoubanSubject, context: HandleContext,
metaProperty:string, objectProperty:string) { metaProperty:string, objectProperty:string) {
if (!movie) {
return;
}
const metaProperties: string[] = html(`head > meta[property='${metaProperty}']`).get() const metaProperties: string[] = html(`head > meta[property='${metaProperty}']`).get()
.map((e) => { .map((e) => {
return html(e).attr('content'); return html(e).attr('content');
}); });
// @ts-ignore // @ts-ignore
const currentArray = movie[objectProperty]; movie[objectProperty]
if (!Array.isArray(currentArray)) {
return;
}
// @ts-ignore
currentArray
// @ts-ignore // @ts-ignore
.filter((p:Person) => p.name) .filter((p:Person) => p.name)
// @ts-ignore // @ts-ignore

@ -30,9 +30,9 @@ export default class DoubanBookLoadHandler extends DoubanAbstractLoadHandler<Dou
parseVariable(beforeContent: string, variableMap:Map<string, DataField>, extract: DoubanBookSubject, context: HandleContext): void { parseVariable(beforeContent: string, variableMap:Map<string, DataField>, extract: DoubanBookSubject, context: HandleContext): void {
variableMap.set(DoubanBookParameter.author, new DataField(DoubanBookParameter.author, 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, 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 { support(extract: DoubanSubject): boolean {

@ -31,7 +31,7 @@ export default class DoubanGameLoadHandler extends DoubanAbstractLoadHandler<Dou
parseVariable(beforeContent: string, variableMap:Map<string, DataField>, extract: DoubanGameSubject, context: HandleContext): void { parseVariable(beforeContent: string, variableMap:Map<string, DataField>, extract: DoubanGameSubject, context: HandleContext): void {
// super.parseAliases(beforeContent, variableMap, extract, context); // super.parseAliases(beforeContent, variableMap, extract, context);
variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases, variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases,
(extract.aliases || []).map(a=>a extract.aliases.map(a=>a
.trim() .trim()
// .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_')
// //replase multiple _ to single _ // //replase multiple _ to single _

@ -35,24 +35,24 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
"director", "director",
DataValueType.array, DataValueType.array,
extract.director, extract.director,
(extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) extract.director.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("actor", new DataField( variableMap.set("actor", new DataField(
"actor", "actor",
DataValueType.array, DataValueType.array,
extract.actor, extract.actor,
(extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) extract.actor.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("author", new DataField( variableMap.set("author", new DataField(
"author", "author",
DataValueType.array, DataValueType.array,
extract.author, 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, variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases,
(extract.aliases || []).map(a=>a extract.aliases.map(a=>a
.trim() .trim()
// .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_')
// //replase multiple _ to single _ // //replase multiple _ to single _
@ -98,7 +98,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
} }
parseSubjectFromHtml(html: CheerioAPI, context: HandleContext): DoubanMovieSubject { parseSubjectFromHtml(html: CheerioAPI, context: HandleContext): DoubanMovieSubject {
let movie: DoubanMovieSubject | undefined = html('script') const movie:DoubanMovieSubject = html('script')
.get() .get()
.filter(scd => "application/ld+json" == html(scd).attr("type")) .filter(scd => "application/ld+json" == html(scd).attr("type"))
.map(i => { .map(i => {
@ -108,7 +108,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
const idPattern = /(\d){5,10}/g; const idPattern = /(\d){5,10}/g;
const id = idPattern.exec(obj.url); const id = idPattern.exec(obj.url);
const name = obj.name; const name = obj.name;
const title = super.getTitleNameByMode(name, PersonNameMode.CH_NAME, context) ?? name; const title = super.getTitleNameByMode(name, PersonNameMode.CH_NAME, context)??name;
const originalTitle = super.getTitleNameByMode(name, PersonNameMode.EN_NAME, context) ?? name; const originalTitle = super.getTitleNameByMode(name, PersonNameMode.EN_NAME, context) ?? name;
const result: DoubanMovieSubject = { const result: DoubanMovieSubject = {
@ -119,14 +119,14 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
originalTitle: originalTitle, originalTitle: originalTitle,
desc: obj.description, desc: obj.description,
url: "https://movie.douban.com" + obj.url, url: "https://movie.douban.com" + obj.url,
director: obj.director || [], director: obj.director,
author: obj.author || [], author: obj.author,
actor: obj.actor || [], actor: obj.actor,
aggregateRating: obj.aggregateRating, aggregateRating: obj.aggregateRating,
datePublished: obj.datePublished ? new Date(obj.datePublished) : undefined, datePublished: obj.datePublished ? new Date(obj.datePublished) : undefined,
image: obj.image, image: obj.image,
imageUrl: obj.image, imageUrl: obj.image,
genre: obj.genre || [], genre: obj.genre,
publisher: '', publisher: '',
aliases: [""], aliases: [""],
language: [""], language: [""],
@ -136,52 +136,10 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
} }
return result; return result;
})[0]; })[0];
// Fallback: if JSON-LD parsing failed (e.g., anti-bot page), extract from meta tags
if (!movie) {
const title = html(html("head > 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:actor', 'actor');
this.handlePersonNameByMeta(html, movie, context, 'video:director', 'director'); this.handlePersonNameByMeta(html, movie, context, 'video:director', 'director');
const desc: string = html("span[property='v:summary']").text(); const desc:string = html("span[property='v:summary']").text();
if (desc) { if (desc) {
movie.desc = desc; movie.desc = desc;
} }
@ -198,7 +156,7 @@ export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<Do
// value = html(info.next.next).text().trim(); // value = html(info.next.next).text().trim();
const vas = html(info.next).text().trim(); const vas = html(info.next).text().trim();
value = vas.split("/").map((v) => v.trim()); value = vas.split("/").map((v) => v.trim());
} else if (key.indexOf('片长') >= 0) { } else if(key.indexOf('片长') >= 0) {
value = html(info.next.next).text().trim() value = html(info.next.next).text().trim()
} else { } else {
value = html(info.next).text().trim(); value = html(info.next).text().trim();

@ -25,22 +25,22 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler<DoubanT
} }
parseVariable(beforeContent: string, variableMap:Map<string, DataField>, extract: DoubanTeleplaySubject, context: HandleContext): void { parseVariable(beforeContent: string, variableMap:Map<string, DataField>, 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( variableMap.set("actor", new DataField(
"actor", "actor",
DataValueType.array, DataValueType.array,
extract.actor, extract.actor,
(extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) extract.actor.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("author", new DataField( variableMap.set("author", new DataField(
"author", "author",
DataValueType.array, DataValueType.array,
extract.author, 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, variableMap.set("aliases", new DataField("aliases", DataValueType.array, extract.aliases,
(extract.aliases || []).map(a=>a extract.aliases.map(a=>a
.trim() .trim()
// .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') // .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_')
// //replase multiple _ to single _ // //replase multiple _ to single _
@ -84,7 +84,7 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler<DoubanT
} }
parseSubjectFromHtml(html: CheerioAPI, context: HandleContext): DoubanTeleplaySubject { parseSubjectFromHtml(html: CheerioAPI, context: HandleContext): DoubanTeleplaySubject {
let teleplay: DoubanTeleplaySubject | undefined = html('script') const teleplay:DoubanTeleplaySubject = html('script')
.get() .get()
.filter(scd => "application/ld+json" == html(scd).attr("type")) .filter(scd => "application/ld+json" == html(scd).attr("type"))
.map(i => { .map(i => {
@ -104,14 +104,14 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler<DoubanT
originalTitle: originalTitle, originalTitle: originalTitle,
desc: obj.description, desc: obj.description,
url: "https://movie.douban.com" + obj.url, url: "https://movie.douban.com" + obj.url,
director: obj.director || [], director: obj.director,
author: obj.author || [], author: obj.author,
actor: obj.actor || [], actor: obj.actor,
aggregateRating: obj.aggregateRating, aggregateRating: obj.aggregateRating,
datePublished: obj.datePublished ? new Date(obj.datePublished) : undefined, datePublished: obj.datePublished ? new Date(obj.datePublished) : undefined,
image: obj.image, image: obj.image,
imageUrl: obj.image, imageUrl: obj.image,
genre: obj.genre || [], genre: obj.genre,
score: obj.aggregateRating ? obj.aggregateRating.ratingValue : undefined, score: obj.aggregateRating ? obj.aggregateRating.ratingValue : undefined,
publisher: "", publisher: "",
aliases: [""], aliases: [""],
@ -124,46 +124,6 @@ export class DoubanTeleplayLoadHandler extends DoubanAbstractLoadHandler<DoubanT
return result; return result;
})[0]; })[0];
// Fallback: if JSON-LD parsing failed, extract from meta tags
if (!teleplay) {
const title = html(html("head > 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:actor', 'actor');
this.handlePersonNameByMeta(html, teleplay, context, 'video:director', 'director'); this.handlePersonNameByMeta(html, teleplay, context, 'video:director', 'director');
const desc:string = html("span[property='v:summary']").text(); const desc:string = html("span[property='v:summary']").text();

@ -33,28 +33,28 @@ export default class DoubanTheaterLoadHandler extends DoubanAbstractLoadHandler<
"director", "director",
DataValueType.array, DataValueType.array,
extract.director, extract.director,
(extract.director || []).map(SchemaOrg.getPersonName).filter(c => c) extract.director.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("actor", new DataField( variableMap.set("actor", new DataField(
"actor", "actor",
DataValueType.array, DataValueType.array,
extract.actor, extract.actor,
(extract.actor || []).map(SchemaOrg.getPersonName).filter(c => c) extract.actor.map(SchemaOrg.getPersonName).filter(c => c)
)); ));
variableMap.set("author", new DataField( variableMap.set("author", new DataField(
"author", "author",
DataValueType.array, DataValueType.array,
extract.author, 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( variableMap.set("aliases", new DataField(
"aliases", "aliases",
DataValueType.array, DataValueType.array,
extract.aliases, extract.aliases,
(extract.aliases || []).map(a => a extract.aliases.map(a => a
.trim() .trim()
.replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_') .replace(TITLE_ALIASES_SPECIAL_CHAR_REG_G, '_')
//replace multiple _ to single _ //replace multiple _ to single _

@ -3,7 +3,8 @@ import {
} from "../../../../constant/Constsant"; } from "../../../../constant/Constsant";
import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface"; import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface";
import {SearchPage} from "../../model/SearchPage"; import {SearchPage} from "../../model/SearchPage";
import SearchParserHandler from "../SearchParser"; import SearchParserHandlerV2 from "../SearchParserV2";
import StringUtil from "../../../../utils/StringUtil";
import {log} from "../../../../utils/Logutil"; import {log} from "../../../../utils/Logutil";
export class AllFirstPageSearchResultPageParser implements SearchResultPageParserInterface { export class AllFirstPageSearchResultPageParser implements SearchResultPageParserInterface {
@ -11,11 +12,22 @@ export class AllFirstPageSearchResultPageParser implements SearchResultPageParse
return pageNum == 1 && type == SupportType.all; return pageNum == 1 && type == SupportType.all;
} }
parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage { parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage {
log.debug("解析给多页面结果"); if (!source || StringUtil.notJsonString(source)) {
if (!source) { //TODO 国际化
return new SearchPage(0, 0, 0, type, []); log.notice("Obsidian-Douban:查询结果为空,无匹配结果,请尝试登录获取获取更多数据(已登录则忽略)");
return SearchPage.empty(type);
} }
return SearchParserHandler.parseSearchJson(source, type, pageNum);
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);
} }

@ -2,7 +2,7 @@ import {SupportType} from "../../../../constant/Constsant";
import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface"; import {SearchResultPageParserInterface} from "./SearchResultPageParserInterface";
import {log} from "../../../../utils/Logutil"; import {log} from "../../../../utils/Logutil";
import {SearchPage} from "../../model/SearchPage"; import {SearchPage} from "../../model/SearchPage";
import SearchParserHandler from "../SearchParser"; import SearchParserHandlerV2 from "../SearchParserV2";
export class OtherAllPageSearchResultPageParser implements SearchResultPageParserInterface { export class OtherAllPageSearchResultPageParser implements SearchResultPageParserInterface {
support(type:SupportType, pageNum:number):boolean { support(type:SupportType, pageNum:number):boolean {
@ -10,10 +10,13 @@ export class OtherAllPageSearchResultPageParser implements SearchResultPageParse
} }
parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage { parse(source:string, type:SupportType, pageNum:number, pageSize:number):SearchPage {
log.debug("解析给多页面结果"); log.debug("解析给多页面结果");
if (!source) { const {contents} = JSON.parse(source);
if (!contents) {
return new SearchPage(0, 0, 0, type, []); return new SearchPage(0, 0, 0, type, []);
} }
return SearchParserHandler.parseSearchJson(source, type, pageNum); 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);
} }
} }

@ -2,8 +2,8 @@ import {AbstractSearchPageFetcher} from "./AbstractSearchPageFetcher";
import { SupportType } from "src/org/wanxp/constant/Constsant"; import { SupportType } from "src/org/wanxp/constant/Constsant";
export class AllPageSearchPageFetcher extends AbstractSearchPageFetcher { export class AllPageSearchPageFetcher extends AbstractSearchPageFetcher {
getUrl(keyword: string, start: number, pageSize: number): string { getUrl(keyword: string, pageNum: number, pageSize: number): string {
return `https://www.douban.com/j/search?q=${keyword}&start=${start}`; return `https://m.douban.com/rexxar/api/v2/search?q=${keyword}&start=${pageNum}&count=${pageSize}`;
} }
support(type: SupportType): boolean { support(type: SupportType): boolean {
return type == SupportType.all; return type == SupportType.all;

@ -10,17 +10,17 @@ export function constructLoginUI(containerEl: HTMLElement, manager: SettingsMana
// containerEl.createEl('h3', { text: i18nHelper.getMessage('1210') }); // containerEl.createEl('h3', { text: i18nHelper.getMessage('1210') });
const userComponent = manager.plugin.userComponent; const userComponent = manager.plugin.userComponent;
if (userComponent.isLogin() && !userComponent.isVerified()) { if (userComponent.needLogin()) {
// Assumed login — verify to get user ID/name for settings display try {
userComponent.login() userComponent.login()
.then(() => constructDoubanLoginSettingsUI(containerEl, manager)) .then(() => {
.catch(() => constructDoubanLoginSettingsUI(containerEl, manager)); constructDoubanLoginSettingsUI(containerEl, manager);
} else if (userComponent.needLogin()) { });
// Has credentials but not yet logged in }catch (e) {
userComponent.login() log.debug(i18nHelper.getMessage('100101'));
.then(() => constructDoubanLoginSettingsUI(containerEl, manager)) constructDoubanLoginSettingsUI(containerEl, manager);
.catch(() => constructDoubanLoginSettingsUI(containerEl, manager)); }
} else { }else {
constructDoubanLoginSettingsUI(containerEl, manager); constructDoubanLoginSettingsUI(containerEl, manager);
} }

@ -15,7 +15,6 @@ import {DoubanHttpUtil} from "../../utils/DoubanHttpUtil";
export default class UserComponent { export default class UserComponent {
private settingsManager: SettingsManager; private settingsManager: SettingsManager;
private user: User; private user: User;
private verified: boolean = false;
constructor(settingsManager: SettingsManager) { constructor(settingsManager: SettingsManager) {
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
@ -40,26 +39,11 @@ export default class UserComponent {
this.user.login = false; this.user.login = false;
} }
this.user = null; this.user = null;
this.verified = false;
this.settingsManager.updateSetting('loginCookiesContent', ''); this.settingsManager.updateSetting('loginCookiesContent', '');
this.settingsManager.updateSetting('loginHeadersContent', ''); 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() { needLogin() {
@ -84,7 +68,6 @@ export default class UserComponent {
this.settingsManager.debug(`配置界面:loginCookie:豆瓣headers信息正常${user&&user.id?'获取用户信息成功id:'+ StringUtil.confuse(user.id) + ',用户名:'+ StringUtil.confuse(user.name) :'获取用户信息失败'}`); this.settingsManager.debug(`配置界面:loginCookie:豆瓣headers信息正常${user&&user.id?'获取用户信息成功id:'+ StringUtil.confuse(user.id) + ',用户名:'+ StringUtil.confuse(user.name) :'获取用户信息失败'}`);
}); });
if(this.user) { if(this.user) {
this.verified = true;
this.settingsManager.updateSetting('loginHeadersContent', JSON.stringify(headers)); this.settingsManager.updateSetting('loginHeadersContent', JSON.stringify(headers));
} }
return this.user; return this.user;
@ -156,9 +139,6 @@ export default class UserComponent {
this.user = user; this.user = user;
this.settingsManager.debug(`主界面:loginByCookie:豆瓣cookies信息正常${user&&user.id?'获取用户信息成功id:'+ StringUtil.confuse(user.id) + ',用户名:'+ StringUtil.confuse(user.name) :'获取用户信息失败'}`); 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; return this.user;
} }
} }

@ -335,8 +335,6 @@ PS: This file could be delete if you want to.
'130105': `Can not use Douban this time, Please try again after 12 hour or 24 hour. Or you can reset your connection `, '130105': `Can not use Douban this time, Please try again after 12 hour or 24 hour. Or you can reset your connection `,
'130106': `Can not use Douban this time, Please try Login In Douban Plugin. If not working please again after 12 hour or 24 hour. Or you can reset your connection `, '130106': `Can not use Douban this time, Please try Login In Douban Plugin. If not working please again after 12 hour or 24 hour. Or you can reset your connection `,
'130404': `404 Url Not Found`, '130404': `404 Url Not Found`,
'130109': `Downloaded image is empty`,
'130110': `Clipboard write failed`,
'130107': `Can not find array setting for {1} in {0} , Please add it in array settings`, '130107': `Can not find array setting for {1} in {0} , Please add it in array settings`,

@ -350,8 +350,6 @@ export default {
'130105': `由于多次频繁请求数据,豆瓣当前暂时不可用. 请于12小时或24小时后再重试或重置你的网络(如重新拨号或更换网络) `, '130105': `由于多次频繁请求数据,豆瓣当前暂时不可用. 请于12小时或24小时后再重试或重置你的网络(如重新拨号或更换网络) `,
'130106': `请尝试在Douban插件中登录后操作. 若还是无效果则尝试于12小时或24小时后再重试或重置你的网络(如重新拨号或更换网络) `, '130106': `请尝试在Douban插件中登录后操作. 若还是无效果则尝试于12小时或24小时后再重试或重置你的网络(如重新拨号或更换网络) `,
'130107': `参数{0}中指定的数组输出类型{1}不存在,请前往配置进行设置`, '130107': `参数{0}中指定的数组输出类型{1}不存在,请前往配置进行设置`,
'130109': `下载图片内容为空`,
'130110': `写入剪贴板失败`,
'130120': `同步时发生错误,但同步将会继续。错误项目是 {}。`, '130120': `同步时发生错误,但同步将会继续。错误项目是 {}。`,
'130121': `总数只有{0}, 选择的开始比总数还要大,将不会同步。`, '130121': `总数只有{0}, 选择的开始比总数还要大,将不会同步。`,

@ -286,7 +286,6 @@ export default class DoubanPlugin extends Plugin {
// this.fetchOnlineData(this.settingsManager); // this.fetchOnlineData(this.settingsManager);
this.userComponent = new UserComponent(this.settingsManager); this.userComponent = new UserComponent(this.settingsManager);
this.netFileHandler = new NetFileHandler(this.fileHandler); this.netFileHandler = new NetFileHandler(this.fileHandler);
this.userComponent.assumeLoggedIn();
this.settingTab = new DoubanSettingTab(this.app, this); this.settingTab = new DoubanSettingTab(this.app, this);
this.addSettingTab(this.settingTab); this.addSettingTab(this.settingTab);
@ -356,16 +355,11 @@ export default class DoubanPlugin extends Plugin {
async checkLogin(context: HandleContext):Promise<boolean> { async checkLogin(context: HandleContext):Promise<boolean> {
this.settingsManager.debug('主界面:同步时的登录状态检测'); this.settingsManager.debug('主界面:同步时的登录状态检测');
const uc = context.userComponent; if (!context.userComponent.needLogin()) {
// If assumed-logged-in but not verified, verify now (sync needs real user ID) this.settingsManager.debug('主界面:同步时的登录状态检测完成: 无用户信息, 尝试获取用户信息');
if (uc.isLogin() && !uc.isVerified()) { await context.userComponent.login();
await uc.login();
} }
// If has saved credentials but not logged in, try login if (!context.userComponent.isLogin()) {
if (uc.needLogin()) {
await uc.login();
}
if (!uc.isLogin()) {
this.settingsManager.debug('主界面:同步时的登录状态检测完成: 尝试获取用户信息失败'); this.settingsManager.debug('主界面:同步时的登录状态检测完成: 尝试获取用户信息失败');
new Notice(i18nHelper.getMessage('140303')); new Notice(i18nHelper.getMessage('140303'));
return false; return false;

@ -25,9 +25,6 @@ export default class NetFileHandler {
if (response.status == 403) { if (response.status == 403) {
throw new Error(i18nHelper.getMessage('130106')); throw new Error(i18nHelper.getMessage('130106'));
} }
if (response.status == 418) {
throw new Error(i18nHelper.getMessage('130105'));
}
return response.textArrayBuffer; return response.textArrayBuffer;
}) })
.then((buffer) => { .then((buffer) => {
@ -59,23 +56,15 @@ export default class NetFileHandler {
async downloadDBUploadPicGoByClipboard(url: string, filename: string, context:HandleContext, showError:boolean, headers?:any): Promise<{ success: boolean, error:string, filepath: string }> { async downloadDBUploadPicGoByClipboard(url: string, filename: string, context:HandleContext, showError:boolean, headers?:any): Promise<{ success: boolean, error:string, filepath: string }> {
return HttpUtil.httpRequestBuffer(url, headers, context.plugin.settingsManager) return HttpUtil.httpRequestBuffer(url, headers, context.plugin.settingsManager)
.then((response) => { .then((response) => {
if (response.status == 404) {
throw new Error(i18nHelper.getMessage('130404'));
}
if (response.status == 403) { if (response.status == 403) {
throw new Error(i18nHelper.getMessage('130106')); throw new Error(i18nHelper.getMessage('130106'));
} }
if (response.status == 418) {
throw new Error(i18nHelper.getMessage('130105'));
}
return response.textArrayBuffer; return response.textArrayBuffer;
}) })
.then(async (buffer) => { .then((buffer) => {
if (!buffer || buffer.byteLength == 0) { ClipboardUtil.writeImage(buffer);
throw new Error(i18nHelper.getMessage('130109')); }).then(() => {
} return this.uploadClipboardFile(context);
await ClipboardUtil.writeImage(buffer);
return await this.uploadClipboardFile(context);
}).then((data) => { }).then((data) => {
if (data.success) { if (data.success) {
return {success: true, error: '', filepath: HttpUtil.extractURLFromString(data.result[0])}; return {success: true, error: '', filepath: HttpUtil.extractURLFromString(data.result[0])};
@ -119,3 +108,4 @@ export default class NetFileHandler {
} }
} }

@ -1,80 +1,14 @@
import {i18nHelper} from "../lang/helper"; export class ClipboardUtil {
export class ClipboardUtil {
public static async writeImage(data:ArrayBuffer, options: ClipboardOptions = defaultClipboardOptions) { public static async writeImage(data:ArrayBuffer, options: ClipboardOptions = defaultClipboardOptions) {
if (!data || data.byteLength == 0) {
throw new Error(i18nHelper.getMessage('130110'));
}
const { clipboard, nativeImage } = require('electron'); const { clipboard, nativeImage } = require('electron');
const isWebp = this.isWebp(data);
let image = nativeImage.createFromBuffer(Buffer.from(data)); await clipboard.writeImage(nativeImage.createFromBuffer(data));
if ((!image || image.isEmpty()) && isWebp) {
image = await this.createNativeImageFromWebp(data);
}
if (!image || image.isEmpty()) {
throw new Error(i18nHelper.getMessage('130110'));
}
clipboard.clear();
clipboard.writeImage(image);
await new Promise(resolve => setTimeout(resolve, 100));
const clipboardImage = clipboard.readImage();
if (!clipboardImage || clipboardImage.isEmpty()) {
throw new Error(i18nHelper.getMessage('130110'));
}
console.log(`Copied to clipboard as HTML`); console.log(`Copied to clipboard as HTML`);
} }
private static isWebp(data: ArrayBuffer): boolean {
const bytes = new Uint8Array(data);
if (bytes.length < 12) {
return false;
}
return bytes[0] === 0x52
&& bytes[1] === 0x49
&& bytes[2] === 0x46
&& bytes[3] === 0x46
&& bytes[8] === 0x57
&& bytes[9] === 0x45
&& bytes[10] === 0x42
&& bytes[11] === 0x50;
}
private static async createNativeImageFromWebp(data: ArrayBuffer) {
const { nativeImage } = require('electron');
const imageElement = await this.loadImage(URL.createObjectURL(new Blob([data], {type: 'image/webp'})));
try {
const canvas = document.createElement('canvas');
canvas.width = imageElement.naturalWidth || imageElement.width;
canvas.height = imageElement.naturalHeight || imageElement.height;
const context = canvas.getContext('2d');
if (!context) {
throw new Error(i18nHelper.getMessage('130110'));
}
context.drawImage(imageElement, 0, 0);
return nativeImage.createFromDataURL(canvas.toDataURL('image/png'));
} finally {
imageElement.src = '';
}
}
private static loadImage(url: string): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => {
URL.revokeObjectURL(url);
resolve(image);
};
image.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error(i18nHelper.getMessage('130110')));
};
image.src = url;
});
}
} }
@ -86,3 +20,4 @@ interface ClipboardOptions {
const defaultClipboardOptions: ClipboardOptions = { const defaultClipboardOptions: ClipboardOptions = {
contentType: 'text/plain', contentType: 'text/plain',
} }

@ -7,19 +7,6 @@ import {HttpResponse} from "./model/HttpResponse";
export default class HttpUtil { export default class HttpUtil {
private static readonly IMAGE_REQUEST_HEADERS_TO_DROP = new Set([
'authority',
'content-length',
'content-type',
'host',
'origin',
'referer',
'sec-fetch-dest',
'sec-fetch-mode',
'sec-fetch-site',
'sec-fetch-user',
]);
/** /**
@ -73,27 +60,6 @@ export default class HttpUtil {
} }
} }
public static buildImageRequestHeaders(headers: Record<string, any> = {}, referer?: string): Record<string, string> {
const nextHeaders: Record<string, string> = {};
Object.entries(headers || {}).forEach(([key, value]) => {
if (value == null || value === '') {
return;
}
const lowerKey = key.toLowerCase();
if (lowerKey === 'referer') {
return;
}
if (this.IMAGE_REQUEST_HEADERS_TO_DROP.has(lowerKey) || lowerKey.startsWith('sec-ch-ua')) {
return;
}
nextHeaders[key] = String(value);
});
if (referer) {
nextHeaders.Referer = referer;
}
return nextHeaders;
}
public static parse(url: string): { protocol: string, host: string, port: string, path: string } { public static parse(url: string): { protocol: string, host: string, port: string, path: string } {
const regex = /^(.*?):\/\/([^\/:]+)(?::(\d+))?([^?]*)$/; const regex = /^(.*?):\/\/([^\/:]+)(?::(\d+))?([^?]*)$/;

@ -61,7 +61,6 @@
"2.2.3": "0.12.0", "2.2.3": "0.12.0",
"2.2.4": "0.12.0", "2.2.4": "0.12.0",
"2.3.0": "0.12.0", "2.3.0": "0.12.0",
"2.3.1": "0.12.0", "2.3.1": "0.12.0"
"2.3.2": "0.12.0"
} }