diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/services/qBittorrent.vue | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/src/components/services/qBittorrent.vue b/src/components/services/qBittorrent.vue new file mode 100644 index 0000000..1f1ef49 --- /dev/null +++ b/src/components/services/qBittorrent.vue | |||
@@ -0,0 +1,120 @@ | |||
1 | <template> | ||
2 | <Generic :item="item"> | ||
3 | <template #content> | ||
4 | <p class="title is-4">{{ item.name }}</p> | ||
5 | <p class="subtitle is-6"> | ||
6 | <span v-if="error" class="error">An error has occurred.</span> | ||
7 | <template v-else> | ||
8 | <span class="down monospace"> | ||
9 | <p class="fas fa-download "></p> {{ downRate }} | ||
10 | </span> | ||
11 | <span class="up monospace"> | ||
12 | <p class="fas fa-upload"></p> {{ upRate }} | ||
13 | </span> | ||
14 | </template> | ||
15 | </p> | ||
16 | </template> | ||
17 | <template #indicator> | ||
18 | <span v-if="!error" class="count">{{ count }} | ||
19 | <template v-if="count === 1">torrent</template> | ||
20 | <template v-else>torrents</template> | ||
21 | </span> | ||
22 | </template> | ||
23 | </Generic> | ||
24 | </template> | ||
25 | |||
26 | <script> | ||
27 | import service from "@/mixins/service.js"; | ||
28 | import Generic from "./Generic.vue"; | ||
29 | const units = ["B", "KB", "MB", "GB"]; | ||
30 | |||
31 | // Take the rate in bytes and keep dividing it by 1k until the lowest | ||
32 | // value for which we have a unit is determined. Return the value with | ||
33 | // up to two decimals as a string and unit/s appended. | ||
34 | const displayRate = (rate) => { | ||
35 | let i = 0; | ||
36 | |||
37 | while (rate > 1000 && i < units.length) { | ||
38 | rate /= 1000; | ||
39 | i++; | ||
40 | } | ||
41 | return ( | ||
42 | Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format( | ||
43 | rate || 0 | ||
44 | ) + ` ${units[i]}/s` | ||
45 | ); | ||
46 | }; | ||
47 | |||
48 | export default { | ||
49 | name: "qBittorrent", | ||
50 | mixins: [service], | ||
51 | props: { item: Object }, | ||
52 | components: { Generic }, | ||
53 | data: () => ({ dl: null, ul: null, count: null, error: null }), | ||
54 | computed: { | ||
55 | downRate: function () { | ||
56 | return displayRate(this.dl); | ||
57 | }, | ||
58 | upRate: function () { | ||
59 | return displayRate(this.ul); | ||
60 | }, | ||
61 | }, | ||
62 | created() { | ||
63 | const rateInterval = parseInt(this.item.rateInterval, 10) || 0; | ||
64 | const torrentInterval = parseInt(this.item.torrentInterval, 10) || 0; | ||
65 | if (rateInterval > 0) { | ||
66 | setInterval(() => this.getRate(), rateInterval); | ||
67 | } | ||
68 | if (torrentInterval > 0) { | ||
69 | setInterval(() => this.fetchCount(), torrentInterval); | ||
70 | } | ||
71 | |||
72 | this.getRate(); | ||
73 | this.fetchCount(); | ||
74 | }, | ||
75 | methods: { | ||
76 | fetchCount: async function () { | ||
77 | try { | ||
78 | const body = await this.fetch('/api/v2/torrents/info'); | ||
79 | this.error = false; | ||
80 | this.count = body.length; | ||
81 | } catch (e) { | ||
82 | this.error = true; | ||
83 | console.error(e); | ||
84 | } | ||
85 | }, | ||
86 | getRate: async function () { | ||
87 | try { | ||
88 | const body = await this.fetch('/api/v2/transfer/info'); | ||
89 | this.error = false; | ||
90 | this.dl = body.dl_info_speed; | ||
91 | this.ul = body.up_info_speed; | ||
92 | } catch (e) { | ||
93 | this.error = true; | ||
94 | console.error(e); | ||
95 | } | ||
96 | }, | ||
97 | }, | ||
98 | }; | ||
99 | |||
100 | </script> | ||
101 | |||
102 | <style scoped lang="scss"> | ||
103 | .error { | ||
104 | color: #e51111 !important; | ||
105 | } | ||
106 | |||
107 | .down { | ||
108 | margin-right: 1em; | ||
109 | } | ||
110 | |||
111 | .count { | ||
112 | color: var(--text); | ||
113 | font-size: 0.8em; | ||
114 | } | ||
115 | |||
116 | .monospace { | ||
117 | font-weight: 300; | ||
118 | font-family: monospace; | ||
119 | } | ||
120 | </style> \ No newline at end of file | ||