Swapped out the timer GET for a signalr connection.
This commit is contained in:
parent
8f35952f90
commit
21ad2ebfd6
14 changed files with 218 additions and 69 deletions
|
|
@ -2,5 +2,10 @@
|
||||||
"/Api": {
|
"/Api": {
|
||||||
"target": "http://localhost:6500/",
|
"target": "http://localhost:6500/",
|
||||||
"secure": false
|
"secure": false
|
||||||
|
},
|
||||||
|
"/hub": {
|
||||||
|
"target": "http://localhost:6500/",
|
||||||
|
"secure": false,
|
||||||
|
"ws": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
client/package-lock.json
generated
64
client/package-lock.json
generated
|
|
@ -1618,6 +1618,18 @@
|
||||||
"schema-utils": "^2.7.0"
|
"schema-utils": "^2.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@microsoft/signalr": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@microsoft/signalr/-/signalr-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-qk5xMEpEqvxmj2/7ZRkHpWyt33gGXiNTkM+iIFr0QYEaynsbwGUxyV5WhuxcOP9LyXgUx6IXBer2HVAGOC4zpw==",
|
||||||
|
"requires": {
|
||||||
|
"abort-controller": "^3.0.0",
|
||||||
|
"eventsource": "^1.0.7",
|
||||||
|
"fetch-cookie": "^0.7.3",
|
||||||
|
"node-fetch": "^2.6.0",
|
||||||
|
"ws": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@ngtools/webpack": {
|
"@ngtools/webpack": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-11.0.5.tgz",
|
||||||
|
|
@ -2002,6 +2014,14 @@
|
||||||
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
|
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"abort-controller": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||||
|
"requires": {
|
||||||
|
"event-target-shim": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"accepts": {
|
"accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
|
|
@ -2333,8 +2353,7 @@
|
||||||
"async-limiter": {
|
"async-limiter": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
|
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"asynckit": {
|
"asynckit": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
|
|
@ -4628,6 +4647,11 @@
|
||||||
"next-tick": "~1.0.0"
|
"next-tick": "~1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"es6-denodeify": {
|
||||||
|
"version": "0.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-denodeify/-/es6-denodeify-0.1.5.tgz",
|
||||||
|
"integrity": "sha1-MdTV/pxVA+ElRgQ5MQ4WoqPznB8="
|
||||||
|
},
|
||||||
"es6-iterator": {
|
"es6-iterator": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||||
|
|
@ -4733,6 +4757,11 @@
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"event-target-shim": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
|
||||||
|
},
|
||||||
"eventemitter3": {
|
"eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
|
@ -4749,7 +4778,6 @@
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
|
||||||
"integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
|
"integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"original": "^1.0.0"
|
"original": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
|
@ -5072,6 +5100,15 @@
|
||||||
"websocket-driver": ">=0.5.1"
|
"websocket-driver": ">=0.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"fetch-cookie": {
|
||||||
|
"version": "0.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.7.3.tgz",
|
||||||
|
"integrity": "sha512-rZPkLnI8x5V+zYAiz8QonAHsTb4BY+iFowFBI1RFn0zrO343AVp9X7/yUj/9wL6Ef/8fLls8b/vGtzUvmyAUGA==",
|
||||||
|
"requires": {
|
||||||
|
"es6-denodeify": "^0.1.1",
|
||||||
|
"tough-cookie": "^2.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"figgy-pudding": {
|
"figgy-pudding": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
|
||||||
|
|
@ -7801,6 +7838,11 @@
|
||||||
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node-fetch": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
||||||
|
},
|
||||||
"node-fetch-npm": {
|
"node-fetch-npm": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
|
||||||
|
|
@ -8311,7 +8353,6 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
|
||||||
"integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
|
"integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"url-parse": "^1.4.3"
|
"url-parse": "^1.4.3"
|
||||||
}
|
}
|
||||||
|
|
@ -9883,8 +9924,7 @@
|
||||||
"psl": {
|
"psl": {
|
||||||
"version": "1.8.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"public-encrypt": {
|
"public-encrypt": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
|
|
@ -9944,8 +9984,7 @@
|
||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"q": {
|
"q": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
|
|
@ -9980,8 +10019,7 @@
|
||||||
"querystringify": {
|
"querystringify": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"randombytes": {
|
"randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
|
|
@ -10281,8 +10319,7 @@
|
||||||
"requires-port": {
|
"requires-port": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.15.1",
|
"version": "1.15.1",
|
||||||
|
|
@ -12004,7 +12041,6 @@
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"psl": "^1.1.28",
|
"psl": "^1.1.28",
|
||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
|
|
@ -12354,7 +12390,6 @@
|
||||||
"version": "1.4.7",
|
"version": "1.4.7",
|
||||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
|
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
|
||||||
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
|
"integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"querystringify": "^2.1.1",
|
"querystringify": "^2.1.1",
|
||||||
"requires-port": "^1.0.0"
|
"requires-port": "^1.0.0"
|
||||||
|
|
@ -13607,7 +13642,6 @@
|
||||||
"version": "6.2.1",
|
"version": "6.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
|
||||||
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
"integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"async-limiter": "~1.0.0"
|
"async-limiter": "~1.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"@angular/platform-browser-dynamic": "~11.0.5",
|
"@angular/platform-browser-dynamic": "~11.0.5",
|
||||||
"@angular/router": "~11.0.5",
|
"@angular/router": "~11.0.5",
|
||||||
"@fortawesome/fontawesome-free": "^5.15.1",
|
"@fortawesome/fontawesome-free": "^5.15.1",
|
||||||
|
"@microsoft/signalr": "^5.0.1",
|
||||||
"bulma": "^0.9.1",
|
"bulma": "^0.9.1",
|
||||||
"curray": "^1.0.8",
|
"curray": "^1.0.8",
|
||||||
"ngx-filesize": "^2.0.16",
|
"ngx-filesize": "^2.0.16",
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
{{ file.path }}
|
{{ file.path }}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ file.bytes | filesize }}</td>
|
<td>{{ file.bytes | filesize }}</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
{{ file | fileStatus }}
|
{{ file | fileStatus }}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
{{ torrent.downloads.length | number }}
|
{{ torrent.downloads.length | number }}
|
||||||
</td>
|
</td>
|
||||||
<td class="auto">
|
<td class="auto">
|
||||||
<i class="fas fa-download" *ngIf="!torrent.autoDownload" title="Auto download"></i>
|
<i class="fas fa-download" *ngIf="torrent.autoDownload" title="Auto download"></i>
|
||||||
<i class="fas fa-box-open" *ngIf="!torrent.autoUnpack" title="Auto unpack"></i>
|
<i class="fas fa-box-open" *ngIf="torrent.autoUnpack" title="Auto unpack"></i>
|
||||||
<i class="fas fa-trash" *ngIf="!torrent.autoDelete" title="Auto delete"></i>
|
<i class="fas fa-trash" *ngIf="torrent.autoDelete" title="Auto delete"></i>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ torrent | status }}
|
{{ torrent | status }}
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
<div class="table-container">
|
<div class="table-container">
|
||||||
<table class="table is-fullwidth is-hoverable">
|
<table class="table is-fullwidth is-hoverable">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col style="width: calc(65% - 550px)" />
|
<col style="width: calc(40% - 550px)" />
|
||||||
<col style="width: 15%" />
|
<col style="width: 15%" />
|
||||||
<col style="width: 150px" />
|
<col style="width: 150px" />
|
||||||
<col style="width: 150px" />
|
<col style="width: 150px" />
|
||||||
<col style="width: 150px" />
|
<col style="width: 150px" />
|
||||||
<col style="width: 20%" />
|
<col style="width: 40%" />
|
||||||
<col style="width: 100px" />
|
<col style="width: 100px" />
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
@ -32,12 +32,7 @@
|
||||||
(click)="selectTorrent(torrent)"
|
(click)="selectTorrent(torrent)"
|
||||||
(delete)="showDeleteModal($event)"
|
(delete)="showDeleteModal($event)"
|
||||||
></tr>
|
></tr>
|
||||||
<ng-container *ngIf="showFiles[torrent.torrentId] && torrent?.files.length === 0">
|
<ng-container *ngIf="showFiles[torrent.torrentId]">
|
||||||
<tr>
|
|
||||||
<td colspan="20">
|
|
||||||
<i class="fas fa-spinner fa-pulse"></i>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr app-torrent-file [file]="file" *ngFor="let file of torrent.files"></tr>
|
<tr app-torrent-file [file]="file" *ngFor="let file of torrent.files"></tr>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,27 @@ export class TorrentTableComponent implements OnInit, OnDestroy {
|
||||||
public deleteRdTorrent: boolean;
|
public deleteRdTorrent: boolean;
|
||||||
public deleteLocalFiles: boolean;
|
public deleteLocalFiles: boolean;
|
||||||
|
|
||||||
private timer: any;
|
|
||||||
|
|
||||||
constructor(private torrentService: TorrentService) {}
|
constructor(private torrentService: TorrentService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.timer = setInterval(() => {
|
this.torrentService.getList().subscribe(
|
||||||
this.torrentService.getList().subscribe(
|
(result) => {
|
||||||
(result) => {
|
this.torrents = result;
|
||||||
this.torrents = result;
|
|
||||||
},
|
this.torrentService.connect();
|
||||||
(err) => {
|
|
||||||
this.error = err.error;
|
this.torrentService.update$.subscribe((result2) => {
|
||||||
clearInterval(this.timer);
|
this.torrents = result2;
|
||||||
}
|
});
|
||||||
);
|
},
|
||||||
}, 1000);
|
(err) => {
|
||||||
|
this.error = err.error;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
clearInterval(this.timer);
|
this.torrentService.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public selectTorrent(torrent: Torrent): void {
|
public selectTorrent(torrent: Torrent): void {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import * as signalR from '@microsoft/signalr';
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { Torrent } from './models/torrent.model';
|
import { Torrent } from './models/torrent.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
|
@ -9,6 +10,23 @@ import { Torrent } from './models/torrent.model';
|
||||||
export class TorrentService {
|
export class TorrentService {
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient) {}
|
||||||
|
|
||||||
|
public update$: Subject<Torrent[]> = new Subject();
|
||||||
|
|
||||||
|
private connection: signalR.HubConnection;
|
||||||
|
|
||||||
|
public connect(): void {
|
||||||
|
this.connection = new signalR.HubConnectionBuilder().withUrl('/hub').withAutomaticReconnect().build();
|
||||||
|
this.connection.start().catch((err) => console.error(err));
|
||||||
|
|
||||||
|
this.connection.on('update', (torrents: Torrent[]) => {
|
||||||
|
this.update$.next(torrents);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public disconnect(): void {
|
||||||
|
this.connection?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
public getList(): Observable<Torrent[]> {
|
public getList(): Observable<Torrent[]> {
|
||||||
return this.http.get<Torrent[]>(`/Api/Torrents`);
|
return this.http.get<Torrent[]>(`/Api/Torrents`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ namespace RdtClient.Service
|
||||||
services.AddScoped<IAuthentication, Authentication>();
|
services.AddScoped<IAuthentication, Authentication>();
|
||||||
services.AddScoped<IDownloads, Downloads>();
|
services.AddScoped<IDownloads, Downloads>();
|
||||||
services.AddScoped<IQBittorrent, QBittorrent>();
|
services.AddScoped<IQBittorrent, QBittorrent>();
|
||||||
|
services.AddScoped<IRemoteService, RemoteService>();
|
||||||
services.AddScoped<ISettings, Settings>();
|
services.AddScoped<ISettings, Settings>();
|
||||||
services.AddScoped<ITorrents, Torrents>();
|
services.AddScoped<ITorrents, Torrents>();
|
||||||
services.AddScoped<ITorrentRunner, TorrentRunner>();
|
services.AddScoped<ITorrentRunner, TorrentRunner>();
|
||||||
|
|
|
||||||
27
server/RdtClient.Service/Services/RdtHub.cs
Normal file
27
server/RdtClient.Service/Services/RdtHub.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace RdtClient.Service.Services
|
||||||
|
{
|
||||||
|
public class RdtHub : Hub
|
||||||
|
{
|
||||||
|
private static readonly ConcurrentDictionary<String, String> Users = new ConcurrentDictionary<String, String>();
|
||||||
|
|
||||||
|
public static Boolean HasConnections => Users.Any();
|
||||||
|
|
||||||
|
public override async Task OnConnectedAsync()
|
||||||
|
{
|
||||||
|
Users.TryAdd(Context.ConnectionId, Context.ConnectionId);
|
||||||
|
await base.OnConnectedAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task OnDisconnectedAsync(Exception exception)
|
||||||
|
{
|
||||||
|
Users.TryRemove(Context.ConnectionId, out _);
|
||||||
|
await base.OnDisconnectedAsync(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
server/RdtClient.Service/Services/RemoteService.cs
Normal file
41
server/RdtClient.Service/Services/RemoteService.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
|
namespace RdtClient.Service.Services
|
||||||
|
{
|
||||||
|
public interface IRemoteService
|
||||||
|
{
|
||||||
|
Task Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoteService : IRemoteService
|
||||||
|
{
|
||||||
|
private readonly IHubContext<RdtHub> _hub;
|
||||||
|
private readonly ITorrents _torrents;
|
||||||
|
|
||||||
|
public RemoteService(IHubContext<RdtHub> hub, ITorrents torrents)
|
||||||
|
{
|
||||||
|
_hub = hub;
|
||||||
|
_torrents = torrents;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Update()
|
||||||
|
{
|
||||||
|
var torrents = await _torrents.Get();
|
||||||
|
|
||||||
|
// Prevent infinite recursion when serializing
|
||||||
|
foreach (var file in torrents.SelectMany(torrent => torrent.Downloads))
|
||||||
|
{
|
||||||
|
file.Torrent = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _hub.Clients.All.SendCoreAsync("update",
|
||||||
|
new Object[]
|
||||||
|
{
|
||||||
|
torrents
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,16 +18,18 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new ConcurrentDictionary<Guid, DownloadClient>();
|
public static readonly ConcurrentDictionary<Guid, DownloadClient> ActiveDownloadClients = new ConcurrentDictionary<Guid, DownloadClient>();
|
||||||
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new ConcurrentDictionary<Guid, UnpackClient>();
|
public static readonly ConcurrentDictionary<Guid, UnpackClient> ActiveUnpackClients = new ConcurrentDictionary<Guid, UnpackClient>();
|
||||||
|
private readonly IDownloads _downloads;
|
||||||
|
private readonly IRemoteService _remoteService;
|
||||||
|
|
||||||
private readonly ISettings _settings;
|
private readonly ISettings _settings;
|
||||||
private readonly ITorrents _torrents;
|
private readonly ITorrents _torrents;
|
||||||
private readonly IDownloads _downloads;
|
|
||||||
|
|
||||||
public TorrentRunner(ISettings settings, ITorrents torrents, IDownloads downloads)
|
public TorrentRunner(ISettings settings, ITorrents torrents, IDownloads downloads, IRemoteService remoteService)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_torrents = torrents;
|
_torrents = torrents;
|
||||||
_downloads = downloads;
|
_downloads = downloads;
|
||||||
|
_remoteService = remoteService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Initialize()
|
public async Task Initialize()
|
||||||
|
|
@ -103,11 +105,17 @@ namespace RdtClient.Service.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only poll RealDebrid every 30 seconds
|
// Only poll RealDebrid every 5 when a hub is connected, otherwise ever 30 seconds
|
||||||
|
|
||||||
if (_nextUpdate < DateTime.UtcNow)
|
if (_nextUpdate < DateTime.UtcNow)
|
||||||
{
|
{
|
||||||
_nextUpdate = DateTime.UtcNow.AddSeconds(5);
|
var updateTime = 30;
|
||||||
|
|
||||||
|
if (RdtHub.HasConnections)
|
||||||
|
{
|
||||||
|
updateTime = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nextUpdate = DateTime.UtcNow.AddSeconds(updateTime);
|
||||||
|
|
||||||
await _torrents.Update();
|
await _torrents.Update();
|
||||||
}
|
}
|
||||||
|
|
@ -167,6 +175,7 @@ namespace RdtClient.Service.Services
|
||||||
if (torrent.Downloads.Count == 0)
|
if (torrent.Downloads.Count == 0)
|
||||||
{
|
{
|
||||||
await _torrents.Unrestrict(torrent.TorrentId);
|
await _torrents.Unrestrict(torrent.TorrentId);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,6 +233,8 @@ namespace RdtClient.Service.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _remoteService.Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,10 @@ namespace RdtClient.Service.Services
|
||||||
|
|
||||||
var torrentPath = Path.Combine(settingDownloadFolder, torrent.RdName);
|
var torrentPath = Path.Combine(settingDownloadFolder, torrent.RdName);
|
||||||
|
|
||||||
Directory.Delete(torrentPath, true);
|
if (Directory.Exists(torrentPath))
|
||||||
|
{
|
||||||
|
Directory.Delete(torrentPath, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deleteData)
|
if (deleteData)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using Microsoft.Extensions.Logging;
|
||||||
using RdtClient.Data.Data;
|
using RdtClient.Data.Data;
|
||||||
using RdtClient.Data.Models.Internal;
|
using RdtClient.Data.Models.Internal;
|
||||||
using RdtClient.Service.Middleware;
|
using RdtClient.Service.Middleware;
|
||||||
|
using RdtClient.Service.Services;
|
||||||
|
|
||||||
namespace RdtClient.Web
|
namespace RdtClient.Web
|
||||||
{
|
{
|
||||||
|
|
@ -39,6 +40,11 @@ namespace RdtClient.Web
|
||||||
|
|
||||||
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "wwwroot"; });
|
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "wwwroot"; });
|
||||||
|
|
||||||
|
services.AddSignalR(options =>
|
||||||
|
{
|
||||||
|
options.EnableDetailedErrors = true;
|
||||||
|
});
|
||||||
|
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
|
|
||||||
services.AddCors(options =>
|
services.AddCors(options =>
|
||||||
|
|
@ -112,7 +118,11 @@ namespace RdtClient.Web
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapHub<RdtHub>("/hub");
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
|
||||||
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
|
app.MapWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue