More work on porting vuetify stuff
This commit is contained in:
parent
4d456c037e
commit
633de3daf1
|
@ -3,13 +3,13 @@ module.exports = {
|
|||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ["plugin:vue/essential", "eslint:recommended"],
|
||||
extends: ["plugin:vue/essential", "@vue/airbnb"],
|
||||
parserOptions: {
|
||||
parser: "babel-eslint"
|
||||
},
|
||||
rules: {
|
||||
"no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",
|
||||
//"import/no-extraneous-dependencies": ["error", { devDependencies: true }]
|
||||
"import/no-extraneous-dependencies": ["error", { devDependencies: true }]
|
||||
}
|
||||
};
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.2 KiB |
|
@ -1,23 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.png" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
|
||||
</head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css?family=Material+Icons"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.</strong>
|
||||
<strong
|
||||
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
|
||||
properly without JavaScript enabled. Please enable it to
|
||||
continue.</strong
|
||||
>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
|
||||
</body>
|
||||
</html>
|
259
src/App.vue
259
src/App.vue
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-app dark style="height:100%">
|
||||
<leftsidebar :drawer="drawer"></leftsidebar>
|
||||
<leftsidebar></leftsidebar>
|
||||
<v-navigation-drawer
|
||||
v-if="showRightDrawerButton"
|
||||
style="padding: 0; z-index: 6"
|
||||
|
@ -14,7 +14,7 @@
|
|||
</v-navigation-drawer>
|
||||
|
||||
<v-app-bar fixed scroll-off-screen :scroll-threshold="1" style="z-index: 5">
|
||||
<v-app-bar-nav-icon @click="drawer = !drawer"></v-app-bar-nav-icon>
|
||||
<v-app-bar-nav-icon @click="TOGGLE_LEFT_SIDEBAR_OPEN"></v-app-bar-nav-icon>
|
||||
<a href="https://synclounge.tv" target="_blank">
|
||||
<img
|
||||
class="ma-1 hidden-xs-only"
|
||||
|
@ -109,18 +109,18 @@
|
|||
|
||||
<script>
|
||||
// Custom css
|
||||
import "./assets/css/style.css";
|
||||
import './assets/css/style.css';
|
||||
|
||||
import fscreen from "fscreen";
|
||||
import fscreen from 'fscreen';
|
||||
|
||||
import { mapActions, mapState } from "vuex";
|
||||
import drawerright from "./sidebar.vue";
|
||||
import leftsidebar from "./leftsidebar.vue";
|
||||
import upnext from "./upnext.vue";
|
||||
import nowplayingchip from "./nowplayingchip.vue";
|
||||
import donate from "./donate.vue";
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import drawerright from './sidebar.vue';
|
||||
import leftsidebar from './leftsidebar.vue';
|
||||
import upnext from './upnext.vue';
|
||||
import nowplayingchip from './nowplayingchip.vue';
|
||||
import donate from './donate.vue';
|
||||
|
||||
const SettingsHelper = require("../SettingsHelper");
|
||||
const SettingsHelper = require('../SettingsHelper');
|
||||
|
||||
const settings = new SettingsHelper();
|
||||
|
||||
|
@ -130,11 +130,10 @@ export default {
|
|||
upnext,
|
||||
nowplayingchip,
|
||||
leftsidebar,
|
||||
donate
|
||||
donate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
drawer: false,
|
||||
mini: false,
|
||||
drawerRight: false,
|
||||
right: null,
|
||||
|
@ -150,38 +149,39 @@ export default {
|
|||
|
||||
items: [
|
||||
{
|
||||
title: "Preferences"
|
||||
title: 'Preferences',
|
||||
},
|
||||
{
|
||||
title: "Signout"
|
||||
}
|
||||
title: 'Signout',
|
||||
},
|
||||
],
|
||||
links: [
|
||||
{
|
||||
title: "Github",
|
||||
href: "https://github.com/samcm/SyncLounge",
|
||||
target: "_blank"
|
||||
title: 'Github',
|
||||
href: 'https://github.com/samcm/SyncLounge',
|
||||
target: '_blank',
|
||||
},
|
||||
{
|
||||
title: "Discord",
|
||||
target: "_blank",
|
||||
href: "https://discord.gg/fKQB3yt"
|
||||
}
|
||||
title: 'Discord',
|
||||
target: '_blank',
|
||||
href: 'https://discord.gg/fKQB3yt',
|
||||
},
|
||||
],
|
||||
appIsFullscreen: false
|
||||
appIsFullscreen: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
...mapActions("config", ["fetchConfig"]),
|
||||
...mapActions('config', ['fetchConfig']),
|
||||
...mapActions(['TOGGLE_LEFT_SIDEBAR_OPEN']),
|
||||
sendNotification() {
|
||||
window.EventBus.$emit("notification", "Copied to clipboard");
|
||||
window.EventBus.$emit('notification', 'Copied to clipboard');
|
||||
},
|
||||
toggleDrawerRight() {
|
||||
this.drawerRight = !this.drawerRight;
|
||||
},
|
||||
goFullscreen() {
|
||||
fscreen.requestFullscreen(document.body);
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
|
@ -195,91 +195,82 @@ export default {
|
|||
//
|
||||
// Set AutoJoin information in order of importance: query -> config -> settings
|
||||
if (this.$route.query.autojoin) {
|
||||
this.$store.commit("SET_AUTOJOIN", true);
|
||||
this.$store.commit("SET_AUTOJOINROOM", this.$route.query.room);
|
||||
this.$store.commit("SET_AUTOJOINURL", this.$route.query.server);
|
||||
this.$store.commit("SET_VALUE", [
|
||||
"autoJoinOwner",
|
||||
this.$route.query.owner
|
||||
]);
|
||||
this.$store.commit('SET_AUTOJOIN', true);
|
||||
this.$store.commit('SET_AUTOJOINROOM', this.$route.query.room);
|
||||
this.$store.commit('SET_AUTOJOINURL', this.$route.query.server);
|
||||
this.$store.commit('SET_VALUE', ['autoJoinOwner', this.$route.query.owner]);
|
||||
if (this.$route.query.password) {
|
||||
this.$store.commit("SET_AUTOJOINPASSWORD", this.$route.query.password);
|
||||
this.$store.commit('SET_AUTOJOINPASSWORD', this.$route.query.password);
|
||||
}
|
||||
} else if (this.config) {
|
||||
if (
|
||||
this.config.autoJoin &&
|
||||
(this.config.autoJoin === true || this.config.autoJoin === "true")
|
||||
(this.config.autoJoin === true || this.config.autoJoin === 'true')
|
||||
) {
|
||||
this.$store.commit("SET_AUTOJOIN", true);
|
||||
this.$store.commit("SET_AUTOJOINROOM", this.config.autoJoinRoom);
|
||||
this.$store.commit("SET_AUTOJOINURL", this.config.autoJoinServer);
|
||||
this.$store.commit(
|
||||
"SET_AUTOJOINPASSWORD",
|
||||
this.config.autoJoinPassword
|
||||
);
|
||||
this.$store.commit('SET_AUTOJOIN', true);
|
||||
this.$store.commit('SET_AUTOJOINROOM', this.config.autoJoinRoom);
|
||||
this.$store.commit('SET_AUTOJOINURL', this.config.autoJoinServer);
|
||||
this.$store.commit('SET_AUTOJOINPASSWORD', this.config.autoJoinPassword);
|
||||
}
|
||||
} else if (settings) {
|
||||
if (
|
||||
settings.autoJoin &&
|
||||
(settings.autoJoin === true || settings.autoJoin === "true")
|
||||
) {
|
||||
this.$store.commit("SET_AUTOJOIN", true);
|
||||
this.$store.commit("SET_AUTOJOINROOM", settings.autoJoinRoom);
|
||||
this.$store.commit("SET_AUTOJOINURL", settings.autoJoinServer);
|
||||
this.$store.commit("SET_AUTOJOINPASSWORD", settings.autoJoinPassword);
|
||||
if (settings.autoJoin && (settings.autoJoin === true || settings.autoJoin === 'true')) {
|
||||
this.$store.commit('SET_AUTOJOIN', true);
|
||||
this.$store.commit('SET_AUTOJOINROOM', settings.autoJoinRoom);
|
||||
this.$store.commit('SET_AUTOJOINURL', settings.autoJoinServer);
|
||||
this.$store.commit('SET_AUTOJOINPASSWORD', settings.autoJoinPassword);
|
||||
}
|
||||
}
|
||||
|
||||
// Get other settings in order of importance: config -> settings
|
||||
// Authentication Mechanism setting
|
||||
if (this.config && this.config.authentication) {
|
||||
this.$store.commit("SET_AUTHENTICATION", this.config.authentication);
|
||||
this.$store.commit('SET_AUTHENTICATION', this.config.authentication);
|
||||
} else if (settings && settings.authentication) {
|
||||
this.$store.commit("SET_AUTHENTICATION", settings.authentication);
|
||||
this.$store.commit('SET_AUTHENTICATION', settings.authentication);
|
||||
} else {
|
||||
this.$store.commit("SET_AUTHENTICATION", {
|
||||
type: "none"
|
||||
this.$store.commit('SET_AUTHENTICATION', {
|
||||
type: 'none',
|
||||
});
|
||||
}
|
||||
|
||||
// Custom Servers list settings
|
||||
let servers = [
|
||||
{
|
||||
name: "SyncLounge AU1",
|
||||
location: "Sydney, Australia",
|
||||
url: "https://v3au1.synclounge.tv/slserver",
|
||||
image: "flags/au.png"
|
||||
name: 'SyncLounge AU1',
|
||||
location: 'Sydney, Australia',
|
||||
url: 'https://v3au1.synclounge.tv/slserver',
|
||||
image: 'flags/au.png',
|
||||
},
|
||||
{
|
||||
name: "SyncLounge EU1",
|
||||
location: "Amsterdam, Netherlands",
|
||||
url: "https://v2eu1.synclounge.tv/server",
|
||||
image: "flags/eu.png"
|
||||
name: 'SyncLounge EU1',
|
||||
location: 'Amsterdam, Netherlands',
|
||||
url: 'https://v2eu1.synclounge.tv/server',
|
||||
image: 'flags/eu.png',
|
||||
},
|
||||
{
|
||||
name: "SyncLounge US1",
|
||||
location: "Miami, United States",
|
||||
url: "https://v2us1.synclounge.tv/server",
|
||||
image: "flags/us.png"
|
||||
name: 'SyncLounge US1',
|
||||
location: 'Miami, United States',
|
||||
url: 'https://v2us1.synclounge.tv/server',
|
||||
image: 'flags/us.png',
|
||||
},
|
||||
{
|
||||
name: "SyncLounge US2",
|
||||
location: "Miami, United States",
|
||||
url: "https://v3us1.synclounge.tv/slserver",
|
||||
image: "flags/us.png"
|
||||
name: 'SyncLounge US2',
|
||||
location: 'Miami, United States',
|
||||
url: 'https://v3us1.synclounge.tv/slserver',
|
||||
image: 'flags/us.png',
|
||||
},
|
||||
{
|
||||
name: "SyncLounge US3",
|
||||
location: "Miami, United States",
|
||||
url: "https://v3us2.synclounge.tv/slserver",
|
||||
image: "flags/us.png"
|
||||
}
|
||||
name: 'SyncLounge US3',
|
||||
location: 'Miami, United States',
|
||||
url: 'https://v3us2.synclounge.tv/slserver',
|
||||
image: 'flags/us.png',
|
||||
},
|
||||
];
|
||||
const customServer = {
|
||||
name: "Custom Server",
|
||||
location: "Anywhere!",
|
||||
url: "custom",
|
||||
image: "synclounge-white.png"
|
||||
name: 'Custom Server',
|
||||
location: 'Anywhere!',
|
||||
url: 'custom',
|
||||
image: 'synclounge-white.png',
|
||||
};
|
||||
|
||||
if (this.config && this.config.servers) {
|
||||
|
@ -304,74 +295,72 @@ export default {
|
|||
servers.push(customServer);
|
||||
}
|
||||
|
||||
this.$store.commit("setSetting", ["SERVERS", servers]);
|
||||
this.$store.commit('setSetting', ['SERVERS', servers]);
|
||||
|
||||
// Auto-join if a single server is provided and autoJoinServer is not
|
||||
if (servers.length === 1 && !this.$store.autoJoinServer) {
|
||||
const server = servers[0];
|
||||
this.$store.commit("SET_AUTOJOIN", true);
|
||||
this.$store.commit("SET_AUTOJOINURL", server.url);
|
||||
this.$store.commit('SET_AUTOJOIN', true);
|
||||
this.$store.commit('SET_AUTOJOINURL', server.url);
|
||||
if (!this.$store.autoJoinRoom && server.defaultRoom) {
|
||||
this.$store.commit("SET_AUTOJOINROOM", server.defaultRoom);
|
||||
this.$store.commit('SET_AUTOJOINROOM', server.defaultRoom);
|
||||
}
|
||||
if (!this.$store.autoJoinPassword && server.defaultPassword) {
|
||||
this.$store.commit("SET_AUTOJOINPASSWORD", server.defaultPassword);
|
||||
this.$store.commit('SET_AUTOJOINPASSWORD', server.defaultPassword);
|
||||
}
|
||||
}
|
||||
//
|
||||
// End Settings
|
||||
//
|
||||
|
||||
window.EventBus.$on("notification", msg => {
|
||||
window.EventBus.$on('notification', msg => {
|
||||
this.snackbarMsg = msg;
|
||||
this.snackbar = true;
|
||||
});
|
||||
window.EventBus.$on("NEW_TIMELINE", timeline => {
|
||||
this.$store.dispatch("NEW_TIMELINE", timeline);
|
||||
window.EventBus.$on('NEW_TIMELINE', timeline => {
|
||||
this.$store.dispatch('NEW_TIMELINE', timeline);
|
||||
});
|
||||
window.EventBus.$on("PLAYBACK_CHANGE", data => {
|
||||
if (this.chosenClient.clientIdentifier !== "PTPLAYER9PLUS10" && data[1]) {
|
||||
this.$router.push(
|
||||
`/nowplaying/${data[2].machineIdentifier}/${data[1]}`
|
||||
);
|
||||
window.EventBus.$on('PLAYBACK_CHANGE', data => {
|
||||
if (this.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10' && data[1]) {
|
||||
this.$router.push(`/nowplaying/${data[2].machineIdentifier}/${data[1]}`);
|
||||
}
|
||||
if (
|
||||
this.chosenClient.clientIdentifier !== "PTPLAYER9PLUS10" &&
|
||||
this.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10' &&
|
||||
!data[1] &&
|
||||
this.$route.fullPath.indexOf("/nowplaying") > -1
|
||||
this.$route.fullPath.indexOf('/nowplaying') > -1
|
||||
) {
|
||||
this.$router.push("/browse/");
|
||||
this.$router.push('/browse/');
|
||||
}
|
||||
this.$store.dispatch("PLAYBACK_CHANGE", data);
|
||||
this.$store.dispatch('PLAYBACK_CHANGE', data);
|
||||
});
|
||||
if (!window.localStorage.getItem("plexuser")) {
|
||||
this.$router.push("/signin");
|
||||
if (!window.localStorage.getItem('plexuser')) {
|
||||
this.$router.push('/signin');
|
||||
this.loading = false;
|
||||
return;
|
||||
}
|
||||
if (this.$route.path === "/") {
|
||||
this.$router.push("/clientselect");
|
||||
if (this.$route.path === '/') {
|
||||
this.$router.push('/clientselect');
|
||||
}
|
||||
const plexstorage = JSON.parse(window.localStorage.getItem("plexuser"));
|
||||
const plexstorage = JSON.parse(window.localStorage.getItem('plexuser'));
|
||||
try {
|
||||
await this.$store.dispatch("PLEX_LOGIN_TOKEN", plexstorage.authToken);
|
||||
await this.$store.dispatch('PLEX_LOGIN_TOKEN', plexstorage.authToken);
|
||||
} catch (e) {
|
||||
//this.$router.push('/signin');
|
||||
// this.$router.push('/signin');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.$store.state.autoJoin) {
|
||||
this.$store.dispatch("autoJoin", {
|
||||
this.$store.dispatch('autoJoin', {
|
||||
server: this.$store.state.autoJoinUrl,
|
||||
password: this.$store.state.autoJoinPassword,
|
||||
room: this.$store.state.autoJoinRoom
|
||||
room: this.$store.state.autoJoinRoom,
|
||||
});
|
||||
}
|
||||
|
||||
fscreen.addEventListener("fullscreenchange", () => {
|
||||
fscreen.addEventListener('fullscreenchange', () => {
|
||||
const isFullscreen = fscreen.fullscreenElement !== null;
|
||||
this.appIsFullscreen = isFullscreen;
|
||||
document.body.classList.toggle("is-fullscreen", isFullscreen);
|
||||
document.body.classList.toggle('is-fullscreen', isFullscreen);
|
||||
});
|
||||
|
||||
this.loading = false;
|
||||
|
@ -381,11 +370,11 @@ export default {
|
|||
if (this.showRightDrawerButton) {
|
||||
this.drawerRight = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState("config", {
|
||||
config: state => state.configuration
|
||||
...mapState('config', {
|
||||
config: state => state.configuration,
|
||||
}),
|
||||
plex() {
|
||||
return this.$store.getters.getPlex;
|
||||
|
@ -401,8 +390,8 @@ export default {
|
|||
},
|
||||
crumbs() {
|
||||
if (
|
||||
this.$route.path.indexOf("browse") === -1 &&
|
||||
this.$route.path.indexOf("nowplaying") === -1
|
||||
this.$route.path.indexOf('browse') === -1 &&
|
||||
this.$route.path.indexOf('nowplaying') === -1
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
@ -410,30 +399,30 @@ export default {
|
|||
try {
|
||||
return this.itemCache[this.$route.params.machineIdentifier][id].title;
|
||||
} catch (e) {
|
||||
return "Loading..";
|
||||
return 'Loading..';
|
||||
}
|
||||
};
|
||||
const getLibrary = id => {
|
||||
try {
|
||||
return this.libraryCache[this.$route.params.machineIdentifier][id];
|
||||
} catch (e) {
|
||||
return "Library";
|
||||
return 'Library';
|
||||
}
|
||||
};
|
||||
const data = [
|
||||
{
|
||||
text: "Home",
|
||||
to: "/browse"
|
||||
}
|
||||
text: 'Home',
|
||||
to: '/browse',
|
||||
},
|
||||
];
|
||||
const map = {
|
||||
machineIdentifier: () => ({
|
||||
text: this.plex.servers[this.$route.params.machineIdentifier].name,
|
||||
to: `/browse/${this.$route.params.machineIdentifier}`
|
||||
to: `/browse/${this.$route.params.machineIdentifier}`,
|
||||
}),
|
||||
sectionId: () => ({
|
||||
text: getLibrary(this.$route.params.sectionId),
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}`
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}`,
|
||||
}),
|
||||
parentKey: () => {
|
||||
let to;
|
||||
|
@ -444,17 +433,17 @@ export default {
|
|||
}
|
||||
return {
|
||||
text: getTitle(this.$route.params.parentKey),
|
||||
to
|
||||
to,
|
||||
};
|
||||
},
|
||||
grandparentKey: () => ({
|
||||
text: getTitle(this.$route.params.grandparentKey),
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/tv/${this.$route.params.grandparentKey}/`
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/tv/${this.$route.params.grandparentKey}/`,
|
||||
}),
|
||||
ratingKey: () => ({
|
||||
text: getTitle(this.$route.params.ratingKey),
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/${this.$route.params.ratingKey}`
|
||||
})
|
||||
to: `/browse/${this.$route.params.machineIdentifier}/${this.$route.params.sectionId}/${this.$route.params.ratingKey}`,
|
||||
}),
|
||||
};
|
||||
Object.keys(this.$route.params).forEach(param => {
|
||||
const link = map[param]();
|
||||
|
@ -468,7 +457,7 @@ export default {
|
|||
return (
|
||||
this.chosenClient &&
|
||||
this.chosenClient.clientPlayingMetadata &&
|
||||
this.$route.name === "browse"
|
||||
this.$route.name === 'browse'
|
||||
);
|
||||
},
|
||||
showRightDrawerButton() {
|
||||
|
@ -487,7 +476,7 @@ export default {
|
|||
return this.logos.light.small;
|
||||
},
|
||||
isPlayer() {
|
||||
if (this.$route.path === "/") {
|
||||
if (this.$route.path === '/') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -523,10 +512,10 @@ export default {
|
|||
mainStyle() {
|
||||
if (this.$store.getters.getBackground !== null) {
|
||||
return {
|
||||
"background-image": `url(${this.$store.getters.getBackground})`,
|
||||
"background-repeat": "no-repeat",
|
||||
"background-size": "cover",
|
||||
"background-position": "center"
|
||||
'background-image': `url(${this.$store.getters.getBackground})`,
|
||||
'background-repeat': 'no-repeat',
|
||||
'background-size': 'cover',
|
||||
'background-position': 'center',
|
||||
};
|
||||
}
|
||||
return {};
|
||||
|
@ -535,21 +524,21 @@ export default {
|
|||
const arr = [];
|
||||
if (this.$store.getters.getBackground !== null) {
|
||||
arr.push({
|
||||
background: "rgba(0,0,0,0.7)"
|
||||
background: 'rgba(0,0,0,0.7)',
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
paddingStyle() {
|
||||
const arr = [];
|
||||
if (this.$route.path.indexOf("/player") === -1) {
|
||||
if (this.$route.path.indexOf('/player') === -1) {
|
||||
arr.push({
|
||||
padding: "16px"
|
||||
padding: '16px',
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
<template>
|
||||
<v-list-item avatar :style="styleObj" class="pa-1">
|
||||
<v-list-item :style="styleObj" class="pa-1">
|
||||
<v-list-item-avatar>
|
||||
<img class="clientLogo" :class="platformClass" :src="url">
|
||||
<img class="clientLogo" :class="platformClass" :src="url" />
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>{{ object.name }}<v-chip v-for="label in object.labels" :key="label[0]" :color="label[1]" small label>{{ label[0] }}</v-chip></v-list-item-title>
|
||||
<v-list-item-title>
|
||||
{{ object.name }}
|
||||
<v-chip
|
||||
v-for="label in object.labels"
|
||||
:key="label[0]"
|
||||
:color="label[1]"
|
||||
small
|
||||
label
|
||||
>{{ label[0] }}</v-chip>
|
||||
</v-list-item-title>
|
||||
<v-list-item-subtitle>{{ object.product }} - last seen {{ lastSeenAgo }}</v-list-item-subtitle>
|
||||
</v-list-item-content>
|
||||
</v-list-item>
|
||||
|
@ -19,27 +28,19 @@ export default {
|
|||
methods: {},
|
||||
computed: {
|
||||
tooltipMsg() {
|
||||
return (`${this.object.name} running ${this.object.product} on ${this.object.device}`);
|
||||
return `${this.object.name} running ${this.object.product} on ${this.object.device}`;
|
||||
},
|
||||
connection_success() {
|
||||
if (this.object.connectedstatus === 'connected') {
|
||||
return true;
|
||||
}
|
||||
return this.object.connectedstatus === 'connected';
|
||||
},
|
||||
connection_wait() {
|
||||
if (this.object.connectedstatus === 'waiting') {
|
||||
return true;
|
||||
}
|
||||
return this.object.connectedstatus === 'waiting';
|
||||
},
|
||||
connection_failed() {
|
||||
if (this.object.connectedstatus === 'failed') {
|
||||
return true;
|
||||
}
|
||||
return this.object.connectedstatus === 'failed';
|
||||
},
|
||||
connection_fresh() {
|
||||
if (this.object.connectedstatus === 'fresh') {
|
||||
return true;
|
||||
}
|
||||
return this.object.connectedstatus === 'fresh';
|
||||
},
|
||||
isTrunc() {
|
||||
if (this.sidebar) {
|
||||
|
@ -54,7 +55,10 @@ export default {
|
|||
return `${difference.humanize()} ago`;
|
||||
},
|
||||
platform() {
|
||||
return this.platformMap[this.object.platform.toLowerCase()] || this.platformMap[this.object.product.toLowerCase()];
|
||||
return (
|
||||
this.platformMap[this.object.platform.toLowerCase()] ||
|
||||
this.platformMap[this.object.product.toLowerCase()]
|
||||
);
|
||||
},
|
||||
platformClass() {
|
||||
return [`platform-${this.platform}`];
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
<template>
|
||||
<v-layout row wrap justify-center>
|
||||
<v-flex xs12 lg8 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%" :src="logo">
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-stepper style="background: rgba(0,0,0,0.3); color: white !important; border-radius: 20px" v-model="e1" class="ma-4">
|
||||
<v-row justify="center">
|
||||
<v-col lg="8" style="background: rgba(0,0,0,0.1); border-radius: 10px" class="pa-4">
|
||||
<v-row justify="center">
|
||||
<v-col md="8" lg="4">
|
||||
<img style="width:100%" :src="logo" />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-stepper
|
||||
style="background: rgba(0,0,0,0.3); color: white !important; border-radius: 20px"
|
||||
v-model="e1"
|
||||
class="ma-4"
|
||||
>
|
||||
<v-stepper-header dark>
|
||||
<v-stepper-step step="1" dark :complete="true">Select a client</v-stepper-step>
|
||||
<v-divider></v-divider>
|
||||
|
@ -16,102 +20,148 @@
|
|||
</v-stepper-header>
|
||||
</v-stepper>
|
||||
<div v-if="!chosenClient">
|
||||
<v-layout row wrap justify-center mb-2>
|
||||
<v-flex xs12 class="ml-4">
|
||||
<v-row class="ml-4">
|
||||
<v-col class="pb-0">
|
||||
<h2>Choose your Plex player</h2>
|
||||
</v-flex>
|
||||
<v-flex xs12 class="ml-4">
|
||||
Choose a client from the list below. Once you've found the client you would like to use, click the connect button. SyncLounge will test to see if it can connect with the client and will let you know if it cannot.
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class="ml-4">
|
||||
<v-col
|
||||
class="pt-0"
|
||||
>Choose a client from the list below. Once you've found the client you would like to use, click the connect button. SyncLounge will test to see if it can connect with the client and will let you know if it cannot.</v-col>
|
||||
</v-row>
|
||||
<div v-if="plex && !plex.gotDevices" class="text-center pa-4">
|
||||
<v-progress-circular indeterminate color="primary"></v-progress-circular>
|
||||
</div>
|
||||
<v-layout v-else row wrap justify-center class="ml-4 mr-4">
|
||||
<v-flex xs10 md6 lg6 v-if="!doReverse">
|
||||
<v-subheader>Plex Players {{ playercount }}
|
||||
<v-row v-else justify="center" class="ml-4 mr-4">
|
||||
<v-col md="6" lg="6" v-if="!doReverse">
|
||||
<v-subheader>
|
||||
Plex Players {{ playercount }}
|
||||
<v-icon @click="PLEX_GET_DEVICES()" class="pl-2" small>refresh</v-icon>
|
||||
</v-subheader>
|
||||
<v-list dense style="background: none">
|
||||
<plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
|
||||
<plexclient
|
||||
v-for="i in recentClients"
|
||||
:key="i.clientIdentifier"
|
||||
@click.native="previewClient(i); gotResponse = true"
|
||||
:selected="isClientSelected(i)"
|
||||
:object="i"
|
||||
style="cursor: pointer"
|
||||
></plexclient>
|
||||
</v-list>
|
||||
</v-flex>
|
||||
<v-flex xs10 md6 lg6>
|
||||
</v-col>
|
||||
<v-col md="6" lg="6">
|
||||
<div v-if="testClient" class="pa-2">
|
||||
<v-subheader>
|
||||
Selected Player
|
||||
</v-subheader>
|
||||
<v-layout row wrap>
|
||||
<v-flex md3 class="text-center" style="position: relative">
|
||||
<v-subheader>Selected Player</v-subheader>
|
||||
<v-row>
|
||||
<v-col md="3" class="text-center" style="position: relative">
|
||||
<img :src="url" style="height: 100px; width: auto; vertical-align: middle" />
|
||||
</v-flex>
|
||||
<v-flex xs12 md9>
|
||||
<div class="pl-1">
|
||||
</v-col>
|
||||
<v-col md="9">
|
||||
<div class="selected-player-details pl-1">
|
||||
<h3>{{ testClient.name }}</h3>
|
||||
<div>
|
||||
<label>Last seen</label><span style="opacity:0.8"> {{ lastSeenAgo(testClient.lastSeenAt) }}</span>
|
||||
<label>Last seen</label>
|
||||
<span style="opacity:0.8">{{ lastSeenAgo(testClient.lastSeenAt) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Device</label><span style="opacity:0.8"> {{ testClient.device }}</span>
|
||||
<label>Device</label>
|
||||
<span style="opacity:0.8">{{ testClient.device }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Running</label><span style="opacity:0.8"> {{ testClient.product }} </span>
|
||||
<label>Running</label>
|
||||
<span style="opacity:0.8">{{ testClient.product }}</span>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
<label>Platform</label><span style="opacity:0.8"> {{ testClient.platform }} </span>
|
||||
<label>Platform</label>
|
||||
<span style="opacity:0.8">{{ testClient.platform }}</span>
|
||||
</div>
|
||||
<div class="red--text text--lighten-1" v-if="testClientErrorMsg">
|
||||
{{ testClientErrorMsg }}
|
||||
<div
|
||||
class="red--text text--lighten-1"
|
||||
v-if="testClientErrorMsg"
|
||||
>{{ testClientErrorMsg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
<v-layout row wrap class="pt-2">
|
||||
<v-flex xs12>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row class="pt-2">
|
||||
<v-col>
|
||||
<div v-if="!gotResponse" class="center spinner-orange">
|
||||
<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>
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
v-bind:size="50"
|
||||
class="amber--text"
|
||||
style="display:inline-block"
|
||||
></v-progress-circular>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="gotResponse">
|
||||
<v-btn block color="primary" v-on:click.native="clientClicked()">Connect</v-btn>
|
||||
</div>
|
||||
<div v-if="testClient.product.indexOf('Web') > -1" class="warning--text">
|
||||
Note: Plex Web is currently not supported.
|
||||
<div
|
||||
v-if="testClient.product.indexOf('Web') > -1"
|
||||
class="warning--text"
|
||||
>Note: Plex Web is currently not supported.</div>
|
||||
<div
|
||||
v-if="testClient.product.indexOf('Plex for Android') > -1"
|
||||
class="warning--text"
|
||||
>
|
||||
Note: Plex for Android applications may not work properly. See "What clients are supported?" in the
|
||||
<a
|
||||
href="http://docs.synclounge.tv/faq/"
|
||||
>FAQ</a> for more details.
|
||||
</div>
|
||||
<div v-if="testClient.product.indexOf('Plex for Android') > -1" class="warning--text">
|
||||
Note: Plex for Android applications may not work properly. See "What clients are supported?" in the <a href="http://docs.synclounge.tv/faq/">FAQ</a> for more details.
|
||||
<div
|
||||
v-if="testClient.product.indexOf('Plex for Windows') > -1"
|
||||
class="warning--text"
|
||||
>
|
||||
Note: Plex Desktop applications may not work properly. See "What clients are supported?" in the
|
||||
<a
|
||||
href="http://docs.synclounge.tv/faq/"
|
||||
>FAQ</a> for more details.
|
||||
</div>
|
||||
<div v-if="testClient.product.indexOf('Plex for Windows') > -1" class="warning--text">
|
||||
Note: Plex Desktop applications may not work properly. See "What clients are supported?" in the <a href="http://docs.synclounge.tv/faq/">FAQ</a> for more details.
|
||||
</div>
|
||||
<div v-if="isHttps && testClient.clientIdentifier !== 'PTPLAYER9PLUS10'" class="warning--text">
|
||||
<div
|
||||
v-if="isHttps && testClient.clientIdentifier !== 'PTPLAYER9PLUS10'"
|
||||
class="warning--text"
|
||||
>
|
||||
Note: You may not be able to connect to external Plex Clients while loading the page via HTTPS.
|
||||
Click <a :href="nohttpslink">here</a> to load the page via HTTP.
|
||||
See "My client isn't working!" in the <a href="http://docs.synclounge.tv/faq/">FAQ</a> for more details.
|
||||
Click
|
||||
<a
|
||||
:href="nohttpslink"
|
||||
>here</a> to load the page via HTTP.
|
||||
See "My client isn't working!" in the
|
||||
<a
|
||||
href="http://docs.synclounge.tv/faq/"
|
||||
>FAQ</a> for more details.
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-flex>
|
||||
<v-flex xs12 md6 lg7 v-if="doReverse">
|
||||
</v-col>
|
||||
<v-col md="6" lg="7" v-if="doReverse">
|
||||
<v-subheader>Plex Players {{ playercount }}</v-subheader>
|
||||
<v-list dense style="background: none">
|
||||
<plexclient v-for="i in recentClients" :key="i.clientIdentifier" @click.native="previewClient(i); ; gotResponse = true" :selected="isClientSelected(i)" :object="i" style="cursor: pointer"></plexclient>
|
||||
<plexclient
|
||||
v-for="i in recentClients"
|
||||
:key="i.clientIdentifier"
|
||||
@click.native="previewClient(i); ; gotResponse = true"
|
||||
:selected="isClientSelected(i)"
|
||||
:object="i"
|
||||
style="cursor: pointer"
|
||||
></plexclient>
|
||||
</v-list>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import plexclient from './plexclient';
|
||||
import joinroom from './joinroom';
|
||||
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
|
||||
import plexclient from './plexclient.vue';
|
||||
|
||||
const moment = require('moment');
|
||||
|
||||
export default {
|
||||
|
@ -165,7 +215,6 @@ export default {
|
|||
},
|
||||
components: {
|
||||
plexclient,
|
||||
joinroom,
|
||||
},
|
||||
computed: {
|
||||
...mapState(['plex']),
|
||||
|
@ -224,9 +273,7 @@ export default {
|
|||
}
|
||||
let url = `http:${window.location.href.substring(window.location.protocol.length)}`;
|
||||
if (this.$store.state.autoJoin) {
|
||||
url = `${url}?server=${this.$store.state.autoJoinUrl}&room=${
|
||||
this.$store.state.autoJoinRoom
|
||||
}&autojoin=true&owner=${this.$store.state.autoJoinOwner}`;
|
||||
url = `${url}?server=${this.$store.state.autoJoinUrl}&room=${this.$store.state.autoJoinRoom}&autojoin=true&owner=${this.$store.state.autoJoinOwner}`;
|
||||
if (this.$store.state.autoJoinPassword) {
|
||||
url = `${url}&password=${this.$store.state.autoJoinPassword}`;
|
||||
}
|
||||
|
@ -264,7 +311,7 @@ export default {
|
|||
this.$store.commit('SET_CHOSENCLIENT', client);
|
||||
this.gotResponse = true;
|
||||
})
|
||||
.catch((e) => {
|
||||
.catch(e => {
|
||||
if (client.clientIdentifier !== this.testClient.clientIdentifier) {
|
||||
return;
|
||||
}
|
||||
|
@ -272,9 +319,6 @@ export default {
|
|||
this.testClientErrorMsg = 'Unable to connect to client';
|
||||
});
|
||||
},
|
||||
openJoinRoomModal() {
|
||||
return this.$parent.$refs.joinroomModal.open();
|
||||
},
|
||||
isClientSelected(client) {
|
||||
if (client === this.testClient) {
|
||||
return true;
|
||||
|
@ -294,3 +338,8 @@ export default {
|
|||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.selected-player-details label + span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<v-layout wrap row ckass="pt-2 pa-4" justify-center>
|
||||
<v-flex xs12 md8>
|
||||
<v-row class="pt-2 pa-4" justify="center">
|
||||
<v-col md="8">
|
||||
<v-card style="background: rgba(0,0,0,0.3)" class="pa-4">
|
||||
<v-layout row wrap justify-center align-center v-if="ready">
|
||||
<v-flex xs12 sm8 lg4>
|
||||
|
@ -40,16 +40,13 @@
|
|||
</v-layout>
|
||||
</div>
|
||||
<div v-if="preAuth && !checkingAuth && !authError" class="text-center">
|
||||
<v-btn
|
||||
class="primary"
|
||||
@click="openPopup()"
|
||||
>Sign in with Plex</v-btn>
|
||||
<v-btn class="primary" @click="openPopup()">Sign in with Plex</v-btn>
|
||||
</div>
|
||||
<div v-if="authError" class="text-center error">
|
||||
<p>You are not authorized to access this server</p>
|
||||
</div>
|
||||
<v-layout wrap row class="pt-4 pa-2">
|
||||
<v-flex xs12 md8 offset-md2 class="center-text">
|
||||
<v-row justify="center" class="pt-4 pa-2">
|
||||
<v-col md="8" class="center-text">
|
||||
<p style="opacity:0.7">
|
||||
Your Plex account is used to fetch the details of your Plex devices. None of your private details are sent to our servers. If you would like to install and run SyncLounge yourself
|
||||
have a look
|
||||
|
@ -59,32 +56,32 @@
|
|||
>here</a>
|
||||
for details.
|
||||
</p>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-card>
|
||||
</v-flex>
|
||||
</v-layout>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const axios = require('axios');
|
||||
const axios = require("axios");
|
||||
|
||||
export default {
|
||||
name: 'signin',
|
||||
name: "signin",
|
||||
data() {
|
||||
return {
|
||||
pin: null,
|
||||
ID: null,
|
||||
token: null,
|
||||
status: 'startup',
|
||||
status: "startup",
|
||||
headers: {
|
||||
'X-Plex-Device': 'Web',
|
||||
'X-Plex-Device-Name': 'SyncLounge',
|
||||
'X-Plex-Product': 'SyncLounge',
|
||||
'X-Plex-Version': this.$store.state.appVersion,
|
||||
'X-Plex-Platform-Version': '',
|
||||
'X-Plex-Client-Identifier': this.$store.state.settings.CLIENTIDENTIFIER,
|
||||
"X-Plex-Device": "Web",
|
||||
"X-Plex-Device-Name": "SyncLounge",
|
||||
"X-Plex-Product": "SyncLounge",
|
||||
"X-Plex-Version": this.$store.state.appVersion,
|
||||
"X-Plex-Platform-Version": "",
|
||||
"X-Plex-Client-Identifier": this.$store.state.settings.CLIENTIDENTIFIER
|
||||
},
|
||||
code: null,
|
||||
preAuth: false,
|
||||
|
@ -93,16 +90,16 @@ export default {
|
|||
openedWindow: null,
|
||||
authError: null,
|
||||
interval: 2000,
|
||||
id: '',
|
||||
id: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async openPopup() {
|
||||
this.openedWindow = window.open(this.url, '_blank');
|
||||
this.openedWindow = window.open(this.url, "_blank");
|
||||
this.ticker = setInterval(async () => {
|
||||
console.log('ticker');
|
||||
console.log("ticker");
|
||||
const result = await axios(`https://plex.tv/api/v2/pins/${this.id}`, {
|
||||
headers: { ...this.headers },
|
||||
headers: { ...this.headers }
|
||||
});
|
||||
if (result && result.data && result.data.authToken) {
|
||||
if (this.openedWindow) {
|
||||
|
@ -113,31 +110,31 @@ export default {
|
|||
await this.setAuth(result.data.authToken);
|
||||
this.letsGo();
|
||||
} else {
|
||||
this.authError = 'You are not authorized to access this server.';
|
||||
this.authError = "You are not authorized to access this server.";
|
||||
}
|
||||
clearInterval(this.ticker);
|
||||
}
|
||||
}, this.interval);
|
||||
},
|
||||
async setAuth(authToken) {
|
||||
window.localStorage.setItem('plexuser', JSON.stringify({ authToken }));
|
||||
await this.$store.dispatch('PLEX_LOGIN_TOKEN', authToken);
|
||||
window.localStorage.setItem("plexuser", JSON.stringify({ authToken }));
|
||||
await this.$store.dispatch("PLEX_LOGIN_TOKEN", authToken);
|
||||
this.token = authToken;
|
||||
this.ready = true;
|
||||
},
|
||||
async letsGo() {
|
||||
if (this.$store.state.autoJoin) {
|
||||
this.$store.dispatch('autoJoin', {
|
||||
this.$store.dispatch("autoJoin", {
|
||||
server: this.$store.state.autoJoinUrl,
|
||||
password: this.$store.state.autoJoinPassword,
|
||||
room: this.$store.state.autoJoinRoom,
|
||||
room: this.$store.state.autoJoinRoom
|
||||
});
|
||||
}
|
||||
this.$router.push('/browse');
|
||||
this.$router.push("/browse");
|
||||
},
|
||||
async checkAuth(authToken) {
|
||||
this.checkingAuth = true;
|
||||
await this.$store.dispatch('PLEX_LOGIN_TOKEN', authToken);
|
||||
await this.$store.dispatch("PLEX_LOGIN_TOKEN", authToken);
|
||||
// Get stored authentication settings
|
||||
const authentication = { ...this.$store.state.authentication };
|
||||
// Authentication defaults to false
|
||||
|
@ -145,28 +142,33 @@ export default {
|
|||
|
||||
if (authentication) {
|
||||
// Authenication via Plex mechanism
|
||||
if (authentication.mechanism === 'plex') {
|
||||
if (authentication.mechanism === "plex") {
|
||||
// Server authorization using server data
|
||||
if (authentication.type.includes('server')) {
|
||||
if (authentication.type.includes("server")) {
|
||||
try {
|
||||
// Retrieve and store the user's servers
|
||||
await this.$store.dispatch('PLEX_GET_DEVICES', true);
|
||||
await this.$store.dispatch("PLEX_GET_DEVICES", true);
|
||||
// Get the user's servers
|
||||
const servers = { ...this.$store.state.plex.servers };
|
||||
|
||||
// Compare servers against the authorized list
|
||||
for (const id in servers) {
|
||||
const server = servers[id];
|
||||
if (authentication.authorized.includes(server.clientIdentifier)) {
|
||||
if (
|
||||
authentication.authorized.includes(server.clientIdentifier)
|
||||
) {
|
||||
authenticationPassed = true;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('An error occurred when authenticating with Plex: ', e);
|
||||
console.error(
|
||||
"An error occurred when authenticating with Plex: ",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
// Authorization using user data
|
||||
if (authentication.type.includes('user')) {
|
||||
if (authentication.type.includes("user")) {
|
||||
// Get the user object
|
||||
const user = this.$store.state.plex.user;
|
||||
// Compare the user's email against the authorized list
|
||||
|
@ -183,20 +185,18 @@ export default {
|
|||
// else if (authentication.mechanism == 'new_mech' ) {
|
||||
// }
|
||||
// Authenication via an unsupported mechanism
|
||||
else if (authentication.mechanism != 'none') {
|
||||
else if (authentication.mechanism != "none") {
|
||||
console.error(
|
||||
`Invalid authentication mechanism provided: '${
|
||||
authentication.mechanism
|
||||
}'. Reverting to default.`,
|
||||
`Invalid authentication mechanism provided: '${authentication.mechanism}'. Reverting to default.`
|
||||
);
|
||||
this.$store.state.authentication = {
|
||||
mechanism: 'none',
|
||||
mechanism: "none"
|
||||
};
|
||||
authenticationPassed = true;
|
||||
}
|
||||
// Authenication mechanism isn't set. This should only happen when authentication mechanism is set to 'none'.
|
||||
else {
|
||||
console.log('No authentication set');
|
||||
console.log("No authentication set");
|
||||
authenticationPassed = true;
|
||||
}
|
||||
this.checkingAuth = false;
|
||||
|
@ -204,7 +204,7 @@ export default {
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
store() {
|
||||
|
@ -215,43 +215,41 @@ export default {
|
|||
return this.$store.getters.getSettings.HIDEUSERNAME;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setSetting', ['HIDEUSERNAME', value]);
|
||||
},
|
||||
this.$store.commit("setSetting", ["HIDEUSERNAME", value]);
|
||||
}
|
||||
},
|
||||
ALTUSERNAME: {
|
||||
get() {
|
||||
return this.$store.getters.getSettings.ALTUSERNAME;
|
||||
},
|
||||
set(value) {
|
||||
this.$store.commit('setSetting', ['ALTUSERNAME', value]);
|
||||
},
|
||||
this.$store.commit("setSetting", ["ALTUSERNAME", value]);
|
||||
}
|
||||
},
|
||||
sBrowser() {
|
||||
let sBrowser;
|
||||
const sUsrAg = navigator.userAgent;
|
||||
|
||||
if (sUsrAg.indexOf('Chrome') > -1) {
|
||||
sBrowser = 'Google Chrome';
|
||||
} else if (sUsrAg.indexOf('Safari') > -1) {
|
||||
sBrowser = 'Apple Safari';
|
||||
if (sUsrAg.indexOf("Chrome") > -1) {
|
||||
sBrowser = "Google Chrome";
|
||||
} else if (sUsrAg.indexOf("Safari") > -1) {
|
||||
sBrowser = "Apple Safari";
|
||||
// this.openPopup();
|
||||
} else if (sUsrAg.indexOf('Opera') > -1) {
|
||||
sBrowser = 'Opera';
|
||||
} else if (sUsrAg.indexOf('Firefox') > -1) {
|
||||
sBrowser = 'Mozilla Firefox';
|
||||
} else if (sUsrAg.indexOf('MSIE') > -1) {
|
||||
sBrowser = 'Microsoft Internet Explorer';
|
||||
} else if (sUsrAg.indexOf("Opera") > -1) {
|
||||
sBrowser = "Opera";
|
||||
} else if (sUsrAg.indexOf("Firefox") > -1) {
|
||||
sBrowser = "Mozilla Firefox";
|
||||
} else if (sUsrAg.indexOf("MSIE") > -1) {
|
||||
sBrowser = "Microsoft Internet Explorer";
|
||||
}
|
||||
return sBrowser;
|
||||
},
|
||||
url() {
|
||||
if (this.code) {
|
||||
return `https://app.plex.tv/auth/#!?clientID=${
|
||||
this.headers['X-Plex-Client-Identifier']
|
||||
}&code=${this.code}`;
|
||||
return `https://app.plex.tv/auth/#!?clientID=${this.headers["X-Plex-Client-Identifier"]}&code=${this.code}`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
return '';
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.ticker);
|
||||
|
@ -259,24 +257,23 @@ export default {
|
|||
async mounted() {
|
||||
let authToken = null;
|
||||
// Check for PlexToken set via SyncLounge or Plex
|
||||
if (window.localStorage.getItem('myPlexAccessToken')) {
|
||||
authToken = window.localStorage.getItem('myPlexAccessToken');
|
||||
}
|
||||
else if($cookies.get('mpt')) {
|
||||
authToken = $cookies.get('mpt');
|
||||
if (window.localStorage.getItem("myPlexAccessToken")) {
|
||||
authToken = window.localStorage.getItem("myPlexAccessToken");
|
||||
} else if ($cookies.get("mpt")) {
|
||||
authToken = $cookies.get("mpt");
|
||||
}
|
||||
|
||||
if (authToken) {
|
||||
this.ticker = setInterval(async () => {
|
||||
try {
|
||||
console.log('--- Check Auth mounted ---')
|
||||
console.log("--- Check Auth mounted ---");
|
||||
const authenticated = await this.checkAuth(authToken);
|
||||
if (authenticated != null) {
|
||||
if (authenticated == true) {
|
||||
await this.setAuth(authToken);
|
||||
this.letsGo();
|
||||
} else {
|
||||
this.authError = 'You are not authorized to access this server.';
|
||||
this.authError = "You are not authorized to access this server.";
|
||||
}
|
||||
this.preAuth = true;
|
||||
clearInterval(this.ticker);
|
||||
|
@ -286,12 +283,16 @@ export default {
|
|||
} else {
|
||||
const { data } = await axios
|
||||
.create()
|
||||
.post('https://plex.tv/api/v2/pins?strong=true', {}, { headers: { ...this.headers } });
|
||||
.post(
|
||||
"https://plex.tv/api/v2/pins?strong=true",
|
||||
{},
|
||||
{ headers: { ...this.headers } }
|
||||
);
|
||||
this.code = data.code;
|
||||
this.id = data.id;
|
||||
this.headers['X-Plex-Platform'] = this.sBrowser;
|
||||
this.headers["X-Plex-Platform"] = this.sBrowser;
|
||||
this.preAuth = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<v-navigation-drawer app temporary v-model="drawer" disable-route-watcher>
|
||||
<v-navigation-drawer
|
||||
app
|
||||
temporary
|
||||
:value="isLeftSidebarOpen"
|
||||
@input="TOGGLE_LEFT_SIDEBAR_OPEN"
|
||||
disable-route-watcher
|
||||
>
|
||||
<v-list-item v-if="plex && plex.user">
|
||||
<v-list-item-avatar>
|
||||
<img class="pa-1" :src="plex.user.thumb" />
|
||||
|
@ -116,29 +122,29 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ptsettings from "./components/application/settings";
|
||||
import plexsettings from "./components/application/plexsettings";
|
||||
import donate from "./donate";
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
|
||||
const moment = require("moment");
|
||||
import ptsettings from './components/application/settings.vue';
|
||||
import plexsettings from './components/application/plexsettings.vue';
|
||||
import donate from './donate.vue';
|
||||
|
||||
const moment = require('moment');
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ptsettings,
|
||||
plexsettings,
|
||||
donate
|
||||
},
|
||||
props: {
|
||||
drawer: Boolean
|
||||
donate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ptsettingstoggle: false,
|
||||
plexsettingstoggle: false,
|
||||
donateDialog: false
|
||||
donateDialog: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['isLeftSidebarOpen']),
|
||||
plex() {
|
||||
return this.$store.getters.getPlex;
|
||||
},
|
||||
|
@ -170,17 +176,10 @@ export default {
|
|||
return this.plex.gotDevices;
|
||||
},
|
||||
showBrowser() {
|
||||
return (
|
||||
this.chosenClient &&
|
||||
!this.chosenClient.clientPlayingMetadata &&
|
||||
this.ptRoom
|
||||
);
|
||||
return this.chosenClient && !this.chosenClient.clientPlayingMetadata && this.ptRoom;
|
||||
},
|
||||
isPTPlayer() {
|
||||
return (
|
||||
this.chosenClient &&
|
||||
this.chosenClient.clientIdentifier === "PTPLAYER9PLUS10"
|
||||
);
|
||||
return this.chosenClient && this.chosenClient.clientIdentifier === 'PTPLAYER9PLUS10';
|
||||
},
|
||||
showMetadata() {
|
||||
return (
|
||||
|
@ -222,27 +221,28 @@ export default {
|
|||
if (this.$store.state.plex && this.$store.state.plex.gotDevices) {
|
||||
return `(${this.$store.state.plex.clients.length})`;
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
},
|
||||
servercount() {
|
||||
if (this.$store.state.plex && this.$store.state.plex.gotDevices) {
|
||||
return `(${this.$store.state.plex.servers.length})`;
|
||||
}
|
||||
return "";
|
||||
return '';
|
||||
},
|
||||
showChatValue() {
|
||||
if (this.$store.getters.getShownChat) {
|
||||
return "block";
|
||||
return 'block';
|
||||
}
|
||||
return "none";
|
||||
return 'none';
|
||||
},
|
||||
messages() {
|
||||
return this.$store.getters.getMessages;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['TOGGLE_LEFT_SIDEBAR_OPEN']),
|
||||
isHost(user) {
|
||||
return user.role === "host";
|
||||
return user.role === 'host';
|
||||
},
|
||||
percent(user) {
|
||||
let perc = (parseInt(user.time) / parseInt(user.maxTime)) * 100;
|
||||
|
@ -267,22 +267,22 @@ export default {
|
|||
if (user.title && user.title.length > 0) {
|
||||
return user.title;
|
||||
}
|
||||
return "Nothing";
|
||||
return 'Nothing';
|
||||
},
|
||||
sendMessage() {
|
||||
this.$store.dispatch("sendNewMessage", this.messageToBeSent);
|
||||
this.messageToBeSent = "";
|
||||
this.$store.dispatch('sendNewMessage', this.messageToBeSent);
|
||||
this.messageToBeSent = '';
|
||||
},
|
||||
playerState(user) {
|
||||
if (user.playerState) {
|
||||
if (user.playerState === "stopped") {
|
||||
return "pause";
|
||||
if (user.playerState === 'stopped') {
|
||||
return 'pause';
|
||||
}
|
||||
if (user.playerState === "paused") {
|
||||
return "pause";
|
||||
if (user.playerState === 'paused') {
|
||||
return 'pause';
|
||||
}
|
||||
if (user.playerState === "playing") {
|
||||
return "play_arrow";
|
||||
if (user.playerState === 'playing') {
|
||||
return 'play_arrow';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -298,8 +298,8 @@ export default {
|
|||
const absoluteSeconds = Math.floor(seconds);
|
||||
const s = absoluteSeconds > 9 ? absoluteSeconds : `0${absoluteSeconds}`;
|
||||
return `${h}:${m}:${s}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
23
src/main.js
23
src/main.js
|
@ -56,6 +56,7 @@ window.EventBus.$on('command', (data) => {
|
|||
});
|
||||
return data.callback(true);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
Vue.mixin({
|
||||
|
@ -63,7 +64,7 @@ Vue.mixin({
|
|||
sinceNow(x) {
|
||||
const time = moment(x);
|
||||
return time.fromNow();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
appVersion() {
|
||||
|
@ -101,20 +102,20 @@ Vue.mixin({
|
|||
},
|
||||
fontSizes() {
|
||||
const w = Math.round(
|
||||
Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
|
||||
Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
|
||||
);
|
||||
const maxPx = 94;
|
||||
const maxRes = 3000;
|
||||
return {
|
||||
largest: {
|
||||
'font-size': `${(w / maxRes) * maxPx}px`
|
||||
'font-size': `${(w / maxRes) * maxPx}px`,
|
||||
},
|
||||
medium: {
|
||||
'font-size': `${(w / maxRes) * maxPx * 0.6}px`
|
||||
}
|
||||
'font-size': `${(w / maxRes) * maxPx * 0.6}px`,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
|
@ -124,17 +125,17 @@ router.beforeEach((to, from, next) => {
|
|||
// if not, redirect to the needed stage
|
||||
if (!store.getters.getChosenClient) {
|
||||
return next({
|
||||
path: '/clientselect'
|
||||
path: '/clientselect',
|
||||
});
|
||||
}
|
||||
if (!store.getters.getRoom) {
|
||||
return next({
|
||||
path: '/joinroom'
|
||||
path: '/joinroom',
|
||||
});
|
||||
}
|
||||
if (!store.getters.getServer) {
|
||||
return next({
|
||||
path: '/joinroom'
|
||||
path: '/joinroom',
|
||||
});
|
||||
}
|
||||
next();
|
||||
|
@ -144,6 +145,8 @@ router.beforeEach((to, from, next) => {
|
|||
}
|
||||
router.push('/browse');
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
new Vue({
|
||||
|
|
360
src/store.js
360
src/store.js
|
@ -1,360 +0,0 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { get, set } from '@/utils/storage';
|
||||
import { generateGuid } from '@/utils/helpers';
|
||||
import { getAll } from '@/utils/settings';
|
||||
import config from './store/modules/config/config.store';
|
||||
|
||||
const plex = require('./store/modules/plex/').default;
|
||||
const syncLounge = require('./store/modules/synclounge.js').default;
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
function sendNotification(message) {
|
||||
return window.EventBus.$emit('notification', message);
|
||||
}
|
||||
|
||||
console.log('Got settings', getAll());
|
||||
// Set up out web app socket for fetching short urls
|
||||
|
||||
const state = {
|
||||
appTitle: 'SyncLounge',
|
||||
appVersion: '2.0.0',
|
||||
background: null,
|
||||
shownChat: false,
|
||||
chosenClient: null,
|
||||
chosenClientTimeSet: new Date().getTime(),
|
||||
plexuser: JSON.parse(window.localStorage.getItem('plexuser')),
|
||||
blockAutoPlay: false,
|
||||
autoJoin: false,
|
||||
autoJoinUrl: null,
|
||||
autoJoinRoom: null,
|
||||
autoJoinPassword: null,
|
||||
autoJoinUsername: null,
|
||||
shortLink: null,
|
||||
extAvailable: false,
|
||||
lastRatingKey: null,
|
||||
manualSyncQueued: false,
|
||||
uuid: generateGuid(),
|
||||
upNextCache: {},
|
||||
|
||||
// SETTINGS
|
||||
settings: getAll(),
|
||||
stats: {},
|
||||
me: {},
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
SET_CHOSENCLIENT(state, client) {
|
||||
// Set up our client poller
|
||||
let commandInProgress = false;
|
||||
function clientPoller(time) {
|
||||
if (!state.chosenClient) {
|
||||
return;
|
||||
}
|
||||
if (state.chosenClientTimeSet !== time) {
|
||||
// We have a new chosen client, we need to stop
|
||||
return;
|
||||
}
|
||||
if (state.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10') {
|
||||
if (!commandInProgress) {
|
||||
state.chosenClient
|
||||
.getTimeline()
|
||||
.then(() => {
|
||||
commandInProgress = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
commandInProgress = false;
|
||||
});
|
||||
commandInProgress = true;
|
||||
}
|
||||
} else {
|
||||
state.chosenClient.getTimeline();
|
||||
}
|
||||
let interval = state.settings.CLIENTPOLLINTERVAL;
|
||||
if (state.chosenClient.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
interval = 500;
|
||||
}
|
||||
setTimeout(() => {
|
||||
clientPoller(time);
|
||||
}, interval);
|
||||
}
|
||||
|
||||
// Check if we need to remove old handlers
|
||||
if (state.chosenClient) {
|
||||
state.chosenClient.events.removeAllListeners();
|
||||
}
|
||||
state.chosenClient = client;
|
||||
if (state.chosenClient && state.chosenClient.lastTimelineObject) {
|
||||
state.chosenClient.lastTimelineObject.ratingKey = -1;
|
||||
}
|
||||
if (state.chosenClient == null) {
|
||||
return;
|
||||
}
|
||||
state.chosenClientTimeSet = new Date().getTime();
|
||||
clientPoller(state.chosenClientTimeSet);
|
||||
state.chosenClient.getTimeline((timeline) => { });
|
||||
},
|
||||
SET_PLEX(state, value) {
|
||||
state.plex = value;
|
||||
},
|
||||
SET_AUTHENTICATION(state, value) {
|
||||
state.authentication = value;
|
||||
},
|
||||
SET_AUTOJOIN(state, value) {
|
||||
state.autoJoin = value;
|
||||
},
|
||||
SET_BACKGROUND(state, value) {
|
||||
state.background = value;
|
||||
},
|
||||
SET_AUTOJOINROOM(state, value) {
|
||||
state.autoJoinRoom = value;
|
||||
},
|
||||
SET_AUTOJOINPASSWORD(state, value) {
|
||||
state.autoJoinPassword = value;
|
||||
},
|
||||
SET_AUTOJOINURL(state, value) {
|
||||
state.autoJoinUrl = value;
|
||||
},
|
||||
SET_SHORTLINK(state, value) {
|
||||
state.shortLink = value;
|
||||
},
|
||||
setSetting(state, data) {
|
||||
Vue.set(state.settings, data[0], data[1]);
|
||||
set(data[0], data[1]);
|
||||
},
|
||||
setSettingPTPLAYERQUALITY(state, data) {
|
||||
window.localStorage.setItem('PTPLAYERQUALITY', JSON.stringify(data));
|
||||
state.PTPLAYERQUALITY = data;
|
||||
},
|
||||
setSettingPTPLAYERVOLUME(state, data) {
|
||||
window.localStorage.setItem('PTPLAYERVOLUME', JSON.stringify(data));
|
||||
state.PTPLAYERVOLUME = data;
|
||||
},
|
||||
setSettingHOMEINIT(state, data) {
|
||||
set('HOMEINIT', data);
|
||||
state.HOMEINIT = data;
|
||||
},
|
||||
REFRESH_PLEXDEVICES() {
|
||||
store.state.plex.getDevices(() => { });
|
||||
},
|
||||
SET_RANDOMBACKROUND(state) {
|
||||
state.plex.getRandomThumb((result) => {
|
||||
if (result) {
|
||||
state.background = result;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
SET_VALUE(state, data) {
|
||||
const [key, value] = data;
|
||||
Vue.set(state, key, value);
|
||||
},
|
||||
};
|
||||
const getters = {
|
||||
getAppVersion: state => state.appVersion,
|
||||
getPlex: state => state.plex,
|
||||
getPlexUser: state => state.plexuser,
|
||||
getBackground: state => state.background,
|
||||
getChosenClient: state => state.chosenClient,
|
||||
getShownChat: state => state.shownChat,
|
||||
getStats: state => state.stats,
|
||||
getBlockAutoPlay: state => state.blockAutoPlay,
|
||||
getAutoJoin: state => state.autoJoin,
|
||||
getAutoJoinRoom: state => state.autoJoinRoom,
|
||||
getAutoJoinPassword: state => state.autoJoinPassword,
|
||||
getAutoJoinUrl: state => state.autoJoinUrl,
|
||||
getShortLink: state => state.shortLink,
|
||||
|
||||
// SETTINGS
|
||||
getSettings: state => state.settings,
|
||||
getSettingHOMEINIT: state => state.HOMEINIT,
|
||||
getSettingPTPLAYERQUALITY: state => state.PTPLAYERQUALITY,
|
||||
getSettingPTPLAYERVOLUME: state => state.PTPLAYERVOLUME,
|
||||
getExtAvailable: state => state.extAvailable,
|
||||
getLogos: () => ({
|
||||
light: {
|
||||
long: 'logo-long-light.png',
|
||||
small: 'logo-small-light.png',
|
||||
},
|
||||
dark: {
|
||||
long: 'logo-long-dark.png',
|
||||
},
|
||||
plex: {
|
||||
standard: 'plexlogo.png',
|
||||
},
|
||||
}),
|
||||
};
|
||||
const actions = {
|
||||
async PLAYBACK_CHANGE({ commit, state, dispatch }, data) {
|
||||
const [client, ratingKey, mediaContainer] = data;
|
||||
if (ratingKey) {
|
||||
// Playing something different!
|
||||
const server = state.plex.servers[mediaContainer.machineIdentifier];
|
||||
commit('setSetting', ['LASTSERVER', mediaContainer.machineIdentifier]);
|
||||
// state.settings.LASTSERVER = mediaContainer.machineIdentifier;
|
||||
// window.localStorage.setItem('LASTSERVER', mediaContainer.machineIdentifier);
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
// Fetch our metadata from this server
|
||||
// console.log('Loading content metadata from store ' + ratingKey)
|
||||
server.getMediaByRatingKey(ratingKey.replace('/library/metadata/', '')).then((data) => {
|
||||
const metadata = data.MediaContainer.Metadata[0];
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
if (metadata.type === 'movie') {
|
||||
sendNotification(`Now Playing: ${metadata.title} from ${
|
||||
state.plex.servers[metadata.machineIdentifier].name
|
||||
}`);
|
||||
}
|
||||
if (metadata.type === 'episode') {
|
||||
sendNotification(`Now Playing: ${metadata.grandparentTitle} S${metadata.parentIndex}E${
|
||||
metadata.index
|
||||
} from ${state.plex.servers[metadata.machineIdentifier].name}`);
|
||||
}
|
||||
state.chosenClient.clientPlayingMetadata = metadata;
|
||||
const w = Math.round(Math.max(document.documentElement.clientWidth, window.innerWidth || 0));
|
||||
const h = Math.round(Math.max(document.documentElement.clientHeight, window.innerHeight || 0));
|
||||
state.background = state.plex.servers[metadata.machineIdentifier].getUrlForLibraryLoc(
|
||||
metadata.thumb,
|
||||
w / 4,
|
||||
h / 4,
|
||||
4,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
state.chosenClient.clientPlayingMetadata = null;
|
||||
const thumb = await state.plex.getRandomThumb(state.plex);
|
||||
if (thumb) {
|
||||
state.background = thumb;
|
||||
}
|
||||
}
|
||||
},
|
||||
NEW_TIMELINE({ commit, state, dispatch }, data) {
|
||||
// return true
|
||||
const timeline = data;
|
||||
const client = state.chosenClient;
|
||||
const metadata = state.chosenClient.clientPlayingMetadata || {};
|
||||
// console.log(state)
|
||||
if (!state.chosenClient || client.clientIdentifier !== state.chosenClient.clientIdentifier) {
|
||||
console.log('Invalid client');
|
||||
return false;
|
||||
}
|
||||
if (state.lastRatingKey !== timeline.ratingKey) {
|
||||
commit('SET_VALUE', ['lastRatingKey', timeline.ratingKey]);
|
||||
dispatch('PLAYBACK_CHANGE', [client, timeline.ratingKey, timeline]);
|
||||
}
|
||||
|
||||
// Check if we need to activate the upnext feature
|
||||
if (state.me && state.me.role === 'host') {
|
||||
if (
|
||||
timeline.duration &&
|
||||
timeline.time &&
|
||||
Math.abs(timeline.duration - timeline.time) < 10000 &&
|
||||
metadata.type === 'episode'
|
||||
) {
|
||||
console.log('Checking upnext');
|
||||
if (!state.upNextCache[timeline.machineIdentifier]) {
|
||||
state.upNextCache[timeline.machineIdentifier] = {};
|
||||
}
|
||||
if (!state.upNextCache[timeline.machineIdentifier][timeline.key]) {
|
||||
state.upNextCache[timeline.machineIdentifier][timeline.key] = {
|
||||
loading: true,
|
||||
};
|
||||
state.plex.servers[timeline.machineIdentifier].getPostplay(timeline.key).then((data) => {
|
||||
data.machineIdentifier = state.chosenClient.lastTimelineObject.machineIdentifier;
|
||||
state.upNextCache[timeline.machineIdentifier][timeline.key] = data;
|
||||
// Only proc upnext if the item upnext is from the same show
|
||||
if (
|
||||
data.MediaContainer.Hub[0].Metadata[0].grandparentTitle === metadata.grandparentTitle
|
||||
) {
|
||||
window.EventBus.$emit('upnext', data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('Already procced an upnext for this item', timeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// state.ourClientResponseTime = timeline.lastResponseTime
|
||||
let title = null;
|
||||
let rawTitle = null;
|
||||
let type = null;
|
||||
let showName = null;
|
||||
if (state.chosenClient.clientPlayingMetadata) {
|
||||
rawTitle = metadata.title;
|
||||
if (metadata.type === 'episode') {
|
||||
title =
|
||||
`${metadata.grandparentTitle} - ${metadata.title} S${metadata.parentIndex}-` +
|
||||
`E${metadata.index}`;
|
||||
showName = metadata.grandparentTitle;
|
||||
} else {
|
||||
title = metadata.title;
|
||||
}
|
||||
type = metadata.type;
|
||||
}
|
||||
let status = 'good';
|
||||
if (!state.synclounge.lastHostTimeline || isNaN(state.synclounge.lastHostTimeline.time)) {
|
||||
status = 'error';
|
||||
} else {
|
||||
const hostAge = Math.abs(new Date().getTime() - state.synclounge.lastHostTimeline.recievedAt);
|
||||
let hostTime = 0 + state.synclounge.lastHostTimeline.time;
|
||||
if (state.synclounge.lastHostTimeline.playerState === 'playing') {
|
||||
hostTime = parseInt(hostTime) + parseInt(hostAge);
|
||||
}
|
||||
const difference = Math.abs(data.time - hostTime);
|
||||
if (difference > state.settings.SYNCFLEXABILITY) {
|
||||
status = 'notok';
|
||||
}
|
||||
}
|
||||
|
||||
const endObj = {
|
||||
time: parseInt(timeline.time),
|
||||
maxTime: parseInt(timeline.duration),
|
||||
title,
|
||||
rawTitle,
|
||||
playerState: timeline.state,
|
||||
clientResponseTime: state.chosenClient.lastResponseTime,
|
||||
playerProduct: state.chosenClient.product,
|
||||
status,
|
||||
type,
|
||||
showName,
|
||||
uuid: state.uuid,
|
||||
};
|
||||
if (state.chosenClient && state.chosenClient.lastTimelineObject) {
|
||||
endObj.machineIdentifier = state.chosenClient.lastTimelineObject.machineIdentifier;
|
||||
endObj.key = state.chosenClient.lastTimelineObject.key;
|
||||
}
|
||||
if (state.synclounge._socket) {
|
||||
const commandId = Object.keys(state.synclounge.commands).length + 1;
|
||||
state.synclounge.commands[commandId] = {
|
||||
start: new Date().getTime(),
|
||||
};
|
||||
endObj.commandId = commandId;
|
||||
if (Object.keys(state.synclounge.commands).length > 1) {
|
||||
const latency =
|
||||
state.synclounge.commands[Object.keys(state.synclounge.commands).length - 1].difference;
|
||||
endObj.latency = latency;
|
||||
}
|
||||
state.synclounge._socket.emit('poll', endObj);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
modules: {
|
||||
synclounge: syncLounge,
|
||||
plex,
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
export default store;
|
|
@ -1,15 +1,365 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import { get, set } from '@/utils/storage';
|
||||
import { generateGuid } from '@/utils/helpers';
|
||||
import { getAll } from '@/utils/settings';
|
||||
import config from './modules/config/config.store';
|
||||
|
||||
const plex = require('./modules/plex/').default;
|
||||
const syncLounge = require('./modules/synclounge.js').default;
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
function sendNotification(message) {
|
||||
return window.EventBus.$emit('notification', message);
|
||||
}
|
||||
|
||||
console.log('Got settings', getAll());
|
||||
// Set up out web app socket for fetching short urls
|
||||
|
||||
const state = {
|
||||
appTitle: 'SyncLounge',
|
||||
appVersion: '2.0.0',
|
||||
background: null,
|
||||
shownChat: false,
|
||||
chosenClient: null,
|
||||
chosenClientTimeSet: new Date().getTime(),
|
||||
plexuser: JSON.parse(window.localStorage.getItem('plexuser')),
|
||||
blockAutoPlay: false,
|
||||
autoJoin: false,
|
||||
autoJoinUrl: null,
|
||||
autoJoinRoom: null,
|
||||
autoJoinPassword: null,
|
||||
autoJoinUsername: null,
|
||||
shortLink: null,
|
||||
extAvailable: false,
|
||||
lastRatingKey: null,
|
||||
manualSyncQueued: false,
|
||||
uuid: generateGuid(),
|
||||
upNextCache: {},
|
||||
|
||||
// SETTINGS
|
||||
settings: getAll(),
|
||||
stats: {},
|
||||
me: {},
|
||||
isLeftSidebarOpen: false,
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
SET_CHOSENCLIENT(state, client) {
|
||||
// Set up our client poller
|
||||
let commandInProgress = false;
|
||||
function clientPoller(time) {
|
||||
if (!state.chosenClient) {
|
||||
return;
|
||||
}
|
||||
if (state.chosenClientTimeSet !== time) {
|
||||
// We have a new chosen client, we need to stop
|
||||
return;
|
||||
}
|
||||
if (state.chosenClient.clientIdentifier !== 'PTPLAYER9PLUS10') {
|
||||
if (!commandInProgress) {
|
||||
state.chosenClient
|
||||
.getTimeline()
|
||||
.then(() => {
|
||||
commandInProgress = false;
|
||||
})
|
||||
.catch((e) => {
|
||||
commandInProgress = false;
|
||||
});
|
||||
commandInProgress = true;
|
||||
}
|
||||
} else {
|
||||
state.chosenClient.getTimeline();
|
||||
}
|
||||
let interval = state.settings.CLIENTPOLLINTERVAL;
|
||||
if (state.chosenClient.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
interval = 500;
|
||||
}
|
||||
setTimeout(() => {
|
||||
clientPoller(time);
|
||||
}, interval);
|
||||
}
|
||||
|
||||
// Check if we need to remove old handlers
|
||||
if (state.chosenClient) {
|
||||
state.chosenClient.events.removeAllListeners();
|
||||
}
|
||||
state.chosenClient = client;
|
||||
if (state.chosenClient && state.chosenClient.lastTimelineObject) {
|
||||
state.chosenClient.lastTimelineObject.ratingKey = -1;
|
||||
}
|
||||
if (state.chosenClient == null) {
|
||||
return;
|
||||
}
|
||||
state.chosenClientTimeSet = new Date().getTime();
|
||||
clientPoller(state.chosenClientTimeSet);
|
||||
state.chosenClient.getTimeline((timeline) => { });
|
||||
},
|
||||
mutations: {
|
||||
SET_PLEX(state, value) {
|
||||
state.plex = value;
|
||||
},
|
||||
actions: {
|
||||
SET_AUTHENTICATION(state, value) {
|
||||
state.authentication = value;
|
||||
},
|
||||
SET_AUTOJOIN(state, value) {
|
||||
state.autoJoin = value;
|
||||
},
|
||||
SET_BACKGROUND(state, value) {
|
||||
state.background = value;
|
||||
},
|
||||
SET_AUTOJOINROOM(state, value) {
|
||||
state.autoJoinRoom = value;
|
||||
},
|
||||
SET_AUTOJOINPASSWORD(state, value) {
|
||||
state.autoJoinPassword = value;
|
||||
},
|
||||
SET_AUTOJOINURL(state, value) {
|
||||
state.autoJoinUrl = value;
|
||||
},
|
||||
SET_SHORTLINK(state, value) {
|
||||
state.shortLink = value;
|
||||
},
|
||||
setSetting(state, data) {
|
||||
Vue.set(state.settings, data[0], data[1]);
|
||||
set(data[0], data[1]);
|
||||
},
|
||||
setSettingPTPLAYERQUALITY(state, data) {
|
||||
window.localStorage.setItem('PTPLAYERQUALITY', JSON.stringify(data));
|
||||
state.PTPLAYERQUALITY = data;
|
||||
},
|
||||
setSettingPTPLAYERVOLUME(state, data) {
|
||||
window.localStorage.setItem('PTPLAYERVOLUME', JSON.stringify(data));
|
||||
state.PTPLAYERVOLUME = data;
|
||||
},
|
||||
setSettingHOMEINIT(state, data) {
|
||||
set('HOMEINIT', data);
|
||||
state.HOMEINIT = data;
|
||||
},
|
||||
REFRESH_PLEXDEVICES() {
|
||||
store.state.plex.getDevices(() => { });
|
||||
},
|
||||
SET_RANDOMBACKROUND(state) {
|
||||
state.plex.getRandomThumb((result) => {
|
||||
if (result) {
|
||||
state.background = result;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
SET_VALUE(state, data) {
|
||||
const [key, value] = data;
|
||||
Vue.set(state, key, value);
|
||||
},
|
||||
TOGGLE_LEFT_SIDEBAR_OPEN: (state, open) => { state.isLeftSidebarOpen = open; },
|
||||
};
|
||||
const getters = {
|
||||
getAppVersion: state => state.appVersion,
|
||||
getPlex: state => state.plex,
|
||||
getPlexUser: state => state.plexuser,
|
||||
getBackground: state => state.background,
|
||||
getChosenClient: state => state.chosenClient,
|
||||
getShownChat: state => state.shownChat,
|
||||
getStats: state => state.stats,
|
||||
getBlockAutoPlay: state => state.blockAutoPlay,
|
||||
getAutoJoin: state => state.autoJoin,
|
||||
getAutoJoinRoom: state => state.autoJoinRoom,
|
||||
getAutoJoinPassword: state => state.autoJoinPassword,
|
||||
getAutoJoinUrl: state => state.autoJoinUrl,
|
||||
getShortLink: state => state.shortLink,
|
||||
|
||||
// SETTINGS
|
||||
getSettings: state => state.settings,
|
||||
getSettingHOMEINIT: state => state.HOMEINIT,
|
||||
getSettingPTPLAYERQUALITY: state => state.PTPLAYERQUALITY,
|
||||
getSettingPTPLAYERVOLUME: state => state.PTPLAYERVOLUME,
|
||||
getExtAvailable: state => state.extAvailable,
|
||||
getLogos: () => ({
|
||||
light: {
|
||||
long: 'logo-long-light.png',
|
||||
small: 'logo-small-light.png',
|
||||
},
|
||||
dark: {
|
||||
long: 'logo-long-dark.png',
|
||||
},
|
||||
plex: {
|
||||
standard: 'plexlogo.png',
|
||||
},
|
||||
}),
|
||||
};
|
||||
const actions = {
|
||||
async PLAYBACK_CHANGE({ commit, state, dispatch }, data) {
|
||||
const [client, ratingKey, mediaContainer] = data;
|
||||
if (ratingKey) {
|
||||
// Playing something different!
|
||||
const server = state.plex.servers[mediaContainer.machineIdentifier];
|
||||
commit('setSetting', ['LASTSERVER', mediaContainer.machineIdentifier]);
|
||||
// state.settings.LASTSERVER = mediaContainer.machineIdentifier;
|
||||
// window.localStorage.setItem('LASTSERVER', mediaContainer.machineIdentifier);
|
||||
if (!server) {
|
||||
return;
|
||||
}
|
||||
// Fetch our metadata from this server
|
||||
// console.log('Loading content metadata from store ' + ratingKey)
|
||||
server.getMediaByRatingKey(ratingKey.replace('/library/metadata/', '')).then((data) => {
|
||||
const metadata = data.MediaContainer.Metadata[0];
|
||||
if (!metadata) {
|
||||
return;
|
||||
}
|
||||
if (metadata.type === 'movie') {
|
||||
sendNotification(`Now Playing: ${metadata.title} from ${
|
||||
state.plex.servers[metadata.machineIdentifier].name
|
||||
}`);
|
||||
}
|
||||
if (metadata.type === 'episode') {
|
||||
sendNotification(`Now Playing: ${metadata.grandparentTitle} S${metadata.parentIndex}E${
|
||||
metadata.index
|
||||
} from ${state.plex.servers[metadata.machineIdentifier].name}`);
|
||||
}
|
||||
state.chosenClient.clientPlayingMetadata = metadata;
|
||||
const w = Math.round(Math.max(document.documentElement.clientWidth, window.innerWidth || 0));
|
||||
const h = Math.round(Math.max(document.documentElement.clientHeight, window.innerHeight || 0));
|
||||
state.background = state.plex.servers[metadata.machineIdentifier].getUrlForLibraryLoc(
|
||||
metadata.thumb,
|
||||
w / 4,
|
||||
h / 4,
|
||||
4,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
state.chosenClient.clientPlayingMetadata = null;
|
||||
const thumb = await state.plex.getRandomThumb(state.plex);
|
||||
if (thumb) {
|
||||
state.background = thumb;
|
||||
}
|
||||
}
|
||||
},
|
||||
NEW_TIMELINE({ commit, state, dispatch }, data) {
|
||||
// return true
|
||||
const timeline = data;
|
||||
const client = state.chosenClient;
|
||||
const metadata = state.chosenClient.clientPlayingMetadata || {};
|
||||
// console.log(state)
|
||||
if (!state.chosenClient || client.clientIdentifier !== state.chosenClient.clientIdentifier) {
|
||||
console.log('Invalid client');
|
||||
return false;
|
||||
}
|
||||
if (state.lastRatingKey !== timeline.ratingKey) {
|
||||
commit('SET_VALUE', ['lastRatingKey', timeline.ratingKey]);
|
||||
dispatch('PLAYBACK_CHANGE', [client, timeline.ratingKey, timeline]);
|
||||
}
|
||||
|
||||
// Check if we need to activate the upnext feature
|
||||
if (state.me && state.me.role === 'host') {
|
||||
if (
|
||||
timeline.duration &&
|
||||
timeline.time &&
|
||||
Math.abs(timeline.duration - timeline.time) < 10000 &&
|
||||
metadata.type === 'episode'
|
||||
) {
|
||||
console.log('Checking upnext');
|
||||
if (!state.upNextCache[timeline.machineIdentifier]) {
|
||||
state.upNextCache[timeline.machineIdentifier] = {};
|
||||
}
|
||||
if (!state.upNextCache[timeline.machineIdentifier][timeline.key]) {
|
||||
state.upNextCache[timeline.machineIdentifier][timeline.key] = {
|
||||
loading: true,
|
||||
};
|
||||
state.plex.servers[timeline.machineIdentifier].getPostplay(timeline.key).then((data) => {
|
||||
data.machineIdentifier = state.chosenClient.lastTimelineObject.machineIdentifier;
|
||||
state.upNextCache[timeline.machineIdentifier][timeline.key] = data;
|
||||
// Only proc upnext if the item upnext is from the same show
|
||||
if (
|
||||
data.MediaContainer.Hub[0].Metadata[0].grandparentTitle === metadata.grandparentTitle
|
||||
) {
|
||||
window.EventBus.$emit('upnext', data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('Already procced an upnext for this item', timeline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// state.ourClientResponseTime = timeline.lastResponseTime
|
||||
let title = null;
|
||||
let rawTitle = null;
|
||||
let type = null;
|
||||
let showName = null;
|
||||
if (state.chosenClient.clientPlayingMetadata) {
|
||||
rawTitle = metadata.title;
|
||||
if (metadata.type === 'episode') {
|
||||
title =
|
||||
`${metadata.grandparentTitle} - ${metadata.title} S${metadata.parentIndex}-` +
|
||||
`E${metadata.index}`;
|
||||
showName = metadata.grandparentTitle;
|
||||
} else {
|
||||
title = metadata.title;
|
||||
}
|
||||
type = metadata.type;
|
||||
}
|
||||
let status = 'good';
|
||||
if (!state.synclounge.lastHostTimeline || isNaN(state.synclounge.lastHostTimeline.time)) {
|
||||
status = 'error';
|
||||
} else {
|
||||
const hostAge = Math.abs(new Date().getTime() - state.synclounge.lastHostTimeline.recievedAt);
|
||||
let hostTime = 0 + state.synclounge.lastHostTimeline.time;
|
||||
if (state.synclounge.lastHostTimeline.playerState === 'playing') {
|
||||
hostTime = parseInt(hostTime) + parseInt(hostAge);
|
||||
}
|
||||
const difference = Math.abs(data.time - hostTime);
|
||||
if (difference > state.settings.SYNCFLEXABILITY) {
|
||||
status = 'notok';
|
||||
}
|
||||
}
|
||||
|
||||
const endObj = {
|
||||
time: parseInt(timeline.time),
|
||||
maxTime: parseInt(timeline.duration),
|
||||
title,
|
||||
rawTitle,
|
||||
playerState: timeline.state,
|
||||
clientResponseTime: state.chosenClient.lastResponseTime,
|
||||
playerProduct: state.chosenClient.product,
|
||||
status,
|
||||
type,
|
||||
showName,
|
||||
uuid: state.uuid,
|
||||
};
|
||||
if (state.chosenClient && state.chosenClient.lastTimelineObject) {
|
||||
endObj.machineIdentifier = state.chosenClient.lastTimelineObject.machineIdentifier;
|
||||
endObj.key = state.chosenClient.lastTimelineObject.key;
|
||||
}
|
||||
if (state.synclounge._socket) {
|
||||
const commandId = Object.keys(state.synclounge.commands).length + 1;
|
||||
state.synclounge.commands[commandId] = {
|
||||
start: new Date().getTime(),
|
||||
};
|
||||
endObj.commandId = commandId;
|
||||
if (Object.keys(state.synclounge.commands).length > 1) {
|
||||
const latency =
|
||||
state.synclounge.commands[Object.keys(state.synclounge.commands).length - 1].difference;
|
||||
endObj.latency = latency;
|
||||
}
|
||||
state.synclounge._socket.emit('poll', endObj);
|
||||
}
|
||||
},
|
||||
TOGGLE_LEFT_SIDEBAR_OPEN: ({ commit }, open) => {
|
||||
commit('TOGGLE_LEFT_SIDEBAR_OPEN', open);
|
||||
},
|
||||
};
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state,
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
modules: {
|
||||
synclounge: syncLounge,
|
||||
plex,
|
||||
config,
|
||||
},
|
||||
});
|
||||
|
||||
export default store;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = {
|
||||
transpileDependencies: [
|
||||
'vuetify',
|
||||
],
|
||||
transpileDependencies: ["vuetify"],
|
||||
configureWebpack: {
|
||||
devtool: "source-map"
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue