Upgrading Vuetify -> .16.9 Vue -> 2.5.2
This commit is contained in:
parent
fa8127386f
commit
0ab3424e3e
|
@ -15,9 +15,9 @@
|
|||
"cors": "^2.8.3",
|
||||
"socket.io": "^2.0.3",
|
||||
"videojs-contrib-hls": "^5.5.2",
|
||||
"vue": "^2.4.2",
|
||||
"vue": "^2.5.2",
|
||||
"vue-router": "^2.3.1",
|
||||
"vuetify": "^0.13.1"
|
||||
"vuetify": "^0.16.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.7.2",
|
||||
|
|
32
src/App.vue
32
src/App.vue
|
@ -1,12 +1,14 @@
|
|||
<template>
|
||||
<v-app dark style="height:100%" toolbar>
|
||||
<v-navigation-drawer temporary v-model="drawer" disable-route-watcher>
|
||||
<v-app dark style="height:100%">
|
||||
<v-navigation-drawer app temporary v-model="drawer" disable-route-watcher>
|
||||
<leftsidebar></leftsidebar>
|
||||
</v-navigation-drawer>
|
||||
<v-navigation-drawer style="padding:0" persistent v-model="drawerRight" right disable-route-watcher enable-resize-watcher>
|
||||
<v-navigation-drawer app
|
||||
permanent
|
||||
clipped v-model="drawerRight" clipped right disable-route-watcher enable-resize-watcher>
|
||||
<drawerright></drawerright>
|
||||
</v-navigation-drawer>
|
||||
<v-toolbar>
|
||||
<v-toolbar app fixed>
|
||||
<v-toolbar-side-icon @click.native.stop="drawer = !drawer"></v-toolbar-side-icon>
|
||||
<v-toolbar-title class="white--text">PlexTogether</v-toolbar-title>
|
||||
<v-spacer></v-spacer>
|
||||
|
@ -14,19 +16,21 @@
|
|||
<img class="ma-2 mr-3" style="height:70%;width:auto" v-bind:src="logo"/>
|
||||
<v-btn primary dark raised v-if="shortUrl != null" v-clipboard="shortUrl" @success="sendNotification()">Invite</v-btn>
|
||||
<v-btn small tag="a" class="hidden-sm-and-down" flat v-for="item in links" :key="item.title" :href="item.href" :target="item.target">{{ item.title }}</v-btn>
|
||||
<v-toolbar-side-icon v-if="showRightDrawerButton" @click.native.stop="drawerRight = !drawerRight"></v-toolbar-side-icon>
|
||||
<v-toolbar-side-icon v-if="showRightDrawerButton" @click="drawerRight = !drawerRight"></v-toolbar-side-icon>
|
||||
</v-toolbar-items>
|
||||
</v-toolbar>
|
||||
<main v-bind:style="mainStyle">
|
||||
<v-container style="padding:0; height:100%; max-width:100%" v-bind:style="containerStyle">
|
||||
<router-view></router-view>
|
||||
<v-snackbar
|
||||
bottom
|
||||
:timeout="4000"
|
||||
v-model="snackbar"
|
||||
> <div style="text-align:center;width:100%">{{snackbarMsg}}</div>
|
||||
</v-snackbar>
|
||||
</v-container>
|
||||
<v-content style="padding:0; max-width:100%; margin: 0; margin-top: 64px" v-bind:style="containerStyle">
|
||||
<v-container fluid class="ma-0 pa-0">
|
||||
<router-view></router-view>
|
||||
<v-snackbar
|
||||
bottom
|
||||
:timeout="4000"
|
||||
v-model="snackbar"
|
||||
> <div style="text-align:center;width:100%">{{snackbarMsg}}</div>
|
||||
</v-snackbar>
|
||||
</v-container>
|
||||
</v-content>
|
||||
</main>
|
||||
</v-app>
|
||||
</template>
|
||||
|
|
|
@ -231,6 +231,29 @@ span {
|
|||
|
||||
/* PTPlayer */
|
||||
|
||||
.video-js.vjs-fluid,
|
||||
.video-js.vjs-16-9,
|
||||
.video-js.vjs-4-3,
|
||||
video.video-js,
|
||||
video.vjs-tech, {
|
||||
max-height: calc(100vh - 64px);
|
||||
position: relative !important;
|
||||
width: 100%;
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
padding-top: 0 !important;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
/* Fix the control bar due to us resetting the line-height on the video-js */
|
||||
.vjs-control-bar {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ptplayer .video-js {
|
||||
max-height: calc(100vh - 64px) !important;
|
||||
}
|
||||
|
||||
.ptplayer .video-js .vjs-menu-button-inline.vjs-slider-active, .video-js .vjs-menu-button-inline:focus, .video-js .vjs-menu-button-inline:hover, .video-js.vjs-no-flex .vjs-menu-button-inline {
|
||||
width: 10em
|
||||
}
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
<template>
|
||||
<div style="margin-bottom: 0; height:100%">
|
||||
<div style="margin-bottom: 0; height:100%">
|
||||
<div style="margin-bottom: 0; height:100%">
|
||||
<v-layout row wrap style="overflow-y: scroll">
|
||||
<!-- MAIN CONTENT -->
|
||||
<v-layout v-if="!validDevices" wrap row style="position:relative" class="pt-4">
|
||||
<v-flex xs12 md4 offset-md4>
|
||||
<div style="width:100%;text-align:center">
|
||||
<v-progress-circular indeterminate v-bind:size="50" class="amber--text" style="display:inline-block"></v-progress-circular>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<div v-if="validDevices">
|
||||
<v-flex xs12 v-if="!validDevices">
|
||||
<v-layout fill-height wrap row style="position:relative" class="pt-4">
|
||||
<v-flex xs12 md4 offset-md4>
|
||||
<div style="width:100%;text-align:center">
|
||||
<v-progress-circular indeterminate v-bind:size="50" class="amber--text" style="display:inline-block"></v-progress-circular>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-flex>
|
||||
<v-flex xs12 v-if="validDevices">
|
||||
<div v-if="!ptConnected || !chosenClient || !ptRoom">
|
||||
<walkthrough class="pa-4"></walkthrough>
|
||||
</div>
|
||||
<div v-else>
|
||||
<plexbrowser v-if="showBrowser" class="pa-4"></plexbrowser>
|
||||
<plexbrowser v-if="showBrowser" class="pa-4"></plexbrowser>
|
||||
<ptplayer v-if="isPTPlayer"></ptplayer>
|
||||
<plexcontent v-if="showMetadata" class="pa-4" nowPlaying :content="chosenClient.clientPlayingMetadata" :server="nowPlayingServer"></plexcontent>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -39,12 +41,10 @@
|
|||
|
||||
// Components
|
||||
import plexclient from './application/plexclient'
|
||||
import plexserver from './application/plexserver'
|
||||
import ptuser from './application/ptuser'
|
||||
import joinroom from './application/joinroom'
|
||||
import chatmessage from './application/chatmessage'
|
||||
import walkthrough from './application/walkthrough'
|
||||
import sidebar from './application/sidebar'
|
||||
import ptplayer from './application/ptplayer'
|
||||
import plexbrowser from './application/plexbrowser'
|
||||
import plexcontent from './application/plexbrowser/plexcontent'
|
||||
|
@ -54,9 +54,7 @@
|
|||
name: 'application',
|
||||
components: {
|
||||
plexclient,
|
||||
plexserver,
|
||||
joinroom,
|
||||
sidebar,
|
||||
ptuser,
|
||||
chatmessage,
|
||||
walkthrough,
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
:autofocus="true"
|
||||
v-on:keyup.enter.native="joinRoom()"
|
||||
v-model="room"
|
||||
class="input-group"
|
||||
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
@ -59,7 +58,6 @@
|
|||
label="Room password"
|
||||
v-on:keyup.enter.native="joinRoom()"
|
||||
v-model="password"
|
||||
class="pt-0 mt-0 input-group orange--text"
|
||||
|
||||
></v-text-field>
|
||||
</v-flex>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<v-flex xs12 lg12 >
|
||||
<v-subheader > Movies ({{filteredMovies.length}})</v-subheader>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 xl1 lg2 class="pb-3" v-for="movie in filteredMovies" :key="movie.key">
|
||||
<v-flex xs6 md3 xl1 lg1 class="pb-3" v-for="movie in filteredMovies" :key="movie.key">
|
||||
<plexthumb :content="movie" :server="movie.server" showServer search @contentSet="setContent(movie)"></plexthumb>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<v-flex xs12 lg12 >
|
||||
<v-subheader > TV Shows ({{filteredShows.length}})</v-subheader>
|
||||
</v-flex>
|
||||
<v-flex xs6 md3 xl1 lg2 class="pb-3" v-for="show in filteredShows" :key="show.key">
|
||||
<v-flex xs6 md3 xl1 lg1 class="pb-3" v-for="show in filteredShows" :key="show.key">
|
||||
<plexthumb :content="show" :server="show.server" showServer search @contentSet="setContent(show)"></plexthumb>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -79,7 +79,7 @@
|
|||
<h4> Browse </h4>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 lg4 md6 xl3 v-for="server in plex.servers" :key="server.machineIdentifier" class="pa-2">
|
||||
<v-card class="white--text" v-on:click="setServer(server)" horizontal height="10em" style="cursor: pointer; z-index: 0; background: rgba(0,0,0,0.4);">
|
||||
<v-card class="white--text" v-on:click.native="setServer(server)" horizontal height="10em" style="cursor: pointer; z-index: 0; background: rgba(0,0,0,0.4);">
|
||||
<v-container fluid grid-list-lg>
|
||||
<v-layout row>
|
||||
<v-flex xs4>
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 style="position:relative">
|
||||
<div style="float:right">
|
||||
<v-chip bottom v-tooltip:top="{ html: 'Resolution' }" class="grey darken-1 white--text" outline left> {{ largestRes }}p</v-chip>
|
||||
<v-chip bottom v-tooltip:top="{ html: 'Year' }" class="grey darken-4 white--text" outline left> {{ contents.year }}</v-chip>
|
||||
<v-chip v-if="contents.contentRating" v-tooltip:top="{ html: 'Content Rating' }" class="grey darken-4 white--text" small label> {{ contents.contentRating }}</v-chip>
|
||||
<v-chip bottom class="grey darken-1 white--text" outline left> {{ largestRes }}p</v-chip>
|
||||
<v-chip bottom class="grey darken-4 white--text" outline left> {{ contents.year }}</v-chip>
|
||||
<v-chip v-if="contents.contentRating" class="grey darken-4 white--text" small label> {{ contents.contentRating }}</v-chip>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -54,9 +54,9 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 style="position:relative">
|
||||
<div style="float:right">
|
||||
<v-chip bottom v-tooltip:top="{ html: 'Resolution' }" class="grey darken-1 white--text" outline left> {{ largestRes }}p</v-chip>
|
||||
<v-chip v-if="contents.contentRating" v-tooltip:top="{ html: 'Content Rating' }" class="grey darken-4 white--text" small label> {{ contents.contentRating }}</v-chip>
|
||||
<v-chip v-if="contents.studio" v-tooltip:top="{ html: 'Studio' }" class="grey darken-4 white--text" small label> {{ contents.studio }}</v-chip>
|
||||
<v-chip bottom class="grey darken-1 white--text" outline left> {{ largestRes }}p</v-chip>
|
||||
<v-chip v-if="contents.contentRating" class="grey darken-4 white--text" small label> {{ contents.contentRating }}</v-chip>
|
||||
<v-chip v-if="contents.studio" class="grey darken-4 white--text" small label> {{ contents.studio }}</v-chip>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -100,14 +100,14 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 sm6 style="position:relative">
|
||||
<div style="float:right">
|
||||
<v-chip v-if="contents.year" v-tooltip:top="{ html: 'Content Rating' }" class="grey darken-4 white--text" small label> {{ contents.year }}</v-chip>
|
||||
<v-chip v-for="copy in contents.Media" :key="copy.key" v-tooltip:top="{ html: 'Quality' }" class="grey darken-4 white--text" small> {{ copy.audioCodec.toUpperCase() }}</v-chip>
|
||||
<v-chip v-if="contents.year" class="grey darken-4 white--text" small label> {{ contents.year }}</v-chip>
|
||||
<v-chip v-for="copy in contents.Media" :key="copy.key" class="grey darken-4 white--text" small> {{ copy.audioCodec.toUpperCase() }}</v-chip>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-divider></v-divider>
|
||||
<v-chip v-for="country in contents.Country" :key="country.tag" v-tooltip:top="{ html: 'Country' }"> {{ country.tag }}</v-chip>
|
||||
<v-chip v-for="genre in contents.Genre" :key="genre.tag" v-tooltip:top="{ html: 'Genre' }"> {{ genre.tag }}</v-chip>
|
||||
<v-chip v-for="country in contents.Country" :key="country.tag"> {{ country.tag }}</v-chip>
|
||||
<v-chip v-for="genre in contents.Genre" :key="genre.tag"> {{ genre.tag }}</v-chip>
|
||||
</v-flex>
|
||||
|
||||
</v-layout>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
</span>
|
||||
<div v-if="!browsingContent && contents" class="mt-3" style="height:90vh; overflow-y:scroll ">
|
||||
<v-layout class="row" row wrap>
|
||||
<v-flex xs4 md3 xl1 lg2 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<v-flex xs4 md3 lg1 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<plexthumb :content="content" :server="server" type="thumb" style="margin:7%" @contentSet="setContent(content)"></plexthumb>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
|
|
@ -31,8 +31,8 @@
|
|||
<p style="font-style: italic" class="pt-3"> {{ content.summary }} </p>
|
||||
<div>
|
||||
<div style="float:right" class="pa-4">
|
||||
<v-chip v-if="contents.MediaContainer.grandparentContentRating" v-tooltip:top="{ html: 'Content Rating' }" label> {{ contents.MediaContainer.grandparentContentRating }}</v-chip>
|
||||
<v-chip v-if="contents.MediaContainer.grandparentStudio" v-tooltip:top="{ html: 'Studio' }" secondary> {{ contents.MediaContainer.grandparentStudio }}</v-chip>
|
||||
<v-chip v-if="contents.MediaContainer.grandparentContentRating" label> {{ contents.MediaContainer.grandparentContentRating }}</v-chip>
|
||||
<v-chip v-if="contents.MediaContainer.grandparentStudio" secondary> {{ contents.MediaContainer.grandparentStudio }}</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<v-divider></v-divider>
|
||||
<div>
|
||||
<v-layout class="row mt-3" row wrap>
|
||||
<v-flex xs6 md3 xl2 lg2 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<v-flex xs6 md3 lg2 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<plexthumb :content="content" :server="server" type="thumb" style="margin:2%" fullTitle @contentSet="setContent(content)"></plexthumb>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</v-flex>
|
||||
<h4 class="mt-3"> Seasons </h4>
|
||||
<v-layout class="row" row wrap>
|
||||
<v-flex xs4 md3 xl1 lg2 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<v-flex xs4 md3 xl1 lg1 class="pb-3" v-for="content in contents.MediaContainer.Metadata" :key="content.key">
|
||||
<plexthumb :content="content" :server="server" type="thumb" style="margin:7%" @contentSet="setContent(content)"></plexthumb>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<v-progress-circular active large></v-progress-circular>
|
||||
</div>
|
||||
<h4> Libraries </h4>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs6 md3 xl2 lg2 v-if="libraries && !browsingLibrary" v-for="library in filteredLibraries" :key="library.name">
|
||||
<v-card v-on:click="setLibrary(library)" :img="getArtLibrary(library)" height="10em" class="text-xs-center hoverable card" style="max-width:100%">
|
||||
<v-layout row wrap v-if="libraries && !browsingLibrary">
|
||||
<v-flex xs6 md3 xl2 lg2 v-for="library in filteredLibraries" class="pa-3 clickable" :key="library.name">
|
||||
<v-card v-on:click.native="setLibrary(library)" :img="getArtLibrary(library)" height="10em" class="text-xs-center hoverable card" style="max-width:100%">
|
||||
<div style="position:relative;width:100%;background: rgba(0,0,0,0.4); height:8em">
|
||||
<img style="height: 70%;display: block; margin-left: auto; margin-right: auto " :src="getThumb(library)"/>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<template>
|
||||
<div class="portrait" ref="root" style="cursor: pointer" @mouseover="hovering = true" @mouseout="hovering = false">
|
||||
|
||||
|
||||
<v-card data-tilt v-on:click="emitContentClicked(content)" class="grey darken-4" style="border:solid">
|
||||
<v-card data-tilt v-on:click.native="emitContentClicked(content)" class="grey darken-4" style="border:solid">
|
||||
<v-card-media
|
||||
class="white--text"
|
||||
style="position:relative"
|
||||
|
@ -32,7 +30,7 @@
|
|||
<v-progress-linear style="position:absolute; top:0; width:100%" class="pa-0 ma-0 pt-content-progress" v-if="showProgressBar" height="2" :value="unwatchedPercent"></v-progress-linear>
|
||||
<v-layout row wrap class="text-xs-left" style="margin:0; margin-left:3px; display:block; max-width:100%; height:100%">
|
||||
<v-flex v-if="!onlyBottom" xs12 style="height:50%" :style="topTextStyle" ref="topText" class="pa-0 ma-0 ml-1 mt-1">
|
||||
<div v-tooltip:top="{ html: getTitle(content) }" class="truncate" style="font-size:1rem">{{ getTitle(content) }}</div>
|
||||
<div class="truncate" style="font-size:1rem">{{ getTitle(content) }}</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 style="height:50%; font-size:0.8rem" :style="bottomTextStyle" ref="bottomText" class="pa-0 ma-0 mt-0 ml-1">
|
||||
<div class="truncate soft-text" style=" position:absolute; bottom:0">{{ getUnder(content) }}</div>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<template>
|
||||
<div v-tooltip="tooltipMsg" class="mdc-list-item" href="#" style="height: 30px">
|
||||
{{ object.name }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['object'],
|
||||
name: 'plexserver',
|
||||
computed: {
|
||||
tooltipMsg: function () {
|
||||
return this.object.name + ' running on ' + this.object.platform + ' (Version ' + this.object.productVersion + ')'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div style="width:100%; height:100%">
|
||||
<div style="width:100%; max-height: calc(100vh - 64px)">
|
||||
<videoplayer v-if="playingMetadata && chosenServer && chosenQuality && ready"
|
||||
@playerMounted="playerMounted()"
|
||||
@timelineUpdate="timelineUpdate"
|
||||
|
|
|
@ -235,21 +235,6 @@
|
|||
// console.log('Succesfully sent Player status to PMS')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
computed: {
|
||||
player () {
|
||||
|
@ -272,7 +257,7 @@
|
|||
// videojs options
|
||||
plugins: {},
|
||||
|
||||
fluid: true,
|
||||
fluid: false,
|
||||
preload: 'auto',
|
||||
volume: 0.5,
|
||||
aspectRatio: '16:9',
|
||||
|
@ -397,7 +382,7 @@
|
|||
console.log(player)
|
||||
},
|
||||
playerStateChanged (playerCurrentState) {
|
||||
console.log("Setting volume to " + this.player.volume() || 0)
|
||||
// console.log("Setting volume to " + this.player.volume() || 0)
|
||||
this.$store.commit('setSettingPTPLAYERVOLUME', this.player.volume() || 0)
|
||||
this.bufferedTill = Math.round(this.player.buffered().end(0) * 1000)
|
||||
this.duration = Math.round(this.player.duration() * 1000)
|
||||
|
@ -421,7 +406,7 @@
|
|||
})
|
||||
},
|
||||
playerReadied (player) {
|
||||
console.log('Setting volume to ' + this.$store.getters.getSettingPTPLAYERVOLUME )
|
||||
// console.log('Setting volume to ' + this.$store.getters.getSettingPTPLAYERVOLUME )
|
||||
this.player.volume(this.$store.getters.getSettingPTPLAYERVOLUME || 0)
|
||||
},
|
||||
|
||||
|
|
|
@ -1,253 +0,0 @@
|
|||
<template>
|
||||
<div v-if="chosenClient && plex" class="col l2 s12" v-bind:style="{ position: isMobile }"
|
||||
style="border-right-color: rgba(0,0,0,0.1); border-right-width: 1px; border-right-style: solid; border-bottom-color: rgba(0,0,0,0.1); border-bottom-width: 1px; border-bottom-style: solid; width: 100% min-height: 50%;">
|
||||
<div style="overflow-y: auto;max-height: 50%;">
|
||||
<div class="mdc-list">
|
||||
<li v-if="chosenClient" class="mdc-list-item mdc-permanent-drawer--selected plex-gamboge-text truncate"
|
||||
href="#">
|
||||
Plex Players {{playercount}}
|
||||
</li>
|
||||
<li v-if="plex && chosenClient" v-for="client in clients" id="plexPlayers">
|
||||
<plexclient :sidebar="true" :object="client"></plexclient>
|
||||
</li>
|
||||
</div>
|
||||
<div v-if="ptConnected" style="margin-bottom: 20px">
|
||||
<div class="mdc-list">
|
||||
<li class="mdc-list-item mdc-permanent-drawer--selected plex-gamboge-text" href="#">
|
||||
PT Server
|
||||
</li>
|
||||
<li class="mdc-list-item truncate" style="height: 30px" id="plexTogetherServerAddress">
|
||||
{{ ptServer }}
|
||||
</li>
|
||||
</div>
|
||||
<div class="mdc-list">
|
||||
<li class="mdc-list-item mdc-permanent-drawer--selected plex-gamboge-text" href="#">
|
||||
PT Room
|
||||
</li>
|
||||
<li class="mdc-list-item truncate" style="height: 30px" id="plexTogetherRoomName">
|
||||
{{ ptRoom }}
|
||||
</li>
|
||||
</div>
|
||||
<div v-if="ptPassword" class="mdc-list">
|
||||
<li class="mdc-list-item mdc-permanent-drawer--selected plex-gamboge-text" href="#">
|
||||
PT Password
|
||||
</li>
|
||||
<li class="mdc-list-item truncate" style="height: 30px" id="plexTogetherRoomPassword">
|
||||
{{ ptPassword }}
|
||||
</li>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="position: absolute;left: 0; margin-bottom: 0; width:100%" v-bind:style="mobileStyle">
|
||||
<div v-if="plex && chosenClient" class="plexTogetherInfo col l12 s12"
|
||||
style="padding-top: 11.25px; min-height:30%; max-height:50%; padding-left: 0; padding-right: 0">
|
||||
<div class="row" v-if="chosenClient.clientPlayingMetadata" style="margin-bottom: 0;" id="userMetadata">
|
||||
<div class="col s12 center">
|
||||
<img id="metaThumb" :src="metadataThumb" style="max-height: 30%; max-width: 100%">
|
||||
</div>
|
||||
<div class="col s12 center white-text" id="metaUnder" style="padding-bottom: 10px;">
|
||||
{{ metadataTitle }}
|
||||
<br> {{ metadataInfo }}
|
||||
<br> <label>Playing on {{ metadataServer }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="ptConnected" class="col s12" style="padding:0">
|
||||
<button class="mdc-button mdc-button--raised mdc-button--accent plex-gamboge ptSettings disconnect"
|
||||
id="plexTogetherLeaveButton" style="width: 100%;" v-on:click="handleDisconnect()">
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import plexclient from './plexclient'
|
||||
import plexserver from './plexserver'
|
||||
|
||||
export default {
|
||||
props: ['object', 'mobile'],
|
||||
name: 'sidebar',
|
||||
data () {
|
||||
return {
|
||||
testClient: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
plexclient,
|
||||
plexserver,
|
||||
},
|
||||
computed: {
|
||||
mobileStyle: function () {
|
||||
if (!this.mobile) {
|
||||
return {
|
||||
bottom: '0px'
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
chosenClient: function () {
|
||||
return this.$store.getters.getChosenClient
|
||||
},
|
||||
plex: function () {
|
||||
return this.$store.getters.getPlex
|
||||
},
|
||||
clients: function () {
|
||||
return this.$store.getters.getPlex.clients
|
||||
},
|
||||
context: function () {
|
||||
return this.$store
|
||||
},
|
||||
logo: function () {
|
||||
if (this.$store.getters.getSettingDARKMODE) {
|
||||
return 'ptweb/logo-long-light.png'
|
||||
}
|
||||
return 'ptweb/logo-long-dark.png'
|
||||
},
|
||||
ptConnected: function () {
|
||||
return this.$store.getters.getConnected
|
||||
},
|
||||
ptServer: function () {
|
||||
return this.$store.getters.getServer
|
||||
},
|
||||
ptRoom: function () {
|
||||
return this.$store.getters.getRoom
|
||||
},
|
||||
ptPassword: function () {
|
||||
return this.$store.getters.getPassword
|
||||
},
|
||||
validPlex: function () {
|
||||
if (!this.$store.state.plex) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
playercount: function () {
|
||||
if (this.$store.state.plex && this.$store.state.plex.gotDevices) {
|
||||
return '(' + this.$store.state.plex.clients.length + ')'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
servercount: function () {
|
||||
if (this.$store.state.plex && this.$store.state.plex.gotDevices) {
|
||||
return '(' + this.$store.state.plex.servers.length + ')'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
metadataThumb: function () {
|
||||
if (!this.validPlex) {
|
||||
return ''
|
||||
}
|
||||
let plexObj = this.$store.state.plex
|
||||
if (!this.$store.getters.getChosenClient) {
|
||||
return ''
|
||||
}
|
||||
let metadata = this.$store.getters.getChosenClient.clientPlayingMetadata
|
||||
if (!metadata) {
|
||||
return ''
|
||||
}
|
||||
let server;
|
||||
let content = metadata.thumb;
|
||||
if (metadata.parentThumb) {
|
||||
content = metadata.parentThumb
|
||||
}
|
||||
if (!plexObj.getServerById(metadata.machineIdentifier)) {
|
||||
return ''
|
||||
}
|
||||
return plexObj.getServerById(metadata.machineIdentifier).getUrlForLibraryLoc(content)
|
||||
},
|
||||
metadataServer: function () {
|
||||
if (!this.validPlex) {
|
||||
return ''
|
||||
}
|
||||
if (!this.$store.getters.getChosenClient) {
|
||||
return ''
|
||||
}
|
||||
let metadata = this.$store.getters.getChosenClient.clientPlayingMetadata
|
||||
if (!metadata) {
|
||||
return ''
|
||||
}
|
||||
return (this.plex.getServerById(this.chosenClient.clientPlayingMetadata.machineIdentifier).name)
|
||||
},
|
||||
metadataTitle: function () {
|
||||
if (!this.validPlex) {
|
||||
return ''
|
||||
}
|
||||
if (!this.$store.getters.getChosenClient) {
|
||||
return ''
|
||||
}
|
||||
let metadata = this.$store.getters.getChosenClient.clientPlayingMetadata
|
||||
if (!metadata) {
|
||||
return ''
|
||||
}
|
||||
if (metadata.type == 'movie') {
|
||||
return metadata.title
|
||||
}
|
||||
if (metadata.type == 'episode') {
|
||||
return metadata.grandparentTitle
|
||||
}
|
||||
return metadata.index
|
||||
},
|
||||
metadataInfo: function () {
|
||||
if (!this.validPlex) {
|
||||
return ''
|
||||
}
|
||||
if (!this.$store.getters.getChosenClient) {
|
||||
return ''
|
||||
}
|
||||
let metadata = this.$store.getters.getChosenClient.clientPlayingMetadata
|
||||
if (!metadata) {
|
||||
return ''
|
||||
}
|
||||
if (metadata.type == 'movie') {
|
||||
return metadata.year
|
||||
}
|
||||
if (metadata.type == 'episode') {
|
||||
return metadata.title + ' (S' + metadata.parentIndex + '·E' + metadata.index + ')'
|
||||
}
|
||||
return metadata.index
|
||||
|
||||
},
|
||||
isMobile: function () {
|
||||
if (!this.mobile) {
|
||||
return 'relative'
|
||||
}
|
||||
return ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
previewClient: function (client) {
|
||||
this.testClient = client
|
||||
},
|
||||
clientClicked: function (client) {
|
||||
let clients = this.$store.getters.getPlex.clients
|
||||
for (let i = 0; i < clients.length; i++) {
|
||||
let _client = clients[i]
|
||||
_client.connectedstatus = 'fresh'
|
||||
}
|
||||
//this.$store.state.plex.chosenClient = null
|
||||
client.connectedstatus = 'waiting'
|
||||
let that = this
|
||||
client.findConnection(function (res) {
|
||||
let plexObj = that.$store.state.plex
|
||||
if (res) {
|
||||
client.connectedstatus = 'connected'
|
||||
that.$store.commit('SET_CHOSENCLIENT', client)
|
||||
|
||||
} else {
|
||||
client.connectedstatus = 'failed'
|
||||
}
|
||||
})
|
||||
},
|
||||
handleDisconnect: function () {
|
||||
this.$store.dispatch('disconnectServer')
|
||||
},
|
||||
openJoinRoomModal: function () {
|
||||
return this.$parent.$refs.joinroomModal.open()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<v-layout >
|
||||
<v-flex xs12 lg8 offset-lg2 xl6 offset-xl3 style="background: rgba(0,0,0,0.1); border-radius: 10px" class="pa-4">
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 md8 offset-md2 lg4 offset-lg4 xl6 offset-xl3>
|
||||
<v-layout row wrap justify-center>
|
||||
<v-flex xs12 lg8 xl6 style="background: rgba(0,0,0,0.1); border-radius: 10px" class="pa-4">
|
||||
<v-layout row wrap justify-center>
|
||||
<v-flex xs12 md8 lg4 xl6>
|
||||
<img style="width:100%" v-bind:src="logo">
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
|
@ -28,7 +28,7 @@
|
|||
<v-divider></v-divider>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 md6 lg7>
|
||||
<v-subheader light>Plex Players {{ playercount }}</v-subheader>
|
||||
<v-subheader>Plex Players {{ playercount }}</v-subheader>
|
||||
<div v-for="i in plex.clients" :key="i.clientIdentifier">
|
||||
<div v-on:click="previewClient(i)">
|
||||
<plexclient :startup="testClient" :sidebar="false" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
|
||||
|
@ -37,11 +37,11 @@
|
|||
</v-flex>
|
||||
<v-flex xs12 md6 lg5>
|
||||
<div v-if="testClient">
|
||||
<v-subheader light>
|
||||
<v-subheader>
|
||||
Selected Player
|
||||
</v-subheader>
|
||||
<div class="pl-1">
|
||||
<h6 light style="opacity:1">{{ testClient.name }}</h6>
|
||||
<h6 style="opacity:1">{{ testClient.name }}</h6>
|
||||
<div>
|
||||
<label >Last seen</label><span style="opacity:0.8"> {{ lastSeenAgo(testClient.lastSeenAt) }}</span>
|
||||
</div>
|
||||
|
@ -98,7 +98,6 @@
|
|||
<script>
|
||||
|
||||
import plexclient from './plexclient'
|
||||
import plexserver from './plexserver'
|
||||
import joinroom from './joinroom'
|
||||
|
||||
import moment from '../../../node_modules/moment/moment.js'
|
||||
|
@ -115,7 +114,6 @@
|
|||
},
|
||||
components: {
|
||||
plexclient,
|
||||
plexserver,
|
||||
joinroom
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -10,13 +10,13 @@
|
|||
<h1 style="color:white">G'day</h1>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout row wrap>
|
||||
<v-flex xs12 md6 offset-md3>
|
||||
<v-layout row wrap justify-center>
|
||||
<v-flex xs12 md6>
|
||||
<p> PlexTogether is a tool to sync Plex playback with friends and family anywhere in the world.
|
||||
We started off with a Python script which we distributed amongst our friends. With a command line based UI we quickly realised this was too difficult for our friends to use reliably.
|
||||
After playing around with the concept we decided to make a version that we could release that all Plex users could enjoy.
|
||||
</p>
|
||||
<h2> How it works </h2>
|
||||
<h4> How it works </h4>
|
||||
<p>
|
||||
PlexTogether aims to keep multiple viewing sessions in sync regardless of whether the clients are in the same room or across the globe. To do this PlexTogether utilizes a middle-man server to communicate between each of PlexTogether clients.
|
||||
Users choose their Plex client, decide on a PlexTogether Server and Room name and join up. Your friends/family can do the same. Whoever joins the room first will become the host.
|
||||
|
|
133
src/sidebar.vue
133
src/sidebar.vue
|
@ -1,70 +1,75 @@
|
|||
<template>
|
||||
<div v-if="ptRoom" class="pl-1 pr-1" style="height:100%; overflow-x:hidden;">
|
||||
<div style="height:60%">
|
||||
<h6 style="text-align:center" class="mb-0 pb-0 pt-3"> Room {{ ptRoom }}</h6>
|
||||
<v-subheader>Users ({{ ptUsers.length }})</v-subheader>
|
||||
<v-list dense style="height:90%; overflow-y:scroll">
|
||||
<div v-for="user in ptUsers" v-bind:key="user.username" style="position:relative;height:7em">
|
||||
<v-list-item style="height:4em" class="mb-0 pb-0">
|
||||
<v-list-tile avatar class="pb-0 mb-0" tag="div" >
|
||||
<v-list-tile-avatar>
|
||||
<img v-bind:src="user.avatarUrl"/>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title> {{ user.username }}</v-list-tile-title>
|
||||
<v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%"><v-icon style="font-size:90%">{{playerState(user)}}</v-icon> - {{getTitle(user)}}</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action v-if="isHost(user)">
|
||||
<v-icon v-if="isHost(user)" style="color: #E5A00D">star</v-icon>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
<div style="width:100%;" class="pl-2 pr-2 pt-2 mt-0 pb-0 mb-0">
|
||||
<span style="float: left;font-size:70%" class="ptuser-time pl-2">{{ getCurrent(user) }}</span>
|
||||
<span style="float: right;font-size:70%" class="ptuser-maxTime pr-2">{{ getMax(user) }}</span>
|
||||
<v-progress-linear class="pt-content-progress " :height="2" :value="percent(user)"></v-progress-linear>
|
||||
<v-container class="pa-0" fill-height>
|
||||
<v-layout column v-if="ptRoom" class="pl-2 pr-2" style="">
|
||||
<v-flex xs12 style="height: 50vh">
|
||||
<h6 style="text-align:center" class="mb-0 pb-0 pt-3"> Room {{ ptRoom }}</h6>
|
||||
<v-subheader>Users ({{ ptUsers.length }})</v-subheader>
|
||||
<v-list dense style="overflow-y:scroll">
|
||||
<div v-for="user in ptUsers" v-bind:key="user.username" style="position:relative;height:7em">
|
||||
<v-list-item style="height:4em" class="mb-0 pb-0">
|
||||
<v-list-tile avatar class="pb-0 mb-0" tag="div" >
|
||||
<v-list-tile-avatar>
|
||||
<img v-bind:src="user.avatarUrl"/>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title> {{ user.username }}</v-list-tile-title>
|
||||
<v-list-tile-sub-title style="opacity:0.6;color:white;font-size:70%"><v-icon style="font-size:90%">{{playerState(user)}}</v-icon> - {{getTitle(user)}}</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action v-if="isHost(user)">
|
||||
<v-icon v-if="isHost(user)" style="color: #E5A00D">star</v-icon>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
<div class="pl-2 pr-2 pt-2 mt-0 pb-0 mb-0">
|
||||
<span style="float: left;font-size:70%" class="ptuser-time pl-2">{{ getCurrent(user) }}</span>
|
||||
<span style="float: right;font-size:70%" class="ptuser-maxTime pr-2">{{ getMax(user) }}</span>
|
||||
<v-progress-linear class="pt-content-progress " :height="2" :value="percent(user)"></v-progress-linear>
|
||||
</div>
|
||||
<v-divider class="mt-0 pt-0" style="height:2px; color:white"></v-divider>
|
||||
</div>
|
||||
<v-divider class="mt-0 pt-0" style="height:2px; color:white" light></v-divider>
|
||||
</div>
|
||||
</v-list>
|
||||
</div>
|
||||
<div style="overflow-y: auto; height: 30%">
|
||||
<div style="height:100%">
|
||||
<v-divider></v-divider>
|
||||
<v-subheader style="height:10%" light>Messages</v-subheader>
|
||||
<v-list id="chatbox" :style="chatboxStyle" style="overflow-y:scroll; height:90%">
|
||||
<v-list-item style="min-height:50px" v-bind:id="getMsgId(msg)" v-for="msg in messages" v-bind:key="msg.msg + msg.time">
|
||||
<v-list-tile style="height:initial; position:relative" tag="div">
|
||||
<v-list-tile-avatar>
|
||||
<img v-bind:src="msg.user.thumb || msg.user.avatarUrl" style="position:absolute;top:0"/>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title style="color:white; position:relative; height:35%">
|
||||
<span style="opacity:1;font-size:80%; float:left"> {{ msg.user.username }}</span>
|
||||
<span style="opacity:0.6;font-size:60%; float:right"> {{ msg.time}}</span>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-sub-title style="opacity:0.8;color:white;font-size:70%; height:65%"> {{ msg.msg }}</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 10%; position:relative" class="pt-2">
|
||||
<v-text-field
|
||||
class="pa-1 ma-0"
|
||||
name="input-1"
|
||||
prepend-icon="message"
|
||||
:label="'Send a message to ' + '#'+ptRoom"
|
||||
autoGrow
|
||||
v-on:keyup.enter.native="sendMessage()"
|
||||
v-model="messageToBeSent"
|
||||
|
||||
></v-text-field>
|
||||
<v-btn style="width:100%; position:absolute; bottom:0" v-on:click.native="handleDisconnect()" class="ma-0 mt-1" primary>Leave room </v-btn>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</v-flex>
|
||||
<v-flex xs12 style="overflow-y: auto; max-height: 50vh">
|
||||
<v-container class="ma-0 pa-0" fill-height>
|
||||
<v-layout column>
|
||||
<v-flex xs12>
|
||||
<v-divider></v-divider>
|
||||
<v-subheader>Messages</v-subheader>
|
||||
<v-list id="chatbox" :style="chatboxStyle" style="overflow-y:scroll; max-height: 34vh">
|
||||
<v-list-item style="min-height:50px" v-bind:id="getMsgId(msg)" v-for="msg in messages" v-bind:key="msg.msg + msg.time">
|
||||
<v-list-tile style="height:initial; position:relative" tag="div">
|
||||
<v-list-tile-avatar>
|
||||
<img v-bind:src="msg.user.thumb || msg.user.avatarUrl" style="position:absolute;top:0; width: 36px; height: 36px"/>
|
||||
</v-list-tile-avatar>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title style="color:white; position:relative;">
|
||||
<span style="opacity:1;font-size:80%; float:left"> {{ msg.user.username }}</span>
|
||||
<span style="opacity:0.6;font-size:60%; float:right"> {{ msg.time}}</span>
|
||||
</v-list-tile-title>
|
||||
<v-list-tile-sub-title style="opacity:0.8;color:white;font-size:70%;"> {{ msg.msg }}</v-list-tile-sub-title>
|
||||
</v-list-tile-content>
|
||||
</v-list-tile>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-flex>
|
||||
<v-flex xs12 style="position:relative" class="pt-2">
|
||||
<div style="position:absolute; bottom:0; width: 100%" class="ma-0 pa-0">
|
||||
<v-text-field
|
||||
prepend-icon="message"
|
||||
:label="'Send a message to ' + '#'+ptRoom"
|
||||
autoGrow
|
||||
v-on:keyup.enter.native="sendMessage()"
|
||||
v-model="messageToBeSent"
|
||||
|
||||
></v-text-field>
|
||||
<v-btn block v-on:click.native="handleDisconnect()" class="ma-0 mt-1" primary>Leave room </v-btn>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
Loading…
Reference in New Issue