mirror of
https://github.com/CzBiX/qb-web.git
synced 2026-02-03 10:34:23 +08:00
Compare commits
14 Commits
nightly-20
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26a1228d17 | ||
|
|
66d4793fc0 | ||
|
|
20930bfba9 | ||
|
|
b53f951b75 | ||
|
|
396e98a29d | ||
|
|
039c9a4b30 | ||
|
|
222acd4ca3 | ||
|
|
6093fd06a2 | ||
|
|
d61085979f | ||
|
|
c28c4f42f4 | ||
|
|
81469468c2 | ||
|
|
e6db6a86b2 | ||
|
|
16f84cdd74 | ||
|
|
49bf2f0694 |
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@@ -9,16 +9,14 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- uses: actions/cache@v2
|
||||
id: cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
node-version: '16'
|
||||
- run: |
|
||||
corepack enable
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
- name: Set env
|
||||
run: echo "RELEASE_FILE=qb-web-${GITHUB_REF#refs/*/}.zip" >> $GITHUB_ENV
|
||||
|
||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@@ -6,16 +6,14 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
- uses: actions/cache@v2
|
||||
id: cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
- run: yarn install --frozen-lockfile
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
node-version: '16'
|
||||
- run: |
|
||||
corepack enable
|
||||
yarn install --frozen-lockfile
|
||||
|
||||
- run: yarn run lint --no-fix --max-warnings 0
|
||||
- run: yarn run test:unit
|
||||
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
35
package.json
35
package.json
@@ -18,7 +18,6 @@
|
||||
"debug": "^4.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"node-polyglot": "^2.4.0",
|
||||
"register-service-worker": "^1.7.1",
|
||||
"roboto-fontface": "*",
|
||||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
@@ -31,24 +30,27 @@
|
||||
"@types/debug": "^4.1.5",
|
||||
"@types/jest": "^25.1.4",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||
"@typescript-eslint/parser": "^2.33.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.6",
|
||||
"@vue/cli-plugin-eslint": "~4.5.6",
|
||||
"@vue/cli-plugin-pwa": "~4.5.0",
|
||||
"@vue/cli-plugin-router": "~4.5.6",
|
||||
"@vue/cli-plugin-typescript": "~4.5.6",
|
||||
"@vue/cli-plugin-unit-jest": "~4.5.6",
|
||||
"@vue/cli-plugin-vuex": "~4.5.6",
|
||||
"@vue/cli-service": "~4.5.6",
|
||||
"@vue/eslint-config-typescript": "^5.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.8",
|
||||
"@vue/cli-plugin-eslint": "~5.0.8",
|
||||
"@vue/cli-plugin-pwa": "~5.0.8",
|
||||
"@vue/cli-plugin-router": "~5.0.8",
|
||||
"@vue/cli-plugin-typescript": "~5.0.8",
|
||||
"@vue/cli-plugin-unit-jest": "~5.0.8",
|
||||
"@vue/cli-plugin-vuex": "~5.0.8",
|
||||
"@vue/cli-service": "~5.0.8",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"@vue/vue2-jest": "^27.0.0-alpha.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"jest": "^27.1.0",
|
||||
"lint-staged": "^10.1.1",
|
||||
"sass": "^1.26.5",
|
||||
"sass-loader": "^8.0.2",
|
||||
"typescript": "~3.9.3",
|
||||
"ts-jest": "^27.0.4",
|
||||
"typescript": "~4.5.5",
|
||||
"vue-cli-plugin-vuetify": "^2.0.5",
|
||||
"vue-template-compiler": "^2.6.11",
|
||||
"vuetify-loader": "^1.4.3"
|
||||
@@ -60,5 +62,6 @@
|
||||
"*.{js,vue,ts}": [
|
||||
"vue-cli-service lint"
|
||||
]
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@3.5.0"
|
||||
}
|
||||
|
||||
@@ -164,10 +164,10 @@ export default class Drawer extends Vue {
|
||||
endItems: MenuItem[] = [
|
||||
{ icon: 'mdi-delta', title: tr('logs'), click: () => this.updateOptions('showLogs', true) },
|
||||
{ icon: 'mdi-card-search-outline', title: tr('search'), click: () => this.updateOptions('showSearch', true) },
|
||||
{ icon: 'mdi-rss-box', title: 'RSS', click: () => this.updateOptions('showRss', true) },
|
||||
]
|
||||
|
||||
pcItems: MenuItem[] = [
|
||||
{ icon: 'mdi-rss-box', title: 'RSS', click: () => this.updateOptions('showRss', true) },
|
||||
{ icon: 'mdi-cog-box', title: tr('settings'), click: () => this.updateOptions('showSettings', true) },
|
||||
{ icon: 'mdi-history', title: tr('label.switch_to_old_ui'), click: this.switchUi },
|
||||
]
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
<v-card-text>
|
||||
<v-tabs v-model="tabSync">
|
||||
<v-tab href="#general">
|
||||
General
|
||||
{{ $t("prop_tab_bar.general") }}
|
||||
</v-tab>
|
||||
<v-tab href="#trackers">
|
||||
Trackers
|
||||
{{ $t("prop_tab_bar.trackers") }}
|
||||
</v-tab>
|
||||
<v-tab href="#peers">
|
||||
Peers
|
||||
{{ $t("prop_tab_bar.peers") }}
|
||||
</v-tab>
|
||||
<v-tab href="#content">
|
||||
Content
|
||||
{{ $t("prop_tab_bar.content") }}
|
||||
</v-tab>
|
||||
</v-tabs>
|
||||
<v-tabs-items
|
||||
|
||||
@@ -50,6 +50,7 @@ import { formatSize } from '../../filters';
|
||||
import BaseTorrentInfo from './baseTorrentInfo';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
import { tr } from '@/locale'
|
||||
|
||||
@Component({
|
||||
filters: {
|
||||
@@ -74,17 +75,17 @@ export default class Peers extends BaseTorrentInfo {
|
||||
readonly hash!: string
|
||||
|
||||
headers = [
|
||||
{ text: 'IP', value: 'ip' },
|
||||
{ text: 'Connection', value: 'connection' },
|
||||
{ text: 'Flags', value: 'flags' },
|
||||
{ text: 'Client', value: 'client' },
|
||||
{ text: 'Progress', value: 'progress' },
|
||||
{ text: 'DL Speed', value: 'dl_speed' },
|
||||
{ text: 'Downloaded', value: 'downloaded' },
|
||||
{ text: 'UP Speed', value: 'up_speed' },
|
||||
{ text: 'Uploaded', value: 'uploaded' },
|
||||
{ text: 'Relevance', value: 'relevance' },
|
||||
{ text: 'Files', value: 'files' },
|
||||
{ text: tr('properties_widget.ip'), value: 'ip' },
|
||||
{ text: tr('properties_widget.connection'), value: 'connection' },
|
||||
{ text: tr('properties_widget.flags'), value: 'flags' },
|
||||
{ text: tr('properties_widget.client'), value: 'client' },
|
||||
{ text: tr('properties_widget.progress'), value: 'progress' },
|
||||
{ text: tr('properties_widget.downloadSpeed'), value: 'dl_speed' },
|
||||
{ text: tr('properties_widget.downloaded'), value: 'downloaded' },
|
||||
{ text: tr('properties_widget.uploadSpeed'), value: 'up_speed' },
|
||||
{ text: tr('properties_widget.uploaded'), value: 'uploaded' },
|
||||
{ text: tr('properties_widget.relevance'), value: 'relevance' },
|
||||
{ text: tr('properties_widget.files'), value: 'files' },
|
||||
]
|
||||
|
||||
peersObj: any = null
|
||||
|
||||
@@ -76,118 +76,119 @@
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="content">
|
||||
<div class="content-inner">
|
||||
<div
|
||||
v-if="!rssNode"
|
||||
class="loading"
|
||||
>
|
||||
<v-progress-circular indeterminate />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="rss-items">
|
||||
<v-treeview
|
||||
open-on-click
|
||||
open-all
|
||||
:items="rssTree"
|
||||
item-key="path"
|
||||
activatable
|
||||
dense
|
||||
@update:active="selectNode = $event[0]"
|
||||
>
|
||||
<template v-slot:prepend="row">
|
||||
<v-progress-circular
|
||||
v-if="isItemLoading(row)"
|
||||
indeterminate
|
||||
size="22"
|
||||
width="2"
|
||||
/>
|
||||
<v-icon
|
||||
v-else
|
||||
v-text="getRowIcon(row)"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:label="row">
|
||||
{{ row.item.name }}
|
||||
<template v-if="row.item.children">
|
||||
({{ row.item.children.length }})
|
||||
</template>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</div>
|
||||
<v-divider vertical />
|
||||
<div class="rss-details">
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a
|
||||
v-if="selectItem"
|
||||
target="_blank"
|
||||
:href="selectItem.url"
|
||||
>{{ selectItem.title }}</a>
|
||||
</p>
|
||||
<p>{{ $t('date') }}: {{ (selectItem ? selectItem.lastBuildDate : null) | date }}</p>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="list-wrapper">
|
||||
<v-list
|
||||
v-if="selectItem"
|
||||
dense
|
||||
>
|
||||
<v-list-item-group
|
||||
v-model="selectArticle"
|
||||
color="primary"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="article in sortArticles(selectItem.articles)"
|
||||
:key="article.id"
|
||||
:value="article"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<span
|
||||
:title="article.title"
|
||||
v-text="article.title"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn
|
||||
icon
|
||||
@click.stop="downloadTorrent(article)"
|
||||
>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider vertical />
|
||||
<div class="rss-desc">
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a
|
||||
v-if="selectArticle"
|
||||
target="_blank"
|
||||
:href="selectArticle.link"
|
||||
>{{ selectArticle.title }}</a>
|
||||
</p>
|
||||
<p>{{ `${$t('category', 1)}: ${selectArticle ? selectArticle.category: ''}` }}</p>
|
||||
<p>{{ $t('date') }}: {{ (selectArticle ? selectArticle.date: null) | date }}</p>
|
||||
</div>
|
||||
<v-divider />
|
||||
<iframe
|
||||
class="iframe"
|
||||
sandbox="allow-same-origin"
|
||||
v-if="selectArticle"
|
||||
v-body="selectArticle.description"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="content"
|
||||
:class="{phone: $vuetify.breakpoint.smAndDown}"
|
||||
>
|
||||
<div
|
||||
v-if="!rssNode"
|
||||
class="loading"
|
||||
>
|
||||
<v-progress-circular indeterminate />
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="rss-items">
|
||||
<v-treeview
|
||||
open-on-click
|
||||
open-all
|
||||
:items="rssTree"
|
||||
item-key="path"
|
||||
activatable
|
||||
dense
|
||||
@update:active="selectNode = $event[0]"
|
||||
>
|
||||
<template v-slot:prepend="row">
|
||||
<v-progress-circular
|
||||
v-if="isItemLoading(row)"
|
||||
indeterminate
|
||||
size="22"
|
||||
width="2"
|
||||
/>
|
||||
<v-icon
|
||||
v-else
|
||||
v-text="getRowIcon(row)"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:label="row">
|
||||
{{ row.item.name }}
|
||||
<template v-if="row.item.children">
|
||||
({{ row.item.children.length }})
|
||||
</template>
|
||||
</template>
|
||||
</v-treeview>
|
||||
</div>
|
||||
<v-divider :vertical="!phoneLayout" />
|
||||
<div class="rss-details">
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a
|
||||
v-if="selectItem"
|
||||
target="_blank"
|
||||
:href="selectItem.url"
|
||||
>{{ selectItem.title }}</a>
|
||||
</p>
|
||||
<p>{{ $t('date') }}: {{ (selectItem ? selectItem.lastBuildDate : null) | date }}</p>
|
||||
</div>
|
||||
<v-divider />
|
||||
<div class="list-wrapper">
|
||||
<v-list
|
||||
v-if="selectItem"
|
||||
dense
|
||||
>
|
||||
<v-list-item-group
|
||||
v-model="selectArticle"
|
||||
color="primary"
|
||||
>
|
||||
<v-list-item
|
||||
v-for="article in sortArticles(selectItem.articles)"
|
||||
:key="article.id"
|
||||
:value="article"
|
||||
>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
<span
|
||||
:title="article.title"
|
||||
v-text="article.title"
|
||||
/>
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-btn
|
||||
icon
|
||||
@click.stop="downloadTorrent(article)"
|
||||
>
|
||||
<v-icon>mdi-download</v-icon>
|
||||
</v-btn>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list-item-group>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
<v-divider :vertical="!phoneLayout" />
|
||||
<div class="rss-desc">
|
||||
<div class="rss-info">
|
||||
<p>
|
||||
{{ $t('title._') }}:
|
||||
<a
|
||||
v-if="selectArticle"
|
||||
target="_blank"
|
||||
:href="selectArticle.link"
|
||||
>{{ selectArticle.title }}</a>
|
||||
</p>
|
||||
<p>{{ `${$t('category', 1)}: ${selectArticle ? selectArticle.category: ''}` }}</p>
|
||||
<p>{{ $t('date') }}: {{ (selectArticle ? selectArticle.date: null) | date }}</p>
|
||||
</div>
|
||||
<v-divider />
|
||||
<iframe
|
||||
class="iframe"
|
||||
sandbox="allow-same-origin"
|
||||
v-if="selectArticle"
|
||||
v-body="selectArticle.description"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
@@ -289,6 +290,10 @@ export default class RssDialog extends HasTask {
|
||||
showSnackBar!: (_: SnackBarConfig) => void
|
||||
closeSnackBar!: () => void
|
||||
|
||||
get phoneLayout() {
|
||||
return this.$vuetify.breakpoint.smAndDown;
|
||||
}
|
||||
|
||||
get rssTree() {
|
||||
if (!this.rssNode) {
|
||||
return [];
|
||||
@@ -535,14 +540,10 @@ export default class RssDialog extends HasTask {
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
.content-inner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
&.phone {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="torrent-info">
|
||||
<div class="progress">
|
||||
<span>Progress:</span>
|
||||
<span>{{ $t('properties_widget.progress') }}:</span>
|
||||
<canvas
|
||||
ref="canvas"
|
||||
class="progress-inner"
|
||||
@@ -9,7 +9,7 @@
|
||||
<span>{{ torrent.progress | progress }}</span>
|
||||
</div>
|
||||
<fieldset>
|
||||
<legend>Transfer</legend>
|
||||
<legend>{{ $t('properties_widget.transfer') }}</legend>
|
||||
<v-container
|
||||
v-if="properties"
|
||||
class="pa-1"
|
||||
@@ -41,7 +41,7 @@
|
||||
</v-container>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Information</legend>
|
||||
<legend>{{ $t('properties_widget.information') }}</legend>
|
||||
<v-container
|
||||
v-if="properties"
|
||||
class="pa-1"
|
||||
@@ -85,6 +85,7 @@ import {Torrent, TorrentProperties} from '@/types'
|
||||
import Component from 'vue-class-component'
|
||||
import {Prop, Watch} from 'vue-property-decorator'
|
||||
import BaseTorrentInfo from './baseTorrentInfo'
|
||||
import { tr } from '@/locale'
|
||||
|
||||
interface Item {
|
||||
label: string;
|
||||
@@ -108,33 +109,33 @@ export default class TorrentInfo extends BaseTorrentInfo {
|
||||
|
||||
transfer: Item[] = [
|
||||
{
|
||||
label: 'Time active',
|
||||
value: prop => formatDuration(prop.time_elapsed) + (prop.seeding_time ? ` (seeded ${formatDuration(prop.seeding_time)})` : ''),
|
||||
label: tr('properties_widget.timeActive'),
|
||||
value: prop => formatDuration(prop.time_elapsed) + (prop.seeding_time ? ` ($tr('properties_widget.seeded') ${formatDuration(prop.seeding_time)})` : ''),
|
||||
},
|
||||
{ label: 'ETA', value: prop => formatDuration(prop.eta, { dayLimit: 100 }) },
|
||||
{ label: 'Connections', value: prop => `${prop.nb_connections} (${prop.nb_connections_limit} max)` },
|
||||
{ label: 'Downloaded', value: prop => `${formatSize(prop.total_downloaded_session)}/${formatSize(prop.total_downloaded)}` },
|
||||
{ label: 'Uploaded', value: prop => `${formatSize(prop.total_uploaded_session)}/${formatSize(prop.total_uploaded)}` },
|
||||
{ label: 'Seeds', value: prop => `${prop.seeds} (${prop.seeds_total} total)` },
|
||||
{ label: 'DL speed', value: prop => `${formatSize(prop.dl_speed)}/s` },
|
||||
{ label: 'UP speed', value: prop => `${formatSize(prop.up_speed)}/s` },
|
||||
{ label: 'Peers', value: prop => `${prop.peers} (${prop.peers_total} total)` },
|
||||
{ label: 'Wasted', value: prop => formatSize(prop.total_wasted) },
|
||||
{ label: 'Share ratio', value: prop => toPrecision(prop.share_ratio, 3) },
|
||||
{ label: 'Reannounce', value: prop => formatDuration(prop.reannounce) },
|
||||
{ label: 'Last seen', value: prop => formatTimestamp(prop.last_seen) },
|
||||
{ label: tr('properties_widget.eta'), value: prop => formatDuration(prop.eta, { dayLimit: 100 }) },
|
||||
{ label: tr('properties_widget.connections'), value: prop => `${prop.nb_connections} (${prop.nb_connections_limit} ${tr('properties_widget.max')})` },
|
||||
{ label: tr('properties_widget.downloaded'), value: prop => `${formatSize(prop.total_downloaded_session)}/${formatSize(prop.total_downloaded)}` },
|
||||
{ label: tr('properties_widget.uploaded'), value: prop => `${formatSize(prop.total_uploaded_session)}/${formatSize(prop.total_uploaded)}` },
|
||||
{ label: tr('properties_widget.seeds'), value: prop => `${prop.seeds} (${prop.seeds_total} ${tr('properties_widget.total')})` },
|
||||
{ label: tr('properties_widget.downloadSpeed'), value: prop => `${formatSize(prop.dl_speed)}/${tr('properties_widget.second')}` },
|
||||
{ label: tr('properties_widget.uploadSpeed'), value: prop => `${formatSize(prop.up_speed)}/${tr('properties_widget.second')}` },
|
||||
{ label: tr('properties_widget.peers'), value: prop => `${prop.peers} (${prop.peers_total} ${tr('properties_widget.total')})` },
|
||||
{ label: tr('properties_widget.wasted'), value: prop => formatSize(prop.total_wasted) },
|
||||
{ label: tr('properties_widget.shareRatio'), value: prop => toPrecision(prop.share_ratio, 3) },
|
||||
{ label: tr('properties_widget.reannounce'), value: prop => formatDuration(prop.reannounce) },
|
||||
{ label: tr('properties_widget.lastSeen'), value: prop => formatTimestamp(prop.last_seen) },
|
||||
]
|
||||
|
||||
information: Item[] = [
|
||||
{ label: 'Total size', value: prop => formatSize(prop.total_size) },
|
||||
{ label: 'Pieces', value: prop => `${prop.pieces_num} x ${formatSize(prop.piece_size)} (have ${prop.pieces_have})` },
|
||||
{ label: 'Created by', value: prop => prop.created_by },
|
||||
{ label: 'Created on', value: prop => formatTimestamp(prop.creation_date) },
|
||||
{ label: 'Added on', value: prop => formatTimestamp(prop.addition_date) },
|
||||
{ label: 'Completed on', value: prop => formatTimestamp(prop.completion_date) },
|
||||
{ label: 'Torrent hash', value: () => this.torrent.hash },
|
||||
{ label: 'Save path', value: prop => prop.save_path },
|
||||
{ label: 'Comment', value: prop => prop.comment },
|
||||
{ label: tr('properties_widget.totalSize'), value: prop => formatSize(prop.total_size) },
|
||||
{ label: tr('properties_widget.pieces'), value: prop => `${prop.pieces_num} x ${formatSize(prop.piece_size)} (${tr('properties_widget.have')} ${prop.pieces_have})` },
|
||||
{ label: tr('properties_widget.createdBy'), value: prop => prop.created_by },
|
||||
{ label: tr('properties_widget.createdOn'), value: prop => formatTimestamp(prop.creation_date) },
|
||||
{ label: tr('properties_widget.addedOn'), value: prop => formatTimestamp(prop.addition_date) },
|
||||
{ label: tr('properties_widget.completedOn'), value: prop => formatTimestamp(prop.completion_date) },
|
||||
{ label: tr('properties_widget.torrentHash'), value: () => this.torrent.hash },
|
||||
{ label: tr('properties_widget.savePath'), value: prop => prop.save_path },
|
||||
{ label: tr('properties_widget.comment'), value: prop => prop.comment },
|
||||
]
|
||||
pieces: PieceState[] = []
|
||||
canvas: CanvasRenderingContext2D | null = null
|
||||
|
||||
@@ -25,16 +25,17 @@ import api from '../../Api';
|
||||
import Component from 'vue-class-component';
|
||||
import { Prop } from 'vue-property-decorator';
|
||||
import BaseTorrentInfo from './baseTorrentInfo';
|
||||
import { tr } from '@/locale'
|
||||
|
||||
@Component({
|
||||
filters: {
|
||||
formatTrackerStatus(status: number) {
|
||||
const map = [
|
||||
'Disabled',
|
||||
'Not contacted',
|
||||
'Working',
|
||||
'Updating',
|
||||
'Not working',
|
||||
tr('properties_widget.disabled'),
|
||||
tr('properties_widget.notContracted'),
|
||||
tr('properties_widget.working'),
|
||||
tr('properties_widget.updating'),
|
||||
tr('properties_widget.notWorking'),
|
||||
];
|
||||
|
||||
return map[status];
|
||||
@@ -53,14 +54,14 @@ export default class Trackers extends BaseTorrentInfo {
|
||||
readonly hash!: string
|
||||
|
||||
readonly headers = [
|
||||
{ text: '#', value: 'tier' },
|
||||
{ text: 'URL', value: 'url' },
|
||||
{ text: 'Status', value: 'status' },
|
||||
{ text: 'Peers', value: 'num_peers' },
|
||||
{ text: 'Seeds', value: 'num_seeds' },
|
||||
{ text: 'Leeches', value: 'num_leeches' },
|
||||
{ text: 'Downloaded', value: 'num_downloaded' },
|
||||
{ text: 'Message', value: 'msg' },
|
||||
{ text: tr('properties_widget.tier'), value: 'tier' },
|
||||
{ text: tr('properties_widget.url'), value: 'url' },
|
||||
{ text: tr('properties_widget.status'), value: 'status' },
|
||||
{ text: tr('properties_widget.numPeers'), value: 'num_peers' },
|
||||
{ text: tr('properties_widget.numSeeds'), value: 'num_seeds' },
|
||||
{ text: tr('properties_widget.numLeeches'), value: 'num_leeches' },
|
||||
{ text: tr('properties_widget.numDownloaded'), value: 'num_downloaded' },
|
||||
{ text: tr('properties_widget.msg'), value: 'msg' },
|
||||
]
|
||||
|
||||
trackers = []
|
||||
|
||||
@@ -260,4 +260,69 @@ export default {
|
||||
moving: 'moving',
|
||||
unknown: 'unknown',
|
||||
},
|
||||
|
||||
|
||||
prop_tab_bar: {
|
||||
general: 'General',
|
||||
trackers: 'Trackers',
|
||||
peers: 'Peers',
|
||||
// httpSource: 'HTTP Sources',
|
||||
content: 'Content',
|
||||
},
|
||||
properties_widget: {
|
||||
disabled: 'Disabled',
|
||||
notContracted: 'Not contacted',
|
||||
working: 'Working',
|
||||
updating: 'Updating',
|
||||
notWorking: 'Not working',
|
||||
|
||||
tier: '#',
|
||||
url: 'URL',
|
||||
status: 'Status',
|
||||
numPeers: 'Peers',
|
||||
numSeeds: 'Seeds',
|
||||
numLeeches: 'Leeches',
|
||||
numDownloaded: 'Downloaded',
|
||||
msg: 'Message',
|
||||
|
||||
progress: 'Progress',
|
||||
transfer: 'Transfer',
|
||||
information: 'Information',
|
||||
|
||||
timeActive: 'Time active',
|
||||
eta: 'ETA',
|
||||
connections: 'Connections',
|
||||
downloaded: 'Downloaded',
|
||||
uploaded: 'Uploaded',
|
||||
seeds: 'Seeds',
|
||||
downloadSpeed: 'DL speed',
|
||||
uploadSpeed: 'UP speed',
|
||||
peers: 'Peers',
|
||||
wasted: 'Wasted',
|
||||
shareRatio: 'Share ratio',
|
||||
reannounce: 'Reannounce',
|
||||
lastSeen: 'Last seen',
|
||||
totalSize: 'Total size',
|
||||
pieces: 'Pieces',
|
||||
CreatedBy: 'Created by',
|
||||
CreatedOn: 'Created on',
|
||||
addedOn: 'Added on',
|
||||
completedOn: 'Completed on',
|
||||
torrentHash: 'Torrent hash',
|
||||
savePath: 'Save path',
|
||||
comment: 'Comment',
|
||||
|
||||
ip: 'IP',
|
||||
connection: 'Connection',
|
||||
flags: 'Flags',
|
||||
client: 'Client',
|
||||
relevance: 'Relevance',
|
||||
files: 'Files',
|
||||
|
||||
seeded: 'seeded',
|
||||
second: 's',
|
||||
total: 'Total',
|
||||
max: 'max',
|
||||
have: 'have',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ export default {
|
||||
resume: '恢复',
|
||||
pause: '暂停',
|
||||
force_start: '强制继续',
|
||||
toggle_sequential: '切换顺序下载',
|
||||
info: '信息',
|
||||
reset: '重置',
|
||||
login: '登录',
|
||||
@@ -45,7 +46,6 @@ export default {
|
||||
added_on: '添加时间',
|
||||
|
||||
settings: '设置',
|
||||
|
||||
logs: '日志',
|
||||
light: '亮色',
|
||||
dark: '暗色',
|
||||
@@ -66,6 +66,9 @@ export default {
|
||||
plugin: '插件',
|
||||
action: '操作',
|
||||
search_engine: '搜索引擎',
|
||||
usage: '用法',
|
||||
plugin_manager: '插件管理',
|
||||
update_plugins: '更新插件',
|
||||
|
||||
preferences: {
|
||||
change_applied: '配置已保存',
|
||||
@@ -108,6 +111,7 @@ export default {
|
||||
connection: '连接',
|
||||
bittorrent: 'BitTorrent',
|
||||
|
||||
rss: 'RSS',
|
||||
rss_processing_enabled: '启用自动刷新',
|
||||
rss_auto_downloading_enabled: '启用自动下载种子',
|
||||
rss_refresh_interval: '订阅刷新间隔',
|
||||
@@ -165,6 +169,7 @@ export default {
|
||||
reannounced: '已重新通告',
|
||||
rechecking: '重新检查中…',
|
||||
dht_nodes: '%{smart_count} 节点',
|
||||
base_url: 'Base URL',
|
||||
},
|
||||
|
||||
msg: {
|
||||
@@ -255,4 +260,68 @@ export default {
|
||||
moving: '移动中',
|
||||
unknown: '未知',
|
||||
},
|
||||
|
||||
prop_tab_bar: {
|
||||
general: '普通',
|
||||
trackers: 'Tracker',
|
||||
peers: '用户',
|
||||
// httpSource: 'HTTP 源',
|
||||
content: '内容',
|
||||
},
|
||||
properties_widget: {
|
||||
disabled: '禁用',
|
||||
notContracted: '未联系',
|
||||
working: '工作',
|
||||
updating: '更新...',
|
||||
notWorking: '未工作',
|
||||
|
||||
tier: '层级',
|
||||
url: 'URL',
|
||||
status: '状态',
|
||||
numPeers: '用户',
|
||||
numSeeds: '种子',
|
||||
numLeeches: '下载',
|
||||
numDownloaded: '下载次数',
|
||||
msg: '消息',
|
||||
|
||||
progress: '进度',
|
||||
transfer: '传输',
|
||||
information: '信息',
|
||||
|
||||
timeActive: '活动时间',
|
||||
eta: '剩余时间',
|
||||
connections: '连接',
|
||||
downloaded: '已下载',
|
||||
uploaded: '已上传',
|
||||
seeds: '种子',
|
||||
downloadSpeed: '下载速度',
|
||||
uploadSpeed: '上传速度',
|
||||
peers: '用户',
|
||||
wasted: '已丢弃',
|
||||
shareRatio: '分享率',
|
||||
reannounce: '下次汇报',
|
||||
lastSeen: '最后完整可见',
|
||||
totalSize: '总大小',
|
||||
pieces: '区块',
|
||||
createdBy: '创建',
|
||||
createdOn: '创建于',
|
||||
addedOn: '添加于',
|
||||
completedOn: '完成于',
|
||||
torrentHash: '种子哈希',
|
||||
savePath: '保存路径',
|
||||
comment: '注释',
|
||||
|
||||
ip: 'IP',
|
||||
connection: '连接',
|
||||
flags: '标志',
|
||||
client: '客户端',
|
||||
relevance: '文件关联',
|
||||
files: '文件',
|
||||
|
||||
seeded: '已做种',
|
||||
second: '秒',
|
||||
total: '总计',
|
||||
max: '最大',
|
||||
have: '已完成',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import App from './App.vue';
|
||||
|
||||
import 'roboto-fontface/css/roboto/roboto-fontface.css';
|
||||
import '@mdi/font/css/materialdesignicons.css';
|
||||
import './registerServiceWorker';
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { register } from 'register-service-worker'
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
register(`${process.env.BASE_URL}service-worker.js`, {
|
||||
ready () {
|
||||
console.log(
|
||||
'App is being served from cache by a service worker.\n' +
|
||||
'For more details, visit https://goo.gl/AFskqB',
|
||||
)
|
||||
},
|
||||
registered () {
|
||||
console.log('Service worker has been registered.')
|
||||
},
|
||||
cached () {
|
||||
console.log('Content has been cached for offline use.')
|
||||
},
|
||||
updatefound () {
|
||||
console.log('New content is downloading.')
|
||||
},
|
||||
updated () {
|
||||
console.log('New content is available; please refresh.')
|
||||
},
|
||||
offline () {
|
||||
console.log('No internet connection found. App is running in offline mode.')
|
||||
},
|
||||
error (error) {
|
||||
console.error('Error during service worker registration:', error)
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import searchEngineStore from './searchEngine';
|
||||
import { RootState } from './types';
|
||||
import stateMerge from '@/utils/vue-object-merge';
|
||||
import api from '@/Api';
|
||||
import { Torrent } from '@/types'
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
@@ -110,7 +111,8 @@ const store = new Vuex.Store<RootState>({
|
||||
}
|
||||
|
||||
const finalTags: any[] = []
|
||||
for (const tag of state.mainData.tags) {
|
||||
const tags = state.mainData.tags ?? [];
|
||||
for (const tag of tags) {
|
||||
finalTags.push({
|
||||
"key": tag,
|
||||
"name": tag,
|
||||
@@ -122,11 +124,15 @@ const store = new Vuex.Store<RootState>({
|
||||
return groupBy(getters.allTorrents, torrent => torrent.category);
|
||||
},
|
||||
torrentGroupByTag(state, getters) {
|
||||
const result: any = {}
|
||||
const result: Record<string, Torrent[]> = {}
|
||||
for (const torrent of getters.allTorrents) {
|
||||
const tags: any[] = torrent.tags.split(",");
|
||||
if (!torrent.tags) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tags: string[] = torrent.tags.split(', ');
|
||||
tags.forEach(tag => {
|
||||
let list: any[] = result[tag]
|
||||
let list: Torrent[] = result[tag]
|
||||
if (!list) {
|
||||
list = []
|
||||
result[tag] = list;
|
||||
|
||||
@@ -15,9 +15,6 @@ module.exports = {
|
||||
maskIcon: null,
|
||||
msTileImage: null,
|
||||
},
|
||||
workboxOptions: {
|
||||
importWorkboxFrom: 'local',
|
||||
},
|
||||
},
|
||||
|
||||
devServer: {
|
||||
|
||||
Reference in New Issue
Block a user