-
+
+
+
+
+
+
-
-
-
-
- Made by Daan Wijns
-
+
+
+
+
+
+
+
+
+
+ Made by Daan Wijns
+
+
diff --git a/src/components/Login.vue b/src/components/Login.vue
index cc5e375f..93d633dc 100644
--- a/src/components/Login.vue
+++ b/src/components/Login.vue
@@ -1,72 +1,75 @@
-
-
-
-
- Login
-
-
-
-
-
-
-
-
- Login
-
-
-
-
-
-
+
+
+
+
+ Login
+
+
+
+
+
+
+
+
+ Login
+
+
+
+
+
+
diff --git a/src/components/Navbar.vue b/src/components/Navbar.vue
index 2ca9bd44..a399046c 100644
--- a/src/components/Navbar.vue
+++ b/src/components/Navbar.vue
@@ -1,108 +1,173 @@
-
+
+
+
+
+
+
+ play_arrow
+
+ Resumes connection to client
+
+
+
+ pause
+
+ Pauses connection to client
+
+
+
+
diff --git a/src/main.js b/src/main.js
index 9cb6b3b7..22accf37 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,20 +1,24 @@
import Vue from 'vue'
import './plugins/vuetify'
import VueApexCharts from 'vue-apexcharts'
+import Toast from 'vue-toastification'
import App from './App.vue'
import router from './router'
import store from './services/store'
import './registerServiceWorker'
+import 'vue-toastification/dist/index.css'
Vue.use(VueApexCharts)
Vue.component('apexchart', VueApexCharts)
+Vue.use(Toast)
+
Vue.config.productionTip = false
new Vue({
- router,
- store,
- render: (h) => h(App),
+ router,
+ store,
+ render: h => h(App)
}).$mount('#app')
diff --git a/src/models/sessionStat.js b/src/models/sessionStat.js
new file mode 100644
index 00000000..32897e0a
--- /dev/null
+++ b/src/models/sessionStat.js
@@ -0,0 +1,21 @@
+export default class Stat {
+ constructor(data) {
+ if (data != undefined && data != null) {
+ this.status = data.connection_status
+ this.downloaded = this.formatBytes(data.dl_info_data, 1)
+ this.uploaded = this.formatBytes(data.up_info_data, 1)
+ this.dlspeed = this.formatBytes(data.dl_info_speed, 1)
+ this.upspeed = this.formatBytes(data.up_info_speed, 1)
+ }
+ }
+
+ formatBytes(a, b) {
+ if (a == 0) return '0 Bytes'
+ const c = 1024
+ const d = b || 2
+ const e = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+ const f = Math.floor(Math.log(a) / Math.log(c))
+ return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
+ }
+
+}
diff --git a/src/models/torrent.js b/src/models/torrent.js
new file mode 100644
index 00000000..252a32a9
--- /dev/null
+++ b/src/models/torrent.js
@@ -0,0 +1,52 @@
+export default class Torrent {
+ constructor(data) {
+ this.id = data.id
+ this.name = data.name
+ this.size = this.formatBytes(data.size)
+ this.birth = new Date(data.added_on * 1000).toLocaleString()
+ this.dlspeed = this.formatBytes(data.dlspeed, 1)
+ this.dloaded = this.formatBytes(data.downloaded)
+ this.upspeed = this.formatBytes(data.upspeed, 1)
+ this.uploaded = this.formatBytes(data.uploaded)
+ this.eta = `${new Date(data.eta).getHours()}h ${new Date(
+ data.eta
+ ).getMinutes()}min`
+ this.num_leechs = data.num_leechs
+ this.num_seeds = data.num_seeds
+ this.path = data.path === undefined ? '/downloads' : data.path
+ this.state = this.formatState(data.state)
+ // hash is used to identify
+ this.hash = data.hash
+ // available seeds
+ this.available_seeds = data.num_complete
+ this.available_peers = data.num_incomplete
+ }
+
+ formatState(state) {
+ switch (state) {
+ case 'pausedDL':
+ return 'paused'
+ case 'downloading':
+ return 'busy'
+ case 'stalledDL':
+ return 'fail'
+ case 'pausedUP':
+ return 'done'
+ case 'missingFiles':
+ return 'fail'
+ case 'stalledUP':
+ return 'done'
+ default:
+ return undefined
+ }
+ }
+
+ formatBytes(a, b) {
+ if (a == 0) return '0 Bytes'
+ const c = 1024
+ const d = b || 2
+ const e = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+ const f = Math.floor(Math.log(a) / Math.log(c))
+ return `${parseFloat((a / Math.pow(c, f)).toFixed(d))} ${e[f]}`
+ }
+}
diff --git a/src/services/qbit.js b/src/services/qbit.js
index c719933a..3789bb67 100644
--- a/src/services/qbit.js
+++ b/src/services/qbit.js
@@ -1,62 +1,294 @@
-const axios = require('axios')
+import axios from 'axios'
class Qbit {
- constructor() {
- this._axios = axios.create({
- timeout: 1000,
- })
- }
+ constructor() {
+ this.axios = axios.create({
+ baseURL: 'api/v2'
+ })
- async getAll(sort) {
- const res = await this._axios.post('/all', sort)
- return res.data
- }
+ this.axios.defaults.headers.post['Content-Type'] =
+ 'application/x-www-form-urlencoded'
+ }
- async get_sessions_stats() {
- const res = await this._axios.get('/session')
- return res.data
- }
+ getAppVersion() {
+ return this.axios.get('/app/version')
+ }
- async pause_torrents(torrents) {
- const res = await this._axios.post('/pause', torrents)
- return res.data
- }
+ getApiVersion() {
+ return this.axios.get('/app/webapiVersion')
+ }
- async pause_all() {
- const res = await this._axios.post('/pause_all')
- return res.data
- }
+ async login(params) {
+ const payload = new URLSearchParams(params)
+ const { data } = await this.axios.post('/auth/login', payload, {
+ validateStatus(status) {
+ return status === 200 || status === 403
+ }
+ })
+ return data
+ }
- async resume_torrents(torrents) {
- const res = await this._axios.post('/resume', torrents)
- return res.data
- }
+ getGlobalTransferInfo() {
+ return this.axios.get('/transfer/info')
+ }
- async resume_all() {
- const res = await this._axios.post('/resume_all')
- return res.data
- }
+ getAppPreferences() {
+ return this.axios.get('/app/preferences')
+ }
- async add_torrent(torrent) {
- const res = await this._axios.post('/add', torrent)
- return res
- }
+ getMainData(rid) {
+ const params = {
+ rid
+ }
+ return this.axios.get('/sync/maindata', {
+ params
+ })
+ }
- async remove_torrents(torrents) {
- const res = await this._axios.post('/remove', torrents)
- return res.data
- }
+ addTorrents(params, torrents) {
+ let data
+ if (torrents) {
+ const formData = new FormData()
+ for (const [key, value] of Object.entries(params)) {
+ // eslint-disable-next-line
+ formData.append(key, value)
+ }
- async login(credentials) {
- let timeout = false
- const res = await this._axios.post('/login', credentials).catch((error) => {
- if (error.code === 'ECONNABORTED') timeout = true
- else throw error
- })
- return timeout ? 'timeout' : res.data
- }
+ for (const torrent of torrents) {
+ formData.append('torrents', torrent)
+ }
+
+ data = formData
+ } else {
+ data = new URLSearchParams(params)
+ }
+ return this.axios.post('/torrents/add', data).then(Api.handleResponse)
+ }
+
+ switchToOldUi() {
+ const params = {
+ alternative_webui_enabled: false
+ }
+
+ return this.setPreferences(params)
+ }
+
+ setPreferences(params) {
+ const data = new URLSearchParams({
+ json: JSON.stringify(params)
+ })
+
+ return this.axios.post('/app/setPreferences', data)
+ }
+
+ setTorrentFilePriority(hash, idList, priority) {
+ const idListStr = idList.join('|')
+ const params = {
+ hash,
+ id: idListStr,
+ priority
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios
+ .post('/torrents/filePrio', data)
+ .then(Api.handleResponse)
+ }
+
+ getLogs(lastId) {
+ const params = {
+ last_known_id: lastId
+ }
+
+ return this.axios
+ .get('/log/main', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ toggleSpeedLimitsMode() {
+ return this.axios.post('/transfer/toggleSpeedLimitsMode')
+ }
+
+ deleteTorrents(hashes, deleteFiles) {
+ return this.actionTorrents('delete', hashes, { deleteFiles })
+ }
+
+ pauseTorrents(hashes) {
+ return this.actionTorrents('pause', hashes)
+ }
+
+ resumeTorrents(hashes) {
+ return this.actionTorrents('resume', hashes)
+ }
+
+ reannounceTorrents(hashes) {
+ return this.actionTorrents('reannounce', hashes)
+ }
+
+ recheckTorrents(hashes) {
+ return this.actionTorrents('recheck', hashes)
+ }
+
+ setTorrentsCategory(hashes, category) {
+ return this.actionTorrents('setCategory', hashes, { category })
+ }
+
+ getTorrentTracker(hash) {
+ const params = {
+ hash
+ }
+
+ return this.axios
+ .get('/torrents/trackers', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ getTorrentPeers(hash, rid) {
+ const params = {
+ hash,
+ rid
+ }
+
+ return this.axios
+ .get('/sync/torrentPeers', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ editTracker(hash, origUrl, newUrl) {
+ return this.actionTorrents('editTracker', [hash], { origUrl, newUrl })
+ }
+
+ setTorrentLocation(hashes, location) {
+ return this.actionTorrents('setLocation', hashes, { location })
+ }
+
+ getTorrentProperties(hash) {
+ const params = {
+ hash
+ }
+
+ return this.axios
+ .get('/torrents/properties', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ getTorrentPieceStates(hash) {
+ const params = {
+ hash
+ }
+
+ return this.axios
+ .get('/torrents/pieceStates', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ getTorrentFiles(hash) {
+ const params = {
+ hash
+ }
+
+ return this.axios
+ .get('/torrents/files', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ getRssItems() {
+ const params = {
+ withData: true
+ }
+
+ return this.axios
+ .get('/rss/items', {
+ params
+ })
+ .then(Api.handleResponse)
+ }
+
+ addRssFeed(url, path = '') {
+ const params = {
+ url,
+ path
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios.post('/rss/addFeed', data).then(Api.handleResponse)
+ }
+
+ removeRssFeed(path) {
+ const params = {
+ path
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios.post('/rss/removeItem', data).then(Api.handleResponse)
+ }
+
+ refreshRssFeed(path) {
+ const params = {
+ itemPath: path
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios
+ .post('/rss/refreshItem', data)
+ .then(Api.handleResponse)
+ }
+
+ moveRssFeed(path, newPath) {
+ const params = {
+ itemPath: path,
+ destPath: newPath
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios.post('/rss/moveItem', data).then(Api.handleResponse)
+ }
+
+ getRssRules() {
+ return this.axios.get('/rss/rules').then(Api.handleResponse)
+ }
+
+ setRssRule(name, def) {
+ const params = {
+ ruleName: name,
+ ruleDef: JSON.stringify(def)
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios.post('/rss/setRule', data).then(Api.handleResponse)
+ }
+
+ removeRssRule(name) {
+ const params = {
+ ruleName: name
+ }
+
+ const data = new URLSearchParams(params)
+ return this.axios.post('/rss/removeRule', data).then(Api.handleResponse)
+ }
+
+ actionTorrents(action, hashes, extra) {
+ const params = {
+ hashes: hashes.join('|'),
+ ...extra
+ }
+ const data = new URLSearchParams(params)
+ return this.axios
+ .post(`/torrents/${action}`, data)
+ .then(Api.handleResponse)
+ }
}
-const qbit = new Qbit()
-
-export default qbit
+export default new Qbit()
diff --git a/src/services/qbitOld.js b/src/services/qbitOld.js
new file mode 100644
index 00000000..9612c44d
--- /dev/null
+++ b/src/services/qbitOld.js
@@ -0,0 +1,64 @@
+const axios = require('axios')
+
+class Qbit {
+ constructor() {
+ this._axios = axios.create({
+ timeout: 1000
+ })
+ }
+
+ async getAll(sort) {
+ const res = await this._axios.post('/all', sort)
+ return res.data
+ }
+
+ async get_sessions_stats() {
+ const res = await this._axios.get('/session')
+ return res.data
+ }
+
+ async pause_torrents(torrents) {
+ const res = await this._axios.post('/pause', torrents)
+ return res.data
+ }
+
+ async pause_all() {
+ const res = await this._axios.post('/pause_all')
+ return res.data
+ }
+
+ async resume_torrents(torrents) {
+ const res = await this._axios.post('/resume', torrents)
+ return res.data
+ }
+
+ async resume_all() {
+ const res = await this._axios.post('/resume_all')
+ return res.data
+ }
+
+ async add_torrent(torrent) {
+ const res = await this._axios.post('/add', torrent)
+ return res
+ }
+
+ async remove_torrents(torrents) {
+ const res = await this._axios.post('/remove', torrents)
+ return res.data
+ }
+
+ async login(credentials) {
+ let timeout = false
+ const res = await this._axios
+ .post('/login', credentials)
+ .catch(error => {
+ if (error.code === 'ECONNABORTED') timeout = true
+ else throw error
+ })
+ return timeout ? 'timeout' : res.data
+ }
+}
+
+const qbit = new Qbit()
+
+export default qbit
diff --git a/src/services/store.js b/src/services/store.js
index ed11ace9..9948b164 100644
--- a/src/services/store.js
+++ b/src/services/store.js
@@ -1,208 +1,120 @@
import Vue from 'vue'
import Vuex from 'vuex'
+import { cloneDeep, merge, map, groupBy, sortBy } from 'lodash'
+import Torrent from '../models/torrent'
+import Stat from '../models/sessionStat'
import qbit from './qbit'
Vue.use(Vuex)
export default new Vuex.Store({
- state: {
- intervals: [],
- stats: {
- status: 'init',
- dlspeed: '6 Mbps',
- upspeed: '1 Mbps',
- downloaded: '6.95 Gb',
- uploaded: '1014 Mb',
+ state: {
+ intervals: [],
+ stats: null,
+ upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ torrents: [],
+ selected_torrents: [],
+ authenticated: false,
+ loading: false,
+ sort_options: { sort: 'name', reverse: false },
+ rid: 0,
+ mainData: undefined,
+ preferences: null,
+ pasteUrl: null
},
- upload_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- download_data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- torrents: [],
- init_torrents: false,
- selected_torrents: [],
- network_error: false,
- snackbar_error: false,
- error_msg: '',
- snackbar: false,
- succes_msg: '',
- authenticated: false,
- loading: false,
- sort_options: { sort: 'name', reverse: false },
- },
- getters: {
- CONTAINS_TORRENT: (state) => (hash) => state.selected_torrents.includes(hash),
- },
- mutations: {
- REFRESH_TORRENTS: async (state) => {
- const torrents = await qbit.getAll(state.sort_options).catch(() => {
- state.network_error = true
- state.error_msg = 'Lost connection with server, reload page'
- state.snackbar_error = true
- })
- if (torrents) {
- state.torrents = torrents.map((a) => ({ ...a }))
- state.init_torrents = true
- }
+ getters: {
+ CONTAINS_TORRENT: state => hash =>
+ state.selected_torrents.includes(hash)
},
- REFRESH_SESSION_STATS: async (state) => {
- const _stats = await qbit.get_sessions_stats()
- // push in array for graph
- state.download_data.splice(0, 1)
- if (_stats.dlspeed.indexOf('KB' > -1)) {
- state.download_data.push(
- _stats.dlspeed.substring(0, _stats.dlspeed.indexOf(' ')) / 1000,
- )
- } else {
- state.download_data.push(
- _stats.dlspeed(0, _stats.dlspeed.indexOf(' ')),
- )
- }
- state.upload_data.splice(0, 1)
- if (_stats.upspeed.indexOf('KB' > -1)) {
- state.upload_data.push(
- _stats.upspeed.substring(0, _stats.upspeed.indexOf(' ')) / 1000,
- )
- } else {
- state.upload_data.push(
- _stats.upspeed.substring(0, _stats.upspeed.indexOf(' ')),
- )
- }
- state.stats = _stats
- },
- CLEAR_INTERVALS: (state) => {
- if (state.intervals.length > 1) { state.intervals.forEach((el) => clearInterval(el)) }
- },
- ADD_SELECTED: (state, payload) => {
- state.selected_torrents.push(payload)
- },
- REMOVE_SELECTED: (state, payload) => {
- state.selected_torrents.splice(
- state.selected_torrents.indexOf(payload),
- 1,
- )
- },
- RESET_SELECTED: (state) => {
- state.selected_torrents = []
- },
- PAUSE_TORRENTS: async (state) => {
- let res
- if (state.selected_torrents.length === 0) {
- res = await qbit.pause_all()
- } else {
- res = await qbit.pause_torrents(state.selected_torrents)
- }
- },
- RESUME_TORRENTS: async (state) => {
- let res
- if (state.selected_torrents.length === 0) {
- res = await qbit.resume_all()
- } else {
- res = await qbit.resume_torrents(state.selected_torrents)
- }
- },
- ADD_TORRENT: async (state, payload) => {
- const res = await qbit.add_torrent(payload)
- if (res.statusText === 'OK') {
- state.snackbar = true
- state.succes_msg = 'Awesome! You added a new Torrent.'
- setTimeout(() => {
- state.snackbar = false
- }, 4000)
- } else {
- state.snackbar_error = true
- state.error_msg = 'Something went wrong'
- setTimeout(() => {
- state.snackbar_error = false
- }, 4000)
- }
- },
- REMOVE_TORRENTS: async (state) => {
- if (state.selected_torrents.length !== 0) {
- const res = await qbit.remove_torrents(state.selected_torrents)
- }
- },
- LOGIN: async (state, payload) => {
- const res = await qbit.login(payload)
- if (res == 'timeout') {
- state.loading = false
- state.snackbar_error = true
- state.error_msg = 'Express server timed out!'
- setTimeout(() => {
- state.snackbar_error = false
- }, 4000)
- } else {
- switch (res) {
- case 'No such user':
- state.snackbar_error = true
- state.error_msg = 'No such user!'
- setTimeout(() => {
- state.snackbar_error = false
- }, 4000)
- break
- case 'Wrong password!':
- state.snackbar_error = true
- state.error_msg = 'Wrong password!'
- setTimeout(() => {
- state.snackbar_error = false
- }, 4000)
- break
- case 'SUCCES':
- state.snackbar = true
- state.succes_msg = 'Succesfully logged in!'
- state.authenticated = true
- setTimeout(() => {
- state.snackbar = false
- }, 4000)
- break
- default:
- state.snackbar_error = true
- state.error_msg = 'Something went wrong'
- setTimeout(() => {
- state.snackbar_error = false
- }, 4000)
- break
+ mutations: {
+ REMOVE_INTERVALS: state => {
+ state.intervals.forEach(el => clearInterval(el))
+ },
+ ADD_SELECTED: (state, payload) => {
+ state.selected_torrents.push(payload)
+ },
+ REMOVE_SELECTED: (state, payload) => {
+ state.selected_torrents.splice(
+ state.selected_torrents.indexOf(payload),
+ 1
+ )
+ },
+ RESET_SELECTED: state => {
+ state.selected_torrents = []
+ },
+ PAUSE_TORRENTS: async state => {
+ let res
+ if (state.selected_torrents.length === 0) {
+ res = await qbit.pause_all()
+ } else {
+ res = await qbit.pause_torrents(state.selected_torrents)
+ }
+ },
+ RESUME_TORRENTS: async state => {
+ let res
+ if (state.selected_torrents.length === 0) {
+ res = await qbit.resume_all()
+ } else {
+ res = await qbit.resume_torrents(state.selected_torrents)
+ }
+ },
+ ADD_TORRENT: async (state, payload) => {
+ const res = await qbit.add_torrent(payload)
+ if (res.statusText === 'OK') {
+ state.snackbar = true
+ state.succes_msg = 'Awesome! You added a new Torrent.'
+ setTimeout(() => {
+ state.snackbar = false
+ }, 4000)
+ } else {
+ state.snackbar_error = true
+ state.error_msg = 'Something went wrong'
+ setTimeout(() => {
+ state.snackbar_error = false
+ }, 4000)
+ }
+ },
+ REMOVE_TORRENTS: async state => {
+ if (state.selected_torrents.length !== 0) {
+ const res = await qbit.remove_torrents(state.selected_torrents)
+ }
+ },
+ LOGIN: async (state, payload) => {
+ const res = await qbit.login(payload)
+ if (res === 'Ok.') {
+ state.loading = false
+ Vue.$toast.success('Successfully logged in!')
+ state.authenticated = true
+ }
+ },
+ updateMainData: async state => {
+ const rid = state.rid ? state.rid : undefined
+ const { data } = await qbit.getMainData(rid)
+
+ // torrents
+ state.torrents = []
+ for (const [key, value] of Object.entries(data.torrents)) {
+ state.torrents.push(new Torrent({ id: key, ...value }))
+ }
+
+ // download speed
+ state.stats = new Stat(data.server_state)
}
- state.loading = false
- }
},
- },
- actions: {
- REFRESH_TORRENTS: (context) => {
- context.state.intervals[1] = setInterval(async () => {
- context.commit('REFRESH_TORRENTS')
- if (context.state.network_error) {
- context.commit('CLEAR_INTERVALS')
+ actions: {
+ INIT_INTERVALS: async (context) => {
+ context.state.intervals[0] = setInterval(() => {
+ context.commit('updateMainData')
+ }, 2000)
+ },
+ LOGIN: async (context, payload) => {
+ context.commit('LOGIN', payload)
+ context.commit('updateMainData')
}
- }, 2000)
},
- REFRESH_SESSION_STATS: (context) => {
- context.state.intervals[0] = setInterval(async () => {
- context.commit('REFRESH_SESSION_STATS')
- }, 1000)
- },
- ADD_SELECTED: (context, payload) => {
- context.commit('ADD_SELECTED', payload)
- },
- REMOVE_SELECTED: (context, payload) => {
- context.commit('REMOVE_SELECTED', payload)
- },
- RESET_SELECTED: (context) => {
- context.commit('RESET_SELECTED')
- },
- PAUSE_TORRENTS: (context) => {
- context.commit('PAUSE_TORRENTS')
- },
- RESUME_TORRENTS: (context) => {
- context.commit('RESUME_TORRENTS')
- },
- ADD_TORRENT: (context, payload) => {
- context.commit('ADD_TORRENT', payload)
- },
- REMOVE_TORRENTS: (context) => {
- context.commit('REMOVE_TORRENTS')
- },
- LOGIN: (context, payload) => {
- context.commit('LOGIN', payload)
- },
- },
+ getters: {
+ getStats: state => () => state.stats
+ }
})
diff --git a/src/views/Dashboard.vue b/src/views/Dashboard.vue
index 260b120a..b1185a5b 100644
--- a/src/views/Dashboard.vue
+++ b/src/views/Dashboard.vue
@@ -1,273 +1,294 @@
-
-
Dashboard
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Torrent title
- {{ torrent.name }}
+
+
Dashboard
+
+
+
+
-
- Size
-
- {{ torrent.size.substring(0, torrent.size.indexOf(' '))}}
- {{ torrent.size.substring(torrent.size.indexOf(' ')) }}
-
-
-
- Done
-
- {{ torrent.dloaded.substring(0, torrent.dloaded.indexOf(' ')) }}
- {{ torrent.dloaded.substring(torrent.dloaded.indexOf(' ')) }}
-
-
-
- Download
-
- {{ torrent.dlspeed.substring(0, torrent.dlspeed.indexOf(' ')) }}
- {{ torrent.dlspeed.substring(torrent.dlspeed.indexOf(' ')) }}
-
-
-
- Upload
-
- {{ torrent.upspeed.substring(0, torrent.upspeed.indexOf(' ')) }}
- {{ torrent.upspeed.substring(torrent.upspeed.indexOf(' ')) }}
-
-
-
- ETA
- {{ torrent.eta }}
-
-
- Peers
-
- {{ torrent.num_leechs }}
- /{{torrent.available_peers}}
-
-
-
- Seeds
-
- {{ torrent.num_seeds }}
- /{{torrent.available_seeds}}
-
-
-
-
- {{ torrent.state }}
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Torrent title
+ {{ torrent.name }}
+
+
+ Size
+
+ {{
+ torrent.size.substring(
+ 0,
+ torrent.size.indexOf(' ')
+ )
+ }}
+ {{
+ torrent.size.substring(
+ torrent.size.indexOf(' ')
+ )
+ }}
+
+
+
+ Done
+
+ {{
+ torrent.dloaded.substring(
+ 0,
+ torrent.dloaded.indexOf(' ')
+ )
+ }}
+ {{
+ torrent.dloaded.substring(
+ torrent.dloaded.indexOf(' ')
+ )
+ }}
+
+
+
+ Download
+
+ {{
+ torrent.dlspeed.substring(
+ 0,
+ torrent.dlspeed.indexOf(' ')
+ )
+ }}
+ {{
+ torrent.dlspeed.substring(
+ torrent.dlspeed.indexOf(' ')
+ )
+ }}
+
+
+
+ Upload
+
+ {{
+ torrent.upspeed.substring(
+ 0,
+ torrent.upspeed.indexOf(' ')
+ )
+ }}
+ {{
+ torrent.upspeed.substring(
+ torrent.upspeed.indexOf(' ')
+ )
+ }}
+
+
+
+ ETA
+ {{ torrent.eta }}
+
+
+ Peers
+
+ {{ torrent.num_leechs }}
+ /{{ torrent.available_peers }}
+
+
+
+ Seeds
+
+ {{ torrent.num_seeds }}
+ /{{ torrent.available_seeds }}
+
+
+
+
+ {{ torrent.state }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/vue.config.js b/vue.config.js
index adc36ab3..5479fabd 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -1,5 +1,13 @@
module.exports = {
- devServer: {
- proxy: 'http://localhost:3001/',
- },
+ outputDir: 'dist/public',
+ publicPath: './',
+
+ devServer: {
+ port: 8000,
+ proxy: {
+ '/api': {
+ target: 'http://127.0.0.1:8080'
+ }
+ }
+ }
}