diff --git a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts index eb38eb0..7f36ff7 100644 --- a/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts +++ b/src/org/wanxp/douban/data/handler/DoubanAbstractLoadHandler.ts @@ -543,24 +543,20 @@ export default abstract class DoubanAbstractLoadHandler } fileName = this.parsePartPath(fileName, extract, context, variableMap) fileName = fileName + fileNameSuffix; - const imageReferer = extract.url || extract.imageUrl || image; + const imageReferer = (extract.id ? this.getSubjectUrl(extract.id) : '') || extract.url; const referHeaders = HttpUtil.buildImageRequestHeaders( context.plugin.settingsManager.getHeaders() as Record, - imageReferer, - image + imageReferer ); if ((syncConfig ? syncConfig.cacheHighQuantityImage : context.settings.cacheHighQuantityImage) && context.userComponent.isLogin()) { try { - const fileNameSpilt = fileName.split('.'); - const highFilename = fileNameSpilt.first() + '.jpg'; - - const highImage = this.getHighQuantityImageUrl(highFilename); + const highImageFilename = this.getImageFilename(image); + const highImage = this.getHighQuantityImageUrl(highImageFilename); const highImageHeaders = HttpUtil.buildImageRequestHeaders( context.plugin.settingsManager.getHeaders() as Record, - imageReferer, - highImage + imageReferer ); - const resultValue = await this.handleImage(highImage, folder, highFilename, context, false, highImageHeaders); + const resultValue = await this.handleImage(highImage, folder, fileName, context, false, highImageHeaders); if (resultValue && resultValue.success) { extract.image = resultValue.filepath; this.initImageVariableMap(extract, context, variableMap); @@ -578,6 +574,14 @@ export default abstract class DoubanAbstractLoadHandler } } + 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) { variableMap.set(DoubanParameterName.IMAGE_URL, new DataField( DoubanParameterName.IMAGE_URL, diff --git a/src/org/wanxp/lang/locale/en.ts b/src/org/wanxp/lang/locale/en.ts index 4781c11..b09801f 100644 --- a/src/org/wanxp/lang/locale/en.ts +++ b/src/org/wanxp/lang/locale/en.ts @@ -335,6 +335,8 @@ 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 `, '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`, + '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`, diff --git a/src/org/wanxp/lang/locale/zh-cn.ts b/src/org/wanxp/lang/locale/zh-cn.ts index 6045132..f3b5eed 100644 --- a/src/org/wanxp/lang/locale/zh-cn.ts +++ b/src/org/wanxp/lang/locale/zh-cn.ts @@ -350,6 +350,8 @@ export default { '130105': `由于多次频繁请求数据,豆瓣当前暂时不可用. 请于12小时或24小时后再重试,或重置你的网络(如重新拨号或更换网络) `, '130106': `请尝试在Douban插件中登录后操作. 若还是无效果则尝试于12小时或24小时后再重试,或重置你的网络(如重新拨号或更换网络) `, '130107': `参数{0}中指定的数组输出类型{1}不存在,请前往配置进行设置`, + '130109': `下载图片内容为空`, + '130110': `写入剪贴板失败`, '130120': `同步时发生错误,但同步将会继续。错误项目是 {}。`, '130121': `总数只有{0}, 选择的开始比总数还要大,将不会同步。`, diff --git a/src/org/wanxp/net/NetFileHandler.ts b/src/org/wanxp/net/NetFileHandler.ts index af2cd4e..4a5b4e3 100644 --- a/src/org/wanxp/net/NetFileHandler.ts +++ b/src/org/wanxp/net/NetFileHandler.ts @@ -5,6 +5,7 @@ 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 { @@ -16,16 +17,19 @@ export default class NetFileHandler { 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.httpRequestBuffer(url, headers, context.plugin.settingsManager) - .then((response) => { - if (response.status == 404) { - throw new Error(i18nHelper.getMessage('130404')); - } - if (response.status == 403) { - throw new Error(i18nHelper.getMessage('130106')); - } - return response.textArrayBuffer; - }) + return HttpUtil.httpRequestBuffer(url, headers, context.plugin.settingsManager) + .then((response) => { + if (response.status == 404) { + throw new Error(i18nHelper.getMessage('130404')); + } + if (response.status == 403) { + throw new Error(i18nHelper.getMessage('130106')); + } + if (response.status == 418) { + throw new Error(i18nHelper.getMessage('130105')); + } + return response.textArrayBuffer; + }) .then((buffer) => { if (!buffer || buffer.byteLength == 0) { return 0; @@ -55,19 +59,23 @@ 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 == 404) { + throw new Error(i18nHelper.getMessage('130404')); + } if (response.status == 403) { throw new Error(i18nHelper.getMessage('130106')); } + if (response.status == 418) { + throw new Error(i18nHelper.getMessage('130105')); + } return response.textArrayBuffer; }) - .then(async (buffer) => { - const tempFilePath = this.getPicGoTempFilePath(filename, context); - try { - await this.fileHandler.creatAttachmentWithData(tempFilePath.relativePath, buffer); - return await this.uploadClipboardFile(context, tempFilePath.absolutePath); - } finally { - await this.fileHandler.deleteFile(tempFilePath.relativePath); - } + .then(async (buffer) => { + if (!buffer || buffer.byteLength == 0) { + throw new Error(i18nHelper.getMessage('130109')); + } + await ClipboardUtil.writeImage(buffer); + return await this.uploadClipboardFile(context); }).then((data) => { if (data.success) { return {success: true, error: '', filepath: HttpUtil.extractURLFromString(data.result[0])}; @@ -90,23 +98,9 @@ export default class NetFileHandler { } - private getPicGoTempFilePath(filename: string, context: HandleContext) { - const tempFileName = `${Date.now()}_${filename}`; - const relativePath = FileUtil.join(this.fileHandler.getTmpPath(), tempFileName); - // @ts-ignore - const adapter = context.plugin.app.vault.adapter; - // @ts-ignore - const basePath = adapter && adapter.getBasePath ? adapter.getBasePath() : this.fileHandler.getRootPath(); - return { - relativePath: relativePath, - absolutePath: FileUtil.join(basePath, relativePath), - }; - } - - async uploadClipboardFile(context:HandleContext, filePath?: string): Promise { - const body = filePath ? JSON.stringify({list: [filePath]}) : null; + async uploadClipboardFile(context:HandleContext): Promise { const response = await HttpUtil.httpRequest( - context.settings.pictureBedSetting.url, {}, context.plugin.settingsManager, {method: "post", body: body}); + context.settings.pictureBedSetting.url, {}, context.plugin.settingsManager, {method: "post"}); const data = response.textJson as ResultI; return data; } @@ -124,3 +118,4 @@ export default class NetFileHandler { } } + diff --git a/src/org/wanxp/utils/ClipboardUtil.ts b/src/org/wanxp/utils/ClipboardUtil.ts index ba773bb..4caba26 100644 --- a/src/org/wanxp/utils/ClipboardUtil.ts +++ b/src/org/wanxp/utils/ClipboardUtil.ts @@ -1,12 +1,78 @@ -export class ClipboardUtil { +import {i18nHelper} from "../lang/helper"; - public static async writeImage(data:ArrayBuffer, options: ClipboardOptions = defaultClipboardOptions) { - const { clipboard, nativeImage } = require('electron'); + export class ClipboardUtil { - await clipboard.writeImage(nativeImage.createFromBuffer(data)); - console.log(`Copied to clipboard as HTML`); + 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 isWebp = this.isWebp(data); + let image = nativeImage.createFromBuffer(Buffer.from(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`); + } + + 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 { + 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; + }); } } @@ -20,4 +86,3 @@ interface ClipboardOptions { const defaultClipboardOptions: ClipboardOptions = { contentType: 'text/plain', } - diff --git a/src/org/wanxp/utils/HttpUtil.ts b/src/org/wanxp/utils/HttpUtil.ts index 6b38a4c..224df5c 100644 --- a/src/org/wanxp/utils/HttpUtil.ts +++ b/src/org/wanxp/utils/HttpUtil.ts @@ -73,16 +73,14 @@ export default class HttpUtil { } } - public static buildImageRequestHeaders(headers: Record = {}, referer?: string, imageUrl?: string): Record { + public static buildImageRequestHeaders(headers: Record = {}, referer?: string): Record { const nextHeaders: Record = {}; - let currentReferer = ''; Object.entries(headers || {}).forEach(([key, value]) => { if (value == null || value === '') { return; } const lowerKey = key.toLowerCase(); if (lowerKey === 'referer') { - currentReferer = String(value); return; } if (this.IMAGE_REQUEST_HEADERS_TO_DROP.has(lowerKey) || lowerKey.startsWith('sec-ch-ua')) { @@ -90,25 +88,12 @@ export default class HttpUtil { } nextHeaders[key] = String(value); }); - const finalReferer = referer || currentReferer || this.getDefaultImageRequestReferer(imageUrl); - if (finalReferer) { - nextHeaders.Referer = finalReferer; + if (referer) { + nextHeaders.Referer = referer; } return nextHeaders; } - private static getDefaultImageRequestReferer(imageUrl?: string): string { - if (!imageUrl) { - return ''; - } - try { - const url = new URL(imageUrl); - return `${url.protocol}//${url.host}/`; - } catch (error) { - return ''; - } - } - public static parse(url: string): { protocol: string, host: string, port: string, path: string } { const regex = /^(.*?):\/\/([^\/:]+)(?::(\d+))?([^?]*)$/; @@ -140,10 +125,10 @@ export default class HttpUtil { * @param str */ public static extractURLFromString(str: string): string { - const urlRegex = /(?:!\[.*?\]\()?(https?:\/\/[^\s)]+)/; - const matches = urlRegex.exec(str); - if (matches && matches[1]) { - return matches[1]; + const urlRegex = /(?:!\[.*?\]\()?(https?:\/\/[^\s)]+)/g; + const matches = str.match(urlRegex); + if (matches && matches.length > 0) { + return matches[0]; } return str; }