mirror of
https://github.com/VueTorrent/VueTorrent.git
synced 2025-04-03 07:14:02 +03:00
330 lines
11 KiB
Vue
330 lines
11 KiB
Vue
<script setup lang="ts">
|
|
import RightClickMenuEntry from '@/components/Dashboard/TRC/RightClickMenuEntry.vue'
|
|
import ConfirmDeleteDialog from '@/components/Dialogs/ConfirmDeleteDialog.vue'
|
|
import MoveTorrentDialog from '@/components/Dialogs/MoveTorrentDialog.vue'
|
|
import RenameTorrentDialog from '@/components/Dialogs/RenameTorrentDialog.vue'
|
|
import ShareLimitDialog from '@/components/Dialogs/ShareLimitDialog.vue'
|
|
import SpeedLimitDialog from '@/components/Dialogs/SpeedLimitDialog.vue'
|
|
import { useDashboardStore, useDialogStore, useMaindataStore, usePreferenceStore, useTorrentStore } from '@/stores'
|
|
import { TRCMenuEntry } from '@/types/vuetorrent'
|
|
import { computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean
|
|
}>()
|
|
const emit = defineEmits(['update:modelValue'])
|
|
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
const dashboardStore = useDashboardStore()
|
|
const dialogStore = useDialogStore()
|
|
const maindataStore = useMaindataStore()
|
|
const preferenceStore = usePreferenceStore()
|
|
const torrentStore = useTorrentStore()
|
|
|
|
const trcVisible = computed({
|
|
get: () => props.modelValue,
|
|
set: value => emit('update:modelValue', value)
|
|
})
|
|
|
|
const isMultiple = computed(() => dashboardStore.selectedTorrents.length > 1)
|
|
const hashes = computed(() => dashboardStore.selectedTorrents)
|
|
const hash = computed(() => hashes.value[0])
|
|
const torrent = computed(() => torrentStore.getTorrentByHash(hash.value))
|
|
const torrents = computed(() => dashboardStore.selectedTorrents.map(torrentStore.getTorrentByHash).filter(torrent => !!torrent))
|
|
const availableCategories = computed(() => [{ name: '' }, ...maindataStore.categories])
|
|
|
|
async function resumeTorrents() {
|
|
await torrentStore.resumeTorrents(hashes)
|
|
}
|
|
|
|
async function forceResumeTorrents() {
|
|
await torrentStore.forceResumeTorrents(hashes)
|
|
}
|
|
|
|
async function pauseTorrents() {
|
|
await torrentStore.pauseTorrents(hashes)
|
|
}
|
|
|
|
function deleteTorrents() {
|
|
dialogStore.createDialog(ConfirmDeleteDialog, { hashes: [...dashboardStore.selectedTorrents] })
|
|
}
|
|
|
|
function setDownloadPath() {
|
|
dialogStore.createDialog(MoveTorrentDialog, { hashes: [...dashboardStore.selectedTorrents], mode: 'dl' })
|
|
}
|
|
|
|
function setSavePath() {
|
|
dialogStore.createDialog(MoveTorrentDialog, { hashes: [...dashboardStore.selectedTorrents], mode: 'save' })
|
|
}
|
|
|
|
function renameTorrents() {
|
|
dialogStore.createDialog(RenameTorrentDialog, { hash: dashboardStore.selectedTorrents[0] })
|
|
}
|
|
|
|
async function forceRecheck() {
|
|
await torrentStore.recheckTorrents(hashes)
|
|
}
|
|
|
|
async function forceReannounce() {
|
|
await maindataStore.reannounceTorrents(hashes)
|
|
}
|
|
|
|
async function toggleSeqDl() {
|
|
await maindataStore.toggleSeqDl(hashes)
|
|
}
|
|
|
|
async function toggleFLPiecePrio() {
|
|
await maindataStore.toggleFLPiecePrio(hashes)
|
|
}
|
|
|
|
async function toggleAutoTMM() {
|
|
await maindataStore.toggleAutoTmm(hashes, !torrent.value?.auto_tmm)
|
|
}
|
|
|
|
function hasTag(tag: string) {
|
|
return torrents.value.every(torrent => torrent && torrent.tags && torrent.tags.includes(tag))
|
|
}
|
|
|
|
async function toggleTag(tag: string) {
|
|
if (hasTag(tag)) await torrentStore.removeTorrentTags(hashes.value, [tag])
|
|
else await torrentStore.addTorrentTags(hashes.value, [tag])
|
|
}
|
|
|
|
async function copyValue(valueToCopy: string) {
|
|
await navigator.clipboard.writeText(valueToCopy)
|
|
}
|
|
|
|
function setDownloadLimit() {
|
|
dialogStore.createDialog(SpeedLimitDialog, { hashes: hashes.value, mode: 'download' })
|
|
}
|
|
|
|
function setUploadLimit() {
|
|
dialogStore.createDialog(SpeedLimitDialog, { hashes: hashes.value, mode: 'upload' })
|
|
}
|
|
|
|
function setShareLimit() {
|
|
dialogStore.createDialog(ShareLimitDialog, { hashes: hashes.value })
|
|
}
|
|
|
|
async function exportTorrents() {
|
|
hashes.value.forEach(hash => {
|
|
torrentStore.exportTorrent(hash).then(blob => {
|
|
const url = window.URL.createObjectURL(blob)
|
|
const link = document.createElement('a')
|
|
link.href = url
|
|
link.style.opacity = '0'
|
|
link.setAttribute('download', `${ hash }.torrent`)
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
document.body.removeChild(link)
|
|
})
|
|
})
|
|
}
|
|
|
|
const menuData = computed<TRCMenuEntry[]>(() => [
|
|
{
|
|
text: t('dashboard.right_click.advanced.title'),
|
|
icon: 'mdi-head-cog',
|
|
children: [
|
|
{
|
|
text: t('dashboard.right_click.advanced.download_path'),
|
|
icon: 'mdi-tray-arrow-down',
|
|
action: setDownloadPath
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.save_path'),
|
|
icon: 'mdi-content-save',
|
|
action: setSavePath
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.rename'),
|
|
icon: 'mdi-rename-box',
|
|
hidden: isMultiple.value,
|
|
action: renameTorrents
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.recheck'),
|
|
icon: 'mdi-playlist-check',
|
|
action: forceRecheck
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.reannounce'),
|
|
icon: 'mdi-bullhorn',
|
|
action: forceReannounce
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.seq_dl'),
|
|
icon: torrent.value?.seq_dl ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline',
|
|
action: toggleSeqDl
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.f_l_prio'),
|
|
icon: torrent.value?.f_l_piece_prio ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline',
|
|
action: toggleFLPiecePrio
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.advanced.auto_tmm'),
|
|
icon: torrent.value?.auto_tmm ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline',
|
|
action: toggleAutoTMM
|
|
}
|
|
]
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.priority.title'),
|
|
icon: 'mdi-priority-high',
|
|
hidden: !preferenceStore.preferences!.queueing_enabled,
|
|
children: [
|
|
{
|
|
text: t('dashboard.right_click.priority.top'),
|
|
icon: 'mdi-priority-high',
|
|
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'topPrio')
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.priority.increase'),
|
|
icon: 'mdi-arrow-up',
|
|
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'increasePrio')
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.priority.decrease'),
|
|
icon: 'mdi-arrow-down',
|
|
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'decreasePrio')
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.priority.bottom'),
|
|
icon: 'mdi-priority-low',
|
|
action: async () => await torrentStore.setTorrentPriority(hashes.value, 'bottomPrio')
|
|
}
|
|
]
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.tags.title'),
|
|
icon: 'mdi-tag',
|
|
disabled: maindataStore.tags.length === 0,
|
|
disabledText: t('dashboard.right_click.tags.disabled_title'),
|
|
disabledIcon: 'mdi-tag-off',
|
|
children: maindataStore.tags.map(tag => ({
|
|
text: tag,
|
|
icon: hasTag(tag) ? 'mdi-checkbox-marked' : 'mdi-checkbox-blank-outline',
|
|
action: async () => await toggleTag(tag)
|
|
}))
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.category.title'),
|
|
icon: 'mdi-label',
|
|
disabled: maindataStore.categories.length === 0,
|
|
disabledText: t('dashboard.right_click.category.disabled_title'),
|
|
disabledIcon: 'mdi-label-off',
|
|
children: availableCategories.value.map(category => ({
|
|
text: category.name === '' ? t('dashboard.right_click.category.clear') : category.name,
|
|
action: async () => await torrentStore.setTorrentCategory(hashes.value, category.name)
|
|
}))
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.speed_limit.title'),
|
|
icon: 'mdi-speedometer-slow',
|
|
children: [
|
|
{
|
|
text: t('dashboard.right_click.speed_limit.download'),
|
|
icon: 'mdi-download',
|
|
action: setDownloadLimit
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.speed_limit.upload'),
|
|
icon: 'mdi-upload',
|
|
action: setUploadLimit
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.speed_limit.share'),
|
|
icon: 'mdi-account-group',
|
|
action: setShareLimit
|
|
}
|
|
]
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.copy.title'),
|
|
icon: 'mdi-content-copy',
|
|
hidden: isMultiple.value,
|
|
children: [
|
|
{
|
|
text: t('dashboard.right_click.copy.name'),
|
|
icon: 'mdi-alphabetical-variant',
|
|
action: async () => torrent.value && (await copyValue(torrent.value.name))
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.copy.hash'),
|
|
icon: 'mdi-pound',
|
|
action: async () => await copyValue(hash.value)
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.copy.magnet'),
|
|
icon: 'mdi-magnet',
|
|
action: async () => torrent.value && (await copyValue(torrent.value.magnet))
|
|
}
|
|
]
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.export', dashboardStore.selectedTorrents.length),
|
|
icon: isMultiple.value ? 'mdi-download-multiple' : 'mdi-download',
|
|
action: exportTorrents
|
|
},
|
|
{
|
|
text: t('dashboard.right_click.info'),
|
|
icon: 'mdi-information',
|
|
hidden: isMultiple.value,
|
|
action: () => router.push({ name: 'torrentDetail', params: { hash: hash.value } })
|
|
}
|
|
])
|
|
</script>
|
|
|
|
<template>
|
|
<v-menu v-if="trcVisible" v-model="trcVisible" activator="parent" :close-on-content-click="true"
|
|
transition="slide-y-transition" scroll-strategy="none">
|
|
<v-list>
|
|
<v-list-item>
|
|
<div class="d-flex justify-space-around">
|
|
<v-tooltip location="top">
|
|
<template v-slot:activator="{ props }">
|
|
<v-btn density="compact" variant="plain" icon="mdi-play" v-bind="props" @click="resumeTorrents" />
|
|
</template>
|
|
<span>Resume</span>
|
|
</v-tooltip>
|
|
|
|
<v-tooltip location="top">
|
|
<template v-slot:activator="{ props }">
|
|
<v-btn density="compact" variant="plain" icon="mdi-fast-forward" v-bind="props"
|
|
@click="forceResumeTorrents" />
|
|
</template>
|
|
<span>Force Resume</span>
|
|
</v-tooltip>
|
|
|
|
<v-tooltip location="top">
|
|
<template v-slot:activator="{ props }">
|
|
<v-btn density="compact" variant="plain" icon="mdi-pause" v-bind="props" @click="pauseTorrents" />
|
|
</template>
|
|
<span>Pause</span>
|
|
</v-tooltip>
|
|
|
|
<v-tooltip location="top">
|
|
<template v-slot:activator="{ props }">
|
|
<v-btn color="red" density="compact" variant="plain" icon="mdi-delete-forever" v-bind="props"
|
|
@click="deleteTorrents" />
|
|
</template>
|
|
<span>Delete</span>
|
|
</v-tooltip>
|
|
</div>
|
|
</v-list-item>
|
|
|
|
<RightClickMenuEntry v-for="entry in menuData" v-bind="entry" />
|
|
</v-list>
|
|
</v-menu>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.menu-scrollable {
|
|
max-height: 500px;
|
|
overflow: visible;
|
|
}
|
|
</style>
|