Feature: add video,text file and markdown file preview

This commit is contained in:
萌萌哒赫萝
2023-02-21 18:42:50 +08:00
parent 893e33d591
commit b3ce9b9543
21 changed files with 967 additions and 23 deletions

View File

@@ -18,6 +18,11 @@ import vue3PhotoPreview from 'vue3-photo-preview'
import 'vue3-photo-preview/dist/index.css'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import 'highlight.js/styles/atom-one-dark.css'
import hljsVuePlugin from '@highlightjs/vue-plugin'
import hljsCommon from 'highlight.js/lib/common'
import VueVideoPlayer from '@videojs-player/vue'
import 'video.js/dist/video-js.css'
webFrame.setVisualZoomLevelLimits(1, 1)
@@ -60,6 +65,9 @@ app.use(store)
app.use(vue3PhotoPreview)
app.use(pinia)
app.use(ContextMenu)
console.log(hljsCommon.highlightAuto('<h1>Highlight.js has been registered successfully!</h1>').value)
app.use(hljsVuePlugin)
app.use(VueVideoPlayer)
app.mount('#app')
initTalkingData()

View File

@@ -6,6 +6,7 @@ import SmmsApi from './smms'
import GithubApi from './github'
import ImgurApi from './imgur'
import S3plistApi from './s3plist'
import WebdavplistApi from './webdavplist'
export default {
TcyunApi,
@@ -15,5 +16,6 @@ export default {
SmmsApi,
GithubApi,
ImgurApi,
S3plistApi
S3plistApi,
WebdavplistApi
}

View File

@@ -18,7 +18,7 @@ import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import https from 'https'
import http from 'http'
import { ManageLogger } from '../utils/logger'
import { formatError, getAgent, getFileMimeType, gotDownload } from '../utils/common'
import { formatEndpoint, formatError, getAgent, getFileMimeType, gotDownload } from '../utils/common'
import { isImage } from '@/manage/utils/common'
import { HttpsProxyAgent, HttpProxyAgent } from 'hpagent'
import windowManager from 'apis/app/window/windowManager'
@@ -64,7 +64,7 @@ class S3plistApi {
accessKeyId,
secretAccessKey
},
endpoint: endpoint ? this.formatEndpoint(endpoint, sslEnabled) : undefined,
endpoint: endpoint ? formatEndpoint(endpoint, sslEnabled) : undefined,
sslEnabled,
s3ForcePathStyle,
httpOptions: {
@@ -75,9 +75,6 @@ class S3plistApi {
this.agent = this.setAgent(proxy, sslEnabled)
}
formatEndpoint = (endpoint: string, sslEnabled: boolean): string =>
!/^https?:\/\//.test(endpoint) ? `${sslEnabled ? 'https' : 'http'}://${endpoint}` : endpoint
setAgent (proxy: string | undefined, sslEnabled: boolean) : HttpProxyAgent | HttpsProxyAgent | undefined {
if (sslEnabled) {
const agent = getAgent(proxy, true).https

View File

@@ -0,0 +1,130 @@
import ManageLogger from '../utils/logger'
import { createClient, WebDAVClient, FileStat } from 'webdav'
import { formatError, formatEndpoint, getInnerAgent } from '../utils/common'
import { isImage } from '@/manage/utils/common'
import http from 'http'
import https from 'https'
import windowManager from 'apis/app/window/windowManager'
import { IWindowList } from '#/types/enum'
import { ipcMain, IpcMainEvent } from 'electron'
class WebdavplistApi {
endpoint: string
username: string
password: string
sslEnabled: boolean
proxy: string | undefined
logger: ManageLogger
agent: https.Agent | http.Agent
ctx: WebDAVClient
constructor (endpoint: string, username: string, password: string, sslEnabled: boolean, proxy: string | undefined, logger: ManageLogger) {
this.endpoint = formatEndpoint(endpoint, sslEnabled)
this.username = username
this.password = password
this.sslEnabled = sslEnabled
this.proxy = proxy
this.logger = logger
this.agent = getInnerAgent(proxy, sslEnabled).agent
this.ctx = createClient(
this.endpoint,
{
username: this.username,
password: this.password,
maxBodyLength: 4 * 1024 * 1024 * 1024,
maxContentLength: 4 * 1024 * 1024 * 1024,
httpsAgent: sslEnabled ? this.agent : undefined,
httpAgent: !sslEnabled ? this.agent : undefined
}
)
}
logParam = (error:any, method: string) =>
this.logger.error(formatError(error, { class: 'WebdavplistApi', method }))
formatFolder (item: FileStat, urlPrefix: string) {
return {
...item,
key: item.filename.replace(/^\/+/, ''),
fileName: item.basename,
fileSize: 0,
Key: item.filename.replace(/^\/+/, ''),
formatedTime: '',
isDir: true,
checked: false,
isImage: false,
match: false,
url: `${urlPrefix}${item.filename}`
}
}
formatFile (item: FileStat, urlPrefix: string) {
return {
...item,
key: item.filename.replace(/^\/+/, ''),
fileName: item.basename,
fileSize: item.size,
Key: item.filename.replace(/^\/+/, ''),
formatedTime: new Date(item.lastmod).toLocaleString(),
isDir: false,
checked: false,
match: false,
isImage: isImage(item.basename),
url: `${urlPrefix}${item.filename}`
}
}
isRequestSuccess = (code: number) => code >= 200 && code < 300
async getBucketListBackstage (configMap: IStringKeyMap): Promise<any> {
const window = windowManager.get(IWindowList.SETTING_WINDOW)!
const { prefix, customUrl, cancelToken } = configMap
const urlPrefix = customUrl || this.endpoint
const cancelTask = [false]
ipcMain.on('cancelLoadingFileList', (_evt: IpcMainEvent, token: string) => {
if (token === cancelToken) {
cancelTask[0] = true
ipcMain.removeAllListeners('cancelLoadingFileList')
}
})
let res = {} as any
const result = {
fullList: <any>[],
success: false,
finished: false
}
try {
res = await this.ctx.getDirectoryContents(prefix, {
deep: false,
details: true
})
if (this.isRequestSuccess(res.status)) {
if (res.data && res.data.length) {
res.data.forEach((item: FileStat) => {
if (item.type === 'directory') {
result.fullList.push(this.formatFolder(item, urlPrefix))
} else {
result.fullList.push(this.formatFile(item, urlPrefix))
}
})
}
} else {
result.finished = true
window.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList')
return
}
} catch (error) {
this.logParam(error, 'getBucketListBackstage')
result.finished = true
window.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList')
}
result.success = true
result.finished = true
window.webContents.send('refreshFileTransferList', result)
ipcMain.removeAllListeners('cancelLoadingFileList')
}
}
export default WebdavplistApi

View File

@@ -69,6 +69,8 @@ export class ManageApi extends EventEmitter implements ManageApiType {
return new API.ImgurApi(this.currentPicBedConfig.imgurUserName, this.currentPicBedConfig.accessToken, this.currentPicBedConfig.proxy, this.logger)
case 's3plist':
return new API.S3plistApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.secretAccessKey, this.currentPicBedConfig.endpoint, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.s3ForcePathStyle, this.currentPicBedConfig.proxy, this.logger)
case 'webdavplist':
return new API.WebdavplistApi(this.currentPicBedConfig.endpoint, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.proxy, this.logger)
default:
return {} as any
}
@@ -172,6 +174,12 @@ export class ManageApi extends EventEmitter implements ManageApiType {
Location: 'smms',
CreationDate: new Date().toISOString()
}]
case 'webdavplist':
return [{
Name: 'webdav',
Location: 'webdav',
CreationDate: new Date().toISOString()
}]
default:
console.log(param)
return []
@@ -309,6 +317,7 @@ export class ManageApi extends EventEmitter implements ManageApiType {
case 'github':
case 'imgur':
case 's3plist':
case 'webdavplist':
try {
client = this.createClient() as any
return await client.getBucketListBackstage(param!)

View File

@@ -14,8 +14,10 @@ import UpDownTaskQueue,
downloadTaskSpecialStatus
} from '../datastore/upDownTaskQueue'
import { ManageLogger } from '../utils/logger'
import { formatHttpProxy } from '@/manage/utils/common'
import { formatHttpProxy, IHTTPProxy } from '@/manage/utils/common'
import { HttpsProxyAgent, HttpProxyAgent } from 'hpagent'
import http from 'http'
import https from 'https'
export const getFSFile = async (
filePath: string,
@@ -244,6 +246,47 @@ export const getAgent = (proxy:any, https: boolean = true) => {
}
}
export const getInnerAgent = (proxy: any, sslEnabled: boolean = true) => {
const formatProxy = formatHttpProxy(proxy, 'object') as IHTTPProxy
if (sslEnabled) {
return formatProxy
? {
agent: new https.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
rejectUnauthorized: false,
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined,
host: formatProxy.host,
port: formatProxy.port
})
}
: {
agent: new https.Agent({
rejectUnauthorized: false,
keepAlive: true
})
}
} else {
return formatProxy
? {
agent: new http.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined,
host: formatProxy.host,
port: formatProxy.port
})
}
: {
agent: new http.Agent({
keepAlive: true,
keepAliveMsecs: 1000,
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
})
}
}
}
export function getOptions (
method?: string,
headers?: IStringKeyMap,
@@ -270,3 +313,10 @@ export function getOptions (
})
return options
}
export const formatEndpoint = (endpoint: string, sslEnabled: boolean): string =>
!/^https?:\/\//.test(endpoint)
? `${sslEnabled ? 'https' : 'http'}://${endpoint}`
: sslEnabled
? endpoint.replace('http://', 'https://')
: endpoint.replace('https://', 'http://')

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

View File

@@ -1,4 +1,4 @@
/*
ea/*
*UI布局和部分样式代码参考了https://github.com/willnewii/qiniuClient
*感谢作者@willnewii
*/
@@ -29,7 +29,7 @@
/>
</el-select>
<el-input
v-else-if="['aliyun', 'qiniu', 'tcyun', 's3plist'].includes(currentPicBedName)"
v-else-if="showCustomUrlInput"
v-model="currentCustomUrl"
placeholder="请输入自定义域名"
style="width: 200px;"
@@ -764,18 +764,97 @@ https://www.baidu.com/img/bd_logo1.png"
</el-tab-pane>
</el-tabs>
</el-drawer>
<el-dialog
v-model="isShowMarkDownDialog"
title="预览MD"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<div
style="-webkit-user-select: text"
v-html="markDownContent"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowMarkDownDialog = false}"
/>
</el-dialog>
<el-dialog
v-model="isShowTextFileDialog"
title="预览"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<highlightjs
style="-webkit-user-select: text;"
language="js"
:code="textfileContent"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowTextFileDialog = false}"
/>
</el-dialog>
<el-dialog
v-model="isShowVideoFileDialog"
title="播放"
center
align-center
draggable
fullscreen
close-on-press-escape
show-close
destroy-on-close
>
<video-player
:src="videoFileUrl"
controls
:loop="true"
:volume="0.6"
:autoplay="true"
:width="1100"
:height="700"
/>
<el-button
type="danger"
:icon="Close"
size="large"
style="position: fixed;bottom: 10px;right: 15px"
circle
@click="() => {isShowVideoFileDialog = false}"
/>
</el-dialog>
</div>
</template>
<script lang="tsx" setup>
import { ref, reactive, watch, onBeforeMount, computed, onBeforeUnmount } from 'vue'
import { useRoute } from 'vue-router'
import { Folder, FolderAdd, Upload, CircleClose, Loading, CopyDocument, Edit, DocumentAdd, Link, Refresh, ArrowRight, HomeFilled, Document, Coin, Download, DeleteFilled, Sort, FolderOpened } from '@element-plus/icons-vue'
import { Close, Folder, FolderAdd, Upload, CircleClose, Loading, CopyDocument, Edit, DocumentAdd, Link, Refresh, ArrowRight, HomeFilled, Document, Coin, Download, DeleteFilled, Sort, FolderOpened } from '@element-plus/icons-vue'
import { useManageStore } from '../store/manageStore'
import { renameFile, formatLink, formatFileName, getFileIconPath, formatFileSize, getExtension, isValidUrl } from '../utils/common'
import { ipcRenderer, clipboard, IpcRendererEvent } from 'electron'
import { fileCacheDbInstance } from '../store/bucketFileDb'
import { trimPath } from '~/main/manage/utils/common'
import axios from 'axios'
import {
ElMessage, ElMessageBox, ElNotification,
ElButton,
@@ -798,7 +877,9 @@ import path from 'path'
import { IUploadTask, IDownloadTask } from '~/main/manage/datastore/upDownTaskQueue'
import fs from 'fs-extra'
import { getConfig, saveConfig } from '../utils/dataSender'
import { marked } from 'marked'
import { textFileExt } from '../utils/textfile'
import { videoExt } from '../utils/videofile'
/*
configMap:{
prefix: string, -> baseDir
@@ -871,12 +952,20 @@ const isLoadingUploadPanelFiles = ref(false)
const tableData = reactive([] as any[])
const customUrlList = ref([] as any[])
const currentCustomUrl = ref('')
const isShowMarkDownDialog = ref(false)
const markDownContent = ref('')
const isShowTextFileDialog = ref(false)
const textfileContent = ref('')
const isShowVideoFileDialog = ref(false)
const videoFileUrl = ref('')
const showCustomUrlSelectList = computed(() => ['tcyun', 'aliyun', 'qiniu', 'github'].includes(currentPicBedName.value))
const showCreateNewFolder = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 'github', 's3plist'].includes(currentPicBedName.value))
const showCustomUrlInput = computed(() => ['aliyun', 'qiniu', 'tcyun', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showRenameFileIcon = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist'].includes(currentPicBedName.value))
const showCreateNewFolder = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 'github', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showRenameFileIcon = computed(() => ['tcyun', 'aliyun', 'qiniu', 'upyun', 's3plist', 'webdavplist'].includes(currentPicBedName.value))
const showPresignedUrl = computed(() => ['tcyun', 'aliyun', 'qiniu', 'github', 's3plist'].includes(currentPicBedName.value))
@@ -1177,6 +1266,40 @@ async function handleClickFile (item: any) {
showLoadingPage.value = true
await resetParam(false)
showLoadingPage.value = false
} else if (item.fileName.endsWith('.md')) {
try {
ElMessage({
message: '开始获取文件',
duration: 300,
type: 'success'
})
const fileUrl = item.url
const res = await axios.get(fileUrl)
const content = res.data
markDownContent.value = marked(content)
isShowMarkDownDialog.value = true
} catch (error) {
ElMessage.error('获取文件内容失败')
}
} else if (textFileExt.includes(path.extname(item.fileName).toLowerCase()) ||
textFileExt.includes(item.fileName.toLowerCase())
) {
try {
ElMessage({
message: '开始获取文件',
duration: 300,
type: 'success'
})
const fileUrl = item.url
const res = await axios.get(fileUrl)
textfileContent.value = res.data
isShowTextFileDialog.value = true
} catch (error) {
ElMessage.error('获取文件内容失败')
}
} else if (videoExt.includes(path.extname(item.fileName).toLowerCase())) {
videoFileUrl.value = item.url
isShowVideoFileDialog.value = true
}
}
@@ -1206,7 +1329,7 @@ async function handelChangeCustomUrl () {
showLoadingPage.value = true
await resetParam(true)
showLoadingPage.value = false
} else if (['aliyun', 'tcyun', 'qiniu', 's3plist'].includes(currentPicBedName.value)) {
} else if (['aliyun', 'tcyun', 'qiniu', 's3plist', 'webdavplist'].includes(currentPicBedName.value)) {
const currentConfigs = await getConfig<any>('picBed')
const currentConfig = currentConfigs[configMap.alias]
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
@@ -1297,6 +1420,20 @@ async function initCustomUrlList () {
}
}
handelChangeCustomUrl()
} else if (currentPicBedName.value === 'webdavplist') {
const currentConfigs = await getConfig<any>('picBed')
const currentConfig = currentConfigs[configMap.alias]
const currentTransformedConfig = JSON.parse(currentConfig.transformedConfig ?? '{}')
if (currentTransformedConfig[configMap.bucketName] && currentTransformedConfig[configMap.bucketName]?.customUrl) {
currentCustomUrl.value = currentTransformedConfig[configMap.bucketName].customUrl
} else {
let endpoint = manageStore.config.picBed[configMap.alias].endpoint
if (!/^https?:\/\//.test(endpoint)) {
endpoint = 'http://' + endpoint
}
currentCustomUrl.value = endpoint
}
handelChangeCustomUrl()
}
}

View File

@@ -153,6 +153,7 @@
v-if="supportedPicBedList[item.icon].configOptions[option].type === 'string'"
v-model.trim="configResult[item.icon + '.' + option]"
:placeholder="supportedPicBedList[item.icon].configOptions[option].placeholder"
:disabled="!!supportedPicBedList[item.icon].configOptions[option].disabled"
/>
<el-switch
v-else-if="supportedPicBedList[item.icon].configOptions[option].type === 'boolean'"

View File

@@ -316,7 +316,8 @@ const urlMap : IStringKeyMap = {
qiniu: 'https://portal.qiniu.com',
tcyun: 'https://console.cloud.tencent.com/cos',
upyun: 'https://console.upyun.com',
s3plist: 'https://aws.amazon.com/cn/s3/'
s3plist: 'https://aws.amazon.com/cn/s3/',
webdavplist: 'https://baike.baidu.com/item/WebDAV/4610909'
}
const openPicBedUrl = () => shell.openExternal(urlMap[currentPagePicBedConfig.picBedName])
@@ -442,7 +443,8 @@ const menuTitleMap:IStringKeyMap = {
s3plist: '存储桶',
smms: '相册',
imgur: '相册',
github: '仓库'
github: '仓库',
webdavplist: ''
}
const showNewIconList = ['aliyun', 'qiniu', 'tcyun']

View File

@@ -27,15 +27,16 @@ export class FileCacheDb extends Dexie {
upyun: Table<IFileCache, string>
imgur: Table<IFileCache, string>
s3plist: Table<IFileCache, string>
webdavplist: Table<IFileCache, string>
constructor () {
super('bucketFileDb')
const tableNames = ['tcyun', 'aliyun', 'qiniu', 'github', 'smms', 'upyun', 'imgur', 's3plist']
const tableNames = ['tcyun', 'aliyun', 'qiniu', 'github', 'smms', 'upyun', 'imgur', 's3plist', 'webdavplist']
const tableNamesMap = tableNames.reduce((acc, cur) => {
acc[cur] = '&key, value'
return acc
}, {} as IStringKeyMap)
this.version(1).stores(tableNamesMap)
this.version(2).stores(tableNamesMap)
this.tcyun = this.table('tcyun')
this.aliyun = this.table('aliyun')
this.qiniu = this.table('qiniu')
@@ -44,6 +45,7 @@ export class FileCacheDb extends Dexie {
this.upyun = this.table('upyun')
this.imgur = this.table('imgur')
this.s3plist = this.table('s3plist')
this.webdavplist = this.table('webdavplist')
}
}

View File

@@ -661,5 +661,100 @@ export const supportedPicBedList: IStringKeyMap = {
options: ['alias', 'accessKeyId', 'secretAccessKey', 'endpoint', 'sslEnabled', 's3ForcePathStyle', 'proxy', 'aclForUpload', 'bucketName', 'baseDir', 'paging', 'itemsPerPage'],
refLink: 'https://github.com/wayjam/picgo-plugin-s3',
referenceText: '配置教程请参考:'
},
webdavplist: {
name: 'WebDAV',
icon: 'webdavplist',
configOptions: {
alias: {
required: true,
description: '配置别名-必需',
placeholder: '该配置的唯一标识',
type: 'string',
rule: aliasRule,
default: 'webdavplist-A',
tooltip: aliasTooltip
},
endpoint: {
required: true,
description: '地址-必需',
placeholder: '例如https://example.com/dav',
type: 'string',
rule: defaultBaseRule('rootDomain'),
tooltip: '请填写完整的WebDAV地址例如https://example.com/dav'
},
username: {
required: true,
description: '用户名-必需',
placeholder: '请输入用户名',
type: 'string',
rule: defaultBaseRule('username')
},
bucketName: {
required: true,
description: '特殊配置',
placeholder: '例如bucket1',
type: 'string',
default: 'webdav',
disabled: true,
tooltip: '此处不可修改,仅为软件兼容性考虑'
},
password: {
required: true,
description: '密码-必需',
placeholder: '请输入密码',
type: 'string',
rule: defaultBaseRule('password')
},
baseDir: {
required: false,
description: '起始目录-可选',
placeholder: '例如:/test1',
type: 'string',
default: '/',
tooltip: baseDirTooltip
},
customUrl: {
required: false,
description: '自定义域名-可选',
placeholder: '例如https://example.com',
type: 'string',
tooltip: '如果您的WebDAV服务器支持自定义域名请填写完整的自定义域名例如https://example.com',
rule: [
{
validator: (rule: any, value: any, callback: any) => {
if (value) {
if (!/^https?:\/\/.+/.test(value)) {
callback(new Error('自定义域名请以http://或https://开头'))
} else {
callback()
}
} else {
callback()
}
},
trigger: 'change'
}
]
},
proxy: {
required: false,
description: '代理-可选',
placeholder: '例如http://127.0.0.1:1080',
type: 'string',
tooltip: '如果需要特殊网络环境才能访问,请使用代理'
},
sslEnabled: {
required: true,
description: '使用HTTPS连接',
default: true,
type: 'boolean',
tooltip: '根据WebDAV服务器的配置如果您的服务器不支持HTTPS请关闭该选项'
}
},
explain: 'WebDAV配置',
options: ['alias', 'endpoint', 'username', 'password', 'bucketName', 'baseDir', 'customUrl', 'proxy', 'sslEnabled'],
refLink: 'https://pichoro.horosama.com/#/PicHoroDocs/configure?id=webdav',
referenceText: '配置教程请参考:'
}
}

View File

@@ -0,0 +1,41 @@
export const textFileExt = [
'.bat',
'.c',
'.cmd',
'.conf',
'.config',
'.cpp',
'.css',
'.csv',
'.dart',
'.gitattributes',
'.gitconfig',
'.gitignore',
'.gitkeep',
'.gitmodules',
'.go',
'.h',
'.hpp',
'.htm',
'.html',
'.ini',
'.java',
'.js',
'.json',
'.log',
'.php',
'.prop',
'.properties',
'.py',
'.rc',
'.sh',
'.tsv',
'.txt',
'.xml',
'.yaml',
'.yml',
'.ts',
'.yarnrc',
'.condarc',
'license'
]

View File

@@ -0,0 +1,29 @@
export const videoExt = [
'.aac',
'.amv',
'.avi',
'.flac',
'.flv',
'.m2ts',
'.m4a',
'.m4v',
'.mp3',
'.mpeg',
'.mpg',
'.mts',
'.ogg',
'.ogv',
'.vob',
'.wav',
'.webm',
'.mp4',
'.3g2',
'.3gp',
'.asf',
'.mov',
'.mxf',
'.rm',
'.rmvb',
'.wmv',
'.mkv'
]

View File

@@ -906,7 +906,7 @@ function handleLanguageChange (val: string) {
}
function goConfigPage () {
sendToMain(OPEN_URL, 'https://picgo.github.io/PicGo-Doc/zh/guide/config.html#picgo设置')
sendToMain(OPEN_URL, 'https://piclist.cn/configure.html')
}
function goShortCutPage () {