add search douban subject

This commit is contained in:
hughwan 2022-06-02 23:54:36 +08:00
parent c3fb407215
commit 143c004c6e
14 changed files with 276 additions and 52 deletions

@ -6,14 +6,6 @@ interface DoubanPluginSettings {
searchHeaders?:string
}
export interface DoubanExtract {
id: string,
type: string;
title: string;
desc: string;
url: string;
}
export const doubanHeadrs = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",

@ -0,0 +1,28 @@
import DoubanPlugin from "main";
import { App } from "obsidian";
import DoubanMovieLoadHandler from "./handler/DoubanMovieLoadHandler";
import DoubanSubjectLoadHandler from "./handler/DoubanSubjectLoadHandler";
import DoubanSubject from "./model/DoubanSubject";
export class DoubanEtractHandler {
private _app:App;
private _doubanPlugin:DoubanPlugin;
private _doubanSubjectHandlers:DoubanSubjectLoadHandler<DoubanSubject>[];
public DoubanEtractHandler(app:App, doubanPlugin:DoubanPlugin) {
this._app = app;
this._doubanPlugin = doubanPlugin;
this._doubanSubjectHandlers = [new DoubanMovieLoadHandler(), ]
}
public getSubjectTextById(searchExtract:DoubanSubject):string | undefined{
if(!searchExtract) {
return;
}
}
}

@ -0,0 +1,24 @@
import { DoubanPluginSettings } from "douban/Douban";
import DoubanSubject from "douban/model/DoubanSubject";
import DoubanSubjectLoadHandler from "./DoubanSubjectLoadHandler";
export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject> implements DoubanSubjectLoadHandler<T> {
doubanSettings:DoubanPluginSettings;
DoubanAbstractLoadHandler(doubanSettings:DoubanPluginSettings) {
this.doubanSettings = doubanSettings;
}
abstract getSubject(url:string): T;
abstract getTextResult(url:string): string;
abstract getType(): string;
support(extract: DoubanSubject): boolean {
return extract && (this.getType() == extract.type);
}
}

@ -0,0 +1,67 @@
import DoubanMovieSubject from "douban/model/DoubanMoveSubject";
import { get, readStream } from "tiny-network";
import { log } from "utils/logutil";
import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler";
import cheerio, { CheerioAPI } from 'cheerio';
export default class DoubanMovieLoadHandler extends DoubanAbstractLoadHandler<DoubanMovieSubject> {
getSubject(url:string): DoubanMovieSubject {
return this.fetchFromDouban(url);
}
getTextResult(url:string): string {
throw new Error("Method not implemented.");
}
getType(): string {
throw new Error("Method not implemented.");
}
fetchFromDouban(url:string):DoubanMovieSubject {
const reuslt = await this.fetchFromDoubanWeb(url);
return reuslt;
}
fetchFromDoubanWeb(url:string):Promise<DoubanMovieSubject> {
return Promise
.resolve()
.then(() => get(url, JSON.parse(this.doubanSettings.searchHeaders)))
.then(readStream)
.then(log.info)
.then(cheerio.load)
.then(this.parseMovieSubjectFromHtml);
}
parseMovieSubjectFromHtml(responseHtml:CheerioAPI):DoubanMovieSubject {
return responseHtml('.result')
.get()
.map((i:any) => {
const item = responseHtml(i);
var idPattern = /(\d){5,10}/g;
var urlPattern = /(https%3A%2F%2F)\S+(\d){5,10}/g;
var linkValue = item.find("div.content > div > h3 > a").text();
var ececResult = idPattern.exec(linkValue);
var urlResult = urlPattern.exec(linkValue);
var cast = item.find(".subject-cast").text();
const result:DoubanMovieSubject = {
id: ececResult?ececResult[0]:'',
title: item.find("div.content > div > h3 > a").text(),
score: item.find(".rating_nums").text(),
// duration: item.attr('data-duration'),
// region: item.attr('data-region'),
// director: item.attr('data-director'),
// actors: item.attr('data-actors'),
// poster: item.find('.poster img').attr('src'),
cast: cast,
type: item.find("div.content > div > h3 > span").text(),
desc: item.find("div.content > p").text(),
url: urlResult?decodeURIComponent(urlResult[0]):'https://www.douban.com',
};
return result;
})[0];
}
}

@ -0,0 +1,17 @@
import DoubanSubject from "douban/model/DoubanSubject";
import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler";
export default class DoubanOtherLoadHandler extends DoubanAbstractLoadHandler<DoubanSubject> {
getSubject(): DoubanSubject {
throw new Error("Method not implemented.");
}
getTextResult(): string {
throw new Error("Method not implemented.");
}
getType(): string {
return undefined;
}
}

@ -0,0 +1,13 @@
import DoubanSubject from "douban/model/DoubanSubject";
export default interface DoubanSubjectLoadHandler<T extends DoubanSubject> {
getType():string;
support(extract:DoubanSubject):boolean;
getSubject(url:string):T;
getTextResult(url:string):string;
}

@ -0,0 +1,6 @@
import DoubanSubject from "./DoubanSubject";
export default class DoubanMovieSubject extends DoubanSubject {
cast:string;
score:string;
}

@ -0,0 +1,6 @@
import DoubanSubject from "./DoubanSubject";
export default class DoubanSearchResultSubject extends DoubanSubject {
score:string;
cast:string;
}

@ -0,0 +1,7 @@
export default class DoubanExtract {
id: string;
type: string;
title: string;
desc: string;
url: string;
}

@ -18,6 +18,21 @@ class DoubanFuzzySuggester extends FuzzySuggestModal<DoubanSearchResultExtract>
this.app = app;
this.plugin = plugin;
this.setPlaceholder("Choose an item...");
// this.inputEl.addEventListener("keydown", (event) => {
// if (event.key === "Enter") {
// log.info("enter")
// this.reloadSearch();
// }
// })
}
async reloadSearch() {
if(this.inputEl.value) {
log.info("reload search")
this.doubanSearchResultExtract = await this.plugin.getDoubanSearchList(this.inputEl.value);
}
}
getItems(): DoubanSearchResultExtract[] {
@ -25,12 +40,12 @@ class DoubanFuzzySuggester extends FuzzySuggestModal<DoubanSearchResultExtract>
}
getItemText(item: DoubanSearchResultExtract): string {
let text:string = item.type + ":" + item.title + " [score]:" + item.score + ",[cast]:" + item.cast;
let text:string = item.type + "/" + item.score + "/" + item.title + "/" + item.cast;
return text;
}
onChooseItem(item: DoubanSearchResultExtract, evt: MouseEvent | KeyboardEvent): void {
log.warn("choose item " + item.title + " id " + item.id);
this.plugin.geDoubanMovieTextForSearchTerm
}
public showSearchList(doubanSearchResultExtractList:DoubanSearchResultExtract[]) {

@ -0,0 +1,69 @@
import { App, Editor, Modal, TextComponent } from "obsidian";
import { log } from "utils/logutil";
import DoubanPlugin from "../../main";
export class DoubanSearchModal extends Modal {
searchTerm: string;
plugin: DoubanPlugin;
editor: Editor;
constructor(app: App, plugin: DoubanPlugin, editor: Editor) {
super(app);
this.plugin = plugin;
this.editor = editor;
}
onOpen() {
let { contentEl } = this;
contentEl.createEl("h2", { text: "Enter Search Term:" });
const inputs = contentEl.createDiv("inputs");
const searchInput = new TextComponent(inputs).onChange((searchTerm) => {
this.searchTerm = searchTerm;
});
searchInput.inputEl.focus();
searchInput.inputEl.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
this.search();
}
});
const controls = contentEl.createDiv("controls");
const searchButton = controls.createEl("button", {
text: "Search",
cls: "mod-cta",
attr: {
autofocus: true,
},
});
searchButton.addEventListener("click", this.close.bind(this));
const cancelButton = controls.createEl("button", { text: "Cancel" });
cancelButton.addEventListener("click", this.close.bind(this));
}
async search() {
log.info("start search :" + this.searchTerm);
let { contentEl } = this;
contentEl.empty();
if (this.searchTerm) {
this.close();
await this.plugin.search(this.searchTerm);
// await this.plugin.pasteIntoEditor(this.editor, null);
}
}
async onClose() {
let { contentEl } = this;
contentEl.empty();
if (this.searchTerm) {
// await this.plugin.pasteIntoEditor(this.editor, this.searchTerm);
}
}
}

@ -2,11 +2,12 @@ import cheerio from 'cheerio';
import { doubanHeadrs, DoubanPluginSettings } from 'douban/Douban';
import { get, readStream } from 'tiny-network';
import { ensureStatusCode } from 'douban/ResponseHandle';
import { DoubanSearchResultExtract, SearchParserHandler } from './SearchParser';
import { log } from 'utils/logutil';
import DoubanSearchResultSubject from 'douban/model/DoubanSearchResultSubject';
import SearchParserHandler from './SearchParser';
class Searcher {
static search(searchItem:string, doubanSettings:DoubanPluginSettings):Promise<DoubanSearchResultExtract[]> {
export default class Searcher {
static search(searchItem:string, doubanSettings:DoubanPluginSettings):Promise<DoubanSearchResultSubject[]> {
// getData();
// getData2();
// return Promise.resolve();
@ -20,5 +21,3 @@ class Searcher {
.then(SearchParserHandler.parseSearch);
};
}
export {Searcher}

@ -1,15 +1,8 @@
import { CheerioAPI } from "cheerio";
import { DoubanExtract } from "douban/Douban";
import { type } from "os";
import DoubanSearchResultSubject from "douban/model/DoubanSearchResultSubject";
interface DoubanSearchResultExtract extends DoubanExtract{
cast: string;
score: string;
}
class SearchParserHandler {
static parseSearch(dataHtml:CheerioAPI):DoubanSearchResultExtract[] {
export default class SearchParserHandler {
static parseSearch(dataHtml:CheerioAPI):DoubanSearchResultSubject[] {
return dataHtml('.result')
.get()
.map((i:any) => {
@ -20,7 +13,7 @@ class SearchParserHandler {
var ececResult = idPattern.exec(linkValue);
var urlResult = urlPattern.exec(linkValue);
var cast = item.find(".subject-cast").text();
const result:DoubanSearchResultExtract = {
const result:DoubanSearchResultSubject = {
id: ececResult?ececResult[0]:'',
title: item.find("div.content > div > h3 > a").text(),
score: item.find(".rating_nums").text(),
@ -38,6 +31,3 @@ class SearchParserHandler {
})
};
}
export {SearchParserHandler}
export type {DoubanSearchResultExtract}

35
main.ts

@ -1,4 +1,5 @@
import { DoubanSearchModal } from "douban/DoubanSearchModal";
import { DoubanEtractHandler } from "douban/DoubanExtractHandler";
import { DoubanSearchModal } from "douban/search/DoubanSearchModal";
import { DoubanSettingTab } from "douban/DoubanSettingTab";
import { DoubanFuzzySuggester } from "douban/search/DoubanSearchFuzzySuggestModal";
import { Editor, Notice, Plugin} from "obsidian";
@ -10,6 +11,7 @@ import { DoubanSearchResultExtract } from "./douban/search/SearchParser";
export default class DoubanPlugin extends Plugin {
public settings: DoubanPluginSettings;
public fuzzySuggester: DoubanFuzzySuggester;
public doubanEtractHandler: DoubanEtractHandler;
formatExtractText(extract: DoubanExtract): string {
@ -29,22 +31,6 @@ export default class DoubanPlugin extends Plugin {
log.error(`Could not automatically resolve disambiguation.`);
}
parseSearchList(extract: DoubanSearchResultExtract[]):DoubanSearchResultExtract[] {
// return extract.map(result => {
// return {
// id: result.id,
// type: result.type,
// title: result.title,
// desc: result.desc,
// url: result.url,
// score: result.score,
// cast: result.cast
// }
// })
return extract;
}
async getDoubanSearchList(title: string): Promise<DoubanSearchResultExtract[] | undefined> {
return Searcher.search(title, this.settings);
}
@ -56,10 +42,15 @@ export default class DoubanPlugin extends Plugin {
return null;
}
getAndPasteIntoEditor(extract: DoubanExtract) {
if (!extract) {
this.handleNotFound("Not Found Subject");
return;
}
}
async pasteIntoEditor(editor: Editor, extract: DoubanExtract) {
if (!extract) {
this.handleNotFound("Not Found Subject");
return;
@ -71,11 +62,10 @@ export default class DoubanPlugin extends Plugin {
async search(searchTerm:string) {
log.info("plugin search :" + searchTerm);
const resultListPromise = this.getDoubanSearchList(searchTerm);
resultListPromise.then(log.info);
const resultList = await resultListPromise;
const result = this.parseSearchList(resultList);
log.info("plugin search result:" + JSON.stringify(result));
this.fuzzySuggester.showSearchList(result);
// const result = this.parseSearchList(resultList);
log.info("plugin search result:" + JSON.stringify(resultList));
this.fuzzySuggester.showSearchList(resultList);
}
async getDoubanMovieTextForActiveFile(editor: Editor) {
@ -113,6 +103,7 @@ export default class DoubanPlugin extends Plugin {
this.addSettingTab(new DoubanSettingTab(this.app, this));
this.fuzzySuggester = new DoubanFuzzySuggester(this.app, this);
this.doubanEtractHandler = new DoubanEtractHandler(this.app, this);
}
async loadSettings() {