]> git.immae.eu Git - github/bastienwirtz/homer.git/blame - src/components/services/Rtorrent.vue
Apply lint fixes
[github/bastienwirtz/homer.git] / src / components / services / Rtorrent.vue
CommitLineData
51ba5ff5 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">
9 <i class="fas fa-download"></i> {{ downRate }}
10 </span>
bdad8933 11 <span class="up"> <i class="fas fa-upload"></i> {{ upRate }} </span>
51ba5ff5 12 </template>
13 </p>
14 </template>
15 <template #indicator>
bdad8933
BW
16 <span v-if="!error" class="count"
17 >{{ count }}
51ba5ff5 18 <template v-if="count === 1">torrent</template>
19 <template v-else>torrents</template>
20 </span>
21 </template>
22 </Generic>
23</template>
24
25<script>
bdad8933 26import Generic from "./Generic.vue";
51ba5ff5 27
28// Units to add to download and upload rates.
bdad8933 29const units = ["B", "kiB", "MiB", "GiB"];
51ba5ff5 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.
34const displayRate = (rate) => {
35 let i = 0;
bdad8933 36
51ba5ff5 37 while (rate > 1000 && i < units.length) {
38 rate /= 1000;
39 i++;
40 }
41
bdad8933
BW
42 return (
43 Intl.NumberFormat(undefined, { maximumFractionDigits: 2 }).format(
44 rate || 0
45 ) + ` ${units[i]}/s`
46 );
47};
51ba5ff5 48
49export default {
bdad8933
BW
50 name: "rTorrent",
51 props: { item: Object },
52 components: { Generic },
51ba5ff5 53 // Properties for download, upload, torrent count and errors.
bdad8933 54 data: () => ({ dl: null, ul: null, count: null, error: null }),
51ba5ff5 55 // Computed properties for the rate labels.
56 computed: {
bdad8933 57 downRate: function () {
51ba5ff5 58 return displayRate(this.dl);
59 },
bdad8933 60 upRate: function () {
51ba5ff5 61 return displayRate(this.ul);
62 },
63 },
64 created() {
65 // Set intervals if configured so the rates and/or torrent count
66 // will be updated.
67 const rateInterval = parseInt(this.item.rateInterval, 10) || 0;
68 const torrentInterval = parseInt(this.item.torrentInterval, 10) || 0;
69
70 if (rateInterval > 0) {
71 setInterval(() => this.fetchRates(), rateInterval);
72 }
73
74 if (torrentInterval > 0) {
75 setInterval(() => this.fetchCount(), torrentInterval);
76 }
bdad8933 77
51ba5ff5 78 // Fetch the initial values.
79 this.fetchRates();
80 this.fetchCount();
81 },
82 methods: {
83 // Perform two calls to the XML-RPC service and fetch download
84 // and upload rates. Values are saved to the `ul` and `dl`
85 // properties.
bdad8933
BW
86 fetchRates: async function () {
87 this.getRate("throttle.global_up.rate")
88 .then((ul) => (this.ul = ul))
89 .catch(() => (this.error = true));
51ba5ff5 90
bdad8933
BW
91 this.getRate("throttle.global_down.rate")
92 .then((dl) => (this.dl = dl))
93 .catch(() => (this.error = true));
51ba5ff5 94 },
95 // Perform a call to the XML-RPC service to fetch the number of
96 // torrents.
bdad8933
BW
97 fetchCount: async function () {
98 this.getCount().catch(() => (this.error = true));
51ba5ff5 99 },
100 // Fetch a numeric value from the XML-RPC service by requesting
101 // the specified method name and parsing the XML. The response
102 // is expected to adhere to the structure of a single numeric
103 // value.
bdad8933
BW
104 getRate: async function (methodName) {
105 return this.getXml(methodName).then((xml) =>
106 parseInt(
107 xml.getElementsByTagName("value")[0].firstChild.textContent,
108 10
109 )
110 );
51ba5ff5 111 },
112 // Fetch the numer of torrents by requesting the download list
113 // and counting the number of entries therein.
bdad8933
BW
114 getCount: async function () {
115 return this.getXml("download_list").then((xml) => {
116 const arrayEl = xml.getElementsByTagName("array");
117 this.count = arrayEl
118 ? arrayEl[0].getElementsByTagName("value").length
119 : 0;
120 });
51ba5ff5 121 },
122 // Perform a call to the XML-RPC service and parse the response
123 // as XML, which is then returned.
bdad8933
BW
124 getXml: async function (methodName) {
125 const headers = { "Content-Type": "text/xml" };
51ba5ff5 126
127 if (this.item.username && this.item.password) {
bdad8933
BW
128 headers[
129 "Authorization"
130 ] = `${this.item.username}:${this.item.password}`;
51ba5ff5 131 }
132
bdad8933
BW
133 return fetch(`${this.item.xmlrpc.replace(/\/$/, "")}/RPC2`, {
134 method: "POST",
51ba5ff5 135 headers,
bdad8933 136 body: `<methodCall><methodName>${methodName}</methodName></methodCall>`,
51ba5ff5 137 })
bdad8933
BW
138 .then((response) => {
139 if (!response.ok) {
140 throw Error(response.statusText);
141 }
51ba5ff5 142
bdad8933
BW
143 return response.text();
144 })
145 .then((text) =>
146 Promise.resolve(new DOMParser().parseFromString(text, "text/xml"))
147 );
148 },
149 },
150};
51ba5ff5 151</script>
152
153<style scoped lang="scss">
154.error {
155 color: #e51111 !important;
156}
157.down {
158 margin-right: 1em;
159}
160.count {
161 color: var(--text);
162 font-size: 0.8em;
163}
bdad8933 164</style>