diff --git a/douban/Douban.ts b/douban/Douban.ts index 9e784fb..a3e2765 100644 --- a/douban/Douban.ts +++ b/douban/Douban.ts @@ -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", diff --git a/douban/DoubanExtractHandler.ts b/douban/DoubanExtractHandler.ts new file mode 100644 index 0000000..e67e047 --- /dev/null +++ b/douban/DoubanExtractHandler.ts @@ -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[]; + + 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; + } + + + } + +} + diff --git a/douban/handler/DoubanAbstractLoadHandler.ts b/douban/handler/DoubanAbstractLoadHandler.ts new file mode 100644 index 0000000..8eb9b47 --- /dev/null +++ b/douban/handler/DoubanAbstractLoadHandler.ts @@ -0,0 +1,24 @@ +import { DoubanPluginSettings } from "douban/Douban"; +import DoubanSubject from "douban/model/DoubanSubject"; +import DoubanSubjectLoadHandler from "./DoubanSubjectLoadHandler"; + +export default abstract class DoubanAbstractLoadHandler implements DoubanSubjectLoadHandler { + + + 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); + } + +} \ No newline at end of file diff --git a/douban/handler/DoubanMovieLoadHandler.ts b/douban/handler/DoubanMovieLoadHandler.ts new file mode 100644 index 0000000..c103f6f --- /dev/null +++ b/douban/handler/DoubanMovieLoadHandler.ts @@ -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 { + + + + 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 { + 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]; + } + + +} \ No newline at end of file diff --git a/douban/handler/DoubanOtherLoadHandler.ts b/douban/handler/DoubanOtherLoadHandler.ts new file mode 100644 index 0000000..cbbbf9d --- /dev/null +++ b/douban/handler/DoubanOtherLoadHandler.ts @@ -0,0 +1,17 @@ +import DoubanSubject from "douban/model/DoubanSubject"; +import DoubanAbstractLoadHandler from "./DoubanAbstractLoadHandler"; + +export default class DoubanOtherLoadHandler extends DoubanAbstractLoadHandler { + getSubject(): DoubanSubject { + throw new Error("Method not implemented."); + } + getTextResult(): string { + throw new Error("Method not implemented."); + } + getType(): string { + return undefined; + } + + + +} \ No newline at end of file diff --git a/douban/handler/DoubanSubjectLoadHandler.ts b/douban/handler/DoubanSubjectLoadHandler.ts new file mode 100644 index 0000000..acb028c --- /dev/null +++ b/douban/handler/DoubanSubjectLoadHandler.ts @@ -0,0 +1,13 @@ +import DoubanSubject from "douban/model/DoubanSubject"; + +export default interface DoubanSubjectLoadHandler { + + getType():string; + + support(extract:DoubanSubject):boolean; + + getSubject(url:string):T; + + getTextResult(url:string):string; + +} \ No newline at end of file diff --git a/douban/model/DoubanMoveSubject.ts b/douban/model/DoubanMoveSubject.ts new file mode 100644 index 0000000..46e2fa9 --- /dev/null +++ b/douban/model/DoubanMoveSubject.ts @@ -0,0 +1,6 @@ +import DoubanSubject from "./DoubanSubject"; + +export default class DoubanMovieSubject extends DoubanSubject { + cast:string; + score:string; +} diff --git a/douban/model/DoubanSearchResultSubject.ts b/douban/model/DoubanSearchResultSubject.ts new file mode 100644 index 0000000..dd31f9b --- /dev/null +++ b/douban/model/DoubanSearchResultSubject.ts @@ -0,0 +1,6 @@ +import DoubanSubject from "./DoubanSubject"; + +export default class DoubanSearchResultSubject extends DoubanSubject { + score:string; + cast:string; +} \ No newline at end of file diff --git a/douban/model/DoubanSubject.ts b/douban/model/DoubanSubject.ts new file mode 100644 index 0000000..9d67dd4 --- /dev/null +++ b/douban/model/DoubanSubject.ts @@ -0,0 +1,7 @@ +export default class DoubanExtract { + id: string; + type: string; + title: string; + desc: string; + url: string; +} diff --git a/douban/search/DoubanSearchFuzzySuggestModal.ts b/douban/search/DoubanSearchFuzzySuggestModal.ts index 7734611..6a812e4 100644 --- a/douban/search/DoubanSearchFuzzySuggestModal.ts +++ b/douban/search/DoubanSearchFuzzySuggestModal.ts @@ -18,6 +18,21 @@ class DoubanFuzzySuggester extends FuzzySuggestModal 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 } 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[]) { diff --git a/douban/search/DoubanSearchModal.ts b/douban/search/DoubanSearchModal.ts new file mode 100644 index 0000000..41a1eb6 --- /dev/null +++ b/douban/search/DoubanSearchModal.ts @@ -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); + } + } + + } \ No newline at end of file diff --git a/douban/search/Search.ts b/douban/search/Search.ts index a3fe7d8..a9b3218 100644 --- a/douban/search/Search.ts +++ b/douban/search/Search.ts @@ -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 { +export default class Searcher { + static search(searchItem:string, doubanSettings:DoubanPluginSettings):Promise { // getData(); // getData2(); // return Promise.resolve(); @@ -20,5 +21,3 @@ class Searcher { .then(SearchParserHandler.parseSearch); }; } - -export {Searcher} diff --git a/douban/search/SearchParser.ts b/douban/search/SearchParser.ts index 504ae8c..e622c99 100644 --- a/douban/search/SearchParser.ts +++ b/douban/search/SearchParser.ts @@ -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(), @@ -37,7 +30,4 @@ class SearchParserHandler { return result; }) }; -} - -export {SearchParserHandler} -export type {DoubanSearchResultExtract} \ No newline at end of file +} \ No newline at end of file diff --git a/main.ts b/main.ts index 6fd6f2d..c30249e 100644 --- a/main.ts +++ b/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 { 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() {