mirror of
https://github.com/putyy/res-downloader.git
synced 2026-01-12 06:04:55 +08:00
fix: batch cancel
This commit is contained in:
@@ -335,7 +335,7 @@ func (h *HttpServer) delete(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *HttpServer) download(w http.ResponseWriter, r *http.Request) {
|
func (h *HttpServer) download(w http.ResponseWriter, r *http.Request) {
|
||||||
var data struct {
|
var data struct {
|
||||||
MediaInfo
|
shared.MediaInfo
|
||||||
DecodeStr string `json:"decodeStr"`
|
DecodeStr string `json:"decodeStr"`
|
||||||
}
|
}
|
||||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||||
@@ -348,7 +348,7 @@ func (h *HttpServer) download(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *HttpServer) cancel(w http.ResponseWriter, r *http.Request) {
|
func (h *HttpServer) cancel(w http.ResponseWriter, r *http.Request) {
|
||||||
var data struct {
|
var data struct {
|
||||||
MediaInfo
|
shared.MediaInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||||
@@ -366,7 +366,7 @@ func (h *HttpServer) cancel(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (h *HttpServer) wxFileDecode(w http.ResponseWriter, r *http.Request) {
|
func (h *HttpServer) wxFileDecode(w http.ResponseWriter, r *http.Request) {
|
||||||
var data struct {
|
var data struct {
|
||||||
MediaInfo
|
shared.MediaInfo
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
DecodeStr string `json:"decodeStr"`
|
DecodeStr string `json:"decodeStr"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,23 +21,6 @@ type Proxy struct {
|
|||||||
Is bool
|
Is bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaInfo struct {
|
|
||||||
Id string
|
|
||||||
Url string
|
|
||||||
UrlSign string
|
|
||||||
CoverUrl string
|
|
||||||
Size string
|
|
||||||
Domain string
|
|
||||||
Classify string
|
|
||||||
Suffix string
|
|
||||||
SavePath string
|
|
||||||
Status string
|
|
||||||
DecodeKey string
|
|
||||||
Description string
|
|
||||||
ContentType string
|
|
||||||
OtherData map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
var pluginRegistry = make(map[string]shared.Plugin)
|
var pluginRegistry = make(map[string]shared.Plugin)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -97,11 +97,11 @@ func (r *Resource) cancel(id string) error {
|
|||||||
return errors.New("task not found")
|
return errors.New("task not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
func (r *Resource) download(mediaInfo shared.MediaInfo, decodeStr string) {
|
||||||
if globalConfig.SaveDirectory == "" {
|
if globalConfig.SaveDirectory == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func(mediaInfo MediaInfo) {
|
go func(mediaInfo shared.MediaInfo) {
|
||||||
rawUrl := mediaInfo.Url
|
rawUrl := mediaInfo.Url
|
||||||
fileName := shared.Md5(rawUrl)
|
fileName := shared.Md5(rawUrl)
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ func (r *Resource) download(mediaInfo MediaInfo, decodeStr string) {
|
|||||||
}(mediaInfo)
|
}(mediaInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) parseHeaders(mediaInfo MediaInfo) (map[string]string, error) {
|
func (r *Resource) parseHeaders(mediaInfo shared.MediaInfo) (map[string]string, error) {
|
||||||
headers := make(map[string]string)
|
headers := make(map[string]string)
|
||||||
|
|
||||||
if hh, ok := mediaInfo.OtherData["headers"]; ok {
|
if hh, ok := mediaInfo.OtherData["headers"]; ok {
|
||||||
@@ -199,7 +199,7 @@ func (r *Resource) parseHeaders(mediaInfo MediaInfo) (map[string]string, error)
|
|||||||
return headers, nil
|
return headers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) wxFileDecode(mediaInfo MediaInfo, fileName, decodeStr string) (string, error) {
|
func (r *Resource) wxFileDecode(mediaInfo shared.MediaInfo, fileName, decodeStr string) (string, error) {
|
||||||
sourceFile, err := os.Open(fileName)
|
sourceFile, err := os.Open(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -224,7 +224,7 @@ func (r *Resource) wxFileDecode(mediaInfo MediaInfo, fileName, decodeStr string)
|
|||||||
return mediaInfo.SavePath, nil
|
return mediaInfo.SavePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) progressEventsEmit(mediaInfo MediaInfo, args ...string) {
|
func (r *Resource) progressEventsEmit(mediaInfo shared.MediaInfo, args ...string) {
|
||||||
Status := shared.DownloadStatusError
|
Status := shared.DownloadStatusError
|
||||||
Message := "ok"
|
Message := "ok"
|
||||||
|
|
||||||
|
|||||||
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@@ -15,6 +15,7 @@ declare module 'vue' {
|
|||||||
NaiveProvider: typeof import('./src/components/NaiveProvider.vue')['default']
|
NaiveProvider: typeof import('./src/components/NaiveProvider.vue')['default']
|
||||||
NButton: typeof import('naive-ui')['NButton']
|
NButton: typeof import('naive-ui')['NButton']
|
||||||
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
NButtonGroup: typeof import('naive-ui')['NButtonGroup']
|
||||||
|
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"save_path_empty": "Please set save location",
|
"save_path_empty": "Please set save location",
|
||||||
"operation": "Operation",
|
"operation": "Operation",
|
||||||
"ready": "Ready",
|
"ready": "Ready",
|
||||||
|
"pending": "Pending",
|
||||||
"running": "Running",
|
"running": "Running",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
"done": "Done",
|
"done": "Done",
|
||||||
|
|||||||
@@ -63,6 +63,7 @@
|
|||||||
"save_path_empty": "请设置保存位置",
|
"save_path_empty": "请设置保存位置",
|
||||||
"operation": "操作",
|
"operation": "操作",
|
||||||
"ready": "就绪",
|
"ready": "就绪",
|
||||||
|
"pending": "待处理",
|
||||||
"running": "运行中",
|
"running": "运行中",
|
||||||
"error": "错误",
|
"error": "错误",
|
||||||
"done": "完成",
|
"done": "完成",
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
<NSpace>
|
<NSpace>
|
||||||
<NButton v-if="isProxy" secondary type="primary" @click.stop="close" style="--wails-draggable:no-drag">
|
<NButton v-if="isProxy" secondary type="primary" @click.stop="close" style="--wails-draggable:no-drag">
|
||||||
<span class="inline-block w-1.5 h-1.5 bg-red-600 rounded-full mr-1 animate-pulse"></span>
|
<span class="inline-block w-1.5 h-1.5 bg-red-600 rounded-full mr-1 animate-pulse"></span>
|
||||||
{{ t("index.close_grab") }}{{ data.length > 0 ? ` ${t('index.total_resources', {count:data.length})}` : ''}}
|
{{ t("index.close_grab") }}{{ data.length > 0 ? ` ${t('index.total_resources', {count: data.length})}` : '' }}
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton v-else tertiary type="tertiary" @click.stop="open" style="--wails-draggable:no-drag">
|
<NButton v-else tertiary type="tertiary" @click.stop="open" style="--wails-draggable:no-drag">
|
||||||
{{ t("index.open_grab") }}{{ data.length > 0 ? ` ${t('index.total_resources', {count:data.length})}` : ''}}
|
{{ t("index.open_grab") }}{{ data.length > 0 ? ` ${t('index.total_resources', {count: data.length})}` : '' }}
|
||||||
</NButton>
|
</NButton>
|
||||||
<NSelect style="min-width: 100px;--wails-draggable:no-drag" :placeholder="t('index.grab_type')" v-model:value="resourcesType" multiple clearable
|
<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>
|
:max-tag-count="3" :options="classify"></NSelect>
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
<NCheckbox
|
<NCheckbox
|
||||||
v-model:checked="rememberChoiceTmp"
|
v-model:checked="rememberChoiceTmp"
|
||||||
>
|
>
|
||||||
<span class="text-gray-400">{{t('index.remember_clear_choice')}}</span>
|
<span class="text-gray-400">{{ t('index.remember_clear_choice') }}</span>
|
||||||
</NCheckbox>
|
</NCheckbox>
|
||||||
</div>
|
</div>
|
||||||
</n-popconfirm>
|
</n-popconfirm>
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
import {NButton, NIcon, NImage, NInput, NSpace, NTooltip, NPopover, NGradientText} from "naive-ui"
|
import {NButton, NIcon, NImage, NInput, NSpace, NTooltip, NPopover, NGradientText} from "naive-ui"
|
||||||
import {computed, h, onMounted, ref, watch} from "vue"
|
import {computed, h, onMounted, ref, watch} from "vue"
|
||||||
import type {appType} from "@/types/app"
|
import type {appType} from "@/types/app"
|
||||||
import type {DataTableRowKey, ImageRenderToolbarProps, DataTableFilterState,DataTableBaseColumn} from "naive-ui"
|
import type {DataTableRowKey, ImageRenderToolbarProps, DataTableFilterState, DataTableBaseColumn} from "naive-ui"
|
||||||
import Preview from "@/components/Preview.vue"
|
import Preview from "@/components/Preview.vue"
|
||||||
import ShowLoading from "@/components/ShowLoading.vue"
|
import ShowLoading from "@/components/ShowLoading.vue"
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@@ -160,7 +160,7 @@ import {
|
|||||||
Apps,
|
Apps,
|
||||||
TrashOutline, CloseOutline
|
TrashOutline, CloseOutline
|
||||||
} from "@vicons/ionicons5"
|
} from "@vicons/ionicons5"
|
||||||
import { useDialog } from 'naive-ui'
|
import {useDialog} from 'naive-ui'
|
||||||
import * as bind from "../../wailsjs/go/core/Bind"
|
import * as bind from "../../wailsjs/go/core/Bind"
|
||||||
import {Quit} from "../../wailsjs/runtime"
|
import {Quit} from "../../wailsjs/runtime"
|
||||||
import {DialogOptions} from "naive-ui/es/dialog/src/DialogProvider"
|
import {DialogOptions} from "naive-ui/es/dialog/src/DialogProvider"
|
||||||
@@ -210,6 +210,7 @@ const classifyAlias: { [key: string]: any } = {
|
|||||||
const dwStatus = computed<any>(() => {
|
const dwStatus = computed<any>(() => {
|
||||||
return {
|
return {
|
||||||
ready: t("index.ready"),
|
ready: t("index.ready"),
|
||||||
|
pending: t("index.pending"),
|
||||||
running: t("index.running"),
|
running: t("index.running"),
|
||||||
error: t("index.error"),
|
error: t("index.error"),
|
||||||
done: t("index.done"),
|
done: t("index.done"),
|
||||||
@@ -238,7 +239,7 @@ const columns = ref<any[]>([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: computed(() => {
|
title: computed(() => {
|
||||||
return checkedRowKeysValue.value.length > 0 ? h(NGradientText, {type:"success"}, t("index.choice") + `(${checkedRowKeysValue.value.length})`) : t("index.domain")
|
return checkedRowKeysValue.value.length > 0 ? h(NGradientText, {type: "success"}, t("index.choice") + `(${checkedRowKeysValue.value.length})`) : t("index.domain")
|
||||||
}),
|
}),
|
||||||
key: "Domain",
|
key: "Domain",
|
||||||
width: 90,
|
width: 90,
|
||||||
@@ -307,11 +308,18 @@ const columns = ref<any[]>([
|
|||||||
key: "Status",
|
key: "Status",
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (row: appType.MediaInfo, index: number) => {
|
render: (row: appType.MediaInfo, index: number) => {
|
||||||
|
let status = "info"
|
||||||
|
if (row.Status === "done" || row.Status === "running") {
|
||||||
|
status = "success"
|
||||||
|
} else if (row.Status === "pending") {
|
||||||
|
status = "warning"
|
||||||
|
}
|
||||||
|
|
||||||
return h(
|
return h(
|
||||||
NButton,
|
NButton,
|
||||||
{
|
{
|
||||||
tertiary: true,
|
tertiary: true,
|
||||||
type: row.Status === "done" ? "success" : "info",
|
type: status as any,
|
||||||
size: "small",
|
size: "small",
|
||||||
style: {
|
style: {
|
||||||
margin: "2px"
|
margin: "2px"
|
||||||
@@ -336,7 +344,7 @@ const columns = ref<any[]>([
|
|||||||
title: () => h('div', {class: 'flex items-center'}, [
|
title: () => h('div', {class: 'flex items-center'}, [
|
||||||
t('index.description'),
|
t('index.description'),
|
||||||
h(NPopover, {
|
h(NPopover, {
|
||||||
style:"--wails-draggable:no-drag",
|
style: "--wails-draggable:no-drag",
|
||||||
trigger: 'click',
|
trigger: 'click',
|
||||||
placement: 'bottom',
|
placement: 'bottom',
|
||||||
showArrow: true,
|
showArrow: true,
|
||||||
@@ -434,8 +442,8 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
checkLoading()
|
checkLoading()
|
||||||
watch(showPassword, ()=>{
|
watch(showPassword, () => {
|
||||||
if (!showPassword.value){
|
if (!showPassword.value) {
|
||||||
checkLoading()
|
checkLoading()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -532,7 +540,7 @@ watch(resourcesType, (n, o) => {
|
|||||||
appApi.setType(resourcesType.value)
|
appApi.setType(resourcesType.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateItem = (id: string, updater: (item: any) => void)=>{
|
const updateItem = (id: string, updater: (item: any) => void) => {
|
||||||
const item = data.value.find(i => i.Id === id)
|
const item = data.value.find(i => i.Id === id)
|
||||||
if (item) updater(item)
|
if (item) updater(item)
|
||||||
}
|
}
|
||||||
@@ -579,7 +587,7 @@ const dataAction = (row: appType.MediaInfo, index: number, type: string) => {
|
|||||||
break
|
break
|
||||||
case "cancel":
|
case "cancel":
|
||||||
if (row.Status === "running") {
|
if (row.Status === "running") {
|
||||||
appApi.cancel({id: row.Id}).then((res)=>{
|
appApi.cancel({id: row.Id}).then((res) => {
|
||||||
updateItem(row.Id, item => {
|
updateItem(row.Id, item => {
|
||||||
item.Status = 'ready'
|
item.Status = 'ready'
|
||||||
item.SavePath = ''
|
item.SavePath = ''
|
||||||
@@ -648,7 +656,7 @@ const handleCheck = (rowKeys: DataTableRowKey[]) => {
|
|||||||
checkedRowKeysValue.value = rowKeys
|
checkedRowKeysValue.value = rowKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateFilters = (filters: DataTableFilterState, initiatorColumn: DataTableBaseColumn)=>{
|
const updateFilters = (filters: DataTableFilterState, initiatorColumn: DataTableBaseColumn) => {
|
||||||
filterClassify.value = filters.Classify as string[]
|
filterClassify.value = filters.Classify as string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,25 +680,29 @@ const batchDown = async () => {
|
|||||||
checkedRowKeysValue.value = []
|
checkedRowKeysValue.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchCancel = () =>{
|
const batchCancel = async () => {
|
||||||
if (checkedRowKeysValue.value.length <= 0) {
|
if (checkedRowKeysValue.value.length <= 0) {
|
||||||
window?.$message?.error(t("index.use_data"))
|
window?.$message?.error(t("index.use_data"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
loading.value = true
|
||||||
data.value.forEach(async (item, index) => {
|
const cancelTasks: Promise<any>[] = []
|
||||||
|
data.value.forEach((item, index) => {
|
||||||
if (checkedRowKeysValue.value.includes(item.Id) && item.Status === "running") {
|
if (checkedRowKeysValue.value.includes(item.Id) && item.Status === "running") {
|
||||||
appApi.cancel({id: item.Id})
|
|
||||||
if (activeDownloads > 0) {
|
if (activeDownloads > 0) {
|
||||||
activeDownloads--
|
activeDownloads--
|
||||||
}
|
}
|
||||||
data.value[index].Status = 'ready'
|
cancelTasks.push(appApi.cancel({id: item.Id}).then(() => {
|
||||||
data.value[index].SavePath = ''
|
item.Status = 'ready'
|
||||||
|
item.SavePath = ''
|
||||||
|
checkQueue()
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
await Promise.allSettled(cancelTasks)
|
||||||
|
loading.value = false
|
||||||
checkedRowKeysValue.value = []
|
checkedRowKeysValue.value = []
|
||||||
cacheData()
|
cacheData()
|
||||||
checkQueue()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchExport = (type?: string) => {
|
const batchExport = (type?: string) => {
|
||||||
@@ -709,9 +721,9 @@ const batchExport = (type?: string) => {
|
|||||||
|
|
||||||
let jsonData = data.value.filter(item => checkedRowKeysValue.value.includes(item.Id))
|
let jsonData = data.value.filter(item => checkedRowKeysValue.value.includes(item.Id))
|
||||||
|
|
||||||
if (type === "url"){
|
if (type === "url") {
|
||||||
jsonData = jsonData.map(item => item.Url)
|
jsonData = jsonData.map(item => item.Url)
|
||||||
} else{
|
} else {
|
||||||
jsonData = jsonData.map(item => encodeURIComponent(JSON.stringify(item)))
|
jsonData = jsonData.map(item => encodeURIComponent(JSON.stringify(item)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -747,6 +759,7 @@ const download = (row: appType.MediaInfo, index: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (activeDownloads >= maxConcurrentDownloads.value) {
|
if (activeDownloads >= maxConcurrentDownloads.value) {
|
||||||
|
row.Status = "pending"
|
||||||
downloadQueue.value.push(row)
|
downloadQueue.value.push(row)
|
||||||
window?.$message?.info(t("index.download_queued", {count: downloadQueue.value.length}))
|
window?.$message?.info(t("index.download_queued", {count: downloadQueue.value.length}))
|
||||||
return
|
return
|
||||||
@@ -917,8 +930,8 @@ const handleInstall = async () => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkLoading = ()=>{
|
const checkLoading = () => {
|
||||||
setTimeout(()=>{
|
setTimeout(() => {
|
||||||
if (loading.value && !isInstall && !showPassword.value) {
|
if (loading.value && !isInstall && !showPassword.value) {
|
||||||
dialog.warning({
|
dialog.warning({
|
||||||
title: t("index.start_err_tip"),
|
title: t("index.start_err_tip"),
|
||||||
|
|||||||
Reference in New Issue
Block a user