forked from platypush/platypush
parent
1774e464cc
commit
3dc1ff3c6e
10 changed files with 879 additions and 108 deletions
|
@ -46,7 +46,7 @@
|
||||||
@download="download"
|
@download="download"
|
||||||
v-if="selectedView === 'search'" />
|
v-if="selectedView === 'search'" />
|
||||||
|
|
||||||
<TorrentView :plugin-name="torrentPlugin"
|
<Transfers :plugin-name="torrentPlugin"
|
||||||
:is-media="true"
|
:is-media="true"
|
||||||
@play="play"
|
@play="play"
|
||||||
v-else-if="selectedView === 'torrents'" />
|
v-else-if="selectedView === 'torrents'" />
|
||||||
|
@ -92,7 +92,7 @@ import MediaView from "@/components/Media/View";
|
||||||
import Nav from "@/components/panels/Media/Nav";
|
import Nav from "@/components/panels/Media/Nav";
|
||||||
import Results from "@/components/panels/Media/Results";
|
import Results from "@/components/panels/Media/Results";
|
||||||
import Subtitles from "@/components/panels/Media/Subtitles";
|
import Subtitles from "@/components/panels/Media/Subtitles";
|
||||||
import TorrentView from "@/components/panels/Torrent/View";
|
import Transfers from "@/components/panels/Torrent/Transfers";
|
||||||
import UrlPlayer from "@/components/panels/Media/UrlPlayer";
|
import UrlPlayer from "@/components/panels/Media/UrlPlayer";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -107,7 +107,7 @@ export default {
|
||||||
Nav,
|
Nav,
|
||||||
Results,
|
Results,
|
||||||
Subtitles,
|
Subtitles,
|
||||||
TorrentView,
|
Transfers,
|
||||||
UrlPlayer,
|
UrlPlayer,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,119 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="header" :class="{'with-filter': filterVisible}">
|
<div class="header" :class="{'with-nav': withNav}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-s-12 col-m-9 col-l-7 left side">
|
<div class="left side" :class="leftSideClasses">
|
||||||
<form @submit.prevent="$emit('torrent-add', torrentURL)">
|
<form @submit.prevent="submit">
|
||||||
<label class="search-box">
|
<label class="search-box">
|
||||||
<input type="search" placeholder="Add torrent URL" v-model="torrentURL">
|
<input
|
||||||
|
type="search"
|
||||||
|
:disabled="loading"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
v-model="torrentURL"
|
||||||
|
v-if="selectedView === 'transfers'"
|
||||||
|
>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:value="query"
|
||||||
|
ref="search"
|
||||||
|
v-else-if="selectedView === 'search'"
|
||||||
|
>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<span class="button-container">
|
||||||
|
<button type="submit" title="Loading" disabled v-if="loading">
|
||||||
|
<Loading />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" title="Add torrent URL" v-else-if="selectedView === 'transfers'">
|
||||||
|
<i class="fa fa-download" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="submit" title="Search" v-else-if="selectedView === 'search'">
|
||||||
|
<i class="fa fa-search" />
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="right side col-1" v-if="!withNav">
|
||||||
|
<button @click="$emit('toggle')" title="Toggle navigation">
|
||||||
|
<i class="fa fa-bars" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Loading from "@/components/Loading";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Header",
|
name: "Header",
|
||||||
emits: ['torrent-add'],
|
emits: ['torrent-add', 'search', 'toggle'],
|
||||||
|
components: {Loading},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
query: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
withNav: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
selectedView: {
|
||||||
|
type: String,
|
||||||
|
default: 'transfers',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
torrentURL: '',
|
torrentURL: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
placeholder() {
|
||||||
|
if (this.selectedView === 'transfers') {
|
||||||
|
return 'Add torrent URL'
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Search torrents'
|
||||||
|
},
|
||||||
|
|
||||||
|
leftSideClasses() {
|
||||||
|
if (!this.withNav) {
|
||||||
|
return {
|
||||||
|
'col-12': true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'col-11': true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
submit() {
|
||||||
|
const query = this.$refs?.search?.value?.trim()
|
||||||
|
if (this.selectedView === 'transfers' && this.torrentURL?.length) {
|
||||||
|
this.$emit('torrent-add', this.torrentURL)
|
||||||
|
} else if (this.selectedView === 'search' && query?.length) {
|
||||||
|
this.$emit('search', query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -46,6 +138,9 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
&.right {
|
&.right {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 1.1em;
|
||||||
|
right: calc(#{$torrent-nav-width} / 4);
|
||||||
justify-content: right;
|
justify-content: right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +149,6 @@ export default {
|
||||||
background: none;
|
background: none;
|
||||||
padding: 0 .25em;
|
padding: 0 .25em;
|
||||||
border: 0;
|
border: 0;
|
||||||
margin-right: .25em;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $default-hover-fg-2;
|
color: $default-hover-fg-2;
|
||||||
|
@ -63,6 +157,7 @@ export default {
|
||||||
|
|
||||||
form {
|
form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
@ -70,8 +165,36 @@ export default {
|
||||||
background: initial;
|
background: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.button-container {
|
||||||
|
width: 3em;
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.loading) {
|
||||||
|
width: 5em;
|
||||||
|
font-size: 1em;
|
||||||
|
left: -0.5em;
|
||||||
|
border-radius: 0 1em 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[type=submit] {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: $default-bg-4;
|
||||||
|
border: $default-border-2;
|
||||||
|
border-radius: 0 1em 1em 0;
|
||||||
|
margin-left: -.5em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $hover-bg-3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.search-box {
|
.search-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
margin-left: .5em;
|
margin-left: .5em;
|
||||||
|
|
||||||
input[type=search] {
|
input[type=search] {
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
<template>
|
||||||
|
<div class="info">
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Title</div>
|
||||||
|
<div class="value">{{ torrent.title }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">URL</div>
|
||||||
|
<div class="value">
|
||||||
|
<button title="Open" @click="openInNewTab(torrent.url)">
|
||||||
|
<i class="fas fa-up-right-from-square" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button title="Copy" @click="copyToClipboard(torrent.url)">
|
||||||
|
<i class="fas fa-clipboard" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Size</div>
|
||||||
|
<div class="value">{{ convertSize(torrent.size) }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Seeders</div>
|
||||||
|
<div class="value">{{ torrent.seeds }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Leechers</div>
|
||||||
|
<div class="value">{{ torrent.peers }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="label">Uploaded</div>
|
||||||
|
<div class="value">{{ formatDate(torrent.created_at, true) }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" v-if="torrent.description">
|
||||||
|
<div class="label">Description</div>
|
||||||
|
<div class="value">{{ torrent.description }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row" v-if="torrent.year">
|
||||||
|
<div class="label">Year</div>
|
||||||
|
<div class="value">{{ torrent.year }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Utils from "@/Utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [Utils],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
torrent: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
openInNewTab(url) {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.info {
|
||||||
|
min-width: 50vw;
|
||||||
|
max-width: 800px;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-bottom: $default-border;
|
||||||
|
|
||||||
|
@include until($desktop) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include from($desktop) {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
width: 10em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
width: calc(100% - 10em);
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $default-hover-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,109 @@
|
||||||
|
<template>
|
||||||
|
<nav>
|
||||||
|
<button class="menu-button" @click="$emit('toggle')">
|
||||||
|
<i class="fa fa-bars" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<li v-for="(view, name) in views" :key="name" :title="view.displayName"
|
||||||
|
:class="{selected: name === selectedView}" @click="$emit('input', name)">
|
||||||
|
<i :class="view.iconClass" />
|
||||||
|
</li>
|
||||||
|
</nav>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
emits: ['input', 'toggle'],
|
||||||
|
props: {
|
||||||
|
selectedView: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
collapsed: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
views: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
search: {
|
||||||
|
displayName: 'Search',
|
||||||
|
iconClass: 'fa fa-search',
|
||||||
|
},
|
||||||
|
|
||||||
|
transfers: {
|
||||||
|
displayName: 'Transfers',
|
||||||
|
iconClass: 'fa fa-download',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "vars";
|
||||||
|
|
||||||
|
nav {
|
||||||
|
width: $torrent-nav-width;
|
||||||
|
height: calc(100% + #{$torrent-header-height});
|
||||||
|
margin-top: -$torrent-header-height;
|
||||||
|
background: $nav-collapsed-bg;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 2.5px 0 4.5px 2px $nav-collapsed-fg;
|
||||||
|
margin-left: 2.5px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.menu-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.75em;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border: 0;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $default-hover-fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.2em;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
padding: .6em;
|
||||||
|
opacity: 0.7;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&:hover {
|
||||||
|
border-radius: 1.2em;
|
||||||
|
margin: 0 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $nav-entry-collapsed-hover-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
background: $nav-entry-collapsed-selected-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,24 +1,78 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="torrent-container">
|
<div class="torrent-container">
|
||||||
<div class="header-container">
|
<Modal
|
||||||
<Header @torrent-add="download($event)" />
|
title="Torrent info"
|
||||||
|
:visible="infoItem !== null"
|
||||||
|
@close="infoIndex = null"
|
||||||
|
v-if="infoItem"
|
||||||
|
>
|
||||||
|
<Info :torrent="infoItem" />
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<div class="header-container" :class="{'with-nav': !navCollapsed}">
|
||||||
|
<Header
|
||||||
|
:with-nav="!navCollapsed"
|
||||||
|
:selected-view="selectedView"
|
||||||
|
:loading="loading"
|
||||||
|
@search="search($event)"
|
||||||
|
@torrent-add="download($event)"
|
||||||
|
@toggle="navCollapsed = !navCollapsed"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view-container">
|
<main>
|
||||||
<TorrentView :plugin-name="pluginName" />
|
<div class="view-container" :class="{'with-nav': !navCollapsed}">
|
||||||
</div>
|
<Transfers
|
||||||
|
:transfers="transfers"
|
||||||
|
@pause="pause($event)"
|
||||||
|
@resume="resume($event)"
|
||||||
|
@remove="remove($event)"
|
||||||
|
v-if="selectedView === 'transfers'"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Results
|
||||||
|
:results="results"
|
||||||
|
@download="download($event)"
|
||||||
|
@info="infoIndex = $event"
|
||||||
|
@next-page="search(query, page + 1)"
|
||||||
|
v-else-if="selectedView === 'search'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-container">
|
||||||
|
<Nav
|
||||||
|
:selected-view="selectedView"
|
||||||
|
@toggle="navCollapsed = !navCollapsed"
|
||||||
|
@input="selectedView = $event"
|
||||||
|
v-if="!navCollapsed"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Info from "./Info";
|
||||||
import Header from "@/components/panels/Torrent/Header";
|
import Header from "@/components/panels/Torrent/Header";
|
||||||
import TorrentView from "@/components/panels/Torrent/View";
|
import Modal from "@/components/Modal";
|
||||||
|
import Nav from "@/components/panels/Torrent/Nav";
|
||||||
|
import Results from "@/components/panels/Torrent/Results";
|
||||||
|
import Transfers from "@/components/panels/Torrent/Transfers";
|
||||||
import Utils from "@/Utils";
|
import Utils from "@/Utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Panel",
|
|
||||||
components: {TorrentView, Header},
|
|
||||||
mixins: [Utils],
|
mixins: [Utils],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Info,
|
||||||
|
Header,
|
||||||
|
Modal,
|
||||||
|
Nav,
|
||||||
|
Results,
|
||||||
|
Transfers,
|
||||||
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
pluginName: {
|
pluginName: {
|
||||||
type: String,
|
type: String,
|
||||||
|
@ -26,25 +80,250 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
transfers: {},
|
||||||
|
results: [],
|
||||||
|
selectedView: "transfers",
|
||||||
|
navCollapsed: false,
|
||||||
|
query: "",
|
||||||
|
page: 1,
|
||||||
|
infoIndex: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
infoItem() {
|
||||||
|
if (this.infoIndex === null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.results[this.infoIndex]
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
torrentId(torrent) {
|
||||||
|
if (torrent?.hash && torrent.hash.length)
|
||||||
|
return torrent.hash
|
||||||
|
|
||||||
|
return torrent.url
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentUpdate(torrent) {
|
||||||
|
this.transfers[this.torrentId(torrent)] = torrent
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentQueued(torrent) {
|
||||||
|
this.onTorrentUpdate(torrent)
|
||||||
|
this.notify({
|
||||||
|
text: 'Torrent queued for download',
|
||||||
|
image: {
|
||||||
|
icon: 'hourglass-start',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentStart(torrent) {
|
||||||
|
this.onTorrentUpdate(torrent)
|
||||||
|
this.notify({
|
||||||
|
html: `Torrent download started: <b>${torrent.name}</b>`,
|
||||||
|
image: {
|
||||||
|
icon: 'play',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentResume(torrent) {
|
||||||
|
this.onTorrentUpdate(torrent)
|
||||||
|
this.notify({
|
||||||
|
html: `Torrent download resumed: <b>${torrent.name}</b>`,
|
||||||
|
image: {
|
||||||
|
icon: 'play',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentPause(torrent) {
|
||||||
|
this.onTorrentUpdate(torrent)
|
||||||
|
this.notify({
|
||||||
|
html: `Torrent download paused: <b>${torrent.name}</b>`,
|
||||||
|
image: {
|
||||||
|
icon: 'pause',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentCompleted(torrent) {
|
||||||
|
this.onTorrentUpdate(torrent)
|
||||||
|
this.transfers[this.torrentId(torrent)].finish_date = new Date().toISOString()
|
||||||
|
this.transfers[this.torrentId(torrent)].progress = 100
|
||||||
|
this.notify({
|
||||||
|
html: `Torrent download completed: <b>${torrent.name}</b>`,
|
||||||
|
image: {
|
||||||
|
icon: 'check',
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
onTorrentRemove(torrent) {
|
||||||
|
const torrentId = this.torrentId(torrent)
|
||||||
|
if (torrentId in this.transfers)
|
||||||
|
delete this.transfers[torrentId]
|
||||||
|
},
|
||||||
|
|
||||||
|
async search(query, page=1) {
|
||||||
|
this.loading = true
|
||||||
|
this.query = query
|
||||||
|
let results = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
results = await this.request(
|
||||||
|
`${this.pluginName}.search`,
|
||||||
|
{query: query, page: page}
|
||||||
|
)
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.results = page === 1 ? results : this.results.concat(results)
|
||||||
|
if (results.length > 0) {
|
||||||
|
this.page = page
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
async download(torrent) {
|
async download(torrent) {
|
||||||
await this.request(`${this.pluginName}.download`, {torrent: torrent})
|
await this.request(`${this.pluginName}.download`, {torrent: torrent})
|
||||||
|
},
|
||||||
|
|
||||||
|
async pause(torrent) {
|
||||||
|
await this.request(`${this.pluginName}.pause`, {torrent: torrent.url})
|
||||||
|
await this.refresh()
|
||||||
|
},
|
||||||
|
|
||||||
|
async resume(torrent) {
|
||||||
|
await this.request(`${this.pluginName}.resume`, {torrent: torrent.url})
|
||||||
|
await this.refresh()
|
||||||
|
},
|
||||||
|
|
||||||
|
async remove(torrent) {
|
||||||
|
await this.request(`${this.pluginName}.remove`, {torrent: torrent.url})
|
||||||
|
await this.refresh()
|
||||||
|
},
|
||||||
|
|
||||||
|
async refresh() {
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.transfers = Object.values(await this.request(`${this.pluginName}.status`) || {})
|
||||||
|
.reduce((obj, torrent) => {
|
||||||
|
obj[this.torrentId(torrent)] = torrent
|
||||||
|
return obj
|
||||||
|
}, {})
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.refresh()
|
||||||
|
this.selectedView = this.transfers.length ? 'transfers' : 'search'
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentUpdate,
|
||||||
|
'on-torrent-update',
|
||||||
|
'platypush.message.event.torrent.TorrentDownloadStartEvent',
|
||||||
|
'platypush.message.event.torrent.TorrentDownloadProgressEvent',
|
||||||
|
'platypush.message.event.torrent.TorrentSeedingStartEvent',
|
||||||
|
'platypush.message.event.torrent.TorrentStateChangeEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentQueued,
|
||||||
|
'on-torrent-queued',
|
||||||
|
'platypush.message.event.torrent.TorrentQueuedEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentStart,
|
||||||
|
'on-torrent-queued',
|
||||||
|
'platypush.message.event.torrent.TorrentDownloadedMetadataEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentResume,
|
||||||
|
'on-torrent-resume',
|
||||||
|
'platypush.message.event.torrent.TorrentResumedEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentPause,
|
||||||
|
'on-torrent-pause',
|
||||||
|
'platypush.message.event.torrent.TorrentPausedEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentStop,
|
||||||
|
'on-torrent-stop',
|
||||||
|
'platypush.message.event.torrent.TorrentDownloadStopEvent',
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentCompleted,
|
||||||
|
'on-torrent-completed',
|
||||||
|
'platypush.message.event.torrent.TorrentDownloadCompletedEvent'
|
||||||
|
)
|
||||||
|
|
||||||
|
this.subscribe(
|
||||||
|
this.onTorrentRemove,
|
||||||
|
'on-torrent-remove',
|
||||||
|
'platypush.message.event.torrent.TorrentRemovedEvent'
|
||||||
|
)
|
||||||
|
|
||||||
|
const searchBox = document.querySelector('.search-box input[type="search"]')
|
||||||
|
if (searchBox) {
|
||||||
|
this.$nextTick(() => searchBox.focus())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.unsubscribe('on-torrent-update')
|
||||||
|
this.unsubscribe('on-torrent-remove')
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "vars";
|
@import "vars";
|
||||||
|
|
||||||
|
.header-container {
|
||||||
|
&.with-nav {
|
||||||
|
width: calc(100% - #{$torrent-nav-width});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.torrent-container {
|
.torrent-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - #{$torrent-header-height});
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - #{$torrent-header-height});
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.view-container {
|
.view-container {
|
||||||
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding-top: .2em;
|
padding-top: .2em;
|
||||||
|
|
||||||
|
&.with-nav {
|
||||||
|
width: calc(100% - #{$torrent-nav-width});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
<template>
|
||||||
|
<div class="results-container">
|
||||||
|
<div class="no-content" v-if="!results?.length">No results</div>
|
||||||
|
<div class="results" ref="body" @scroll="onScroll" v-else>
|
||||||
|
<div class="result" v-for="(result, i) in results" :key="i">
|
||||||
|
<div class="info">
|
||||||
|
<div class="title">{{ result.title }}</div>
|
||||||
|
<div class="additional-info">
|
||||||
|
<span class="info-pill size">
|
||||||
|
<span class="label">
|
||||||
|
<i class="fa fa-hdd" />
|
||||||
|
</span>
|
||||||
|
<span class="separator" />
|
||||||
|
<span class="value">{{ convertSize(result.size) }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="separator"> | </span>
|
||||||
|
|
||||||
|
<span class="info-pill seeds">
|
||||||
|
<span class="label">
|
||||||
|
<i class="fa fa-users" />
|
||||||
|
</span>
|
||||||
|
<span class="separator" />
|
||||||
|
<span class="value">{{ result.seeds }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="separator"> | </span>
|
||||||
|
|
||||||
|
<span class="info-pill created-at">
|
||||||
|
<span class="label">
|
||||||
|
<i class="fa fa-calendar" />
|
||||||
|
</span>
|
||||||
|
<span class="separator" />
|
||||||
|
<span class="value">{{ formatDate(result.created_at, true) }}</span>
|
||||||
|
</span>
|
||||||
|
<span class="separator"> | </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actions">
|
||||||
|
<button title="Torrent info" @click="$emit('info', i)">
|
||||||
|
<i class="fa fa-info-circle" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button title="Download" @click="$emit('download', result.url)">
|
||||||
|
<i class="fa fa-download" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Utils from "@/Utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['download', 'info', 'next-page'],
|
||||||
|
mixins: [Utils],
|
||||||
|
|
||||||
|
props: {
|
||||||
|
results: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
|
||||||
|
page: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
scrollTimeout: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onScroll() {
|
||||||
|
const offset = this.$refs.body.scrollTop
|
||||||
|
const bodyHeight = parseFloat(getComputedStyle(this.$refs.body).height)
|
||||||
|
const scrollHeight = this.$refs.body.scrollHeight
|
||||||
|
|
||||||
|
if (offset >= (scrollHeight - bodyHeight - 5)) {
|
||||||
|
if (this.scrollTimeout || !this.results.length)
|
||||||
|
return
|
||||||
|
|
||||||
|
this.scrollTimeout = setTimeout(() => {
|
||||||
|
this.scrollTimeout = null
|
||||||
|
}, 250)
|
||||||
|
|
||||||
|
this.$emit('next-page', this.page + 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.results-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: $background-color;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
.no-content {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
border-bottom: $default-border;
|
||||||
|
gap: 1em;
|
||||||
|
padding: .5em 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $hover-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
width: calc(100% - 5em);
|
||||||
|
padding: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.additional-info {
|
||||||
|
font-size: .8em;
|
||||||
|
opacity: .7;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-right: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: .5em;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
margin: -.2em .25em 0 .25em;
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 5em;
|
||||||
|
|
||||||
|
button {
|
||||||
|
background: none;
|
||||||
|
opacity: .8;
|
||||||
|
border: none;
|
||||||
|
padding: .25em;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $hover-fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -9,8 +9,8 @@
|
||||||
<div class="col-8 left side">
|
<div class="col-8 left side">
|
||||||
<i class="icon fa" :class="{
|
<i class="icon fa" :class="{
|
||||||
'fa-check': torrent.finish_date != null,
|
'fa-check': torrent.finish_date != null,
|
||||||
'fa-play': !torrent.finish_date && torrent.state === 'downloading',
|
'fa-play': !torrent.finish_date && torrent.state === 'downloading' && !torrent.paused,
|
||||||
'fa-pause': !torrent.finish_date && torrent.state === 'paused',
|
'fa-pause': !torrent.finish_date && torrent.state === 'downloading' && torrent.paused,
|
||||||
'fa-stop': !torrent.finish_date && torrent.state === 'stopped',
|
'fa-stop': !torrent.finish_date && torrent.state === 'stopped',
|
||||||
}" />
|
}" />
|
||||||
<div class="title" v-text="torrent.name || torrent.hash || torrent.url" />
|
<div class="title" v-text="torrent.name || torrent.hash || torrent.url" />
|
||||||
|
@ -22,11 +22,11 @@
|
||||||
|
|
||||||
<div class="col-2 right side">
|
<div class="col-2 right side">
|
||||||
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h" @click="selectedItem = i">
|
<Dropdown title="Actions" icon-class="fa fa-ellipsis-h" @click="selectedItem = i">
|
||||||
<DropdownItem icon-class="fa fa-pause" text="Pause transfer" @click="pause(torrentId(torrent))"
|
<DropdownItem icon-class="fa fa-pause" text="Pause transfer" @click="$emit('pause', torrent)"
|
||||||
v-if="torrent.state === 'downloading'" />
|
v-if="torrent.state === 'downloading' && !torrent.paused" />
|
||||||
<DropdownItem icon-class="fa fa-play" text="Resume transfer" @click="resume(torrentId(torrent))"
|
<DropdownItem icon-class="fa fa-play" text="Resume transfer" @click="$emit('resume', torrent)"
|
||||||
v-if="torrent.state === 'paused'" />
|
v-if="torrent.state === 'downloading' && torrent.paused" />
|
||||||
<DropdownItem icon-class="fa fa-trash" text="Remove transfer" @click="remove(torrentId(torrent))" />
|
<DropdownItem icon-class="fa fa-trash" text="Remove transfer" @click="$emit('remove', torrent)" />
|
||||||
<DropdownItem icon-class="fa fa-folder" text="View files" @click="$refs.torrentFiles.isVisible = true" />
|
<DropdownItem icon-class="fa fa-folder" text="View files" @click="$refs.torrentFiles.isVisible = true" />
|
||||||
<DropdownItem icon-class="fa fa-info" text="Torrent info" @click="$refs.torrentInfo.isVisible = true" />
|
<DropdownItem icon-class="fa fa-info" text="Torrent info" @click="$refs.torrentInfo.isVisible = true" />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
@ -96,6 +96,15 @@
|
||||||
<div class="attr">Save path</div>
|
<div class="attr">Save path</div>
|
||||||
<div class="value" v-text="transfers[selectedItem].save_path" />
|
<div class="value" v-text="transfers[selectedItem].save_path" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row" v-if="transfers[selectedItem].files">
|
||||||
|
<div class="attr">Files</div>
|
||||||
|
<div class="value">
|
||||||
|
<div class="file" v-for="(file, i) in transfers[selectedItem].files" :key="i">
|
||||||
|
<a :href="`/file?path=${encodeURIComponent(file)}`" target="_blank" v-text="file" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
@ -126,26 +135,31 @@ import Dropdown from "@/components/elements/Dropdown";
|
||||||
import DropdownItem from "@/components/elements/DropdownItem";
|
import DropdownItem from "@/components/elements/DropdownItem";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "View",
|
emits: [
|
||||||
emits: ['play', 'play-with-captions'],
|
'pause',
|
||||||
|
'play',
|
||||||
|
'play-with-captions',
|
||||||
|
'refresh',
|
||||||
|
'remove',
|
||||||
|
'resume',
|
||||||
|
],
|
||||||
components: {Dropdown, DropdownItem, Loading, Modal},
|
components: {Dropdown, DropdownItem, Loading, Modal},
|
||||||
mixins: [Utils, MediaUtils],
|
mixins: [Utils, MediaUtils],
|
||||||
props: {
|
props: {
|
||||||
pluginName: {
|
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
isMedia: {
|
isMedia: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
transfers: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
transfers: {},
|
|
||||||
selectedItem: null,
|
selectedItem: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -158,79 +172,6 @@ export default {
|
||||||
return this.transfers[this.selectedItem].files.map((file) => file.split('/').pop())
|
return this.transfers[this.selectedItem].files.map((file) => file.split('/').pop())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
|
||||||
torrentId(torrent) {
|
|
||||||
if (torrent?.hash && torrent.hash.length)
|
|
||||||
return torrent.hash
|
|
||||||
|
|
||||||
return torrent.url
|
|
||||||
},
|
|
||||||
|
|
||||||
async refresh() {
|
|
||||||
this.loading = true
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.transfers = Object.values(await this.request(`${this.pluginName}.status`) || {})
|
|
||||||
.reduce((obj, torrent) => {
|
|
||||||
obj[this.torrentId(torrent)] = torrent
|
|
||||||
return obj
|
|
||||||
}, {})
|
|
||||||
} finally {
|
|
||||||
this.loading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async pause(torrent) {
|
|
||||||
await this.request(`${this.pluginName}.pause`, {torrent: torrent})
|
|
||||||
await this.refresh()
|
|
||||||
},
|
|
||||||
|
|
||||||
async resume(torrent) {
|
|
||||||
await this.request(`${this.pluginName}.resume`, {torrent: torrent})
|
|
||||||
await this.refresh()
|
|
||||||
},
|
|
||||||
|
|
||||||
async remove(torrent) {
|
|
||||||
await this.request(`${this.pluginName}.remove`, {torrent: torrent})
|
|
||||||
await this.refresh()
|
|
||||||
},
|
|
||||||
|
|
||||||
onTorrentUpdate(torrent) {
|
|
||||||
this.transfers[this.torrentId(torrent)] = torrent
|
|
||||||
},
|
|
||||||
|
|
||||||
onTorrentRemove(torrent) {
|
|
||||||
const torrentId = this.torrentId(torrent)
|
|
||||||
if (torrentId in this.transfers)
|
|
||||||
delete this.transfers[torrentId]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.refresh()
|
|
||||||
|
|
||||||
this.subscribe(
|
|
||||||
this.onTorrentUpdate,'on-torrent-update',
|
|
||||||
'platypush.message.event.torrent.TorrentQueuedEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentDownloadedMetadataEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentDownloadStartEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentDownloadProgressEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentResumedEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentPausedEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentSeedingStartEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentStateChangeEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentDownloadStopEvent',
|
|
||||||
'platypush.message.event.torrent.TorrentDownloadCompletedEvent')
|
|
||||||
|
|
||||||
this.subscribe(this.onTorrentRemove,'on-torrent-remove',
|
|
||||||
'platypush.message.event.torrent.TorrentRemovedEvent')
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
this.unsubscribe('on-torrent-update')
|
|
||||||
this.unsubscribe('on-torrent-remove')
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
$torrent-header-height: 3.3em;
|
$torrent-header-height: 3.3em;
|
||||||
|
$torrent-nav-width: 2.8em;
|
||||||
|
|
|
@ -38,6 +38,7 @@ module.exports = {
|
||||||
'^/ws/requests': wsProxy,
|
'^/ws/requests': wsProxy,
|
||||||
'^/ws/shell': wsProxy,
|
'^/ws/shell': wsProxy,
|
||||||
'^/execute': httpProxy,
|
'^/execute': httpProxy,
|
||||||
|
'^/file': httpProxy,
|
||||||
'^/auth': httpProxy,
|
'^/auth': httpProxy,
|
||||||
'^/camera/': httpProxy,
|
'^/camera/': httpProxy,
|
||||||
'^/sound/': httpProxy,
|
'^/sound/': httpProxy,
|
||||||
|
|
|
@ -6,10 +6,19 @@ class TorrentMediaSearcher(MediaSearcher):
|
||||||
def search(self, query, **kwargs):
|
def search(self, query, **kwargs):
|
||||||
self.logger.info('Searching torrents for "{}"'.format(query))
|
self.logger.info('Searching torrents for "{}"'.format(query))
|
||||||
|
|
||||||
torrents = get_plugin(self.media_plugin.torrent_plugin if self.media_plugin else 'torrent')
|
torrents = get_plugin(
|
||||||
|
self.media_plugin.torrent_plugin if self.media_plugin else 'torrent'
|
||||||
|
)
|
||||||
if not torrents:
|
if not torrents:
|
||||||
raise RuntimeError('Torrent plugin not available/configured')
|
raise RuntimeError('Torrent plugin not available/configured')
|
||||||
return torrents.search(query, ).output
|
|
||||||
|
return [
|
||||||
|
torrent
|
||||||
|
for torrent in torrents.search(
|
||||||
|
query,
|
||||||
|
).output
|
||||||
|
if torrent.get('is_media')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
Loading…
Reference in a new issue