mirror of
https://github.com/Kuingsmile/PicList.git
synced 2025-02-02 02:58:13 -05:00
Merge branch 'dev' into release
This commit is contained in:
commit
fa4e1bf0b2
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,3 +1,26 @@
|
|||||||
|
## :tada: 2.9.5 (2024-11-16)
|
||||||
|
|
||||||
|
|
||||||
|
### :sparkles: Features
|
||||||
|
|
||||||
|
* **custom:** add support for sink ([b843278](https://github.com/Kuingsmile/piclist/commit/b843278)), closes [#254](https://github.com/Kuingsmile/piclist/issues/254)
|
||||||
|
* **custom:** optimize short url ([fd5316a](https://github.com/Kuingsmile/piclist/commit/fd5316a)), closes [#252](https://github.com/Kuingsmile/piclist/issues/252)
|
||||||
|
* **custom:** support use presigned url for image preview ([4209838](https://github.com/Kuingsmile/piclist/commit/4209838)), closes [#265](https://github.com/Kuingsmile/piclist/issues/265)
|
||||||
|
|
||||||
|
|
||||||
|
### :bug: Bug Fixes
|
||||||
|
|
||||||
|
* **custom:** await RPC call for download directory selection ([2079faa](https://github.com/Kuingsmile/piclist/commit/2079faa))
|
||||||
|
* **custom:** quality must be an int between 1-100 ([cd48b24](https://github.com/Kuingsmile/piclist/commit/cd48b24))
|
||||||
|
|
||||||
|
|
||||||
|
### :pencil: Documentation
|
||||||
|
|
||||||
|
* **custom:** prepare for 2.9.5 ([c5aaa37](https://github.com/Kuingsmile/piclist/commit/c5aaa37))
|
||||||
|
* **custom:** update FAQ ([a9eed2d](https://github.com/Kuingsmile/piclist/commit/a9eed2d))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## :tada: 2.9.4 (2024-10-22)
|
## :tada: 2.9.4 (2024-10-22)
|
||||||
|
|
||||||
|
|
||||||
|
13
FAQ.md
13
FAQ.md
@ -1,6 +1,6 @@
|
|||||||
# FAQ
|
# FAQ
|
||||||
|
|
||||||
该FAQ修改自PicGo的FAQ,感谢PicGo的作者Molunerfinn。
|
本 FAQ 修改自 PicGo 的 FAQ,感谢 PicGo 的作者 Molunerfinn。
|
||||||
|
|
||||||
## 常见问题
|
## 常见问题
|
||||||
|
|
||||||
@ -8,13 +8,13 @@
|
|||||||
|
|
||||||
## 1. PicList 和 PicGo 有什么关系?
|
## 1. PicList 和 PicGo 有什么关系?
|
||||||
|
|
||||||
PicList项目fork自PicGo项目,基于PicGo进行了二次开发,同时核心功能内核PicGo-Core也进行了二次开发,重命名为[PicList-Core](https://github.com/Kuingsmile/PicList-Core)。
|
PicList 项目是从 PicGo 项目 fork 而来,基于 PicGo 进行了二次开发。同时,核心功能内核 PicGo-Core 也进行了二次开发,并重命名为 [PicList-Core](https://github.com/Kuingsmile/PicList-Core)。
|
||||||
|
|
||||||
PicList所有新功能的添加没有影响到PicGo的原有功能,所以你可以在PicList中使用PicGo的大部分插件。同时仍然可以配合typora、obsidian等软件进行使用。
|
PicList 添加的所有新功能未影响 PicGo 的原有功能,因此你可以在 PicList 中使用 PicGo 的大部分插件。同时,仍然可以配合 Typora、Obsidian 等软件使用。
|
||||||
|
|
||||||
## 2. 使用图床管理功能时,出现无法获取目录等错误
|
## 2. 使用图床管理功能时,出现无法获取目录等错误
|
||||||
|
|
||||||
请查看日志文件 `manage.log`,此外,各平台的API调用基本都有每小时次数限制,如果出现错误,请稍后再试。
|
请查看日志文件 `manage.log`。此外,各平台的 API 调用基本都有每小时次数限制,如果出现错误,请稍后再试。
|
||||||
|
|
||||||
## 3. 支持哪些图床远端同步删除
|
## 3. 支持哪些图床远端同步删除
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ PicList所有新功能的添加没有影响到PicGo的原有功能,所以你
|
|||||||
|
|
||||||
可以,通过新添加的图床管理功能,你可以上传任意格式的文件,包括视频文件。同时,在管理界面内上传时,使用分片上传/流式上传等方式,相对于PicGo内置的转换为base64的方式,上传更快,更稳定。
|
可以,通过新添加的图床管理功能,你可以上传任意格式的文件,包括视频文件。同时,在管理界面内上传时,使用分片上传/流式上传等方式,相对于PicGo内置的转换为base64的方式,上传更快,更稳定。
|
||||||
|
|
||||||
## 5. 能否支持某某某图床
|
## 5. 能否支持xxx图床
|
||||||
|
|
||||||
PicList本体支持了如下图床:
|
PicList本体支持了如下图床:
|
||||||
|
|
||||||
@ -57,6 +57,7 @@ PicList本体支持了如下图床:
|
|||||||
- `SFTP`
|
- `SFTP`
|
||||||
- `兰空图床`
|
- `兰空图床`
|
||||||
- `PicList(套娃)`
|
- `PicList(套娃)`
|
||||||
|
- `高级自定义图床`
|
||||||
|
|
||||||
PicList计划整合和优化现有插件,内置更多的常用图床。
|
PicList计划整合和优化现有插件,内置更多的常用图床。
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ GitHub 服务器和国内 GFW 的问题会导致有时上传成功,有时上
|
|||||||
|
|
||||||
## 11. macOS系统安装完PicList显示「文件已损坏」或者安装完打开没有反应
|
## 11. macOS系统安装完PicList显示「文件已损坏」或者安装完打开没有反应
|
||||||
|
|
||||||
请升级PicList 1.4.1或以上版本,自1.4.1开始,PicList已经经过Apple的签名,不会再出现这种情况。
|
请升级至 PicList 1.4.1 或以上版本。
|
||||||
|
|
||||||
## 12. 水印没有正常添加
|
## 12. 水印没有正常添加
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ PicList itself supports the following image hosting platforms:
|
|||||||
- SFTP
|
- SFTP
|
||||||
- Lsky Pro
|
- Lsky Pro
|
||||||
- PicList (nested)
|
- PicList (nested)
|
||||||
|
- Advanced custom image hosting
|
||||||
|
|
||||||
PicList plans to integrate and optimize existing plugins and embed more commonly used image hosting platforms.
|
PicList plans to integrate and optimize existing plugins and embed more commonly used image hosting platforms.
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ Or right-click on the icon of PicList in the Docker bar to find the menu of "Ope
|
|||||||
|
|
||||||
## 11. After installing PicList on macOS, it shows "The file is damaged" or there is no response after installing and opening
|
## 11. After installing PicList on macOS, it shows "The file is damaged" or there is no response after installing and opening
|
||||||
|
|
||||||
Please upgrade PicList to version 1.4.1 or above. Starting from 1.4.1, PicList has been signed by Apple and will not have this problem again.
|
Please upgrade PicList to version 1.4.1 or above.
|
||||||
|
|
||||||
## 12. Watermark is not added normally
|
## 12. Watermark is not added normally
|
||||||
|
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
### ✨ Features
|
### ✨ Features
|
||||||
|
|
||||||
- 优化了第二图床的上传逻辑,现在会使用相同文件名和压缩方式
|
- 优化了短链接处理逻辑:
|
||||||
- 移除了telegra.ph图床(官方现已关闭匿名上传功能)
|
- 现在相册界面中复制链接时会得到相同的短链接,而不是每次重新获取
|
||||||
- 默认上传快捷键修改为`Ctrl+Alt+U`,避免与vscode命令面板冲突
|
- 现在开启短链接时上传结果通知会正确显示
|
||||||
|
- 现在开启短链接时上传接口会返回短链接
|
||||||
|
- 管理界面新增使用预签名链接显示预览图片选项
|
||||||
|
- 现在图片压缩质量只允许设置1-100之间的正整数
|
||||||
|
- 新增对短链项目[Sink](https://github.com/ccbikai/Sink)的支持
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- 修复了webdav图床链接拼接错误的问题
|
- 修复了管理页面设置项界面中,部分没有tooltip的选项依旧会显示图标的问题
|
||||||
- 修复了开启云删除时部分第三方图床图片无法批量删除的问题
|
- 修复了在管理页面无法修改下载文件夹的问题
|
||||||
|
- 修复了对HEIC图片无法转换格式的问题
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
### ✨ Features
|
### ✨ Features
|
||||||
|
|
||||||
- Optimize the upload logic of the second image bed, now it will use the same file name and compression method
|
- Optimized the short link processing logic:
|
||||||
- Removed telegra.ph image bed (officially closed anonymous upload function)
|
- Now when copying links in the album interface, the same short link will be obtained, instead of reacquiring it each time
|
||||||
- The default upload shortcut key is changed to `Ctrl+Alt+U` to avoid conflicts with the vscode command panel
|
- Now the upload result notification will be displayed correctly when the short link is enabled
|
||||||
|
- Now the upload interface will return a short link when the short link is enabled
|
||||||
|
- Added the option to display preview images using presigned links in the management interface
|
||||||
|
- Now the image compression quality only allows setting positive integers between 1-100
|
||||||
|
- Added support for the short link project [Sink](https://github.com/ccbikai/Sink)
|
||||||
|
|
||||||
### 🐛 Bug Fixes
|
### 🐛 Bug Fixes
|
||||||
|
|
||||||
- Fixed the problem of incorrect splicing of webdav image bed links
|
- Fixed the issue where some options without tooltips in the management page settings interface would still display icons
|
||||||
- Fixed the problem that some third-party image bed pictures cannot be deleted in batches when cloud deletion is enabled
|
- Fixed the issue where the download folder could not be modified in the management page
|
||||||
|
- Fixed the issue where HEIC images could not be converted
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "piclist",
|
"name": "piclist",
|
||||||
"version": "2.9.4",
|
"version": "2.9.5",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Kuingsmile",
|
"name": "Kuingsmile",
|
||||||
"email": "pkukuing@gmail.com"
|
"email": "pkukuing@gmail.com"
|
||||||
@ -67,7 +67,7 @@
|
|||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"node-ssh-no-cpu-features": "^2.0.0",
|
"node-ssh-no-cpu-features": "^2.0.0",
|
||||||
"nodejs-file-downloader": "^4.12.1",
|
"nodejs-file-downloader": "^4.12.1",
|
||||||
"piclist": "^1.9.6",
|
"piclist": "^1.9.7",
|
||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"proxy-agent": "^5.0.0",
|
"proxy-agent": "^5.0.0",
|
||||||
|
@ -121,7 +121,7 @@ UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR: Watermark Color, Please select from the color
|
|||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: Watermark Image Path (leave blank to use default image)
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: Watermark Image Path (leave blank to use default image)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: Watermark Position
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: Watermark Position
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: Remove EXIF Info
|
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: Remove EXIF Info
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: Compression Quality
|
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: Compression Quality(1-100)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: Convert Format
|
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: Convert Format
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: Destination Format
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: Destination Format
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: 'Specific Format, Please enter in json format, e.g. {"png": "jpg"}'
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: 'Specific Format, Please enter in json format, e.g. {"png": "jpg"}'
|
||||||
@ -263,6 +263,8 @@ SETTINGS_SHORT_URL_C1N_TOKEN: C1N Token
|
|||||||
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS domain
|
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS domain
|
||||||
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
||||||
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker Host
|
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker Host
|
||||||
|
SETTINGS_SHORT_SINK_DOMAIN: sink domain
|
||||||
|
SETTINGS_SHORT_SINK_TOKEN: sink token
|
||||||
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: Delete local file after upload
|
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: Delete local file after upload
|
||||||
SETTINGS_SYNC_CONFIG: Settings Sync Configuration
|
SETTINGS_SYNC_CONFIG: Settings Sync Configuration
|
||||||
SETTINGS_SYNC_CONFIG_TITLE: Sync Settings
|
SETTINGS_SYNC_CONFIG_TITLE: Sync Settings
|
||||||
@ -390,6 +392,7 @@ MANAGE_SETTING_CLEAR_CACHE_TIPS: After clearing, the file list will be reloaded
|
|||||||
MANAGE_SETTING_CLEAR_CACHE_PROMPT: Are you sure you want to clear the file list cache database?
|
MANAGE_SETTING_CLEAR_CACHE_PROMPT: Are you sure you want to clear the file list cache database?
|
||||||
MANAGE_SETTING_CLEAR_CACHE_BUTTON: Clear
|
MANAGE_SETTING_CLEAR_CACHE_BUTTON: Clear
|
||||||
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: Display the original image instead of format icon (requires public access permissions)
|
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: Display the original image instead of format icon (requires public access permissions)
|
||||||
|
MANAGE_SETTING_ISUSEPRESIGNEDURL_TITLE: Use presigned URL for image display
|
||||||
MANAGE_SETTING_ISSHOWLIST_TITLE: Default display mode for the file list
|
MANAGE_SETTING_ISSHOWLIST_TITLE: Default display mode for the file list
|
||||||
MANAGE_SETTING_ISSHOWLIST_ON: List
|
MANAGE_SETTING_ISSHOWLIST_ON: List
|
||||||
MANAGE_SETTING_ISSHOWLIST_OFF: Card
|
MANAGE_SETTING_ISSHOWLIST_OFF: Card
|
||||||
|
@ -121,7 +121,7 @@ UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR: 水印颜色,请从取色器中选择
|
|||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: 水印图片路径(留空使用默认图片)
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: 水印图片路径(留空使用默认图片)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: 水印位置
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: 水印位置
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: 是否移除EXIF信息
|
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: 是否移除EXIF信息
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: 压缩质量
|
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: 压缩质量(1-100)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: 是否转换格式
|
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: 是否转换格式
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: 转换目的格式
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: 转换目的格式
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: '精细化转换格式, 请输入JSON格式,如: {"png": "jpg"}'
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: '精细化转换格式, 请输入JSON格式,如: {"png": "jpg"}'
|
||||||
@ -266,6 +266,8 @@ SETTINGS_SHORT_URL_C1N_TOKEN: C1N Token
|
|||||||
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
|
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
|
||||||
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
||||||
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker域名
|
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker域名
|
||||||
|
SETTINGS_SHORT_SINK_DOMAIN: sink域名
|
||||||
|
SETTINGS_SHORT_SINK_TOKEN: sink token
|
||||||
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上传后删除本地文件
|
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上传后删除本地文件
|
||||||
SETTINGS_SYNC_CONFIG: 设置配置同步
|
SETTINGS_SYNC_CONFIG: 设置配置同步
|
||||||
SETTINGS_SYNC_CONFIG_TITLE: 同步设置
|
SETTINGS_SYNC_CONFIG_TITLE: 同步设置
|
||||||
@ -392,6 +394,7 @@ MANAGE_SETTING_CLEAR_CACHE_TIPS: 清空后下次进入新目录时将会重新
|
|||||||
MANAGE_SETTING_CLEAR_CACHE_PROMPT: 确定要清空文件列表缓存数据库吗?
|
MANAGE_SETTING_CLEAR_CACHE_PROMPT: 确定要清空文件列表缓存数据库吗?
|
||||||
MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空
|
MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空
|
||||||
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 图片显示为原图而非默认文件格式图标(需要存储桶可公开访问)
|
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 图片显示为原图而非默认文件格式图标(需要存储桶可公开访问)
|
||||||
|
MANAGE_SETTING_ISUSEPRESIGNEDURL_TITLE: 使用预签名URL预览图片
|
||||||
MANAGE_SETTING_ISSHOWLIST_TITLE: 文件列表默认显示方式
|
MANAGE_SETTING_ISSHOWLIST_TITLE: 文件列表默认显示方式
|
||||||
MANAGE_SETTING_ISSHOWLIST_ON: 列表
|
MANAGE_SETTING_ISSHOWLIST_ON: 列表
|
||||||
MANAGE_SETTING_ISSHOWLIST_OFF: 卡片
|
MANAGE_SETTING_ISSHOWLIST_OFF: 卡片
|
||||||
|
@ -121,7 +121,7 @@ UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR: 水印顏色,請從取色器中選擇
|
|||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: 水印圖片路徑(留空使用預設圖片)
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPATH: 水印圖片路徑(留空使用預設圖片)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: 水印位置
|
UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION: 水印位置
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: 是否移除EXIF信息
|
UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF: 是否移除EXIF信息
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: 壓縮質量
|
UPLOAD_PAGE_IMAGE_PROCESS_QUALITY: 壓縮質量(1-100)
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: 是否轉換格式
|
UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT: 是否轉換格式
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: 轉換目的格式
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT: 轉換目的格式
|
||||||
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: '指定格式, 请输入JSON格式配置,如{"jpg":"png"}'
|
UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC: '指定格式, 请输入JSON格式配置,如{"jpg":"png"}'
|
||||||
@ -264,6 +264,8 @@ SETTINGS_SHORT_URL_C1N_TOKEN: C1N Token
|
|||||||
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
|
SETTINGS_SHORT_URL_YOURLS_DOMAIN: YOURLS域名
|
||||||
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: YOURLS signature
|
||||||
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker Host
|
SETTINGS_SHORT_URL_CF_WORKER_HOST: Cloudflare Worker Host
|
||||||
|
SETTINGS_SHORT_SINK_DOMAIN: sink域名
|
||||||
|
SETTINGS_SHORT_SINK_TOKEN: sink token
|
||||||
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上傳後刪除本地檔案
|
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: 上傳後刪除本地檔案
|
||||||
SETTINGS_SYNC_CONFIG: 設置同步配置
|
SETTINGS_SYNC_CONFIG: 設置同步配置
|
||||||
SETTINGS_SYNC_CONFIG_TITLE: 同步設置
|
SETTINGS_SYNC_CONFIG_TITLE: 同步設置
|
||||||
@ -390,6 +392,7 @@ MANAGE_SETTING_CLEAR_CACHE_TIPS: 清空後下次進入新目錄時將會重新
|
|||||||
MANAGE_SETTING_CLEAR_CACHE_PROMPT: 確定要清空檔案列表快取資料庫嗎?
|
MANAGE_SETTING_CLEAR_CACHE_PROMPT: 確定要清空檔案列表快取資料庫嗎?
|
||||||
MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空
|
MANAGE_SETTING_CLEAR_CACHE_BUTTON: 清空
|
||||||
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 顯示圖片的原始圖像而非預設的檔案格式圖示(需要存儲桶公開訪問權限)
|
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: 顯示圖片的原始圖像而非預設的檔案格式圖示(需要存儲桶公開訪問權限)
|
||||||
|
MANAGE_SETTING_ISUSEPRESIGNEDURL_TITLE: 使用預簽名URL预览圖片
|
||||||
MANAGE_SETTING_ISSHOWLIST_TITLE: 檔案列表預設顯示方式
|
MANAGE_SETTING_ISSHOWLIST_TITLE: 檔案列表預設顯示方式
|
||||||
MANAGE_SETTING_ISSHOWLIST_ON: 列表
|
MANAGE_SETTING_ISSHOWLIST_ON: 列表
|
||||||
MANAGE_SETTING_ISSHOWLIST_OFF: 卡片
|
MANAGE_SETTING_ISSHOWLIST_OFF: 卡片
|
||||||
|
@ -326,7 +326,13 @@ export function createTray(tooltip: string) {
|
|||||||
if (deleteLocalFile) {
|
if (deleteLocalFile) {
|
||||||
await fs.remove(rawInput[i])
|
await fs.remove(rawInput[i])
|
||||||
}
|
}
|
||||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
const [pasteTextItem, shortUrl] = await pasteTemplate(
|
||||||
|
pasteStyle,
|
||||||
|
imgs[i],
|
||||||
|
db.get(configPaths.settings.customLink)
|
||||||
|
)
|
||||||
|
imgs[i].shortUrl = shortUrl
|
||||||
|
pasteText.push(pasteTextItem)
|
||||||
const isShowResultNotification =
|
const isShowResultNotification =
|
||||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||||
? true
|
? true
|
||||||
@ -334,7 +340,7 @@ export function createTray(tooltip: string) {
|
|||||||
if (isShowResultNotification) {
|
if (isShowResultNotification) {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: T('UPLOAD_SUCCEED'),
|
title: T('UPLOAD_SUCCEED'),
|
||||||
body: imgs[i].imgUrl!
|
body: shortUrl || imgs[i].imgUrl!
|
||||||
// icon: files[i]
|
// icon: files[i]
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -54,7 +54,9 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
|||||||
if (img.length > 0) {
|
if (img.length > 0) {
|
||||||
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
const trayWindow = windowManager.get(IWindowList.TRAY_WINDOW)
|
||||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||||
handleCopyUrl(await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))
|
const [pastedText, shortUrl] = await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))
|
||||||
|
img[0].shortUrl = shortUrl
|
||||||
|
handleCopyUrl(pastedText)
|
||||||
const isShowResultNotification =
|
const isShowResultNotification =
|
||||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||||
? true
|
? true
|
||||||
@ -62,7 +64,7 @@ export const uploadClipboardFiles = async (): Promise<IStringKeyMap> => {
|
|||||||
if (isShowResultNotification) {
|
if (isShowResultNotification) {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: T('UPLOAD_SUCCEED'),
|
title: T('UPLOAD_SUCCEED'),
|
||||||
body: img[0].imgUrl!
|
body: shortUrl || img[0].imgUrl!
|
||||||
// icon: img[0].imgUrl
|
// icon: img[0].imgUrl
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -128,7 +130,13 @@ export const uploadChoosedFiles = async (
|
|||||||
picgo.log.error(err)
|
picgo.log.error(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
const [pasteTextItem, shortUrl] = await pasteTemplate(
|
||||||
|
pasteStyle,
|
||||||
|
imgs[i],
|
||||||
|
db.get(configPaths.settings.customLink)
|
||||||
|
)
|
||||||
|
imgs[i].shortUrl = shortUrl
|
||||||
|
pasteText.push(pasteTextItem)
|
||||||
const isShowResultNotification =
|
const isShowResultNotification =
|
||||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||||
? true
|
? true
|
||||||
@ -136,7 +144,7 @@ export const uploadChoosedFiles = async (
|
|||||||
if (isShowResultNotification) {
|
if (isShowResultNotification) {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: T('UPLOAD_SUCCEED'),
|
title: T('UPLOAD_SUCCEED'),
|
||||||
body: imgs[i].imgUrl!
|
body: shortUrl || imgs[i].imgUrl!
|
||||||
// icon: files[i].path
|
// icon: files[i].path
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { BrowserWindow, clipboard, ipcMain, Notification, WebContents } from 'electron'
|
import { BrowserWindow, clipboard, ipcMain, Notification, WebContents } from 'electron'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import util from 'util'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { IPicGo } from 'piclist'
|
import { IPicGo } from 'piclist'
|
||||||
|
import util from 'util'
|
||||||
import writeFile from 'write-file-atomic'
|
import writeFile from 'write-file-atomic'
|
||||||
|
|
||||||
import windowManager from 'apis/app/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
@ -22,7 +22,6 @@ import { CLIPBOARD_IMAGE_FOLDER } from '#/utils/static'
|
|||||||
|
|
||||||
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => {
|
const waitForRename = (window: BrowserWindow, id: number): Promise<string | null> => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const windowId = window.id
|
|
||||||
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: Event, newName: string) => {
|
ipcMain.once(`${RENAME_FILE_NAME}${id}`, (_: Event, newName: string) => {
|
||||||
resolve(newName)
|
resolve(newName)
|
||||||
window.close()
|
window.close()
|
||||||
@ -30,20 +29,21 @@ const waitForRename = (window: BrowserWindow, id: number): Promise<string | null
|
|||||||
window.on('close', () => {
|
window.on('close', () => {
|
||||||
resolve(null)
|
resolve(null)
|
||||||
ipcMain.removeAllListeners(`${RENAME_FILE_NAME}${id}`)
|
ipcMain.removeAllListeners(`${RENAME_FILE_NAME}${id}`)
|
||||||
windowManager.deleteById(windowId)
|
windowManager.deleteById(window.id)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) => {
|
const handleTalkingData = (webContents: WebContents, options: IAnalyticsData) => {
|
||||||
|
const { type, fromClipboard, count, duration } = options
|
||||||
const data: ITalkingDataOptions = {
|
const data: ITalkingDataOptions = {
|
||||||
EventId: 'upload',
|
EventId: 'upload',
|
||||||
Label: options.type,
|
Label: type,
|
||||||
MapKv: {
|
MapKv: {
|
||||||
by: options.fromClipboard ? 'clipboard' : 'files',
|
by: fromClipboard ? 'clipboard' : 'files',
|
||||||
count: options.count,
|
count,
|
||||||
duration: calcDurationRange(options.duration || 0),
|
duration: calcDurationRange(duration || 0),
|
||||||
type: options.type
|
type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
webContents.send(TALKING_DATA_EVENT, data)
|
webContents.send(TALKING_DATA_EVENT, data)
|
||||||
@ -58,8 +58,7 @@ class Uploader {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: Electron.NotificationConstructorOptions | undefined) => {
|
picgo.on(ICOREBuildInEvent.NOTIFICATION, (message: Electron.NotificationConstructorOptions | undefined) => {
|
||||||
const notification = new Notification(message)
|
new Notification(message).show()
|
||||||
notification.show()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
picgo.on(ICOREBuildInEvent.UPLOAD_PROGRESS, (progress: any) => {
|
picgo.on(ICOREBuildInEvent.UPLOAD_PROGRESS, (progress: any) => {
|
||||||
@ -84,15 +83,11 @@ class Uploader {
|
|||||||
await Promise.all(
|
await Promise.all(
|
||||||
ctx.output.map(async (item, index) => {
|
ctx.output.map(async (item, index) => {
|
||||||
let name: undefined | string | null
|
let name: undefined | string | null
|
||||||
let fileName: string | undefined
|
const fileName = autoRename
|
||||||
if (autoRename) {
|
? `${dayjs().add(index, 'ms').format('YYYYMMDDHHmmSSS')}${item.extname}`
|
||||||
fileName = dayjs().add(index, 'ms').format('YYYYMMDDHHmmSSS') + item.extname
|
: item.fileName
|
||||||
} else {
|
|
||||||
fileName = item.fileName
|
|
||||||
}
|
|
||||||
if (rename) {
|
if (rename) {
|
||||||
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
const window = windowManager.create(IWindowList.RENAME_WINDOW)!
|
||||||
logger.info('create rename window')
|
|
||||||
ipcMain.on(GET_RENAME_FILE_NAME, evt => {
|
ipcMain.on(GET_RENAME_FILE_NAME, evt => {
|
||||||
try {
|
try {
|
||||||
if (evt.sender.id === window.webContents.id) {
|
if (evt.sender.id === window.webContents.id) {
|
||||||
@ -118,61 +113,52 @@ class Uploader {
|
|||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getClipboardImagePath(): Promise<string | false> {
|
||||||
|
const imgPath = getClipboardFilePath()
|
||||||
|
if (imgPath) return imgPath
|
||||||
|
|
||||||
|
const nativeImage = clipboard.readImage()
|
||||||
|
if (nativeImage.isEmpty()) return false
|
||||||
|
|
||||||
|
const buffer = nativeImage.toPNG()
|
||||||
|
const baseDir = picgo.baseDir
|
||||||
|
const fileName = `${dayjs().format('YYYYMMDDHHmmSSS')}.png`
|
||||||
|
const filePath = path.join(baseDir, CLIPBOARD_IMAGE_FOLDER, fileName)
|
||||||
|
await writeFile(filePath, buffer)
|
||||||
|
return filePath
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* use electron's clipboard image to upload
|
* use electron's clipboard image to upload
|
||||||
*/
|
*/
|
||||||
async uploadWithBuildInClipboard(): Promise<ImgInfo[] | false> {
|
async uploadWithBuildInClipboard(): Promise<ImgInfo[] | false> {
|
||||||
let filePath = ''
|
let imgPath: string | false = false
|
||||||
try {
|
try {
|
||||||
const imgPath = getClipboardFilePath()
|
imgPath = await this.getClipboardImagePath()
|
||||||
if (!imgPath) {
|
if (!imgPath) return false
|
||||||
const nativeImage = clipboard.readImage()
|
|
||||||
if (nativeImage.isEmpty()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const buffer = nativeImage.toPNG()
|
|
||||||
const baseDir = picgo.baseDir
|
|
||||||
const fileName = `${dayjs().format('YYYYMMDDHHmmSSS')}.png`
|
|
||||||
filePath = path.join(baseDir, CLIPBOARD_IMAGE_FOLDER, fileName)
|
|
||||||
await writeFile(filePath, buffer)
|
|
||||||
return await this.upload([filePath])
|
|
||||||
} else {
|
|
||||||
return await this.upload([imgPath])
|
return await this.upload([imgPath])
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
if (filePath) {
|
if (imgPath) {
|
||||||
fs.remove(filePath)
|
fs.remove(imgPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadWithBuildInClipboardReturnCtx(img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
async uploadWithBuildInClipboardReturnCtx(img?: IUploadOption, skipProcess = false): Promise<IPicGo | false> {
|
||||||
let filePath = ''
|
let imgPath: string | false = false
|
||||||
try {
|
try {
|
||||||
const imgPath = getClipboardFilePath()
|
imgPath = await this.getClipboardImagePath()
|
||||||
if (!imgPath) {
|
if (!imgPath) return false
|
||||||
const nativeImage = clipboard.readImage()
|
|
||||||
if (nativeImage.isEmpty()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const buffer = nativeImage.toPNG()
|
|
||||||
const baseDir = picgo.baseDir
|
|
||||||
const fileName = `${dayjs().format('YYYYMMDDHHmmSSS')}.png`
|
|
||||||
filePath = path.join(baseDir, CLIPBOARD_IMAGE_FOLDER, fileName)
|
|
||||||
await writeFile(filePath, buffer)
|
|
||||||
return await this.uploadReturnCtx(img ?? [filePath], skipProcess)
|
|
||||||
} else {
|
|
||||||
return await this.uploadReturnCtx(img ?? [imgPath], skipProcess)
|
return await this.uploadReturnCtx(img ?? [imgPath], skipProcess)
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return false
|
return false
|
||||||
} finally {
|
} finally {
|
||||||
if (filePath) {
|
if (imgPath) {
|
||||||
fs.remove(filePath)
|
fs.remove(imgPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,7 +167,8 @@ class Uploader {
|
|||||||
try {
|
try {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
const ctx = await picgo.uploadReturnCtx(img, skipProcess)
|
const ctx = await picgo.uploadReturnCtx(img, skipProcess)
|
||||||
if (Array.isArray(ctx.output) && ctx.output.some((item: ImgInfo) => item.imgUrl)) {
|
if (!Array.isArray(ctx.output) || !ctx.output.some((item: ImgInfo) => item.imgUrl)) return false
|
||||||
|
|
||||||
if (this.webContents) {
|
if (this.webContents) {
|
||||||
handleTalkingData(this.webContents, {
|
handleTalkingData(this.webContents, {
|
||||||
fromClipboard: !img,
|
fromClipboard: !img,
|
||||||
@ -190,13 +177,12 @@ class Uploader {
|
|||||||
duration: Date.now() - startTime
|
duration: Date.now() - startTime
|
||||||
} as IAnalyticsData)
|
} as IAnalyticsData)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.output.forEach((item: ImgInfo) => {
|
ctx.output.forEach((item: ImgInfo) => {
|
||||||
item.config = JSON.parse(JSON.stringify(db.get(`picBed.${item.type}`)))
|
item.config = JSON.parse(JSON.stringify(db.get(`picBed.${item.type}`)))
|
||||||
})
|
})
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -216,7 +202,8 @@ class Uploader {
|
|||||||
try {
|
try {
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
const output = await picgo.upload(img)
|
const output = await picgo.upload(img)
|
||||||
if (Array.isArray(output) && output.some((item: ImgInfo) => item.imgUrl)) {
|
if (!Array.isArray(output) || !output.some((item: ImgInfo) => item.imgUrl)) return false
|
||||||
|
|
||||||
if (this.webContents) {
|
if (this.webContents) {
|
||||||
handleTalkingData(this.webContents, {
|
handleTalkingData(this.webContents, {
|
||||||
fromClipboard: !img,
|
fromClipboard: !img,
|
||||||
@ -229,9 +216,6 @@ class Uploader {
|
|||||||
item.config = JSON.parse(JSON.stringify(db.get(`picBed.${item.type}`)))
|
item.config = JSON.parse(JSON.stringify(db.get(`picBed.${item.type}`)))
|
||||||
})
|
})
|
||||||
return output.filter(item => item.imgUrl)
|
return output.filter(item => item.imgUrl)
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -21,14 +21,10 @@ const checkLogFileIsLarge = (logPath: string): CheckLogFileResult => {
|
|||||||
logFileSizeLimit: DEFAULT_LOG_FILE_SIZE_LIMIT
|
logFileSizeLimit: DEFAULT_LOG_FILE_SIZE_LIMIT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return { isLarge: false }
|
||||||
isLarge: false
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
return {
|
return { isLarge: true }
|
||||||
isLarge: true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +45,7 @@ const recreateLogFile = (logPath: string): void => {
|
|||||||
const getLogger = (logPath: string, logType: string) => {
|
const getLogger = (logPath: string, logType: string) => {
|
||||||
let hasUncathcedError = false
|
let hasUncathcedError = false
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(logPath)) {
|
|
||||||
fs.ensureFileSync(logPath)
|
fs.ensureFileSync(logPath)
|
||||||
}
|
|
||||||
if (checkLogFileIsLarge(logPath).isLarge) {
|
if (checkLogFileIsLarge(logPath).isLarge) {
|
||||||
recreateLogFile(logPath)
|
recreateLogFile(logPath)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,13 @@ class GuiApi implements IGuiApi {
|
|||||||
if (deleteLocalFile) {
|
if (deleteLocalFile) {
|
||||||
await fs.remove(rawInput[i])
|
await fs.remove(rawInput[i])
|
||||||
}
|
}
|
||||||
pasteText.push(await pasteTemplate(pasteStyle, imgs[i], db.get(configPaths.settings.customLink)))
|
const [pasteTextItem, shortUrl] = await pasteTemplate(
|
||||||
|
pasteStyle,
|
||||||
|
imgs[i],
|
||||||
|
db.get(configPaths.settings.customLink)
|
||||||
|
)
|
||||||
|
imgs[i].shortUrl = shortUrl
|
||||||
|
pasteText.push(pasteTextItem)
|
||||||
const isShowResultNotification =
|
const isShowResultNotification =
|
||||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||||
? true
|
? true
|
||||||
@ -103,7 +109,7 @@ class GuiApi implements IGuiApi {
|
|||||||
if (isShowResultNotification) {
|
if (isShowResultNotification) {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: T('UPLOAD_SUCCEED'),
|
title: T('UPLOAD_SUCCEED'),
|
||||||
body: imgs[i].imgUrl as string
|
body: shortUrl || (imgs[i].imgUrl! as string)
|
||||||
// icon: imgs[i].imgUrl
|
// icon: imgs[i].imgUrl
|
||||||
})
|
})
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -27,11 +27,11 @@ const galleryRoutes = [
|
|||||||
const [item, copy = true] = args
|
const [item, copy = true] = args
|
||||||
const pasteStyle = picgo.getConfig<IPasteStyle>(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
const pasteStyle = picgo.getConfig<IPasteStyle>(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||||
const customLink = picgo.getConfig<string>(configPaths.settings.customLink)
|
const customLink = picgo.getConfig<string>(configPaths.settings.customLink)
|
||||||
const txt = await pasteTemplate(pasteStyle, item, customLink)
|
const [txt, shortUrl] = await pasteTemplate(pasteStyle, item, customLink)
|
||||||
if (copy) {
|
if (copy) {
|
||||||
clipboard.writeText(txt)
|
clipboard.writeText(txt)
|
||||||
}
|
}
|
||||||
return txt
|
return [txt, shortUrl]
|
||||||
},
|
},
|
||||||
type: IRPCType.INVOKE
|
type: IRPCType.INVOKE
|
||||||
},
|
},
|
||||||
|
@ -40,7 +40,9 @@ const trayRoutes = [
|
|||||||
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
const img = await uploader.setWebContents(trayWindow.webContents).uploadWithBuildInClipboard()
|
||||||
if (img !== false) {
|
if (img !== false) {
|
||||||
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
const pasteStyle = db.get(configPaths.settings.pasteStyle) || IPasteStyle.MARKDOWN
|
||||||
handleCopyUrl(await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink)))
|
const [pasteText, shortUrl] = await pasteTemplate(pasteStyle, img[0], db.get(configPaths.settings.customLink))
|
||||||
|
img[0].shortUrl = shortUrl
|
||||||
|
handleCopyUrl(pasteText)
|
||||||
const isShowResultNotification =
|
const isShowResultNotification =
|
||||||
db.get(configPaths.settings.uploadResultNotification) === undefined
|
db.get(configPaths.settings.uploadResultNotification) === undefined
|
||||||
? true
|
? true
|
||||||
@ -48,7 +50,7 @@ const trayRoutes = [
|
|||||||
if (isShowResultNotification) {
|
if (isShowResultNotification) {
|
||||||
const notification = new Notification({
|
const notification = new Notification({
|
||||||
title: T('UPLOAD_SUCCEED'),
|
title: T('UPLOAD_SUCCEED'),
|
||||||
body: img[0].imgUrl!
|
body: shortUrl || img[0].imgUrl!
|
||||||
// icon: file[0]
|
// icon: file[0]
|
||||||
// icon: img[0].imgUrl
|
// icon: img[0].imgUrl
|
||||||
})
|
})
|
||||||
|
@ -41,18 +41,20 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
|||||||
|
|
||||||
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
const handleStartUpFiles = (argv: string[], cwd: string) => {
|
||||||
const files = getUploadFiles(argv, cwd, logger)
|
const files = getUploadFiles(argv, cwd, logger)
|
||||||
if (files === null || files.length > 0) {
|
|
||||||
// 如果有文件列表作为参数,说明是命令行启动
|
|
||||||
if (files === null) {
|
if (files === null) {
|
||||||
logger.info('cli -> uploading file from clipboard')
|
logger.info('cli -> uploading file from clipboard')
|
||||||
uploadClipboardFiles()
|
uploadClipboardFiles()
|
||||||
} else {
|
|
||||||
logger.info('cli -> uploading files from cli', ...files.map(item => item.path))
|
|
||||||
const win = windowManager.getAvailableWindow()
|
|
||||||
uploadChoosedFiles(win.webContents, files)
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (files.length > 0) {
|
||||||
|
logger.info('cli -> uploading files from cli', ...files.map(file => file.path))
|
||||||
|
const win = windowManager.getAvailableWindow()
|
||||||
|
uploadChoosedFiles(win.webContents, files)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ router.post(
|
|||||||
const picbed = urlparams?.get('picbed')
|
const picbed = urlparams?.get('picbed')
|
||||||
const passedKey = urlparams?.get('key')
|
const passedKey = urlparams?.get('key')
|
||||||
const serverKey = picgo.getConfig<string>(configPaths.settings.serverKey) || ''
|
const serverKey = picgo.getConfig<string>(configPaths.settings.serverKey) || ''
|
||||||
|
const useShortUrl = picgo.getConfig<boolean>(configPaths.settings.useShortUrl)
|
||||||
if (serverKey && passedKey !== serverKey) {
|
if (serverKey && passedKey !== serverKey) {
|
||||||
handleResponse({
|
handleResponse({
|
||||||
response,
|
response,
|
||||||
@ -92,8 +93,9 @@ router.post(
|
|||||||
// upload with clipboard
|
// upload with clipboard
|
||||||
logger.info('[PicList Server] upload clipboard file')
|
logger.info('[PicList Server] upload clipboard file')
|
||||||
const result = await uploadClipboardFiles()
|
const result = await uploadClipboardFiles()
|
||||||
const res = result.url
|
const res = useShortUrl ? result.fullResult.shortUrl || result.url : result.url
|
||||||
const fullResult = result.fullResult
|
const fullResult = result.fullResult
|
||||||
|
fullResult.imgUrl = useShortUrl ? fullResult.shortUrl || fullResult.imgUrl : fullResult.imgUrl
|
||||||
logger.info('[PicList Server] upload result:', res)
|
logger.info('[PicList Server] upload result:', res)
|
||||||
if (res) {
|
if (res) {
|
||||||
const treatedFullResult = {
|
const treatedFullResult = {
|
||||||
@ -130,7 +132,7 @@ router.post(
|
|||||||
const win = windowManager.getAvailableWindow()
|
const win = windowManager.getAvailableWindow()
|
||||||
const result = await uploadChoosedFiles(win.webContents, pathList)
|
const result = await uploadChoosedFiles(win.webContents, pathList)
|
||||||
const res = result.map(item => {
|
const res = result.map(item => {
|
||||||
return item.url
|
return useShortUrl ? item.fullResult.shortUrl || item.url : item.url
|
||||||
})
|
})
|
||||||
const fullResult = result.map((item: any) => {
|
const fullResult = result.map((item: any) => {
|
||||||
const treatedItem = {
|
const treatedItem = {
|
||||||
@ -139,6 +141,7 @@ router.post(
|
|||||||
...item.fullResult
|
...item.fullResult
|
||||||
}
|
}
|
||||||
delete treatedItem.config
|
delete treatedItem.config
|
||||||
|
treatedItem.imgUrl = useShortUrl ? treatedItem.shortUrl || treatedItem.imgUrl : treatedItem.imgUrl
|
||||||
return treatedItem
|
return treatedItem
|
||||||
})
|
})
|
||||||
logger.info('[PicList Server] upload result', res.join(' ; '))
|
logger.info('[PicList Server] upload result', res.join(' ; '))
|
||||||
|
@ -6,15 +6,13 @@ import { configPaths } from '#/utils/configPaths'
|
|||||||
import { DEFAULT_AES_PASSWORD } from '#/utils/static'
|
import { DEFAULT_AES_PASSWORD } from '#/utils/static'
|
||||||
|
|
||||||
export class AESHelper {
|
export class AESHelper {
|
||||||
key: Buffer
|
private key: Buffer = crypto.pbkdf2Sync(
|
||||||
|
picgo.getConfig<string>(configPaths.settings.aesPassword) || DEFAULT_AES_PASSWORD,
|
||||||
constructor() {
|
Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex'),
|
||||||
const userPassword = picgo.getConfig<string>(configPaths.settings.aesPassword) || DEFAULT_AES_PASSWORD
|
100000,
|
||||||
const fixedSalt = Buffer.from('a8b3c4d2e4f5098712345678feedc0de', 'hex')
|
32,
|
||||||
const fixedIterations = 100000
|
'sha512'
|
||||||
const keyLength = 32
|
)
|
||||||
this.key = crypto.pbkdf2Sync(userPassword, fixedSalt, fixedIterations, keyLength, 'sha512')
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypt(plainText: string) {
|
encrypt(plainText: string) {
|
||||||
const iv = crypto.randomBytes(16)
|
const iv = crypto.randomBytes(16)
|
||||||
@ -26,11 +24,9 @@ export class AESHelper {
|
|||||||
|
|
||||||
decrypt(encryptedData: string) {
|
decrypt(encryptedData: string) {
|
||||||
const [ivHex, encryptedText] = encryptedData.split(':')
|
const [ivHex, encryptedText] = encryptedData.split(':')
|
||||||
if (!ivHex || !encryptedText) {
|
if (!ivHex || !encryptedText) return '{}'
|
||||||
return '{}'
|
|
||||||
}
|
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, Buffer.from(ivHex, 'hex'))
|
||||||
const iv = Buffer.from(ivHex, 'hex')
|
|
||||||
const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv)
|
|
||||||
let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
|
let decrypted = decipher.update(encryptedText, 'hex', 'utf8')
|
||||||
decrypted += decipher.final('utf8')
|
decrypted += decipher.final('utf8')
|
||||||
return decrypted
|
return decrypted
|
||||||
|
@ -210,6 +210,34 @@ const generateCFWORKERShortUrl = async (url: string) => {
|
|||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const generateSinkShortUrl = async (url: string) => {
|
||||||
|
let sinkDomain = db.get(configPaths.settings.sinkDomain) || ''
|
||||||
|
const sinkToken = db.get(configPaths.settings.sinkToken) || ''
|
||||||
|
if (!sinkDomain || !sinkToken) {
|
||||||
|
logger.warn('Sink domain or token is not set')
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
if (!/^https?:\/\//.test(sinkDomain)) {
|
||||||
|
sinkDomain = `http://${sinkDomain}`
|
||||||
|
}
|
||||||
|
if (sinkDomain.endsWith('/')) {
|
||||||
|
sinkDomain = sinkDomain.slice(0, -1)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await axios.post(
|
||||||
|
`${sinkDomain}/api/link/create`,
|
||||||
|
{ url },
|
||||||
|
{ headers: { Authorization: `Bearer ${sinkToken}` } }
|
||||||
|
)
|
||||||
|
if (res.data?.link?.slug) {
|
||||||
|
return `${sinkDomain}/${res.data.link.slug}`
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error(e)
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
export const generateShortUrl = async (url: string) => {
|
export const generateShortUrl = async (url: string) => {
|
||||||
const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N
|
const server = db.get(configPaths.settings.shortUrlServer) || IShortUrlServer.C1N
|
||||||
switch (server) {
|
switch (server) {
|
||||||
@ -219,6 +247,8 @@ export const generateShortUrl = async (url: string) => {
|
|||||||
return generateYOURLSShortUrl(url)
|
return generateYOURLSShortUrl(url)
|
||||||
case IShortUrlServer.CFWORKER:
|
case IShortUrlServer.CFWORKER:
|
||||||
return generateCFWORKERShortUrl(url)
|
return generateCFWORKERShortUrl(url)
|
||||||
|
case IShortUrlServer.SINK:
|
||||||
|
return generateSinkShortUrl(url)
|
||||||
default:
|
default:
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
@ -9,61 +9,25 @@ interface IResultFileObject {
|
|||||||
}
|
}
|
||||||
type Result = IResultFileObject[]
|
type Result = IResultFileObject[]
|
||||||
|
|
||||||
interface IHandleArgvResult {
|
|
||||||
success: boolean
|
|
||||||
fileList?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleArgv = (argv: string[]): IHandleArgvResult => {
|
|
||||||
const uploadIndex = argv.indexOf('upload')
|
|
||||||
if (uploadIndex !== -1) {
|
|
||||||
const fileList = argv.slice(1 + uploadIndex)
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
fileList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
success: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger) => {
|
const getUploadFiles = (argv = process.argv, cwd = process.cwd(), logger: Logger) => {
|
||||||
const { success, fileList } = handleArgv(argv)
|
const uploadIndex = argv.indexOf('upload')
|
||||||
if (!success) {
|
if (uploadIndex === -1) return []
|
||||||
return []
|
const fileList = argv.slice(uploadIndex + 1)
|
||||||
} else {
|
|
||||||
if (fileList?.length === 0) {
|
if (fileList.length === 0) return null // for uploading images in clipboard
|
||||||
return null // for uploading images in clipboard
|
|
||||||
} else if ((fileList?.length || 0) > 0) {
|
return fileList
|
||||||
const result = fileList!
|
|
||||||
.map(item => {
|
.map(item => {
|
||||||
if (isUrl(item)) {
|
if (isUrl(item) || path.isAbsolute(item)) return { path: item }
|
||||||
return {
|
|
||||||
path: item
|
const resolvedPath = path.join(cwd, item)
|
||||||
|
if (fs.existsSync(resolvedPath)) {
|
||||||
|
return { path: resolvedPath }
|
||||||
}
|
}
|
||||||
}
|
logger.warn(`cli -> can't get file: ${resolvedPath}, invalid path`)
|
||||||
if (path.isAbsolute(item)) {
|
|
||||||
return {
|
|
||||||
path: item
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const tempPath = path.join(cwd, item)
|
|
||||||
if (fs.existsSync(tempPath)) {
|
|
||||||
return {
|
|
||||||
path: tempPath
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn(`cli -> can't get file: ${tempPath}, invalid path`)
|
|
||||||
return null
|
return null
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.filter(item => item !== null) as Result
|
.filter(item => item !== null) as Result
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { getUploadFiles }
|
export { getUploadFiles }
|
||||||
|
@ -32,7 +32,7 @@ export default async (style: IPasteStyle, item: ImgInfo, customLink: string | un
|
|||||||
url = handleUrlEncodeWithSetting(url)
|
url = handleUrlEncodeWithSetting(url)
|
||||||
const useShortUrl = db.get(configPaths.settings.useShortUrl) || false
|
const useShortUrl = db.get(configPaths.settings.useShortUrl) || false
|
||||||
if (useShortUrl) {
|
if (useShortUrl) {
|
||||||
url = await generateShortUrl(url)
|
url = item.shortUrl && item.shortUrl !== url ? item.shortUrl : await generateShortUrl(url)
|
||||||
}
|
}
|
||||||
const _customLink = customLink || '![$fileName]($url)'
|
const _customLink = customLink || '![$fileName]($url)'
|
||||||
const tpl = {
|
const tpl = {
|
||||||
@ -45,5 +45,5 @@ export default async (style: IPasteStyle, item: ImgInfo, customLink: string | un
|
|||||||
url
|
url
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return tpl[style]
|
return [tpl[style], useShortUrl ? url : '']
|
||||||
}
|
}
|
||||||
|
51
src/renderer/components/ImagePreSign.vue
Normal file
51
src/renderer/components/ImagePreSign.vue
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<template>
|
||||||
|
<el-image :src="imageSource" fit="contain" style="height: 100px; width: 100%; margin: 0 auto">
|
||||||
|
<template #placeholder>
|
||||||
|
<el-icon>
|
||||||
|
<Loading />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
<template #error>
|
||||||
|
<el-image :src="iconPath" fit="contain" style="height: 100px; width: 100%; margin: 0 auto" />
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, watch, computed } from 'vue'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
import { getFileIconPath } from '@/manage/utils/common'
|
||||||
|
import { IRPCActionType } from '#/types/enum'
|
||||||
|
import { triggerRPC } from '@/utils/common'
|
||||||
|
|
||||||
|
const preSignedUrl = ref('')
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
item: {
|
||||||
|
key: string
|
||||||
|
isImage: boolean
|
||||||
|
fileName: string | null | undefined
|
||||||
|
}
|
||||||
|
alias: string
|
||||||
|
url: string
|
||||||
|
config: any
|
||||||
|
isShowThumbnail: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const imageSource = computed(() => {
|
||||||
|
return props.isShowThumbnail && props.item.isImage
|
||||||
|
? preSignedUrl.value
|
||||||
|
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`))
|
||||||
|
|
||||||
|
async function getUrl() {
|
||||||
|
preSignedUrl.value = await triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => [props.url, props.item], getUrl, { deep: true })
|
||||||
|
|
||||||
|
onMounted(getUrl)
|
||||||
|
</script>
|
65
src/renderer/components/ImagePreSignTsx.tsx
Normal file
65
src/renderer/components/ImagePreSignTsx.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { ElImage, ElIcon } from 'element-plus'
|
||||||
|
import { defineComponent, ref, onMounted, watch, computed } from 'vue'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
import { getFileIconPath } from '@/manage/utils/common'
|
||||||
|
import { IRPCActionType } from '#/types/enum'
|
||||||
|
import { triggerRPC } from '@/utils/common'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
isShowThumbnail: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
item: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
alias: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const preSignedUrl = ref('')
|
||||||
|
|
||||||
|
const imageSource = computed(() => {
|
||||||
|
return props.isShowThumbnail && props.item.isImage
|
||||||
|
? preSignedUrl.value
|
||||||
|
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||||
|
})
|
||||||
|
const iconPath = computed(() =>
|
||||||
|
require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
async function getUrl() {
|
||||||
|
preSignedUrl.value = await triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => [props.url, props.item], getUrl, { deep: true })
|
||||||
|
onMounted(getUrl)
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<ElImage src={imageSource.value} fit='contain' style='height: 100px;width: 100%;margin: 0 auto;'>
|
||||||
|
{{
|
||||||
|
placeholder: () => (
|
||||||
|
<ElIcon>
|
||||||
|
<Loading />
|
||||||
|
</ElIcon>
|
||||||
|
),
|
||||||
|
error: () => <ElImage src={iconPath.value} fit='contain' style='height: 100px;width: 100%;margin: 0 auto;' />
|
||||||
|
}}
|
||||||
|
</ElImage>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
@ -63,7 +63,7 @@
|
|||||||
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
|
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
|
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
|
||||||
<el-input-number v-model="compressForm.quality" :min="0" :max="100" :step="1" />
|
<el-input-number v-model="compressForm.quality" :min="1" :max="100" :step="1" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')">
|
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')">
|
||||||
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
|
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<span v-for="(segment, index) in segments" :key="index" :style="segment.style">
|
<span v-for="(segment, index) in segments" :key="index" :style="segment.style">
|
||||||
{{ segment.text }}
|
{{ segment.text }}
|
||||||
</span>
|
</span>
|
||||||
<el-tooltip :content="tooltip" effect="dark" placement="right" :persistent="false" teleported>
|
<el-tooltip v-if="tooltip" :content="tooltip" effect="dark" placement="right" :persistent="false" teleported>
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<InfoFilled />
|
<InfoFilled />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
|
@ -391,12 +391,7 @@ https://www.baidu.com/img/bd_logo1.png"
|
|||||||
shadow="hover"
|
shadow="hover"
|
||||||
>
|
>
|
||||||
<el-image
|
<el-image
|
||||||
v-if="
|
v-if="!item.isDir && !['webdavplist', 'sftp', 'local', 's3plist'].includes(currentPicBedName)"
|
||||||
!item.isDir &&
|
|
||||||
currentPicBedName !== 'webdavplist' &&
|
|
||||||
currentPicBedName !== 'sftp' &&
|
|
||||||
currentPicBedName !== 'local'
|
|
||||||
"
|
|
||||||
:src="
|
:src="
|
||||||
isShowThumbnail && item.isImage
|
isShowThumbnail && item.isImage
|
||||||
? item.url
|
? item.url
|
||||||
@ -419,6 +414,39 @@ https://www.baidu.com/img/bd_logo1.png"
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
|
<el-image
|
||||||
|
v-else-if="!item.isDir && currentPicBedName === 's3plist' && !isUsePreSignedUrl"
|
||||||
|
:src="
|
||||||
|
isShowThumbnail && item.isImage
|
||||||
|
? item.url
|
||||||
|
: require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)
|
||||||
|
"
|
||||||
|
fit="contain"
|
||||||
|
style="height: 100px; width: 100%; margin: 0 auto"
|
||||||
|
@click="handleClickFile(item)"
|
||||||
|
>
|
||||||
|
<template #placeholder>
|
||||||
|
<el-icon>
|
||||||
|
<Loading />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
<template #error>
|
||||||
|
<el-image
|
||||||
|
:src="require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
|
||||||
|
fit="contain"
|
||||||
|
style="height: 100px; width: 100%; margin: 0 auto"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<ImagePreSign
|
||||||
|
v-else-if="!item.isDir && currentPicBedName === 's3plist' && isUsePreSignedUrl"
|
||||||
|
:is-show-thumbnail="isShowThumbnail"
|
||||||
|
:item="item"
|
||||||
|
:alias="configMap.alias"
|
||||||
|
:url="item.url"
|
||||||
|
:config="handleGetS3Config(item)"
|
||||||
|
@click="handleClickFile(item)"
|
||||||
|
/>
|
||||||
<ImageWebdav
|
<ImageWebdav
|
||||||
v-else-if="!item.isDir && currentPicBedName === 'webdavplist' && item.isImage"
|
v-else-if="!item.isDir && currentPicBedName === 'webdavplist' && item.isImage"
|
||||||
:is-show-thumbnail="isShowThumbnail"
|
:is-show-thumbnail="isShowThumbnail"
|
||||||
@ -1222,8 +1250,10 @@ import { textFileExt } from '@/manage/utils/textfile'
|
|||||||
import { videoExt } from '@/manage/utils/videofile'
|
import { videoExt } from '@/manage/utils/videofile'
|
||||||
|
|
||||||
import ImageWebdav from '@/components/ImageWebdav.vue'
|
import ImageWebdav from '@/components/ImageWebdav.vue'
|
||||||
|
import ImagePreSign from '@/components/ImagePreSign.vue'
|
||||||
import ImageLocal from '@/components/ImageLocal.vue'
|
import ImageLocal from '@/components/ImageLocal.vue'
|
||||||
import ImageWebdavTsx from '@/components/ImageWebdavTsx'
|
import ImageWebdavTsx from '@/components/ImageWebdavTsx'
|
||||||
|
import ImagePreSignTsx from '@/components/ImagePreSignTsx'
|
||||||
|
|
||||||
import { T as $T } from '@/i18n'
|
import { T as $T } from '@/i18n'
|
||||||
|
|
||||||
@ -1231,7 +1261,7 @@ import { getExtension, trimPath } from '#/utils/common'
|
|||||||
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '#/utils/static'
|
||||||
import { IUploadTask, IDownloadTask } from '#/types/manage'
|
import { IUploadTask, IDownloadTask } from '#/types/manage'
|
||||||
import { sendRPC, triggerRPC } from '@/utils/common'
|
import { sendRPC, triggerRPC } from '@/utils/common'
|
||||||
import { IRPCActionType } from 'root/src/universal/types/enum'
|
import { IRPCActionType } from '#/types/enum'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
configMap:{
|
configMap:{
|
||||||
@ -1378,6 +1408,7 @@ const calculateAllFileSize = computed(
|
|||||||
'0'
|
'0'
|
||||||
)
|
)
|
||||||
const isShowThumbnail = computed(() => manageStore.config.settings.isShowThumbnail ?? false)
|
const isShowThumbnail = computed(() => manageStore.config.settings.isShowThumbnail ?? false)
|
||||||
|
const isUsePreSignedUrl = computed(() => manageStore.config.settings.isUsePreSignedUrl ?? false)
|
||||||
const isAutoRefresh = computed(() => manageStore.config.settings.isAutoRefresh ?? false)
|
const isAutoRefresh = computed(() => manageStore.config.settings.isAutoRefresh ?? false)
|
||||||
const isIgnoreCase = computed(() => manageStore.config.settings.isIgnoreCase ?? false)
|
const isIgnoreCase = computed(() => manageStore.config.settings.isIgnoreCase ?? false)
|
||||||
|
|
||||||
@ -2901,6 +2932,18 @@ function singleRename() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleGetS3Config(item: any) {
|
||||||
|
return {
|
||||||
|
bucketName: configMap.bucketName,
|
||||||
|
region: configMap.bucketConfig.Location,
|
||||||
|
key: item.key,
|
||||||
|
customUrl: currentCustomDomain.value,
|
||||||
|
expires: manageStore.config.settings.PreSignedExpire,
|
||||||
|
githubPrivate: configMap.bucketConfig.private,
|
||||||
|
rawUrl: item.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function getPreSignedUrl(item: any) {
|
async function getPreSignedUrl(item: any) {
|
||||||
const param = {
|
const param = {
|
||||||
// tcyun
|
// tcyun
|
||||||
@ -3180,6 +3223,15 @@ const columns: Column<any>[] = [
|
|||||||
reference: () =>
|
reference: () =>
|
||||||
!item.isDir ? (
|
!item.isDir ? (
|
||||||
currentPicBedName.value !== 'webdavplist' ? (
|
currentPicBedName.value !== 'webdavplist' ? (
|
||||||
|
currentPicBedName.value === 's3plist' && item.isImage && isUsePreSignedUrl.value ? (
|
||||||
|
<ImagePreSignTsx
|
||||||
|
isShowThumbnail={isShowThumbnail.value}
|
||||||
|
item={item}
|
||||||
|
config={handleGetS3Config(item)}
|
||||||
|
url={item.url}
|
||||||
|
alias={configMap.alias}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
<ElImage
|
<ElImage
|
||||||
src={
|
src={
|
||||||
isShowThumbnail.value
|
isShowThumbnail.value
|
||||||
@ -3206,6 +3258,7 @@ const columns: Column<any>[] = [
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
</ElImage>
|
</ElImage>
|
||||||
|
)
|
||||||
) : item.isImage ? (
|
) : item.isImage ? (
|
||||||
<ImageWebdavTsx
|
<ImageWebdavTsx
|
||||||
isShowThumbnail={isShowThumbnail.value}
|
isShowThumbnail={isShowThumbnail.value}
|
||||||
@ -3235,6 +3288,14 @@ const columns: Column<any>[] = [
|
|||||||
config={handleGetWebdavConfig()}
|
config={handleGetWebdavConfig()}
|
||||||
url={item.url}
|
url={item.url}
|
||||||
/>
|
/>
|
||||||
|
) : currentPicBedName.value === 's3plist' && item.isImage && isUsePreSignedUrl.value ? (
|
||||||
|
<ImagePreSignTsx
|
||||||
|
isShowThumbnail={isShowThumbnail.value}
|
||||||
|
item={item}
|
||||||
|
config={handleGetS3Config(item)}
|
||||||
|
url={item.url}
|
||||||
|
alias={configMap.alias}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ElImage
|
<ElImage
|
||||||
src={item.isImage ? item.url : require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)}
|
src={item.isImage ? item.url : require(`./assets/icons/${getFileIconPath(item.fileName ?? '')}`)}
|
||||||
|
@ -214,6 +214,7 @@ const form = ref<IStringKeyMap>({
|
|||||||
isAutoRefresh: false,
|
isAutoRefresh: false,
|
||||||
isShowThumbnail: false,
|
isShowThumbnail: false,
|
||||||
isShowList: false,
|
isShowList: false,
|
||||||
|
isUsePreSignedUrl: false,
|
||||||
isIgnoreCase: false,
|
isIgnoreCase: false,
|
||||||
isForceCustomUrlHttps: false,
|
isForceCustomUrlHttps: false,
|
||||||
isEncodeUrl: false,
|
isEncodeUrl: false,
|
||||||
@ -246,6 +247,7 @@ const switchFieldsList = [
|
|||||||
'isAutoRefresh',
|
'isAutoRefresh',
|
||||||
'isShowThumbnail',
|
'isShowThumbnail',
|
||||||
'isShowList',
|
'isShowList',
|
||||||
|
'isUsePreSignedUrl',
|
||||||
'isForceCustomUrlHttps',
|
'isForceCustomUrlHttps',
|
||||||
'isEncodeUrl',
|
'isEncodeUrl',
|
||||||
'isUploadKeepDirStructure',
|
'isUploadKeepDirStructure',
|
||||||
@ -254,7 +256,7 @@ const switchFieldsList = [
|
|||||||
'randomStringRename',
|
'randomStringRename',
|
||||||
'customRename'
|
'customRename'
|
||||||
]
|
]
|
||||||
const switchFieldsNoTipsList = ['isShowThumbnail', 'isShowList']
|
const switchFieldsNoTipsList = ['isShowThumbnail', 'isShowList', 'isUsePreSignedUrl']
|
||||||
const switchFieldsHasActiveTextList = ['isShowList']
|
const switchFieldsHasActiveTextList = ['isShowList']
|
||||||
|
|
||||||
const switchFieldsConfigList = switchFieldsList.map(item => ({
|
const switchFieldsConfigList = switchFieldsList.map(item => ({
|
||||||
@ -321,7 +323,7 @@ async function initData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDownloadDirClick() {
|
async function handleDownloadDirClick() {
|
||||||
const result = triggerRPC<any>(IRPCActionType.MANAGE_SELECT_DOWNLOAD_FOLDER)
|
const result = await triggerRPC<any>(IRPCActionType.MANAGE_SELECT_DOWNLOAD_FOLDER)
|
||||||
if (result) {
|
if (result) {
|
||||||
form.value.downloadDir = result
|
form.value.downloadDir = result
|
||||||
}
|
}
|
||||||
|
@ -546,15 +546,21 @@ function handleClose() {
|
|||||||
|
|
||||||
async function copy(item: ImgInfo) {
|
async function copy(item: ImgInfo) {
|
||||||
item.config = JSON.parse(JSON.stringify(item.config) || '{}')
|
item.config = JSON.parse(JSON.stringify(item.config) || '{}')
|
||||||
const copyLink = await triggerRPC<string>(IRPCActionType.GALLERY_PASTE_TEXT, item)
|
const result = await triggerRPC<[string, string]>(IRPCActionType.GALLERY_PASTE_TEXT, item)
|
||||||
|
if (result && result[1] && item.id) {
|
||||||
|
await $$db.updateById(item.id, {
|
||||||
|
shortUrl: result[1]
|
||||||
|
})
|
||||||
|
}
|
||||||
const obj = {
|
const obj = {
|
||||||
title: $T('COPY_LINK_SUCCEED'),
|
title: $T('COPY_LINK_SUCCEED'),
|
||||||
body: copyLink
|
body: result ? result[0] : ''
|
||||||
}
|
}
|
||||||
const myNotification = new Notification(obj.title, obj)
|
const myNotification = new Notification(obj.title, obj)
|
||||||
myNotification.onclick = () => {
|
myNotification.onclick = () => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
updateGallery()
|
||||||
}
|
}
|
||||||
|
|
||||||
function remove(item: ImgInfo) {
|
function remove(item: ImgInfo) {
|
||||||
@ -743,8 +749,13 @@ async function multiCopy() {
|
|||||||
if (choosedList[key]) {
|
if (choosedList[key]) {
|
||||||
const item = await $$db.getById<ImgInfo>(key)
|
const item = await $$db.getById<ImgInfo>(key)
|
||||||
if (item) {
|
if (item) {
|
||||||
const txt = await triggerRPC<string>(IRPCActionType.GALLERY_PASTE_TEXT, item)
|
const result = await triggerRPC<string>(IRPCActionType.GALLERY_PASTE_TEXT, item)
|
||||||
copyString.push(txt!)
|
copyString.push(result ? result[0] : '')
|
||||||
|
if (result && result[1] && item.id) {
|
||||||
|
await $$db.updateById(item.id, {
|
||||||
|
shortUrl: result[1]
|
||||||
|
})
|
||||||
|
}
|
||||||
choosedList[key] = false
|
choosedList[key] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -758,6 +769,7 @@ async function multiCopy() {
|
|||||||
myNotification.onclick = () => {
|
myNotification.onclick = () => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
updateGallery()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,6 +360,28 @@
|
|||||||
:placeholder="$T('SETTINGS_SHORT_URL_CF_WORKER_HOST')"
|
:placeholder="$T('SETTINGS_SHORT_URL_CF_WORKER_HOST')"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formOfSetting.useShortUrl && formOfSetting.shortUrlServer === 'sink'"
|
||||||
|
:label="$T('SETTINGS_SHORT_SINK_DOMAIN')"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formOfSetting.sinkDomain"
|
||||||
|
size="small"
|
||||||
|
style="width: 50%"
|
||||||
|
:placeholder="$T('SETTINGS_SHORT_SINK_DOMAIN')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="formOfSetting.useShortUrl && formOfSetting.shortUrlServer === 'sink'"
|
||||||
|
:label="$T('SETTINGS_SHORT_SINK_TOKEN')"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="formOfSetting.sinkToken"
|
||||||
|
size="small"
|
||||||
|
style="width: 50%"
|
||||||
|
:placeholder="$T('SETTINGS_SHORT_SINK_TOKEN')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item :label="$T('SETTINGS_ENCODE_OUTPUT_URL')">
|
<el-form-item :label="$T('SETTINGS_ENCODE_OUTPUT_URL')">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="formOfSetting.encodeOutputURL"
|
v-model="formOfSetting.encodeOutputURL"
|
||||||
@ -976,6 +998,10 @@ const shortUrlServerList = [
|
|||||||
{
|
{
|
||||||
label: 'xyTom/Url-Shorten-Worker',
|
label: 'xyTom/Url-Shorten-Worker',
|
||||||
value: 'cf_worker'
|
value: 'cf_worker'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ccbikai/Sink',
|
||||||
|
value: 'sink'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1050,6 +1076,8 @@ const formOfSetting = ref<ISettingForm>({
|
|||||||
yourlsDomain: '',
|
yourlsDomain: '',
|
||||||
yourlsSignature: '',
|
yourlsSignature: '',
|
||||||
cfWorkerHost: '',
|
cfWorkerHost: '',
|
||||||
|
sinkDomain: '',
|
||||||
|
sinkToken: '',
|
||||||
deleteLocalFile: false,
|
deleteLocalFile: false,
|
||||||
serverKey: '',
|
serverKey: '',
|
||||||
aesPassword: 'PicList-aesPassword',
|
aesPassword: 'PicList-aesPassword',
|
||||||
@ -1089,6 +1117,8 @@ const autoWatchKeys = [
|
|||||||
'yourlsDomain',
|
'yourlsDomain',
|
||||||
'yourlsSignature',
|
'yourlsSignature',
|
||||||
'cfWorkerHost',
|
'cfWorkerHost',
|
||||||
|
'sinkDomain',
|
||||||
|
'sinkToken',
|
||||||
'registry',
|
'registry',
|
||||||
'proxy',
|
'proxy',
|
||||||
'autoCopy',
|
'autoCopy',
|
||||||
|
@ -241,7 +241,8 @@ export enum II18nLanguage {
|
|||||||
export enum IShortUrlServer {
|
export enum IShortUrlServer {
|
||||||
C1N = 'c1n',
|
C1N = 'c1n',
|
||||||
YOURLS = 'yourls',
|
YOURLS = 'yourls',
|
||||||
CFWORKER = 'cf_worker'
|
CFWORKER = 'cf_worker',
|
||||||
|
SINK = 'sink'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum commonTaskStatus {
|
export enum commonTaskStatus {
|
||||||
|
3
src/universal/types/i18n.d.ts
vendored
3
src/universal/types/i18n.d.ts
vendored
@ -259,6 +259,8 @@ interface ILocales {
|
|||||||
SETTINGS_SHORT_URL_YOURLS_DOMAIN: string
|
SETTINGS_SHORT_URL_YOURLS_DOMAIN: string
|
||||||
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: string
|
SETTINGS_SHORT_URL_YOURLS_SIGNATURE: string
|
||||||
SETTINGS_SHORT_URL_CF_WORKER_HOST: string
|
SETTINGS_SHORT_URL_CF_WORKER_HOST: string
|
||||||
|
SETTINGS_SHORT_SINK_DOMAIN: string
|
||||||
|
SETTINGS_SHORT_SINK_TOKEN: string
|
||||||
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: string
|
SETTINGS_DELETE_LOCAL_FILE_AFTER_UPLOAD: string
|
||||||
SETTINGS_SYNC_CONFIG: string
|
SETTINGS_SYNC_CONFIG: string
|
||||||
SETTINGS_SYNC_CONFIG_TITLE: string
|
SETTINGS_SYNC_CONFIG_TITLE: string
|
||||||
@ -366,6 +368,7 @@ interface ILocales {
|
|||||||
MANAGE_SETTING_CLEAR_CACHE_PROMPT: string
|
MANAGE_SETTING_CLEAR_CACHE_PROMPT: string
|
||||||
MANAGE_SETTING_CLEAR_CACHE_BUTTON: string
|
MANAGE_SETTING_CLEAR_CACHE_BUTTON: string
|
||||||
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: string
|
MANAGE_SETTING_ISSHOWTHUMBNAIL_TITLE: string
|
||||||
|
MANAGE_SETTING_ISUSEPRESIGNEDURL_TITLE: string
|
||||||
MANAGE_SETTING_ISSHOWLIST_TITLE: string
|
MANAGE_SETTING_ISSHOWLIST_TITLE: string
|
||||||
MANAGE_SETTING_ISSHOWLIST_ON: string
|
MANAGE_SETTING_ISSHOWLIST_ON: string
|
||||||
MANAGE_SETTING_ISSHOWLIST_OFF: string
|
MANAGE_SETTING_ISSHOWLIST_OFF: string
|
||||||
|
2
src/universal/types/view.d.ts
vendored
2
src/universal/types/view.d.ts
vendored
@ -26,6 +26,8 @@ interface ISettingForm {
|
|||||||
yourlsDomain: string
|
yourlsDomain: string
|
||||||
yourlsSignature: string
|
yourlsSignature: string
|
||||||
cfWorkerHost: string
|
cfWorkerHost: string
|
||||||
|
sinkDomain: string
|
||||||
|
sinkToken: string
|
||||||
deleteLocalFile: boolean
|
deleteLocalFile: boolean
|
||||||
serverKey: string
|
serverKey: string
|
||||||
aesPassword: string
|
aesPassword: string
|
||||||
|
@ -71,6 +71,8 @@ export interface IConfigStruct {
|
|||||||
cfWorkerHost: string
|
cfWorkerHost: string
|
||||||
yourlsDomain: string
|
yourlsDomain: string
|
||||||
yourlsSignature: string
|
yourlsSignature: string
|
||||||
|
sinkDomain: string
|
||||||
|
sinkToken: string
|
||||||
isSilentNotice: boolean
|
isSilentNotice: boolean
|
||||||
proxy: string
|
proxy: string
|
||||||
registry: string
|
registry: string
|
||||||
@ -153,6 +155,8 @@ export const configPaths = {
|
|||||||
cfWorkerHost: 'settings.cfWorkerHost',
|
cfWorkerHost: 'settings.cfWorkerHost',
|
||||||
yourlsDomain: 'settings.yourlsDomain',
|
yourlsDomain: 'settings.yourlsDomain',
|
||||||
yourlsSignature: 'settings.yourlsSignature',
|
yourlsSignature: 'settings.yourlsSignature',
|
||||||
|
sinkDomain: 'settings.sinkDomain',
|
||||||
|
sinkToken: 'settings.sinkToken',
|
||||||
isSilentNotice: 'settings.isSilentNotice',
|
isSilentNotice: 'settings.isSilentNotice',
|
||||||
proxy: 'settings.proxy',
|
proxy: 'settings.proxy',
|
||||||
registry: 'settings.registry',
|
registry: 'settings.registry',
|
||||||
|
@ -11956,10 +11956,10 @@ performance-now@^2.1.0:
|
|||||||
resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
|
||||||
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
|
||||||
|
|
||||||
piclist@^1.9.6:
|
piclist@^1.9.7:
|
||||||
version "1.9.6"
|
version "1.9.7"
|
||||||
resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.9.6.tgz#010753f7b7cedc076251bd1639f7859e48508386"
|
resolved "https://registry.yarnpkg.com/piclist/-/piclist-1.9.7.tgz#f34714248c8b72b009626fd7feaa25ac663a0bb4"
|
||||||
integrity sha512-CAUbU43/eibk/Jq+SXPL96TXog1vNjpE1pwbsof+D8A8SEpXRg+K5cLAsRjGXubi/SmLonU+imtbldUNuCoHjA==
|
integrity sha512-52cRCmGZx3jK9unzK4CAmgzIhOOYrfI71wvdigXjNnWGwfVjhxgWj8UkTLSZ9zPRJXzrOLmJvck30UhWUElBsg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@aws-sdk/client-s3" "3.421.0"
|
"@aws-sdk/client-s3" "3.421.0"
|
||||||
"@aws-sdk/lib-storage" "3.421.0"
|
"@aws-sdk/lib-storage" "3.421.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user