feature: search next page

This commit is contained in:
wanxp 2023-02-15 22:38:37 +08:00
parent 29db644ac1
commit e19fbc648c
10 changed files with 271 additions and 5 deletions

@ -1,4 +1,5 @@
import {i18nHelper} from "../lang/helper";
import DoubanSearchResultSubject from "../douban/data/model/DoubanSearchResultSubject";
/**
*
@ -162,6 +163,58 @@ export enum SyncItemStatus {
fail= 'fail',
unHandle='unHandle',
}
export enum NavigateType {
previous = "previous",
next = "next",
nextNeedLogin = "nextNeedLogin"
}
export const DoubanSearchResultSubjectPreviousPage:DoubanSearchResultSubject = {
cast: "",
datePublished: undefined,
desc: "",
genre: [],
id: "",
image: "",
publisher: "",
score: 0,
title: i18nHelper.getMessage("150102"),
type: "navigate",
url: NavigateType.previous
}
export const DoubanSearchResultSubjectNextPage:DoubanSearchResultSubject = {
cast: "",
datePublished: undefined,
desc: "",
genre: [],
id: "",
image: "",
publisher: "",
score: 0,
title: i18nHelper.getMessage("150103"),
type: "navigate",
url: NavigateType.next
}
export const DoubanSearchResultSubjectNextPageNeedLogin:DoubanSearchResultSubject = {
cast: "",
datePublished: undefined,
desc: "",
genre: [],
id: "",
image: "",
publisher: "",
score: 0,
title: i18nHelper.getMessage("150104"),
type: "navigate",
url: NavigateType.nextNeedLogin
}
export const SEARCH_ITEM_PAGE_SIZE:number = 20;

@ -8,6 +8,8 @@ import SyncStatusHolder from "../../sync/model/SyncStatusHolder";
import {SyncConfig} from "../../sync/model/SyncConfig";
import DoubanSubject from "./DoubanSubject";
import GlobalStatusHolder from "../../model/GlobalStatusHolder";
import {SearchResultsPage} from "schema-dts";
import {SearchPageInfo} from "./SearchPageInfo";
export default interface HandleContext {
plugin:DoubanPlugin;
@ -21,4 +23,6 @@ export default interface HandleContext {
action:string;
syncConfig?: SyncConfig;
listItem?:DoubanSubject;
searchPage:SearchPageInfo;
}

@ -0,0 +1,17 @@
import {SearchPageInfo} from "./SearchPageInfo";
export class SearchPage extends SearchPageInfo{
private _list:any[];
constructor(total: number, pageNum: number, pageSize: number, hasNext: boolean, list: any[]) {
super(total, pageNum, pageSize, hasNext);
this._list = list;
}
public get list() {
return this._list;
}
}

@ -0,0 +1,58 @@
export class SearchPageInfo {
private _total: number;
private _hasNext: boolean;
private _pageSize: number;
private _pageNum: number;
constructor(total: number, pageNum: number, pageSize: number, hasNext: boolean) {
this._total = total;
this._pageNum = pageNum;
this._pageSize = pageSize;
this._hasNext = hasNext;
}
public nextPage(): SearchPageInfo {
if (!this._hasNext) {
return this;
}
if (((this._pageNum + 1) * this._pageSize) > this.total) {
return this;
}
return new SearchPageInfo(this.total, this._pageNum + 1,
this._pageSize, ((this._pageNum + 2) * this._pageSize) > this.total);
}
public previousPage(): SearchPageInfo {
if (this._pageNum == 0) {
return this;
}
return new SearchPageInfo(this.total, this._pageNum - 1,
this._pageSize, true);
}
public get hasNext() {
return this._hasNext;
}
public get hasPrevious() {
return this._pageNum > 0;
}
public get start() {
return this._pageNum * this._pageSize + 1;
}
public get total() {
return this._total;
}
get pageSize(): number {
return this._pageSize;
}
get pageNum(): number {
return this._pageNum;
}
}

@ -1,10 +1,21 @@
import {FuzzySuggestModal} from "obsidian";
import {FuzzySuggestModal, request, RequestUrlParam} from "obsidian";
import DoubanPlugin from "../../../main";
import DoubanSearchResultSubject from "../model/DoubanSearchResultSubject";
import {log} from "src/org/wanxp/utils/Logutil";
import {i18nHelper} from "../../../lang/helper";
import HandleContext from "../model/HandleContext";
import {init} from "cjs-module-lexer";
import {
DoubanSearchResultSubjectNextPage,
DoubanSearchResultSubjectNextPageNeedLogin, DoubanSearchResultSubjectPreviousPage, NavigateType
} from "../../../constant/Constsant";
import {SearchPageInfo} from "../model/SearchPageInfo";
import {flat} from "builtin-modules";
import User from "../../user/User";
import {load} from "cheerio";
import Searcher from "./Search";
import {SearchPage} from "../model/SearchPage";
export {DoubanFuzzySuggester}
@ -14,11 +25,13 @@ class DoubanFuzzySuggester extends FuzzySuggestModal<DoubanSearchResultSubject>
private plugin: DoubanPlugin;
private doubanSearchResultExtract: DoubanSearchResultSubject[];
private context: HandleContext;
private searchItem:string;
constructor(plugin: DoubanPlugin, context: HandleContext) {
constructor(plugin: DoubanPlugin, context: HandleContext, searchItem:string) {
super(app);
this.plugin = plugin;
this.context = context;
this.searchItem = searchItem;
this.setPlaceholder(i18nHelper.getMessage('150101'));
}
@ -28,11 +41,24 @@ class DoubanFuzzySuggester extends FuzzySuggestModal<DoubanSearchResultSubject>
}
getItemText(item: DoubanSearchResultSubject): string {
if (this.isNavigate(item)) {
return item.title;
}
let text: string = item.type + "/" + (item.score ? item.score : '-') + "/" + item.title + "/" + item.cast;
return text;
}
private isNavigate(item: DoubanSearchResultSubject) {
return item.type == "navigate";
}
onChooseItem(item: DoubanSearchResultSubject, evt: MouseEvent | KeyboardEvent): void {
if(this.isNavigate(item)) {
if (this.handleNavigate(item)) {
this.start();
}
return;
}
this.plugin.showStatus(i18nHelper.getMessage('140204', item.title));
this.context.listItem = item;
if (item) {
@ -41,11 +67,58 @@ class DoubanFuzzySuggester extends FuzzySuggestModal<DoubanSearchResultSubject>
this.plugin.doubanExtractHandler.handle(item, this.context);
}
async handleNavigate(item: DoubanSearchResultSubject):Promise<boolean> {
const {searchPage} = this.context;
let currentPage:SearchPageInfo = searchPage;
let result:boolean = false;
switch (item.type) {
case NavigateType.previous:
currentPage = searchPage.previousPage();
result = true;
break;
case NavigateType.next:
currentPage = searchPage.nextPage();
result = true;
break;
case NavigateType.nextNeedLogin:
log.warn(i18nHelper.getMessage("140304"));
break;
}
const searchPageResult:SearchPage = await Searcher.loadSearchItem(this.searchItem, currentPage.start, this.plugin.settings, this.plugin.settingsManager);
this.updatePageResult(searchPageResult);
this.context.searchPage = new SearchPageInfo(searchPageResult.total, currentPage.pageNum, searchPageResult.pageSize, searchPageResult.hasNext);
return result;
}
private updatePageResult(searchPageResult: SearchPage) {
this.initItems(searchPageResult.list);
}
public showSearchList(doubanSearchResultExtractList: DoubanSearchResultSubject[]) {
this.doubanSearchResultExtract = doubanSearchResultExtractList;
this.initItems(doubanSearchResultExtractList);
this.start();
}
private initItems(doubanSearchResultExtractList: DoubanSearchResultSubject[]) {
let doubanList: DoubanSearchResultSubject[] = doubanSearchResultExtractList;
const {searchPage} = this.context;
if (searchPage.hasNext) {
if (this.plugin.userComponent.isLogin()) {
doubanList.push(DoubanSearchResultSubjectNextPage)
}else {
doubanList.push(DoubanSearchResultSubjectNextPageNeedLogin)
}
}
if (searchPage.hasPrevious) {
doubanList.unshift(DoubanSearchResultSubjectPreviousPage);
}
this.doubanSearchResultExtract = doubanList;
}
public start(): void {
try {
this.open();

@ -8,6 +8,8 @@ import {load} from 'cheerio';
import {DoubanPluginSetting} from "../../setting/model/DoubanPluginSetting";
import {DEFAULT_SETTINGS} from "../../../constant/DefaultSettings";
import SettingsManager from "../../setting/SettingsManager";
import User from "../../user/User";
import {SearchPage} from "../model/SearchPage";
export default class Searcher {
static search(searchItem: string, doubanSettings: DoubanPluginSetting, settingsManager:SettingsManager): Promise<DoubanSearchResultSubject[]> {
@ -40,4 +42,36 @@ export default class Searcher {
;
};
static loadSearchItem(searchItem: string, start:number, doubanSettings: DoubanPluginSetting, settingsManager:SettingsManager): Promise<SearchPage> {
const myHeaders:Record<string, string> = JSON.parse(doubanSettings.searchHeaders);
if (doubanSettings.loginCookiesContent) {
myHeaders.Cookie = doubanSettings.loginCookiesContent
}
log.debug("请求更多页面");
let requestUrlParam: RequestUrlParam = {
url: `https://www.douban.com/j/search?q=${searchItem}&start=${start}&subtype=item`,
method: "GET",
headers: myHeaders,
throw: true
};
return requestUrl(requestUrlParam)
.then(requestUrlResponse => {
if (requestUrlResponse.status == 403) {
throw new Error(i18nHelper.getMessage('130106'));
}
return requestUrlResponse.text;
})
.then(e=>SearchParserHandler.parseSearchJson(e, start))
.catch(e => {
if(e.toString().indexOf('403') > 0) {
throw new Error(i18nHelper.getMessage('130106'));
}else {
throw log.error(i18nHelper.getMessage('130101').replace('{0}', e.toString()), e);
}
});
;
};
}

@ -1,5 +1,8 @@
import {CheerioAPI} from "cheerio";
import {CheerioAPI, load} from "cheerio";
import DoubanSearchResultSubject from "../model/DoubanSearchResultSubject";
import {SearchPage} from "../model/SearchPage";
import {SEARCH_ITEM_PAGE_SIZE} from "../../../constant/Constsant";
import {log} from "../../../utils/Logutil";
export default class SearchParserHandler {
static parseSearch(dataHtml: CheerioAPI): DoubanSearchResultSubject[] {
@ -33,4 +36,16 @@ export default class SearchParserHandler {
return result;
})
};
static parseSearchJson(result: string, start:number): SearchPage {
log.debug("解析给多页面结果");
const data:{total:number, limit:number, more:boolean, items:string[]} = JSON.parse(result);
const list:string[] = data.items;
const resultList:DoubanSearchResultSubject[] = list
.map(e => load(e))
.map(e=>this.parseSearch(e))
.map(e => e? e[0]:null);
return new SearchPage(data.total, start / SEARCH_ITEM_PAGE_SIZE, data.limit, data.more, resultList);
};
}

@ -251,8 +251,13 @@ PS: This file could be delete if you want to.
'140301': `Douban: Syncing[{0}]...`,
'140303': `Douban: User Info Expire, Please login again`,
'140302': `Douban: Sync complete`,
'140304': `Douban: Need Login, Please login in Obsidian-Douban plugin`,
'150101': `Choose an item...`,
'150102': `[Previous Page]...`,
'150103': `[Next Page]...`,
'150104': `[Next Page (Please Login First)]...`,

@ -253,9 +253,13 @@ export default {
'140301': `Douban: 开始同步[{0}]...`,
'140302': `Douban: 同步完成`,
'140303': `Douban: 用户信息已过期,请至插件中重新登录`,
'140304': `Douban: 此功能需要登录, 请先至插件中登录豆瓣`,
'150101': `选择一项内容...`,
'121902': `重置为默认值`,
'150102': `[上一页]...`,
'150103': `[下一页]...`,
'150104': `[下一页(插件中登录开启此功能)]...`,
//content
'200101': ``,

@ -22,6 +22,8 @@ import {DoubanSyncModal} from "./douban/component/DoubanSyncModal";
import SyncHandler from "./douban/sync/handler/SyncHandler";
import {SyncConfig} from "./douban/sync/model/SyncConfig";
import GlobalStatusHolder from "./douban/model/GlobalStatusHolder";
import DoubanSearchResultSubject from "./douban/data/model/DoubanSearchResultSubject";
import {SearchPageInfo} from "./douban/data/model/SearchPageInfo";
export default class DoubanPlugin extends Plugin {
public settings: DoubanPluginSetting;
@ -111,8 +113,9 @@ export default class DoubanPlugin extends Plugin {
async search(searchTerm: string, context: HandleContext) {
try {
this.showStatus(i18nHelper.getMessage('140201', searchTerm));
const resultList = await Searcher.search(searchTerm, this.settings, context.plugin.settingsManager);
const resultList:DoubanSearchResultSubject[] = await Searcher.search(searchTerm, this.settings, context.plugin.settingsManager);
this.showStatus(i18nHelper.getMessage('140202', resultList.length.toString()));
context.searchPage = new SearchPageInfo(0,0,20, true);
new DoubanFuzzySuggester(this, context).showSearchList(resultList);
} catch (e) {
log.error(i18nHelper.getMessage('140206').replace('{0}', e.message), e);