add picture bed

This commit is contained in:
wanxp 2024-05-16 00:09:33 +08:00
parent 5c5b1b6348
commit 661e8e7e2e
24 changed files with 546 additions and 185 deletions

@ -21,7 +21,10 @@
这是一款[Obsidian](https://obsidian.md/)插件支持在Obsidian中导入[豆瓣]()中的 _电影、书籍、音乐、电视剧、日记、游戏_
甚至是 _你标记过的书影音_ , 包含你的评分、观看日期、评论、阅读状态等信息.
访问[Get started/指导手册](https://wanxp.github.io/obsidian-douban/) 获取更多信息
访问[Get Started/指导手册](https://wanxp.github.io/obsidian-douban/) 获取更多
[//]: # (访问[Get Started/指导手册](https://obsidian-douban.wanxuping.com/) 获取更多 )
![background](./doc/background.png)
---
@ -36,6 +39,7 @@
- ☑️ 同步个人听过/看过的电影、电视剧、书籍、音乐
- ☑️ 导入个人的评论,评论时间,阅读状态,个人评分
- ☑️ 支持保存封面至本地
- ☑️ 支持保存封面至图床
- ☑️ 支持自定义参数
## 效果

@ -0,0 +1,22 @@
---
title: 图床
layout: default
nav_order: 380
parent: 如何使用
---
## 图床
### PicGo
#### 设置步骤
1. 安装并下载PicGo图床软件
2. 设置PicGo图床
3. 由于Obsidian-Douban是通过剪贴板上传图片的需要在PicGo设置中开启剪贴板上传
4. 需要设置Server开启并设置 端口36677
5. 设置完成之后可以尝试点击PicGo主界面的`剪贴板上传`按钮,验证是否可以上传图片
6. 若在Obsidian-Douban设置中使用PicGo上传图片至图床则每次导入书影音数据前需要保证提前打开了PicGo软件
#### 注意事项
Obsidian-Douban插件使用PicGo上传图片至图床仅在Linux系统下测试通过其他系统未测试其它系统有问题欢迎及时反馈
##### Linux
1. x11图形界面下还需要安装xclip软件否则无法使用剪贴板上传图片
2. wayland图形界面下, 还需要安装wl-clipboard软件否则无法使用剪贴板上传图片
3. 若无法上传图片可尝试开启PicGo软件设置中的`使用内置剪贴板上传`选项

@ -440,3 +440,15 @@ export const EXAMPLE_SUBJECT_MAP: Map<string, DataField> = new Map([
]);
export const MAX_STAR_NUMBER = 100;
export enum PictureBedType {
PicGo = "PicGo"
}
export const PictureBedSetting_PicGo ={
url: "http://127.0.0.1:36677/upload"
}
export const PictureBedTypeRecords: { [key in PictureBedType]: string } = {
[PictureBedType.PicGo]: PictureBedType.PicGo
}

@ -1,7 +1,10 @@
import {DoubanPluginSetting} from "../douban/setting/model/DoubanPluginSetting";
import {PersonNameMode, SupportType} from "./Constsant";
import {PersonNameMode, PictureBedSetting_PicGo, PictureBedType, SupportType} from "./Constsant";
export const DEFAULT_SETTINGS: DoubanPluginSetting = {
pictureBedFlag: false,
pictureBedSetting: PictureBedSetting_PicGo,
pictureBedType: PictureBedType.PicGo,
arraySettings: [
{
"arrayName": "ArrayType1",

@ -33,6 +33,7 @@ import HtmlUtil from "../../../utils/HtmlUtil";
import {VariableUtil} from "../../../utils/VariableUtil";
import {DataField} from "../../../utils/model/DataField";
import NumberUtil from "../../../utils/NumberUtil";
import {DoubanHttpUtil} from "../../../utils/DoubanHttpUtil";
export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject> implements DoubanSubjectLoadHandler<T> {
@ -100,7 +101,7 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
const url:string = this.getSubjectUrl(id);
context.plugin.settingsManager.debug(`开始请求地址:${url}`)
context.plugin.settingsManager.debug(`(注意:请勿向任何人透露你的Cookie,此处若需要截图请**打码**)请求header:${context.settings.loginHeadersContent}`)
return await HttpUtil.httpRequestGet(url, context.plugin.settingsManager.getHeaders(), context.plugin.settingsManager)
return await DoubanHttpUtil.httpRequestGet(url, context.plugin.settingsManager.getHeaders(), context.plugin.settingsManager)
.then(load)
.then(data => this.analysisUserState(data, context))
.then(({data, userState}) => {
@ -513,22 +514,32 @@ export default abstract class DoubanAbstractLoadHandler<T extends DoubanSubject>
const highFilename = fileNameSpilt.first() + '.jpg';
const highImage = this.getHighQuantityImageUrl(highFilename);
const resultValue = await context.netFileHandler.downloadFile(highImage, folder, highFilename, context, false, referHeaders);
const resultValue = await this.handleImage(highImage, folder, highFilename, context, false, referHeaders);
if (resultValue && resultValue.success) {
extract.image = resultValue.filepath;
return;
}
}catch (e) {
console.error(e);
console.error('下载高清封面失败,将会使用普通封面')
}
}
const resultValue = await context.netFileHandler.downloadFile(image, folder, filename, context, true, referHeaders);
const resultValue = await this.handleImage(image, folder, filename, context, true, referHeaders);
if (resultValue && resultValue.success) {
extract.image = resultValue.filepath;
}
}
private async handleImage(image: string, folder: string, filename: string, context: HandleContext, showError: boolean, headers?: any) {
if (context.settings.pictureBedFlag) {
return await context.netFileHandler.downloadDBUploadPicGoByClipboard(image, filename, context, showError, headers);
}else {
return await context.netFileHandler.downloadDBFile(image, folder, filename, context, false, headers);
}
}
abstract getHighQuantityImageUrl(fileName:string):string;
abstract getSubjectUrl(id:string):string;

@ -12,10 +12,11 @@ import {load} from 'cheerio';
import {log} from 'src/org/wanxp/utils/Logutil';
import HttpUtil from "../../../utils/HttpUtil";
import {SupportType} from "../../../constant/Constsant";
import {DoubanHttpUtil} from "../../../utils/DoubanHttpUtil";
export default class Searcher {
static search(searchItem: string, type:SupportType, doubanSettings: DoubanPluginSetting, settingsManager:SettingsManager): Promise<DoubanSearchResultSubject[]> {
return HttpUtil.httpRequestGet(DEFAULT_SETTINGS.searchUrl + searchItem, settingsManager.getHeaders(), settingsManager)
return DoubanHttpUtil.httpRequestGet(DEFAULT_SETTINGS.searchUrl + searchItem, settingsManager.getHeaders(), settingsManager)
.then(load)
.then(SearchParserHandler.parseSearch)
.catch(e => {
@ -27,7 +28,7 @@ export default class Searcher {
static loadSearchItem(searchItem: string, type:SupportType, start:number, doubanSettings: DoubanPluginSetting, settingsManager:SettingsManager): Promise<SearchPage> {
const url:string = `https://www.douban.com/j/search?q=${searchItem}&start=${start}&subtype=item`;
log.debug(`请求更多页面:${url}`);
return HttpUtil.httpRequestGet(url, settingsManager.getHeaders(), settingsManager)
return DoubanHttpUtil.httpRequestGet(url, settingsManager.getHeaders(), settingsManager)
.then(e=>SearchParserHandler.parseSearchJson(e, type, start))
.catch(e => {
throw log.error(i18nHelper.getMessage('130101').replace('{0}', e.toString()), e);

@ -4,6 +4,7 @@ import {SearchPageFetcherInterface} from "./SearchPageFetcherInterface";
import HttpUtil from "../../../../utils/HttpUtil";
import {log} from "../../../../utils/Logutil";
import {i18nHelper} from "../../../../lang/helper";
import {DoubanHttpUtil} from "../../../../utils/DoubanHttpUtil";
export abstract class AbstractSearchPageFetcher implements SearchPageFetcherInterface {
@ -22,7 +23,7 @@ export abstract class AbstractSearchPageFetcher implements SearchPageFetcherInte
if (!url) {
return Promise.resolve("");
}
return HttpUtil.httpRequestGet(url, this.settingsManager.getHeaders(), this.settingsManager)
return DoubanHttpUtil.httpRequestGet(url, this.settingsManager.getHeaders(), this.settingsManager)
.catch(e => {
throw log.error(i18nHelper.getMessage('130101').replace('{0}', e.toString()), e);
}); }

@ -6,7 +6,7 @@ import {
DEFAULT_SETTINGS_ARRAY_INPUT_SIZE, EXAMPLE_RATE, EXAMPLE_RATE_MAX,
EXAMPLE_SUBJECT_MAP, MAX_STAR_NUMBER,
PersonNameMode,
PersonNameModeRecords,
PersonNameModeRecords, PictureBedSetting_PicGo, PictureBedType, PictureBedTypeRecords,
SupportType
} from "../../constant/Constsant";
import SettingsManager from "./SettingsManager";
@ -199,7 +199,25 @@ export function constructAttachmentFileSettingsUI(containerEl: HTMLElement, mana
});
if(manager.plugin.settings.cacheImage) {
new Setting(containerEl)
.setName(i18nHelper.getMessage('121440'))
.setDesc(i18nHelper.getMessage('121441'))
.addToggle((toggleComponent) => {
toggleComponent
// .setTooltip(i18nHelper.getMessage('121403'))
.setValue(manager.plugin.settings.pictureBedFlag)
.onChange(async (value) => {
manager.plugin.settings.pictureBedFlag = value;
await manager.plugin.saveSettings();
constructAttachmentFileSettingsUI(containerEl, manager);
});
});
if (manager.plugin.settings.pictureBedFlag) {
constructAttachmentFilePictureBedSettingsUI(containerEl, manager);
}else {
new Setting(containerEl).then(createFolderSelectionSetting({name: '121432', desc: '121433', placeholder: '121434', key: 'attachmentPath', manager: manager}));
}
new Setting(containerEl)
.setName(i18nHelper.getMessage('121435'))
.setDesc(i18nHelper.getMessage('121436'))
@ -216,3 +234,47 @@ export function constructAttachmentFileSettingsUI(containerEl: HTMLElement, mana
}
}
export function constructAttachmentFilePictureBedSettingsUI(containerEl: HTMLElement, manager: SettingsManager) {
var pictureBedSetting = manager.plugin.settings.pictureBedSetting
var pictureBedType = manager.plugin.settings.pictureBedType
if (manager.plugin.settings.pictureBedFlag && !pictureBedType) {
pictureBedType = PictureBedType.PicGo
if (pictureBedSetting == null) {
pictureBedSetting = PictureBedSetting_PicGo
}
}
var pictureBedTypeSettingsUI = new Setting(containerEl);
var pictureBedPropertySettingsUI = new Setting(containerEl).settingEl;
pictureBedTypeSettingsUI.setName(i18nHelper.getMessage('121451')).then((setting) => {
setting.addDropdown((dropdwon) => {
dropdwon.addOptions(PictureBedTypeRecords)
dropdwon.setValue(manager.plugin.settings.pictureBedType)
.onChange(async (value: string) => {
manager.plugin.settings.pictureBedType = value as PictureBedType;
constructAttachmentFilePictureBedPropertySettingsUI(pictureBedPropertySettingsUI, manager);
await manager.plugin.saveSettings();
});
});
});
constructAttachmentFilePictureBedPropertySettingsUI(pictureBedPropertySettingsUI, manager);
}
export function constructAttachmentFilePictureBedPropertySettingsUI(containerEl: HTMLElement, manager: SettingsManager) {
containerEl.empty();
const pictureBedSetting = manager.plugin.settings.pictureBedSetting
const pictureBedType = manager.plugin.settings.pictureBedType
if (pictureBedType == PictureBedType.PicGo) {
new Setting(containerEl)
.setName(i18nHelper.getMessage('121461'))
.addText((textField) => {
textField.setPlaceholder(PictureBedSetting_PicGo.url)
.setValue(pictureBedSetting.url)
.onChange(async (value) => {
pictureBedSetting.url = value;
await manager.plugin.saveSettings();
constructAttachmentFilePictureBedPropertySettingsUI(containerEl, manager);
});
});
}
}

@ -2,6 +2,7 @@ import {CustomProperty} from "./CustomProperty";
import {SyncHandledData} from "./SyncHandledData";
import {ArraySetting} from "./ArraySetting";
import {ScoreSetting} from "./ScoreSetting";
import PictureBedSetting from "./PictureBedSetting";
export interface DoubanPluginSetting {
onlineSettingsFileName: string;
@ -31,6 +32,9 @@ export interface DoubanPluginSetting {
cacheImage: boolean,
cacheHighQuantityImage: boolean,
attachmentPath: string,
pictureBedFlag: boolean
pictureBedType: string;
pictureBedSetting: PictureBedSetting;
syncHandledDataArray: SyncHandledData[],
arraySettings: ArraySetting[],
scoreSetting: ScoreSetting,

@ -0,0 +1,3 @@
export default class PictureBedSetting{
url: string
}

@ -11,6 +11,7 @@ import {SyncConfig} from "../../model/SyncConfig";
import { sleepRange} from "../../../../utils/TimeUtil";
import {ALL} from "../../../../constant/DoubanUserState";
import HttpUtil from "../../../../utils/HttpUtil";
import {DoubanHttpUtil} from "../../../../utils/DoubanHttpUtil";
export default abstract class DoubanAbstractListHandler implements DoubanListHandler{
@ -50,7 +51,7 @@ export default abstract class DoubanAbstractListHandler implements DoubanListHan
}
async getPageList(url: string, context: HandleContext):Promise<SubjectListItem[]> {
return HttpUtil.httpRequestGet(url, context.plugin.settingsManager.getHeaders(), context.plugin.settingsManager)
return DoubanHttpUtil.httpRequestGet(url, context.plugin.settingsManager.getHeaders(), context.plugin.settingsManager)
.then(load)
.then(data => this.parseSubjectFromHtml(data, context))
.catch(e => log

@ -10,6 +10,7 @@ import {doubanHeaders} from "../../constant/Douban";
import { request } from "https";
import HttpUtil from "../../utils/HttpUtil";
import {DEFAULT_DOUBAN_HEADERS} from "../../constant/Constsant";
import {DoubanHttpUtil} from "../../utils/DoubanHttpUtil";
export default class UserComponent {
private settingsManager: SettingsManager;
@ -73,7 +74,7 @@ export default class UserComponent {
}
async loadUserInfoByHeaders(headers: object): Promise<User> {
return HttpUtil.httpRequestGet('https://www.douban.com/mine/', headers, this.settingsManager)
return DoubanHttpUtil.httpRequestGet('https://www.douban.com/mine/', headers, this.settingsManager)
.then(load)
.then(this.getUserInfo);
}
@ -95,7 +96,7 @@ export default class UserComponent {
...DEFAULT_DOUBAN_HEADERS,
Cookie: cookie
}
return HttpUtil.httpRequestGet('https://www.douban.com/mine/', headers1, this.settingsManager)
return DoubanHttpUtil.httpRequestGet('https://www.douban.com/mine/', headers1, this.settingsManager)
.then(load)
.then(this.getUserInfo);
};

@ -197,6 +197,25 @@ export default class FileHandler {
}
}
async deleteFile(filePath: string): Promise<void> {
const {vault} = this._app;
const {adapter} = vault;
const fileExists = await adapter.exists(filePath);
if (fileExists) {
await adapter.remove(filePath);
}
}
getRootPath(): string {
const {vault} = this._app;
return vault.getRoot().path;
}
getTmpPath(): string {
return FileUtil.join('.tmp', 'obsidian-douban');
}
}

@ -220,6 +220,11 @@ PS: This file could be delete if you want to.
'121436': `High Definition Cover looks better but it will take more space, and you must login douban in this plugin`,
'121437': `Please login first, Then this function could be enable`,
'121438': `High Definition Cover looks better but it will take more space`,
'121440': `Use Picture Bed`,
'121441': `Save attachment file to picture bed`,
'121450': `Picture Bed Type`,
'121451': `Select picture bed type`,
'121461': `PicGo Upload Url`,
'121501': `Note folder`,

@ -241,6 +241,12 @@ export default {
'121436': `高清封面图片质量更高清晰度更好, 需要您在此插件 登录豆瓣 才能生效, 若未登录则默认使用低精度版本封面`,
'121437': `登录后此功能才会生效`,
'121438': `高清封面图片质量更高, 清晰度更好, 但占用空间会比普通清晰度封面更多`,
'121440': `使用附件图床`,
'121441': `开启此选项将会把导入的封面文件上传图床,而不是保存在本地`,
'121450': `图床类型`,
'121451': `选择图床类型`,
'121461': `PicGo上传Url`,
'121501': `笔记存放位置`,
'121502': `创建的笔记将会存放至该文件夹中. 如果为空, 笔记将会存放到Obsidian的默认位置`,

@ -5,6 +5,8 @@ import FileHandler from "../file/FileHandler";
import {FileUtil} from "../utils/FileUtil";
import HandleContext from "../douban/data/model/HandleContext";
import HttpUtil from "../utils/HttpUtil";
import {ClipboardUtil} from "../utils/ClipboardUtil";
import {ResultI} from "../utils/model/Result";
export default class NetFileHandler {
private fileHandler: FileHandler;
@ -13,9 +15,15 @@ export default class NetFileHandler {
this.fileHandler = fileHandler;
}
async downloadFile(url: string, folder:string, filename: string, context:HandleContext, showError:boolean, headers?:any): Promise<{ success: boolean, error:string, filepath: string }> {
async downloadDBFile(url: string, folder:string, filename: string, context:HandleContext, showError:boolean, headers?:any): Promise<{ success: boolean, error:string, filepath: string }> {
const filePath:string = FileUtil.join(folder, filename);
return HttpUtil.httpRequestGetBuffer(url, headers, context.plugin.settingsManager)
return HttpUtil.httpRequestBuffer(url, headers, context.plugin.settingsManager)
.then((response) => {
if (response.status == 403) {
throw new Error(i18nHelper.getMessage('130106'));
}
return response.textArrayBuffer;
})
.then((buffer) => {
this.fileHandler.creatAttachmentWithData(filePath, buffer);
}).then(() => {
@ -34,6 +42,47 @@ export default class NetFileHandler {
});
;
}
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)
.then((response) => {
if (response.status == 403) {
throw new Error(i18nHelper.getMessage('130106'));
}
return response.textArrayBuffer;
})
.then((buffer) => {
ClipboardUtil.writeImage(buffer);
}).then(() => {
return this.uploadClipboardFile(context);
}).then((data) => {
if (data.success) {
return {success: true, error: '', filepath: HttpUtil.extractURLFromString(data.result[0])};
}else {
throw new Error('图片上传图床失败,使用原始图片地址,错误消息:' + data.message)
}
})
.catch(e => {
if (showError) {
return log
.error(
i18nHelper.getMessage('130101')
.replace('{0}', e.toString())
, e);
}else {
console.error(e);
}
});
;
}
async uploadClipboardFile(context:HandleContext): Promise<ResultI> {
const response = await HttpUtil.httpRequest(
context.settings.pictureBedSetting.url, {}, context.plugin.settingsManager, {method: "post"});
const data = response.textJson as ResultI;
return data;
}
}

@ -0,0 +1,21 @@
const { clipboard, nativeImage } = require('electron');
export class ClipboardUtil {
public static async writeImage(data:ArrayBuffer, options: ClipboardOptions = defaultClipboardOptions) {
await clipboard.writeImage(nativeImage.createFromBuffer(data));
console.log(`Copied to clipboard as HTML`);
}
}
interface ClipboardOptions {
contentType?: string,
}
const defaultClipboardOptions: ClipboardOptions = {
contentType: 'text/plain',
}

@ -0,0 +1,56 @@
import SettingsManager from "../douban/setting/SettingsManager";
import {Platform, RequestUrlResponse} from "obsidian";
import DesktopHttpUtil from "./desktop/DesktopHttpUtil";
import MobileHttpUtil from "./mobile/MobileHttpUtil";
import FileHandler from "../file/FileHandler";
import HandleContext from "../douban/data/model/HandleContext";
import {FileUtil} from "./FileUtil";
import HttpUtil from "./HttpUtil";
import {log} from "./Logutil";
import {i18nHelper} from "../lang/helper";
import {ClipboardUtil} from "./ClipboardUtil";
import DoubanHumanCheckModel from "../douban/component/DoubanHumanCheckModel";
export class DoubanHttpUtil {
/**
* get请求
* @param url
* @param headers
* @param settingsManager
*/
// Cookie: 'll="108296"; bid=xHRJLeWBrjQ; _pk_id.100001.8cb4=f8f83e81ec224a1a.1691572669.; __utmv=30149280.13103; __yadk_uid=ce95W7OsgT0iKFceWgbMSUdw1oOqxNTk; __gads=ID=62585f60f3f637d0-2234f63fc6e200a5:T=1691572672:RT=1691572672:S=ALNI_MaIqTxSWHsfpnX9nAmMHcPQEsaezg; __gpi=UID=00000c29a9f98e5b:T=1691572672:RT=1691572672:S=ALNI_MbLAq8XNoKrRPKNqGCMdgXSPZvidw; ap_v=0,6.0; __utma=30149280.135860784.1691572641.1691572641.1694509646.2; __utmc=30149280; __utmz=30149280.1694509646.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1694509648%2C%22https%3A%2F%2Fmovie.douban.com%2Ftv%2F%22%5D; _pk_ses.100001.8cb4=1; __utmt=1; dbcl2="131038721:LUssju34QFw"; ck=dCQj; push_noty_num=0; push_doumail_num=0; __utmb=30149280.3.10.1694509646'
public static async httpRequestGet(url: string, headers: any, settingsManager?: SettingsManager): Promise<string> {
settingsManager.debug(`请求地址:${url}`);
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
settingsManager.debug(`Obsidian-Douban:从网络获取网页开始:\nurl:${url}\nheaders:${JSON.stringify(headers)}`);
const response = await HttpUtil.getText(url, headers, settingsManager);
if (response.status == 403) {
throw new Error(i18nHelper.getMessage('130106'));
}
const html = response.textString;
return await this.humanCheck(html, url)
}
private fileHandler: FileHandler;
constructor(fileHandler: FileHandler) {
this.fileHandler = fileHandler;
}
public static async humanCheck(html: any, url: string, settingsManager?: SettingsManager): Promise<any> {
if (!html) {
return html;
}
if (settingsManager) {
settingsManager.debug(html);
}
if (html && html.toString().indexOf("<title>禁止访问</title>") != -1) {
const loginModel = new DoubanHumanCheckModel(url);
await loginModel.load();
return '';
} else {
return html;
}
}
}

@ -60,5 +60,8 @@ export const FileUtil = {
*/
replaceSpecialCharactersForFileName(fileName: string): string {
return fileName.replaceAll(/[\\/:*?"<>|]/g, '_');
}
},
};

@ -2,31 +2,12 @@ import SettingsManager from "../douban/setting/SettingsManager";
import {Platform, RequestUrlResponse} from "obsidian";
import DesktopHttpUtil from "./desktop/DesktopHttpUtil";
import MobileHttpUtil from "./mobile/MobileHttpUtil";
import {HttpResponse} from "./model/HttpResponse";
export default class HttpUtil {
/**
* get请求
* @param url
* @param headers
* @param settingsManager
*/
// Cookie: 'll="108296"; bid=xHRJLeWBrjQ; _pk_id.100001.8cb4=f8f83e81ec224a1a.1691572669.; __utmv=30149280.13103; __yadk_uid=ce95W7OsgT0iKFceWgbMSUdw1oOqxNTk; __gads=ID=62585f60f3f637d0-2234f63fc6e200a5:T=1691572672:RT=1691572672:S=ALNI_MaIqTxSWHsfpnX9nAmMHcPQEsaezg; __gpi=UID=00000c29a9f98e5b:T=1691572672:RT=1691572672:S=ALNI_MbLAq8XNoKrRPKNqGCMdgXSPZvidw; ap_v=0,6.0; __utma=30149280.135860784.1691572641.1691572641.1694509646.2; __utmc=30149280; __utmz=30149280.1694509646.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1694509648%2C%22https%3A%2F%2Fmovie.douban.com%2Ftv%2F%22%5D; _pk_ses.100001.8cb4=1; __utmt=1; dbcl2="131038721:LUssju34QFw"; ck=dCQj; push_noty_num=0; push_doumail_num=0; __utmb=30149280.3.10.1694509646'
public static httpRequestGet(url: string, headers: any, settingsManager?: SettingsManager): Promise<string> {
settingsManager.debug(`请求地址:${url}`);
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
settingsManager.debug(`Obsidian-Douban:从网络获取网页开始:\nurl:${url}\nheaders:${JSON.stringify(headers)}`);
if (Platform.isDesktopApp) {
return DesktopHttpUtil.httpRequestGet(url, headers, settingsManager)
}else {
return MobileHttpUtil.httpRequestGet(url, headers, settingsManager).then((response:RequestUrlResponse) => {
return response.text;
});
}
}
/**
* get请求
@ -35,15 +16,14 @@ export default class HttpUtil {
* @param settingsManager
*/
// Cookie: 'll="108296"; bid=xHRJLeWBrjQ; _pk_id.100001.8cb4=f8f83e81ec224a1a.1691572669.; __utmv=30149280.13103; __yadk_uid=ce95W7OsgT0iKFceWgbMSUdw1oOqxNTk; __gads=ID=62585f60f3f637d0-2234f63fc6e200a5:T=1691572672:RT=1691572672:S=ALNI_MaIqTxSWHsfpnX9nAmMHcPQEsaezg; __gpi=UID=00000c29a9f98e5b:T=1691572672:RT=1691572672:S=ALNI_MbLAq8XNoKrRPKNqGCMdgXSPZvidw; ap_v=0,6.0; __utma=30149280.135860784.1691572641.1691572641.1694509646.2; __utmc=30149280; __utmz=30149280.1694509646.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1694509648%2C%22https%3A%2F%2Fmovie.douban.com%2Ftv%2F%22%5D; _pk_ses.100001.8cb4=1; __utmt=1; dbcl2="131038721:LUssju34QFw"; ck=dCQj; push_noty_num=0; push_doumail_num=0; __utmb=30149280.3.10.1694509646'
public static httpRequestGetJson(url: string, headers: any, settingsManager?: SettingsManager): Promise<any> {
public static async httpRequest(url: string, headers: any, settingsManager?: SettingsManager, options?: any): Promise<HttpResponse> {
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
settingsManager.debug(`Obsidian-Douban:从网络获取json开始:\nurl:${url}\nheaders:${JSON.stringify(headers)}`);
if (Platform.isDesktopApp) {
return DesktopHttpUtil.httpRequestGetJson(url, headers, settingsManager)
return DesktopHttpUtil.request(url, headers, settingsManager, options)
} else {
return MobileHttpUtil.httpRequestGet(url, headers, settingsManager).then((response:RequestUrlResponse) => {
return response.json;
});
const response = await MobileHttpUtil.httpRequestGet(url, headers, settingsManager);
return new HttpResponse(response.status, response.headers, response.text);
}
}
@ -53,15 +33,62 @@ export default class HttpUtil {
* @param headers
* @param settingsManager
*/
public static httpRequestGetBuffer(url: string, headers: any, settingsManager?: SettingsManager): Promise<ArrayBuffer> {
public static async getText(url: string, headers: any, settingsManager?: SettingsManager): Promise<HttpResponse> {
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
settingsManager.debug(`Obsidian-Douban:从网络获取json开始:\nurl:${url}\nheaders:${JSON.stringify(headers)}`);
if (Platform.isDesktopApp) {
return DesktopHttpUtil.httpRequestGetBuffer(url, headers, settingsManager)
return DesktopHttpUtil.request(url, headers, settingsManager, {method: 'GET'})
} else {
const response = await MobileHttpUtil.httpRequestGet(url, headers, settingsManager);
return new HttpResponse(response.status, response.headers, response.text);
}
}
/**
* get请求
* @param url
* @param headers
* @param settingsManager
*/
public static httpRequestBuffer(url: string, headers: any, settingsManager?: SettingsManager): Promise<HttpResponse> {
if (Platform.isDesktopApp) {
return DesktopHttpUtil.requestBuffer(url, headers, settingsManager)
}else {
return MobileHttpUtil.httpRequestGet(url, headers, settingsManager).then((response: RequestUrlResponse) => {
return response.arrayBuffer;
return new HttpResponse(response.status, response.headers, response.arrayBuffer);
});
}
}
public static parse(url: string): { protocol: string, host: string, port: string, path: string } {
const regex = /^(.*?):\/\/([^\/:]+)(?::(\d+))?([^?]*)$/;
const matches = url.match(regex);
if (matches) {
const protocol = matches[1];
const host = matches[2];
const port = matches[3] || '';
const path = matches[4];
return { protocol, host, port, path };
}
throw new Error('Invalid URL');
}
/**
* url
* @param str
*/
public static extractURLFromString(str: string): string {
const urlRegex = /(?:!\[.*?\]\()?(https?:\/\/[^\s)]+)/g;
const matches = str.match(urlRegex);
if (matches && matches.length > 0) {
return matches[0];
}
return str;
}
}

@ -2,15 +2,14 @@ import SettingsManager from "../../douban/setting/SettingsManager";
import {i18nHelper} from "../../lang/helper";
import {LoginUtil} from "../LoginUtil";
import DoubanHumanCheckModel from "../../douban/component/DoubanHumanCheckModel";
import HttpUtil from "../HttpUtil";
import {HttpResponse} from "../model/HttpResponse";
var https:any = null;
var http:any = null;
export default class DesktopHttpUtil {
/**
* get请求
* @param url
@ -18,78 +17,39 @@ export default class DesktopHttpUtil {
* @param settingsManager
*/
// Cookie: 'll="108296"; bid=xHRJLeWBrjQ; _pk_id.100001.8cb4=f8f83e81ec224a1a.1691572669.; __utmv=30149280.13103; __yadk_uid=ce95W7OsgT0iKFceWgbMSUdw1oOqxNTk; __gads=ID=62585f60f3f637d0-2234f63fc6e200a5:T=1691572672:RT=1691572672:S=ALNI_MaIqTxSWHsfpnX9nAmMHcPQEsaezg; __gpi=UID=00000c29a9f98e5b:T=1691572672:RT=1691572672:S=ALNI_MbLAq8XNoKrRPKNqGCMdgXSPZvidw; ap_v=0,6.0; __utma=30149280.135860784.1691572641.1691572641.1694509646.2; __utmc=30149280; __utmz=30149280.1694509646.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1694509648%2C%22https%3A%2F%2Fmovie.douban.com%2Ftv%2F%22%5D; _pk_ses.100001.8cb4=1; __utmt=1; dbcl2="131038721:LUssju34QFw"; ck=dCQj; push_noty_num=0; push_doumail_num=0; __utmb=30149280.3.10.1694509646'
public static httpRequestGet(url: string, headers: any, settingsManager?: SettingsManager): Promise<string> {
if (!https) {
https = require("follow-redirects").https;
}
settingsManager.debug(`请求地址:${url}`);
public static request(url: string, headers: any, settingsManager?: SettingsManager, options?: any): Promise<HttpResponse> {
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
let options = {
headers: headersInner
const optionsInner = {
headers: headersInner,
...options
}
return new Promise((resolve, rejects) => {
this.httpRequestGetInner(url, options, 0, resolve, rejects, settingsManager);
this.httpRequest(url, optionsInner, 0, resolve, rejects, settingsManager);
})
}
private static httpRequestGetInner(url: string, options: any, times:number, resolve:any, rejects:any, settingsManager?: SettingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取网页[开始]${times}:url:${url}\nheaders:${JSON.stringify(options)}`);
https.get(url, { ...options }, function (response: any) {
let chunks: any = [],
size = 0;
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取网页[完成]${times}:url:${url}\nresponse-header:${JSON.stringify(response.headers)}`);
settingsManager.debug(`Obsidian-Douban:从网络获取网页[完成]${times}:response-body:\n${response.text}`);
}
if (response.statusCode == 403) {
rejects(new Error(i18nHelper.getMessage('130106')));
return
}
response.on("data", function (chunk: any) {
chunks.push(chunk)
size += chunk.length
})
response.on("end", function () {
const data = Buffer.concat(chunks, size)
const html = data.toString()
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取网页完成:\nhtml:\n${html}`);
}
if (LoginUtil.contentNeedLogin(html)) {
rejects(new Error(i18nHelper.getMessage('140304')));
}
resolve(html)
})
})
}
/**
* get请求
* @param url
* @param headers
* @param settingsManager
*/
// Cookie: 'll="108296"; bid=xHRJLeWBrjQ; _pk_id.100001.8cb4=f8f83e81ec224a1a.1691572669.; __utmv=30149280.13103; __yadk_uid=ce95W7OsgT0iKFceWgbMSUdw1oOqxNTk; __gads=ID=62585f60f3f637d0-2234f63fc6e200a5:T=1691572672:RT=1691572672:S=ALNI_MaIqTxSWHsfpnX9nAmMHcPQEsaezg; __gpi=UID=00000c29a9f98e5b:T=1691572672:RT=1691572672:S=ALNI_MbLAq8XNoKrRPKNqGCMdgXSPZvidw; ap_v=0,6.0; __utma=30149280.135860784.1691572641.1691572641.1694509646.2; __utmc=30149280; __utmz=30149280.1694509646.2.2.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1694509648%2C%22https%3A%2F%2Fmovie.douban.com%2Ftv%2F%22%5D; _pk_ses.100001.8cb4=1; __utmt=1; dbcl2="131038721:LUssju34QFw"; ck=dCQj; push_noty_num=0; push_doumail_num=0; __utmb=30149280.3.10.1694509646'
public static httpRequestGetJson(url: string, headers: any, settingsManager?: SettingsManager): Promise<any> {
if (!https) {
https = require("follow-redirects").https;
}
const {['Accept-Encoding']: acceptEncoding, ...headersInner} = headers;
const options = {
headers: headersInner
}
return new Promise((resolve, rejects) => {
this.httpRequestGetJsonInner(url, options, 0, resolve, rejects, settingsManager);
})
}
private static httpRequestGetJsonInner(url: string, options: any, times:number, resolve:any, rejects:any, settingsManager?: SettingsManager) {
private static httpRequest(url: string, options: any, times:number, resolve:any, rejects:any, settingsManager?: SettingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取json开始:\nurl:${url}\nheaders:${JSON.stringify(options)}`);
https.get(url, { ...options }, function (response: any) {
var method = options.method;
if (method == null) {
method = "GET";
}
if (method.toUpperCase() == "POST") {
const {protocol, host, port, path} = HttpUtil.parse(url);
const optionsInner = {
hostname: host,
port: port,
path: path,
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
};
const req = DesktopHttpUtil.getHttpClient(url).request(optionsInner, function (response: any) {
let chunks: any = [],
size = 0;
if (settingsManager) {
@ -97,10 +57,7 @@ export default class DesktopHttpUtil {
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成${times}:header:\n${JSON.stringify(response.headers)}`);
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成${times}:body:\n${response.text}`);
}
if (response.statusCode == 403) {
rejects(new Error(i18nHelper.getMessage('130106')));
return
}
response.on("data", function (chunk: any) {
chunks.push(chunk)
size += chunk.length
@ -109,15 +66,53 @@ export default class DesktopHttpUtil {
response.on("end", function () {
const data = Buffer.concat(chunks, size)
const html = data.toString()
resolve(new HttpResponse(response.statusCode, response.headers, html))
})
});
const body = options.body;
if (body) {
req.write(body);
}else {
req.write('');
}
req.end();
}else {
this.getHttpClient(url).get(url, { ...options }, function (response:any) {
let chunks: any = [],
size = 0;
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成:\nhtml:\n${html}`);
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成${times}:url:\n${url}`);
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成${times}:header:\n${JSON.stringify(response.headers)}`);
settingsManager.debug(`Obsidian-Douban:从网络获取JSON完成${times}:body:\n${response.text}`);
}
if (LoginUtil.contentNeedLogin(html)) {
rejects(new Error(i18nHelper.getMessage('140304')));
response.on("data", function (chunk: any) {
chunks.push(chunk)
size += chunk.length
})
response.on("end", function () {
const data = Buffer.concat(chunks, size)
const html = data.toString()
resolve(new HttpResponse(response.statusCode, response.headers, html))
})
});
}
resolve(html)
})
})
}
private static getHttpClient(url?: string) {
if (url && url.startsWith("https")) {
if (!https) {
https = require("follow-redirects").https;
}
return https;
}else {
if (!http) {
http = require("follow-redirects").http;
}
return http;
}
}
/**
@ -126,10 +121,7 @@ export default class DesktopHttpUtil {
* @param headers
* @param settingsManager
*/
public static httpRequestGetBuffer(url: string, headers: any, settingsManager?: SettingsManager): Promise<ArrayBuffer> {
if (!https) {
https = require("follow-redirects").https;
}
public static requestBuffer(url: string, headers: any, settingsManager?: SettingsManager): Promise<HttpResponse> {
let options = {
headers: headers
}
@ -143,17 +135,13 @@ export default class DesktopHttpUtil {
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取文件开始:\n${url}\nheaders:${JSON.stringify(options)}`);
https.get(url, {...options}, function (response: any) {
this.getHttpClient(url).get(url, {...options}, function (response: any) {
let chunks: any = [],
size = 0;
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取文件完成${times}:url:\n${url}`);
settingsManager.debug(`Obsidian-Douban:从网络获取文件完成${times}:header:\n${JSON.stringify(response.headers)}`);
}
if (response.statusCode == 403) {
rejects(new Error(i18nHelper.getMessage('130106')));
return
}
response.on("data", function (chunk: any) {
chunks.push(chunk)
@ -162,31 +150,10 @@ export default class DesktopHttpUtil {
response.on("end", function () {
const data = Buffer.concat(chunks, size)
if (settingsManager) {
settingsManager.debug(`Obsidian-Douban:从网络获取文件完成:`);
}
resolve(data)
resolve(new HttpResponse(response.statusCode, response.headers, data))
})
})
}
}
public static async humanCheck(html: any, url: string, settingsManager?: SettingsManager): Promise<any> {
if (!html) {
return html;
}
if (settingsManager) {
settingsManager.debug(html);
}
if (html && html.toString().indexOf("<title>禁止访问</title>") != -1) {
const loginModel = new DoubanHumanCheckModel(url);
await loginModel.load();
return '';
} else {
return html;
}
}
}

@ -2,7 +2,7 @@ import SettingsManager from "../../douban/setting/SettingsManager";
import {requestUrl, RequestUrlParam, RequestUrlResponse} from "obsidian";
import {log} from "../Logutil";
import {i18nHelper} from "../../lang/helper";
import DoubanHumanCheckModel from "../../douban/component/DoubanHumanCheckModel";
import {DoubanHttpUtil} from "../DoubanHttpUtil";
export default class MobileHttpUtil {
/**
@ -45,7 +45,7 @@ export default class MobileHttpUtil {
settingsManager.debug(`Obsidian-Douban:获取网页如下:\n${response}`);
return response;
})
.then(s => this.humanCheck(s, url, settingsManager))
.then(s => DoubanHttpUtil.humanCheck(s, url, settingsManager))
.catch(e => {
if (e.toString().indexOf('403') > 0) {
throw log.error(i18nHelper.getMessage('130105'), e)
@ -56,21 +56,6 @@ export default class MobileHttpUtil {
}
public static async humanCheck(html: any, url: string, settingsManager?: SettingsManager): Promise<any> {
if (!html) {
return html;
}
if (settingsManager) {
settingsManager.debug(html);
}
if (html && html.toString().indexOf("<title>禁止访问</title>") != -1) {
const loginModel = new DoubanHumanCheckModel(url);
await loginModel.load();
return '';
} else {
return html;
}
}
}

@ -0,0 +1,56 @@
export class HttpResponse {
private readonly _status:number;
private readonly _headers:Record<string, string>;
private readonly _text:string | ArrayBuffer;
constructor(status:number, headers:Record<string, string>, text:string | ArrayBuffer) {
this._status = status;
this._headers = headers;
this._text = text;
}
get status():number {
return this._status;
}
get headers():Record<string, string> {
return this._headers;
}
get text():string | ArrayBuffer {
return this._text;
}
get textString():string {
if (this.text instanceof ArrayBuffer) {
return new TextDecoder().decode(this.text as ArrayBuffer);
}
if (typeof this.text === 'string') {
return this.text as string;
}
return '';
}
get textArrayBuffer():ArrayBuffer {
if (this.text instanceof Buffer) {
return this.text as Buffer;
}
if (this.text instanceof ArrayBuffer) {
return this.text as ArrayBuffer;
}
if (typeof this.text === 'string') {
return new TextEncoder().encode(this.text as string);
}
return new ArrayBuffer(0);
}
get textBlob():Blob {
return new Blob([this.textArrayBuffer]);
}
get textJson():Record<string, any> {
return JSON.parse(this.textString);
}
}

@ -0,0 +1,42 @@
export class Result implements ResultI{
private _success:boolean
private _message:string
private _result:any
constructor(success:boolean, msg:string , data:any) {
}
get success():boolean {
return this._success;
}
set success(value:boolean) {
this._success = value;
}
set message(value:string) {
this._message = value;
}
get message():string {
return this._message;
}
set result (value:any) {
this._result = value;
}
get result():any {
return this._result;
}
}
export interface ResultI {
success:boolean
message:string
result:any
}