This commit is contained in:
2025-04-13 11:59:50 +02:00
parent 80640d2580
commit fe3191d4a2
21 changed files with 567 additions and 117 deletions

View File

@@ -11,24 +11,26 @@
</v-row>
</v-list-item-action>
</v-list-item>
<v-divider/>
</v-list>
<v-divider/>
<UserStats :stats="user_stats"/>
<v-divider/>
{{dm_status}}
<DM v-if="dm_activated" :dm_files="dm_files" :dm_status="dm_status" :dm_download_location="dm_download_location"/>
</v-navigation-drawer>
<v-navigation-drawer v-model="right_drawer" location="right" temporary>
<Friend v-if="right_drawer" :active_user="display_user.id" @userSelected="userSelectedUpdated"/>
</v-navigation-drawer>
<v-app-bar app>
<v-app-bar-title>
<v-row align="center" justify="start" style="max-width: 100%">
<v-col class="v-col-1 text-center">
<v-app-bar>
<v-app-bar-title style="max-width: 150px">
<v-btn variant="plain" href="/">Oxpanel</v-btn>
</v-col>
<v-col class="v-col-3">
<v-text-field flat solo-inverted hide-details prepend-inner-icon="mdi-magnify" label="Search" v-model="filters.search" clearable @click:clear="filters.search = ''"/>
</v-col>
</v-row>
</v-app-bar-title>
<v-row align="center" justify="center" style="max-width: 500px" class="mx-auto">
<v-col>
<v-text-field density="comfortable" flat solo-inverted hide-details prepend-inner-icon="mdi-magnify" label="Search" v-model="filters.search" clearable @click:clear="filters.search = ''"/>
</v-col>
</v-row>
<template v-slot:append>
<v-menu>
<template v-slot:activator="{ props }">
@@ -67,6 +69,7 @@ import TorrentList from "@/components/torrent/TorrentList.vue";
import UploadForm from "@/components/torrent/UploadForm.vue";
import Friend from "@/components/auth/Friend.vue";
import UserStats from "@/components/torrent/UserStats.vue";
import DM from "@/components/torrent/DM.vue";
</script>
<script>
@@ -83,18 +86,23 @@ export default {
filters: {},
torrents: [],
user_stats: {
"torrents_size": 0,
"torrents_len": 0,
"torrent_len_shared": 0,
"torrents_total_len": 0,
"user_max_size": 0,
"user_usage_percent": 0,
"disk_total": 0,
"disk_used": 0,
"disk_free": 0,
"disk_usage_percent": 0,
torrents_size: 0,
torrents_len: 0,
torrent_len_shared: 0,
torrents_total_len: 0,
user_max_size: 0,
user_usage_percent: 0,
disk_total: 0,
disk_used: 0,
disk_free: 0,
disk_usage_percent: 0,
},
filter_timer: null,
dm_files: {},
// dm_stats: {},
dm_status: {},
dm_download_location: "",
dm_activated: false
}
},
watch: {
@@ -134,6 +142,29 @@ export default {
})
await this.fetchTorrents();
await this.fetchUserStats();
if(this.$qt.connect()){
this.$qt.on("ready", () => {
this.dm_status = this.$qt.getProperty("dm_status");
this.dm_files = this.$qt.getProperty("dm_files");
// this.dm_stats = this.$qt.getProperty("dm_stats");
this.dm_download_location = this.$qt.getProperty("dm_download_location");
this.dm_activated = true;
// console.log(JSON.stringify(this.dm_files))
this.$qt.on("status_updated", data => {
this.dm_status = data;
})
// this.$qt.on("stats_updated", data => {
// this.dm_stats = data;
// })
this.$qt.on("files_updated", data => {
this.dm_files = data;
})
this.$qt.on("download_location_updated", data => {
this.dm_download_location = data;
})
})
}
},
methods: {
async fetchUserStats(){
@@ -218,4 +249,8 @@ export default {
}
}
}
</script>
</script>
<style scoped>
</style>

View File

@@ -0,0 +1,75 @@
<template>
<v-list>
<v-list-item prepend-icon="mdi-details" @click="details_enabled = true" title="Download Manager"/>
<v-dialog width="80%" v-model="details_enabled">
<DMDetails :dm_files="dm_files" :dm_status="dm_status" :dm_download_location="dm_download_location"/>
</v-dialog>
<v-list-item
:prepend-icon="dm_status.pause ? 'mdi-play' : 'mdi-pause'"
@click="$qt.callMethod('set_pause', !dm_status.pause)"
>
<template v-slot:title>
{{ dm_status.pause ? 'Start':'Pause' }}
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-speedometer">
<template v-slot:title>
{{fs_speed_format(dm_status.speed)}}/s
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-file-download" >
<template v-slot:title>
{{dm_status.downloaded_files}}/{{dm_status.total_files}}
</template>
</v-list-item>
<v-list-item prepend-icon="mdi-tape-drive">
<template v-slot:title>
{{fs_format(dm_status.downloaded_size)}}/{{fs_format(dm_status.total_size)}}
</template>
<template v-slot:subtitle>
<v-progress-linear :model-value="percentDownloaded" height="12" color="green">{{percentDownloaded.toFixed(1)}}%</v-progress-linear>
</template>
</v-list-item>
</v-list>
</template>
<script setup>
import DMDetails from "@/components/torrent/DMDetails.vue";
</script>
<script>
import {fs_format, fs_speed_format} from "@/plugins/utils.js";
export default {
methods: {fs_format, fs_speed_format},
props: {
dm_files: {
required: true,
type: Object
},
dm_status: {
required: true,
type: Object
},
dm_download_location: {
required: true,
type: String
}
},
data(){
return {
details_enabled: false,
}
},
computed: {
percentDownloaded(){
return this.dm_status.total_size > 0 ? this.dm_status.downloaded_size / this.dm_status.total_size * 100: 0;
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,123 @@
<template>
<v-card class="dm-details-card">
<v-card-actions>
<v-text-field :model-value="dm_download_location" readonly @click="$qt.callMethod('change_path')"></v-text-field>
</v-card-actions>
<v-divider/>
<v-card-text class="scrollable-content">
<!-- Liste des fichiers en téléchargement -->
<div v-if="Object.keys(filterFiles.downloading).length > 0">
<h3>Fichiers en téléchargement</h3>
<v-list density="compact">
<v-list-item v-for="(file, key) in filterFiles.downloading" :key="key">
<template v-slot:title>
{{ file.rel_path }}
</template>
<template v-slot:subtitle>
</template>
</v-list-item>
</v-list>
</div>
<!-- Liste des fichiers en attente -->
<div>
<h3>Fichiers en attente</h3>
<v-list density="compact">
<v-list-item v-for="(file, key) in filterFiles.waiting" :key="key">
<template v-slot:title>
{{ file.rel_path }}
</template>
<template v-slot:subtitle>
En attente
</template>
</v-list-item>
</v-list>
</div>
<!-- Liste des fichiers terminés -->
<div>
<h3>Fichiers terminés</h3>
<v-list density="compact">
<v-list-item v-for="(file, key) in filterFiles.finished" :key="key">
<template v-slot:title>
{{ file.rel_path }}
</template>
<template v-slot:subtitle>
</template>
</v-list-item>
</v-list>
</div>
</v-card-text>
</v-card>
</template>
<script setup>
import {fs_format, fs_speed_format} from "@/plugins/utils.js";
</script>
<script>
export default {
props: {
dm_files: {
required: true,
type: Object
},
dm_status: {
required: true,
type: Object
},
dm_download_location: {
required: true,
type: String
}
},
data(){
return {
}
},
mounted(){
},
methods: {
},
computed: {
filterFiles(){
let waiting = {},
downloading = {},
finished = {};
for(const [file_id, file] of Object.entries(this.dm_files)){
if(file.downloaded){
finished[file_id] = file;
}else if(file_id in this.dm_status.downloading){
downloading[file_id] = file;
}else{
waiting[file_id] = file;
}
}
return {waiting, downloading, finished};
}
}
};
</script>
<style>
.scrollable-content {
max-height: 400px; /* Définissez la hauteur maximale souhaitée */
overflow-y: auto; /* Active le défilement vertical quand nécessaire */
overflow-x: hidden; /* Empêche le défilement horizontal */
padding-right: 8px; /* Espace pour la barre de défilement */
}
.dm-details-card {
display: flex;
flex-direction: column;
}
</style>

View File

@@ -44,10 +44,15 @@ export default {
methods: {
downloadClicked(){
if(!this.is_download_finished) return;
let a = document.createElement("a");
a.href = this.file.download_url;
a.setAttribute("download", "download");
a.click();
if(this.$qt.is_active){
this.$qt.callMethod("add_files", this.file);
}else{
let a = document.createElement("a");
a.href = this.file.download_url;
a.setAttribute("download", "download");
a.click();
}
}
}
}

View File

@@ -42,6 +42,7 @@ export default {
let response = await fetch(`/api/torrent/files?${new URLSearchParams(this.filters)}`);
this.files = await response.json();
this.loading = false;
return this.files;
}
}
}

View File

@@ -2,14 +2,14 @@
<v-card @click="show_files = !show_files">
<v-progress-linear height="20" :color="progressData.color" :model-value="progressData.value">
<!-- barre de progression -->
<v-progress-circular
v-if="!torrent.transmission_data.rateDownload && !torrent.transmission_data.rateUpload"
size="15"
indeterminate
:color="torrent.transmission_data.progress < 100 ? 'green':'red'"
/>
<!-- <v-progress-circular-->
<!-- v-if="!torrent.transmission_data.rateDownload && !torrent.transmission_data.rateUpload"-->
<!-- size="15"-->
<!-- indeterminate-->
<!-- :color="torrent.transmission_data.progress < 100 ? 'green':'red'"-->
<!-- />-->
<v-icon
v-else
v-if="torrent.transmission_data.rateDownload && torrent.transmission_data.rateUpload"
:color="torrent.transmission_data.rateDownload ? 'green':'red'"
:icon="torrent.transmission_data.rateDownload ? 'mdi-arrow-down-bold':'mdi-arrow-up-bold'"
/>
@@ -52,7 +52,11 @@
<v-expand-transition>
<div v-if="show_files">
<v-divider />
<FileList v-if="show_files" :torrent_id="torrent.id" :is_download_finished="torrent.transmission_data.progress >= 100"/>
<FileList
v-if="show_files"
:torrent_id="torrent.id"
:is_download_finished="torrent.transmission_data.progress >= 100"
/>
</div>
</v-expand-transition>
</v-card>
@@ -88,10 +92,16 @@ export default {
methods: {
async downloadClicked(){
if(this.torrent.transmission_data.progress < 100) return;
let a = document.createElement("a");
a.href = this.torrent.download_url;
a.setAttribute("download", "download");
a.click();
if(this.$qt.is_active){
let response = await fetch(`/api/torrent/files/?torrent=${this.torrent.id}`);
let files = await response.json();
this.$qt.callMethod("add_files", files);
}else{
let a = document.createElement("a");
a.href = this.torrent.download_url;
a.setAttribute("download", "download");
a.click();
}
},
async deleteClicked(){
this.$emit("delete", this.torrent.id);