mirror of
https://github.com/putyy/res-downloader.git
synced 2026-01-12 14:14:55 +08:00
771 lines
22 KiB
Vue
771 lines
22 KiB
Vue
<template>
|
||
<div class="h-full flex flex-col px-5 pt-5 overflow-y-auto [&::-webkit-scrollbar]:hidden">
|
||
<div class="pb-2 z-40" id="header">
|
||
<NSpace>
|
||
<NButton v-if="isProxy" secondary type="primary" @click.stop="close" style="--wails-draggable:no-drag">
|
||
{{ t("index.close_grab") }}
|
||
</NButton>
|
||
<NButton v-else tertiary type="tertiary" @click.stop="open" style="--wails-draggable:no-drag">
|
||
{{ t("index.open_grab") }}
|
||
</NButton>
|
||
<NSelect style="min-width: 100px;--wails-draggable:no-drag" :placeholder="t('index.grab_type')" v-model:value="resourcesType" multiple clearable
|
||
:max-tag-count="3" :options="classify"></NSelect>
|
||
<n-popconfirm
|
||
@positive-click="clear"
|
||
>
|
||
<template #trigger>
|
||
<NButton tertiary type="error" style="--wails-draggable:no-drag">
|
||
<template #icon>
|
||
<n-icon>
|
||
<TrashOutline/>
|
||
</n-icon>
|
||
</template>
|
||
{{ t("index.clear_list") }}
|
||
</NButton>
|
||
</template>
|
||
{{ t("index.clear_list_tip") }}
|
||
</n-popconfirm>
|
||
<NButtonGroup style="--wails-draggable:no-drag">
|
||
<NButton tertiary type="primary" @click.stop="batchDown">
|
||
<template #icon>
|
||
<n-icon>
|
||
<DownloadOutline/>
|
||
</n-icon>
|
||
</template>
|
||
{{ t('index.batch_download') }}
|
||
</NButton>
|
||
<NButton tertiary type="warning" @click.stop="batchExport">
|
||
<template #icon>
|
||
<n-icon>
|
||
<ArrowRedoCircleOutline/>
|
||
</n-icon>
|
||
</template>
|
||
{{ t('index.batch_export') }}
|
||
</NButton>
|
||
<NButton tertiary type="info" @click.stop="showImport=true">
|
||
<template #icon>
|
||
<n-icon>
|
||
<ServerOutline/>
|
||
</n-icon>
|
||
</template>
|
||
{{ t('index.batch_import') }}
|
||
</NButton>
|
||
</NButtonGroup>
|
||
</NSpace>
|
||
</div>
|
||
<div class="flex-1">
|
||
<NDataTable
|
||
:columns="columns"
|
||
:data="filteredData"
|
||
:bordered="false"
|
||
:max-height="tableHeight"
|
||
:row-key="rowKey"
|
||
:virtual-scroll="true"
|
||
:header-height="48"
|
||
:height-for-row="()=> 48"
|
||
:checked-row-keys="checkedRowKeysValue"
|
||
@update:checked-row-keys="handleCheck"
|
||
style="--wails-draggable:no-drag"
|
||
/>
|
||
</div>
|
||
<div class="flex justify-center items-center text-blue-400" id="bottom">
|
||
<span class="cursor-pointer px-2 py-1" @click="BrowserOpenURL(certUrl)">{{ t('footer.cert_download') }}</span>
|
||
<span class="cursor-pointer px-2 py-1" @click="BrowserOpenURL('https://github.com/putyy/res-downloader')">{{ t('footer.source_code') }}</span>
|
||
<span class="cursor-pointer px-2 py-1" @click="BrowserOpenURL('https://github.com/putyy/res-downloader/issues')">{{ t('footer.help') }}</span>
|
||
<span class="cursor-pointer px-2 py-1" @click="BrowserOpenURL('https://github.com/putyy/res-downloader/releases')">{{ t('footer.update_log') }}</span>
|
||
</div>
|
||
<Preview v-model:showModal="showPreviewRow" :previewRow="previewRow"/>
|
||
<ShowLoading :loadingText="loadingText" :isLoading="loading"/>
|
||
<ImportJson v-model:showModal="showImport" @submit="handleImport"/>
|
||
<Password v-model:showModal="showPassword" @submit="handlePassword"/>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import {NButton, NIcon, NImage, NInput, NSpace, NTooltip} from "naive-ui"
|
||
import {computed, h, onMounted, ref, watch} from "vue"
|
||
import type {appType} from "@/types/app"
|
||
import type {DataTableRowKey, ImageRenderToolbarProps} from "naive-ui"
|
||
import Preview from "@/components/Preview.vue"
|
||
import ShowLoading from "@/components/ShowLoading.vue"
|
||
// @ts-ignore
|
||
import {getDecryptionArray} from '@/assets/js/decrypt.js'
|
||
import {useIndexStore} from "@/stores"
|
||
import appApi from "@/api/app"
|
||
import Action from "@/components/Action.vue"
|
||
import ActionDesc from "@/components/ActionDesc.vue"
|
||
import ImportJson from "@/components/ImportJson.vue"
|
||
import {useEventStore} from "@/stores/event"
|
||
import {BrowserOpenURL, ClipboardSetText} from "../../wailsjs/runtime"
|
||
import Password from "@/components/Password.vue"
|
||
import {useI18n} from 'vue-i18n'
|
||
import {
|
||
DownloadOutline,
|
||
ArrowRedoCircleOutline,
|
||
ServerOutline,
|
||
SearchOutline,
|
||
TrashOutline
|
||
} from "@vicons/ionicons5"
|
||
|
||
const {t} = useI18n()
|
||
const eventStore = useEventStore()
|
||
const isProxy = computed(() => {
|
||
return store.isProxy
|
||
})
|
||
const certUrl = computed(()=>{
|
||
return store.baseUrl + "/api/cert"
|
||
})
|
||
const data = ref<any[]>([])
|
||
|
||
const filteredData = computed(() => {
|
||
let result = data.value
|
||
|
||
if (resourcesType.value.length > 0 && !resourcesType.value.includes("all")) {
|
||
result = result.filter(item => resourcesType.value.includes(item.Classify))
|
||
}
|
||
|
||
if (descriptionSearchValue.value) {
|
||
result = result.filter(item => item.Description?.toLowerCase().includes(descriptionSearchValue.value.toLowerCase()))
|
||
}
|
||
|
||
return result
|
||
})
|
||
|
||
const store = useIndexStore()
|
||
const tableHeight = ref(800)
|
||
const resourcesType = ref<string[]>(["all"])
|
||
|
||
const classifyAlias: { [key: string]: any } = {
|
||
image: computed(() => t("index.image")),
|
||
audio: computed(() => t("index.audio")),
|
||
video: computed(() => t("index.video")),
|
||
m3u8: computed(() => t("index.m3u8")),
|
||
live: computed(() => t("index.live")),
|
||
xls: computed(() => t("index.xls")),
|
||
doc: computed(() => t("index.doc")),
|
||
pdf: computed(() => t("index.pdf")),
|
||
font: computed(() => t("index.font"))
|
||
}
|
||
|
||
const dwStatus = computed<any>(() => {
|
||
return {
|
||
ready: t("index.ready"),
|
||
running: t("index.running"),
|
||
error: t("index.error"),
|
||
done: t("index.done"),
|
||
handle: t("index.handle")
|
||
}
|
||
})
|
||
|
||
const maxConcurrentDownloads = computed(() => {
|
||
return store.globalConfig.DownNumber
|
||
})
|
||
|
||
const classify = ref([
|
||
{
|
||
value: "all",
|
||
label: computed(() => t("index.all")),
|
||
},
|
||
])
|
||
|
||
const descriptionSearchValue = ref("")
|
||
|
||
const columns = ref<any[]>([
|
||
{
|
||
type: "selection",
|
||
},
|
||
{
|
||
title: computed(() => t("index.domain")),
|
||
key: "Domain",
|
||
width: 80,
|
||
},
|
||
{
|
||
title: computed(() => t("index.type")),
|
||
key: "Classify",
|
||
width: 80,
|
||
filterOptions: computed(() => Array.from(classify.value).slice(1)),
|
||
filterMultiple: true,
|
||
filter: (value: string, row: appType.MediaInfo) => {
|
||
return !!~row.Classify.indexOf(String(value))
|
||
},
|
||
render: (row: appType.MediaInfo) => {
|
||
for (const key in classify.value) {
|
||
if (classify.value[key].value === row.Classify) {
|
||
return classify.value[key].label
|
||
}
|
||
}
|
||
return row.Classify
|
||
}
|
||
},
|
||
{
|
||
title: computed(() => t("index.preview")),
|
||
key: "Url",
|
||
width: 80,
|
||
render: (row: appType.MediaInfo) => {
|
||
if (row.Classify === "image") {
|
||
return h(NImage, {
|
||
maxWidth: "80px",
|
||
lazy: true,
|
||
"render-toolbar": renderToolbar,
|
||
src: row.Url
|
||
})
|
||
}
|
||
return [
|
||
h(
|
||
NButton,
|
||
{
|
||
strong: true,
|
||
tertiary: true,
|
||
type: "info",
|
||
size: "small",
|
||
style: {
|
||
margin: "2px"
|
||
},
|
||
onClick: () => {
|
||
if (row.Classify === "audio" || row.Classify === "video" || row.Classify === "m3u8" || row.Classify === "live") {
|
||
previewRow.value = row
|
||
showPreviewRow.value = true
|
||
}
|
||
}
|
||
},
|
||
{
|
||
default: () => {
|
||
if (row.Classify === "audio" || row.Classify === "video" || row.Classify === "m3u8" || row.Classify === "live") {
|
||
return t("index.preview")
|
||
}
|
||
return t("index.preview_tip")
|
||
}
|
||
}
|
||
),
|
||
]
|
||
}
|
||
},
|
||
{
|
||
title: computed(() => t("index.status")),
|
||
key: "Status",
|
||
width: 80,
|
||
render: (row: appType.MediaInfo, index: number) => {
|
||
return h(
|
||
NButton,
|
||
{
|
||
tertiary: true,
|
||
type: row.Status === "done" ? "success" : "info",
|
||
size: "small",
|
||
style: {
|
||
margin: "2px"
|
||
},
|
||
onClick: () => {
|
||
if (row.SavePath && row.Status === "done") {
|
||
appApi.openFolder({filePath: row.SavePath})
|
||
} else if (row.Status === "ready") {
|
||
download(row, index)
|
||
}
|
||
}
|
||
},
|
||
{
|
||
default: () => {
|
||
return row.Status === "running" ? row.SavePath : dwStatus.value[row.Status as keyof typeof dwStatus]
|
||
}
|
||
}
|
||
)
|
||
}
|
||
},
|
||
{
|
||
title: () => h('div', { class: 'flex items-center' }, [
|
||
t('index.description'),
|
||
h(NTooltip, {
|
||
trigger: 'click',
|
||
placement: 'bottom',
|
||
showArrow: false,
|
||
}, {
|
||
trigger: () => h(NIcon, {
|
||
size: "18",
|
||
class: "ml-1 text-gray-500 cursor-pointer",
|
||
onClick: (e: MouseEvent) => e.stopPropagation()
|
||
}, h(SearchOutline)),
|
||
default: () => h('div', { class: 'p-2 w-64' }, [
|
||
h(NInput, {
|
||
value: descriptionSearchValue.value,
|
||
'onUpdate:value': (val: string) => descriptionSearchValue.value = val,
|
||
placeholder: t('index.search_description'),
|
||
clearable: true
|
||
}, {
|
||
prefix: () => h(NIcon, { component: SearchOutline })
|
||
})
|
||
])
|
||
})
|
||
]),
|
||
key: "Description",
|
||
width: 150,
|
||
render: (row: appType.MediaInfo, index: number) => {
|
||
return h(NTooltip, {trigger: 'hover', placement: 'top'}, {
|
||
trigger: () => h("div", {}, row.Description.length > 16 ? row.Description.substring(0, 16) + "..." : row.Description),
|
||
default: () => h("div", {
|
||
style: {
|
||
"max-width": " 400px",
|
||
"white-space": "normal",
|
||
"word-wrap": "break-word"
|
||
}
|
||
}, row.Description)
|
||
})
|
||
}
|
||
},
|
||
{
|
||
title: computed(() => t("index.resource_size")),
|
||
key: "Size",
|
||
width: 120,
|
||
},
|
||
{
|
||
title: computed(() => t("index.save_path")),
|
||
key: "SavePath",
|
||
render(row: appType.MediaInfo, index: number) {
|
||
return h("a",
|
||
{
|
||
href: "javascript:;",
|
||
class:"ellipsis-2",
|
||
style: {
|
||
color: "#5a95d0"
|
||
},
|
||
onClick: () => {
|
||
if (row.SavePath && row.Status === "done") {
|
||
appApi.openFolder({filePath: row.SavePath})
|
||
}
|
||
}
|
||
},
|
||
row.Status === "running" ? "" : row.SavePath
|
||
)
|
||
}
|
||
},
|
||
{
|
||
key: "actions",
|
||
width: 210,
|
||
render(row: appType.MediaInfo, index: number) {
|
||
return h(Action, {key: index, row: row, index: index, onAction: dataAction})
|
||
},
|
||
title() {
|
||
return h(ActionDesc)
|
||
}
|
||
}
|
||
])
|
||
|
||
const checkedRowKeysValue = ref<DataTableRowKey[]>([])
|
||
const showPreviewRow = ref(false)
|
||
const previewRow = ref<appType.MediaInfo>()
|
||
const loading = ref(false)
|
||
const loadingText = ref("")
|
||
const showImport = ref(false)
|
||
const showPassword = ref(false)
|
||
const downloadQueue = ref<appType.MediaInfo[]>([])
|
||
let activeDownloads = 0
|
||
let isOpenProxy = false
|
||
|
||
onMounted(() => {
|
||
try {
|
||
window.addEventListener("resize", ()=>{
|
||
resetTableHeight()
|
||
})
|
||
loading.value = true
|
||
handleInstall().then((is: boolean) => {
|
||
loading.value = false
|
||
})
|
||
} catch (e) {
|
||
window.$message?.error(JSON.stringify(e), {duration: 5000})
|
||
}
|
||
|
||
buildClassify()
|
||
|
||
const temp = localStorage.getItem("resources-type")
|
||
if (temp) {
|
||
resourcesType.value = JSON.parse(temp).res
|
||
} else {
|
||
appApi.setType(resourcesType.value)
|
||
}
|
||
|
||
const cache = localStorage.getItem("resources-data")
|
||
if (cache) {
|
||
data.value = JSON.parse(cache)
|
||
}
|
||
resetTableHeight()
|
||
eventStore.addHandle({
|
||
type: "newResources",
|
||
event: (res: appType.MediaInfo) => {
|
||
data.value.push(res)
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
}
|
||
})
|
||
|
||
eventStore.addHandle({
|
||
type: "downloadProgress",
|
||
event: (res: { Id: string, SavePath: string, Status: string, Message: string }) => {
|
||
switch (res.Status) {
|
||
case "running":
|
||
for (const i in data.value) {
|
||
if (data.value[i].Id === res.Id) {
|
||
data.value[i].SavePath = res.Message
|
||
data.value[i].Status = "running"
|
||
break
|
||
}
|
||
}
|
||
break
|
||
case "done":
|
||
for (const i in data.value) {
|
||
if (data.value[i].Id === res.Id) {
|
||
data.value[i].SavePath = res.SavePath
|
||
data.value[i].Status = "done"
|
||
break
|
||
}
|
||
}
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
checkQueue()
|
||
break
|
||
case "error":
|
||
for (const i in data.value) {
|
||
if (data.value[i].Id === res.Id) {
|
||
data.value[i].SavePath = res.Message
|
||
data.value[i].Status = "error"
|
||
break
|
||
}
|
||
}
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
checkQueue()
|
||
break
|
||
}
|
||
}
|
||
})
|
||
})
|
||
|
||
watch(() => {
|
||
return store.globalConfig.MimeMap
|
||
}, () => {
|
||
buildClassify()
|
||
})
|
||
|
||
watch(resourcesType, (n, o) => {
|
||
localStorage.setItem("resources-type", JSON.stringify({res: resourcesType.value}))
|
||
appApi.setType(resourcesType.value)
|
||
})
|
||
|
||
const resetTableHeight = ()=>{
|
||
try {
|
||
const headerHeight = document.getElementById("header")?.offsetHeight || 0
|
||
const bottomHeight = document.getElementById("bottom")?.offsetHeight || 0
|
||
// @ts-ignore
|
||
const theadHeight = document.getElementsByClassName("n-data-table-thead")[0]?.offsetHeight || 0
|
||
const height = document.documentElement.clientHeight || window.innerHeight
|
||
tableHeight.value = height - headerHeight - bottomHeight - theadHeight - 20
|
||
}catch (e) {
|
||
console.log(e)
|
||
}
|
||
}
|
||
|
||
const buildClassify = () => {
|
||
const mimeMap = store.globalConfig.MimeMap ?? {}
|
||
const seen = new Set()
|
||
classify.value = [
|
||
{value: "all", label: computed(() => t("index.all"))},
|
||
...Object.values(mimeMap)
|
||
.filter(({Type}) => {
|
||
if (seen.has(Type)) return false
|
||
seen.add(Type)
|
||
return true
|
||
})
|
||
.map(({Type}) => ({
|
||
value: Type,
|
||
label: classifyAlias[Type] ?? Type,
|
||
})),
|
||
]
|
||
}
|
||
|
||
const dataAction = (row: appType.MediaInfo, index: number, type: string) => {
|
||
switch (type) {
|
||
case "down":
|
||
download(row, index)
|
||
break
|
||
case "copy":
|
||
ClipboardSetText(row.Url).then((is: boolean) => {
|
||
if (is) {
|
||
window?.$message?.success(t("common.copy_success"))
|
||
} else {
|
||
window?.$message?.error(t("common.copy_fail"))
|
||
}
|
||
})
|
||
break
|
||
case "json":
|
||
ClipboardSetText(encodeURIComponent(JSON.stringify(row))).then((is: boolean) => {
|
||
if (is) {
|
||
window?.$message?.success(t("common.copy_success"))
|
||
} else {
|
||
window?.$message?.error(t("common.copy_fail"))
|
||
}
|
||
})
|
||
break
|
||
case "open":
|
||
BrowserOpenURL(row.Url)
|
||
break
|
||
case "decode":
|
||
decodeWxFile(row, index)
|
||
break
|
||
case "delete":
|
||
appApi.delete({sign: row.UrlSign}).then(() => {
|
||
let arr = data.value
|
||
arr.splice(index, 1)
|
||
data.value = arr
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
})
|
||
break
|
||
}
|
||
}
|
||
|
||
const renderToolbar = ({nodes}: ImageRenderToolbarProps) => {
|
||
return [
|
||
nodes.rotateCounterclockwise,
|
||
nodes.rotateClockwise,
|
||
nodes.resizeToOriginalSize,
|
||
nodes.zoomOut,
|
||
nodes.zoomIn,
|
||
nodes.close
|
||
]
|
||
}
|
||
|
||
const rowKey = (row: appType.MediaInfo) => {
|
||
return row.Id
|
||
}
|
||
|
||
const handleCheck = (rowKeys: DataTableRowKey[]) => {
|
||
checkedRowKeysValue.value = rowKeys
|
||
}
|
||
|
||
const batchDown = async () => {
|
||
if (checkedRowKeysValue.value.length <= 0) {
|
||
return
|
||
}
|
||
|
||
if (!store.globalConfig.SaveDirectory) {
|
||
window?.$message?.error(t("index.save_path_empty"))
|
||
return
|
||
}
|
||
|
||
for (let i = 0; i < data.value.length; i++) {
|
||
if (checkedRowKeysValue.value.includes(data.value[i].Id) && data.value[i].Classify != "live" && data.value[i].Classify != "m3u8") {
|
||
download(data.value[i], i)
|
||
}
|
||
}
|
||
checkedRowKeysValue.value = []
|
||
}
|
||
|
||
const batchExport = () => {
|
||
if (checkedRowKeysValue.value.length <= 0) {
|
||
window?.$message?.error(t("index.use_data"))
|
||
return
|
||
}
|
||
if (!store.globalConfig.SaveDirectory) {
|
||
window?.$message?.error(t("index.save_path_empty"))
|
||
return
|
||
}
|
||
loadingText.value = t("common.loading")
|
||
loading.value = true
|
||
let jsonData = []
|
||
for (let i = 0; i < data.value.length; i++) {
|
||
jsonData.push(encodeURIComponent(JSON.stringify(data.value[i])))
|
||
}
|
||
appApi.batchExport({content: jsonData.join("\n")}).then((res: appType.Res) => {
|
||
loading.value = false
|
||
if (res.code === 0) {
|
||
window?.$message?.error(res.message)
|
||
return
|
||
}
|
||
window?.$message?.success(t("index.import_success"))
|
||
window?.$message?.info(t("index.save_path") + ":" + res.data?.file_name, {
|
||
duration: 5000
|
||
})
|
||
})
|
||
}
|
||
|
||
const uint8ArrayToBase64 = (bytes: any) => {
|
||
let binary = ''
|
||
const len = bytes.byteLength
|
||
for (let i = 0; i < len; i++) {
|
||
binary += String.fromCharCode(bytes[i])
|
||
}
|
||
return window.btoa(binary)
|
||
}
|
||
|
||
const download = (row: appType.MediaInfo, index: number) => {
|
||
if (!store.globalConfig.SaveDirectory) {
|
||
window?.$message?.error(t("index.save_path_empty"))
|
||
return
|
||
}
|
||
|
||
if (data.value.some(item => item.Id === row.Id && item.Status === "running")) {
|
||
return
|
||
}
|
||
|
||
if (downloadQueue.value.some(item => item.Id === row.Id || item.Url === row.Url)) {
|
||
return
|
||
}
|
||
|
||
if (activeDownloads >= maxConcurrentDownloads.value) {
|
||
downloadQueue.value.push(row)
|
||
window?.$message?.info((row.Description ? `「${row.Description}」` : "")
|
||
+ t("index.download_queued", {count: downloadQueue.value.length}))
|
||
return
|
||
}
|
||
|
||
startDownload(row, index)
|
||
}
|
||
|
||
const startDownload = (row: appType.MediaInfo, index: number) => {
|
||
activeDownloads++
|
||
|
||
const decodeStr = row.DecodeKey
|
||
? uint8ArrayToBase64(getDecryptionArray(row.DecodeKey))
|
||
: ""
|
||
|
||
appApi.download({...row, decodeStr}).then((res: appType.Res) => {
|
||
if (res.code === 0) {
|
||
window?.$message?.error(res.message)
|
||
}
|
||
}).finally(() => {
|
||
activeDownloads--
|
||
checkQueue()
|
||
})
|
||
}
|
||
|
||
const checkQueue = () => {
|
||
if (downloadQueue.value.length > 0 && activeDownloads < maxConcurrentDownloads.value) {
|
||
const nextItem = downloadQueue.value.shift()
|
||
if (nextItem) {
|
||
const index = data.value.findIndex(item => item.Id === nextItem.Id)
|
||
if (index !== -1) {
|
||
startDownload(nextItem, index)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
const open = () => {
|
||
isOpenProxy = true
|
||
store.openProxy().then((res: appType.Res) => {
|
||
if (res.code === 1) {
|
||
return
|
||
}
|
||
|
||
if (["darwin", "linux"].includes(store.envInfo.platform)) {
|
||
showPassword.value = true
|
||
} else {
|
||
window.$message?.error(res.message)
|
||
}
|
||
})
|
||
}
|
||
|
||
const close = () => {
|
||
store.unsetProxy()
|
||
}
|
||
|
||
const clear = () => {
|
||
data.value = []
|
||
localStorage.setItem("resources-data", "")
|
||
appApi.clear()
|
||
}
|
||
|
||
const decodeWxFile = (row: appType.MediaInfo, index: number) => {
|
||
if (!row.DecodeKey) {
|
||
window?.$message?.error(t("index.video_decode_no"))
|
||
return
|
||
}
|
||
appApi.openFileDialog().then((res: appType.Res) => {
|
||
if (res.code === 0) {
|
||
window?.$message?.error(res.message)
|
||
return
|
||
}
|
||
if (res.data.file) {
|
||
loadingText.value = t("index.video_decode_loading")
|
||
loading.value = true
|
||
appApi.wxFileDecode({
|
||
...row,
|
||
filename: res.data.file,
|
||
decodeStr: uint8ArrayToBase64(getDecryptionArray(row.DecodeKey))
|
||
}).then((res: appType.Res) => {
|
||
loading.value = false
|
||
if (res.code === 0) {
|
||
window?.$message?.error(res.message)
|
||
return
|
||
}
|
||
data.value[index].SavePath = res.data.save_path
|
||
data.value[index].Status = "done"
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
window?.$message?.success(t("index.video_decode_success"))
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleImport = (content: string) => {
|
||
if (!content) {
|
||
window?.$message?.error(t("view.import_empty"))
|
||
return
|
||
}
|
||
content.split("\n").forEach((line, index) => {
|
||
try {
|
||
let res = JSON.parse(decodeURIComponent(line))
|
||
if (res && res?.Id) {
|
||
res.Id = res.Id + Math.floor(Math.random() * 100000)
|
||
res.SavePath = ""
|
||
res.Status = "ready"
|
||
data.value.unshift(res)
|
||
}
|
||
} catch (e) {
|
||
console.log(e)
|
||
}
|
||
})
|
||
localStorage.setItem("resources-data", JSON.stringify(data.value))
|
||
showImport.value = false
|
||
}
|
||
|
||
const handlePassword = async (password: string, isCache: boolean) => {
|
||
const res = await appApi.setSystemPassword({password, isCache})
|
||
if (res.code === 0) {
|
||
window.$message?.error(res.message)
|
||
return
|
||
}
|
||
|
||
if (isOpenProxy) {
|
||
showPassword.value = false
|
||
store.openProxy()
|
||
return
|
||
}
|
||
|
||
handleInstall().then((is: boolean) => {
|
||
if (is) {
|
||
showPassword.value = false
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleInstall = async () => {
|
||
isOpenProxy = false
|
||
const res = await appApi.install()
|
||
if (res.code === 1) {
|
||
store.globalConfig.AutoProxy && store.openProxy()
|
||
return true
|
||
}
|
||
|
||
window.$message?.error(res.message, {duration: 5000})
|
||
|
||
if (store.envInfo.platform === "windows" && res.message.includes("Access is denied")) {
|
||
window.$message?.error(t("index.win_install_tip"))
|
||
} else if (["darwin", "linux"].includes(store.envInfo.platform)) {
|
||
showPassword.value = true
|
||
}
|
||
return false
|
||
}
|
||
</script>
|
||
<style>
|
||
.ellipsis-2 {
|
||
display: -webkit-box;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
</style> |