All lint fixes

This commit is contained in:
Travis Shivers 2020-06-07 20:24:05 -05:00
parent f728d67af3
commit a396588322
19 changed files with 382 additions and 394 deletions

54
package-lock.json generated
View File

@ -1221,12 +1221,6 @@
"integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==",
"dev": true
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
@ -5326,24 +5320,23 @@
}
},
"eslint-plugin-import": {
"version": "2.21.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.0.tgz",
"integrity": "sha512-fbbjKv3jv23WZ1+uCyYOGx+J8H4vAVlVcc9YLHRCycDry1LdgjdSvt7O0dQS1PwOUu/s0QWfR1xE7mq0GuqYnA==",
"version": "2.20.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz",
"integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==",
"dev": true,
"requires": {
"array-includes": "^3.1.1",
"array.prototype.flat": "^1.2.3",
"array-includes": "^3.0.3",
"array.prototype.flat": "^1.2.1",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.3",
"eslint-module-utils": "^2.6.0",
"eslint-import-resolver-node": "^0.3.2",
"eslint-module-utils": "^2.4.1",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.1",
"object.values": "^1.1.0",
"read-pkg-up": "^2.0.0",
"resolve": "^1.17.0",
"tsconfig-paths": "^3.9.0"
"resolve": "^1.12.0"
}
},
"eslint-plugin-vue": {
@ -5958,6 +5951,12 @@
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
"dev": true
},
"fast-xml-parser": {
"version": "3.17.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.17.3.tgz",
"integrity": "sha512-g3OSnHBWq5hrpS4LUgFWOS87F7B6UDklkI6v2VLC/89Ech00fabXleKEHTVXQBkKyVsfOxD+1QY6fkoIZMIO/Q==",
"dev": true
},
"faye-websocket": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@ -12637,29 +12636,6 @@
"integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==",
"dev": true
},
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
"integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
}
}
},
"tslib": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",

View File

@ -48,6 +48,7 @@
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-vue": "^6.2.2",
"fast-xml-parser": "^3.17.3",
"fscreen": "^1.0.2",
"humanize-duration": "^3.23.0",
"moment": "^2.26.0",
@ -67,4 +68,4 @@
"vuex-persistedstate": "^3.0.1",
"vuex-router-sync": "^5.0.0"
}
}
}

View File

@ -10,7 +10,8 @@
:value="GET_CLIENTPOLLINTERVAL"
:min="100"
:max="10000"
hint="Sets how frequently SyncLounge will poll external plex clients for new information in milliseconds. Default is 1000ms (1 second)"
hint="Sets how frequently SyncLounge will poll plex clients for new information in
milliseconds.Default is 1000ms (1 second)"
persistent-hint
@change="SET_CLIENTPOLLINTERVAL"
/>
@ -29,7 +30,8 @@
:value="GET_SYNCFLEXIBILITY"
:min="0"
:max="10000"
hint="Sets the acceptable distance away from the host in milliseconds. Default is 3000ms (3 seconds)."
hint="Sets the acceptable distance away from the host in milliseconds.
Default is 3000ms (3 seconds)."
persistent-hint
@change="SET_SYNCFLEXIBILITY"
/>
@ -66,7 +68,8 @@
</h4>
<v-switch
label="Enabled"
hint="If enabled SyncLounge will attempt to automatically play the same content as the host."
hint="If enabled SyncLounge will attempt to automatically play the
same content as the host."
:input-value="GET_AUTOPLAY"
@change="SET_AUTOPLAY"
/>

View File

@ -25,7 +25,7 @@ export default {
this.$store.state.plex = null;
this.$store.state.signedin = 'notsignedin';
setTimeout(() => {
location.reload();
window.location.reload();
}, 2500);
},
methods: {

View File

@ -30,7 +30,6 @@
Now Playing
</div>
<div><small><b>{{ getTitle }}</b> - {{ getUnder }}</small></div>
<!-- <div class="hidden-xs-only soft-text" style="font-size: 12px">Click for more info</div> -->
</v-flex>
</v-layout>
</v-card>
@ -127,14 +126,5 @@ export default {
}
},
},
watch: {
},
async mounted() {
},
methods: {
},
};
</script>

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
import ShakaUtils from '@/player/ui/utils';

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
import ShakaUtils from '@/player/ui/utils';

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
class CloseButton extends shaka.ui.Element {

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
class Forward30Button extends shaka.ui.Element {
@ -23,7 +24,7 @@ class Forward30Button extends shaka.ui.Element {
}
onButtonClicked() {
this.video.currentTime = this.video.currentTime + 30;
this.video.currentTime += 30;
}
// Updates whether this should be enabled or disabled depending on

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
import ShakaUtils from '@/player/ui/utils';

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
class Replay10Button extends shaka.ui.Element {
@ -23,7 +24,7 @@ class Replay10Button extends shaka.ui.Element {
}
onButtonClicked() {
this.video.currentTime = this.video.currentTime - 10;
this.video.currentTime -= 10;
}
// Updates whether this should be enabled or disabled depending on

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line max-classes-per-file
import shaka from 'shaka-player/dist/shaka-player.ui.debug';
import ShakaUtils from '@/player/ui/utils';

View File

@ -7,13 +7,13 @@ Vue.use(Router);
// Good guide: https://blog.sqreen.com/authentication-best-practices-vue/
const ifNotAuthenticated = (to, from, next) => {
if (!store.getters.IS_AUTHENTICATED) {
next();
return;
}
next('/');
};
// const ifNotAuthenticated = (to, from, next) => {
// if (!store.getters.IS_AUTHENTICATED) {
// next();
// return;
// }
// next('/');
// };
const ifAuthenticated = (to, from, next) => {
if (store.getters.IS_AUTHENTICATED) {

View File

@ -75,11 +75,13 @@ export default {
&& metadata.type === 'episode'
) {
if (!getters.GET_UP_NEXT_TRIGGERED) {
state.plex.servers[timeline.machineIdentifier].getPostplay(timeline.ratingKey).then((data) => {
if (data.MediaContainer.Hub[0].Metadata[0].grandparentTitle === metadata.grandparentTitle) {
commit('SET_UP_NEXT_POST_PLAY_DATA', data);
}
});
state.plex.servers[timeline.machineIdentifier]
.getPostplay(timeline.ratingKey).then((data) => {
if (data.MediaContainer.Hub[0].Metadata[0].grandparentTitle
=== metadata.grandparentTitle) {
commit('SET_UP_NEXT_POST_PLAY_DATA', data);
}
});
commit('SET_UP_NEXT_TRIGGERED', true);
}

View File

@ -1,6 +1,5 @@
import axios from 'axios';
import plexauth from './helpers/PlexAuth';
import PlexServer from './helpers/PlexServer';
import PlexClient from './helpers/PlexClient';

View File

@ -11,8 +11,7 @@ export default {
getItemCache: (state) => state.itemCache,
getLibraryCache: (state) => state.libraryCache,
recentClients: (state) => {
let clients = [];
for (const client in state.clients) clients.push(state.clients[client]);
let clients = Object.values(state.clients);
clients = clients.sort((a, b) => parseInt(b.lastSeenAt, 10) - parseInt(a.lastSeenAt, 10));
return clients;
},

View File

@ -1,5 +1,7 @@
import axios from 'axios';
import parser from 'fast-xml-parser';
import xmlutils from '@/utils/xmlutils';
import { encodeUrlParams } from '@/utils/encoder';
import delay from '@/utils/delay';
import plexauth from './PlexAuth';
const EventEmitter = require('events');
@ -68,9 +70,10 @@ class PlexClient {
return `${s4() + s4()}-${s4()}`;
}
async hitApi(command, params, connection, needResponse, dontSub) {
async hitApi(command, params, con) {
let connection = con;
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
return new Promise(async (resolve, reject) => {
return new Promise((resolve) => {
// We are using the SyncLounge Player
const data = {
command,
@ -89,47 +92,40 @@ class PlexClient {
if (!connection) {
throw new Error('No connection specified');
}
let query = '';
Object.assign(params, {
type: 'video',
commandID: this.commandId,
});
for (const key in params) {
query += `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}&`;
}
query = query.substring(0, query.length - 1);
const query = encodeUrlParams(params);
if (connection.uri.charAt(connection.uri.length - 1) === '/') {
// Remove a trailing / that some clients broadcast
connection.uri = connection.uri.slice(0, connection.uri.length - 1);
}
const _url = `${connection.uri + command}?${query}`;
const url = `${connection.uri + command}?${query}`;
this.setValue('commandId', this.commandId + 1);
const options = plexauth.getClientApiOptions(this.clientIdentifier, 5000, this.accessToken);
const { data } = await axios.get(_url, options);
if (needResponse) {
return parser.parse(data);
}
const options = {
...plexauth.getClientApiOptions(this.clientIdentifier, 5000, this.accessToken),
transformResponse: xmlutils.parseXML,
};
await axios.get(url, options);
return true;
}
getTimeline() {
return new Promise(async (resolve, reject) => {
let data;
try {
data = await this.hitApi('/player/timeline/poll', { wait: 0 }, this.chosenConnection, true);
if (data) {
return resolve(this.updateTimelineObject(data));
}
return reject(new Error('Invalid data recieved from client'));
} catch (e) {
return reject(e);
}
});
// Get the timeline object from the client
async getTimeline() {
const data = await this.hitApi('/player/timeline/poll', { wait: 0 }, this.chosenConnection, true);
if (data) {
return this.updateTimelineObject(data);
}
throw new Error('Invalid data recieved from client');
}
updateTimelineObject(result) {
updateTimelineObject(timeline) {
// Check if we are the SLPlayer
let result = timeline;
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
// SLPLAYER
const tempObj = {
@ -138,11 +134,12 @@ class PlexClient {
},
};
result = tempObj;
if (!this.previousTimeline.MediaContainer || result.MediaContainer.Timeline[0].ratingKey !== this.previousTimeline.MediaContainer.Timeline[0].ratingKey) {
if (!this.previousTimeline.MediaContainer || result.MediaContainer.Timeline[0].ratingKey
!== this.previousTimeline.MediaContainer.Timeline[0].ratingKey) {
window.EventBus.$emit('PLAYBACK_CHANGE', [this, result.MediaContainer.Timeline[0].ratingKey, result.MediaContainer.Timeline[0]]);
}
this.previousTimeline = tempObj;
this.lastTimelineObject = result.MediaContainer.Timeline[0];
[this.lastTimelineObject] = result.MediaContainer.Timeline;
this.lastTimelineObject.recievedAt = new Date().getTime();
window.EventBus.$emit('NEW_TIMELINE', result.MediaContainer.Timeline[0]);
return result.MediaContainer.Timeline[0];
@ -151,9 +148,9 @@ class PlexClient {
const timelines = result.MediaContainer.Timeline;
let videoTimeline = {};
for (let i = 0; i < timelines.length; i += 1) {
const _timeline = timelines[i].$;
if (_timeline.type === 'video') {
videoTimeline = _timeline;
const subTimeline = timelines[i].$;
if (subTimeline.type === 'video') {
videoTimeline = subTimeline;
if (videoTimeline.ratingKey !== this.previousTimeline.ratingKey) {
window.EventBus.$emit('PLAYBACK_CHANGE', [this, videoTimeline.ratingKey, videoTimeline]);
}
@ -188,7 +185,7 @@ class PlexClient {
}
waitForMovement(startTime) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
let time = 500;
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
time = 50;
@ -204,20 +201,17 @@ class PlexClient {
});
}
skipAhead(current, duration) {
return new Promise(async (resolve, reject) => {
const startedAt = new Date().getTime();
const now = this.lastTimelineObject.time;
await this.seekTo(current + duration);
await this.waitForMovement(now);
// The client is now ready
await this.pressPause();
// Calculate how long it took to get to our ready state
const elapsed = Math.abs(startedAt - new Date().getTime());
await wait(duration - elapsed);
await this.pressPlay();
resolve();
});
async skipAhead(current, duration) {
const startedAt = new Date().getTime();
const now = this.lastTimelineObject.time;
await this.seekTo(current + duration);
await this.waitForMovement(now);
// The client is now ready
await this.pressPause();
// Calculate how long it took to get to our ready state
const elapsed = Math.abs(startedAt - new Date().getTime());
await delay(duration - elapsed);
await this.pressPlay();
}
cleanSeek(time, isSoft) {
@ -227,55 +221,55 @@ class PlexClient {
return this.seekTo(time);
}
sync(hostTimeline, SYNCFLEXIBILITY, SYNCMODE, POLLINTERVAL) {
return new Promise(async (resolve, reject) => {
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
await this.getTimeline();
}
const lastCommandTime = Math.abs(this.lastSyncCommand - new Date().getTime());
if (this.lastSyncCommand && this.clientIdentifier !== 'PTPLAYER9PLUS10' && lastCommandTime < POLLINTERVAL) {
return reject(new Error('too soon for another sync command'));
}
const lagTime = Math.abs(hostTimeline.recievedAt - new Date().getTime());
if (lagTime) {
hostTimeline.time += lagTime;
}
const timelineAge = new Date().getTime() - this.lastTimelineObject.recievedAt;
const ourTime = parseInt(this.lastTimelineObject.time) + parseInt(timelineAge);
const difference = Math.abs((parseInt(ourTime)) - parseInt(hostTimeline.time));
// console.log('Difference with host is', difference);
const bothPaused = hostTimeline.playerState === 'paused' && this.lastTimelineObject.state === 'paused';
async sync(hostTime, SYNCFLEXIBILITY, SYNCMODE, POLLINTERVAL) {
const hostTimeline = hostTime;
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
await this.getTimeline();
}
const lastCommandTime = Math.abs(this.lastSyncCommand - new Date().getTime());
if (this.lastSyncCommand && this.clientIdentifier !== 'PTPLAYER9PLUS10' && lastCommandTime < POLLINTERVAL) {
throw new Error('too soon for another sync command');
}
const lagTime = Math.abs(hostTimeline.recievedAt - new Date().getTime());
if (lagTime) {
hostTimeline.time += lagTime;
}
const timelineAge = new Date().getTime() - this.lastTimelineObject.recievedAt;
const ourTime = parseInt(this.lastTimelineObject.time, 10) + parseInt(timelineAge, 10);
const difference = Math.abs((parseInt(ourTime, 10)) - parseInt(hostTimeline.time, 10));
// console.log('Difference with host is', difference);
const bothPaused = hostTimeline.playerState === 'paused' && this.lastTimelineObject.state === 'paused';
if (parseInt(difference, 10) > parseInt(SYNCFLEXIBILITY, 10) || (bothPaused && difference > 10)) {
// We need to seek!
this.lastSyncCommand = new Date().getTime();
// Decide what seeking method we want to use
if (SYNCMODE === 'cleanseek' || hostTimeline.playerState === 'paused') {
return resolve(await this.cleanSeek(hostTimeline.time));
}
if (SYNCMODE === 'skipahead') {
return resolve(await this.skipAhead(hostTimeline.time, 10000));
}
// Fall back to skipahead
return resolve(await this.skipAhead(hostTimeline.time, 10000));
if (parseInt(difference, 10) > parseInt(SYNCFLEXIBILITY, 10)
|| (bothPaused && difference > 10)) {
// We need to seek!
this.lastSyncCommand = new Date().getTime();
// Decide what seeking method we want to use
if (SYNCMODE === 'cleanseek' || hostTimeline.playerState === 'paused') {
return this.cleanSeek(hostTimeline.time);
}
// Calc the average delay of the last 10 host timeline updates
// We do this to avoid any issues with random lag spikes
this.differenceCache.unshift(difference);
if (this.differenceCache.length > 5) {
this.differenceCache.pop();
if (SYNCMODE === 'skipahead') {
return this.skipAhead(hostTimeline.time, 10000);
}
let total = 0;
for (let i = 0; i < this.differenceCache.length; i += 1) {
total += this.differenceCache[i];
}
const avg = total / this.differenceCache.length;
if (this.clientIdentifier === 'PTPLAYER9PLUS10' && avg > 1500) {
console.log('Soft syncing because difference is', difference);
return resolve(await this.cleanSeek(hostTimeline.time, true));
}
return resolve('No sync needed');
});
// Fall back to skipahead
return this.skipAhead(hostTimeline.time, 10000);
}
// Calc the average delay of the last 10 host timeline updates
// We do this to avoid any issues with random lag spikes
this.differenceCache.unshift(difference);
if (this.differenceCache.length > 5) {
this.differenceCache.pop();
}
let total = 0;
for (let i = 0; i < this.differenceCache.length; i += 1) {
total += this.differenceCache[i];
}
const avg = total / this.differenceCache.length;
if (this.clientIdentifier === 'PTPLAYER9PLUS10' && avg > 1500) {
console.log('Soft syncing because difference is', difference);
return this.cleanSeek(hostTimeline.time, true);
}
return 'No sync needed';
}
async playMedia(data) {
@ -285,117 +279,116 @@ class PlexClient {
// Server Ip, Server Port, Server Protocol, Path
// First we will mirror the item so the user has an idea of what we're about to play
return new Promise(async (resolve, reject) => {
const command = '/player/playback/playMedia';
const offset = Math.round(data.offset) || 0;
const serverId = data.server.clientIdentifier;
const uri = new URL(data.server.chosenConnection.uri);
const address = uri.hostname;
const port = uri.port !== '' ? uri.port : (uri.protocol === 'https:' ? '443' : '80'); // port not specified if standard
const protocol = uri.protocol.replace(':', ''); // remove extra colon
const path = data.server.chosenConnection.uri + data.key;
const params = {
'X-Plex-Client-Identifier': 'SyncLounge',
key: data.key,
offset,
machineIdentifier: serverId,
address,
port,
protocol,
path,
wait: 0,
token: data.server.accessToken,
};
const command = '/player/playback/playMedia';
const offset = Math.round(data.offset) || 0;
const serverId = data.server.clientIdentifier;
const uri = new URL(data.server.chosenConnection.uri);
const address = uri.hostname;
// eslint-disable-next-line no-nested-ternary
const port = uri.port !== '' ? uri.port : (uri.protocol === 'https:' ? '443' : '80'); // port not specified if standard
const protocol = uri.protocol.replace(':', ''); // remove extra colon
const path = data.server.chosenConnection.uri + data.key;
if (data.mediaIndex !== undefined || data.mediaIndex !== null) {
params.mediaIndex = data.mediaIndex;
}
const params = {
'X-Plex-Client-Identifier': 'SyncLounge',
key: data.key,
offset,
machineIdentifier: serverId,
address,
port,
protocol,
path,
wait: 0,
token: data.server.accessToken,
};
// Now that we've built our params, it's time to hit the client api
await this.hitApi(command, params, this.chosenConnection);
await this.waitForMovement();
resolve(true);
});
if (data.mediaIndex !== undefined || data.mediaIndex !== null) {
params.mediaIndex = data.mediaIndex;
}
// Now that we've built our params, it's time to hit the client api
await this.hitApi(command, params, this.chosenConnection);
await this.waitForMovement();
return true;
}
playContentAutomatically(client, hostData, servers, offset) {
async playContentAutomatically(client, hostData, servers, offset) {
// Automatically play content on the client searching all servers based on the title
return new Promise(async (resolve, reject) => {
// First lets find all of our playable items
let playables = [];
const serversArr = [];
for (const i in servers) {
serversArr.push(servers[i]);
}
await Promise.all(serversArr.map(async (server) => new Promise(async (resolve, reject) => {
if (!server.chosenConnection) {
return resolve();
}
const results = await server.search(hostData.rawTitle);
for (let k = 0; k < results.length; k++) {
// Now we need to check the result
if (checkResult(results[k], hostData)) {
// Its a match!
playables.push({
server,
result: results[k],
});
}
}
resolve();
})));
playables = playables.sort((a, b) => parseInt(b.server.publicAddressMatches) - parseInt(a.server.publicAddressMatches));
const start = async (index) => {
// Now lets try and play our items one by one
if (playables.length === 0 || index === playables.length) {
return reject(new Error('Didnt find any playable items'));
}
const { server } = playables[index];
const { key } = playables[index].result;
const data = {
key,
mediaIndex: 0,
server,
offset: offset || 0,
};
const res = await this.playMedia(data).catch(() => {
start(parseInt(parseInt(index) + 1));
});
if (!res) {
return;
}
return resolve();
};
start(0);
function checkResult(data, hostData) {
// Do a series of checks to see if this result is OK
// Check if rawTitle matches
if (data.title !== hostData.rawTitle) {
return false;
}
// Check if length is close enough
if (Math.abs(parseInt(data.duration) - parseInt(hostData.maxTime)) > 1000 || !data.duration) {
return false;
}
if (data.type === 'movie') {
// We're good!
return true;
}
if (data.type === 'episode') {
// Check if the show name is the same
const similarity = stringSimilarity.compareTwoStrings(data.grandparentTitle, hostData.showName);
return similarity > 0.40;
}
if (data.type === 'track') {
// We're good!
return true;
}
function checkResult(data) {
// Do a series of checks to see if this result is OK
// Check if rawTitle matches
if (data.title !== hostData.rawTitle) {
return false;
}
});
// Check if length is close enough
if (Math.abs(parseInt(data.duration, 10) - parseInt(hostData.maxTime, 10)) > 1000
|| !data.duration) {
return false;
}
if (data.type === 'movie') {
// We're good!
return true;
}
if (data.type === 'episode') {
// Check if the show name is the same
const similarity = stringSimilarity.compareTwoStrings(data.grandparentTitle,
hostData.showName);
return similarity > 0.40;
}
if (data.type === 'track') {
// We're good!
return true;
}
return false;
}
// First lets find all of our playable items
let playables = [];
const serversArr = Object.values(servers);
await Promise.all(serversArr.map(async (server) => {
if (!server.chosenConnection) {
return;
}
const results = await server.search(hostData.rawTitle);
for (let k = 0; k < results.length; k += 1) {
// Now we need to check the result
if (checkResult(results[k])) {
// Its a match!
playables.push({
server,
result: results[k],
});
}
}
}));
playables = playables.sort((a, b) => parseInt(b.server.publicAddressMatches, 10)
- parseInt(a.server.publicAddressMatches, 10));
const start = async (index) => {
// Now lets try and play our items one by one
if (playables.length === 0 || index === playables.length) {
throw new Error('Didnt find any playable items');
}
const { server } = playables[index];
const { key } = playables[index].result;
const data = {
key,
mediaIndex: 0,
server,
offset: offset || 0,
};
await this.playMedia(data).catch(() => {
start(parseInt(parseInt(index, 10) + 1, 10), 10);
});
};
start(0);
}
}

View File

@ -35,7 +35,7 @@ export default {
},
socketConnect({
state, commit, rootState, rootGetters,
state, commit, rootGetters,
}, data) {
return new Promise((resolve, reject) => {
const { address } = data;
@ -91,8 +91,8 @@ export default {
}
console.log('Joining room', data.roomName);
data.password = data.password || '';
commit('SET_PASSWORD', data.password);
commit('SET_PASSWORD', data.password || '');
let { username } = data.user;
if (rootGetters['settings/GET_HIDEUSERNAME']) {
@ -101,10 +101,10 @@ export default {
state.socket.emit(
'join',
new HandshakeUser(data.user, data.roomName, data.password, rootState.uuid, username),
new HandshakeUser(data.user, data.roomName, data.password || '', rootState.uuid, username),
);
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
state.socket.on('join-result', async (result, _data, details, currentUsers, partyPausing) => {
console.log('Got join result', result);
commit('CLEAR_MESSAGES');
@ -133,7 +133,7 @@ export default {
urlOrigin = 'http://app.synclounge.tv';
}
const data = {
const inviteData = {
urlOrigin,
owner: rootGetters['settings/GET_PLEX_USER'].username || rootGetters['settings/GET_PLEX_USER'].title,
server: state.server,
@ -141,7 +141,7 @@ export default {
password: state.password || '',
};
// if (settings.webroot) urlOrigin = urlOrigin + settings.webroot
axios.post(`${urlOrigin}/invite`, data).then((res) => {
axios.post(`${urlOrigin}/invite`, inviteData).then((res) => {
commit('SET_SHORTLINK', res.data.url);
});
@ -214,149 +214,154 @@ export default {
});
});
state.socket.on('host-update', async (data) => {
data.recievedAt = new Date().getTime();
const hostTimeline = data;
state.socket.on('host-update', async (hostData) => {
const hostUpdateData = hostData;
hostUpdateData.recievedAt = new Date().getTime();
const hostTimeline = hostUpdateData;
if (
!state.lastHostTimeline
|| state.lastHostTimeline.playerState !== data.playerState
|| state.lastHostTimeline.playerState !== hostUpdateData.playerState
) {
window.EventBus.$emit('host-playerstate-change');
}
const diffBetweenLastUpdate = Math.abs(state.lastHostTimeline.time - data.time);
const diffBetweenLastUpdate = Math.abs(state.lastHostTimeline.time
- hostUpdateData.time);
if (diffBetweenLastUpdate > 5000) {
window.EventBus.$emit('host-playerstate-change');
}
state.lastHostTimeline = data;
const decisionMaker = (timelineAge) => {
state.lastHostTimeline = hostUpdateData;
const decisionMaker = async () => {
const ourTimeline = rootState.chosenClient.lastTimelineObject;
return new Promise(async (resolve, reject) => {
if (ourTimeline.playerState === 'buffering') {
return resolve();
}
if (
(hostTimeline.playerState === 'stopped' || !hostTimeline.playerState)
&& ourTimeline.state !== 'stopped'
) {
sendNotification('The host pressed stop');
await rootState.chosenClient.pressStop();
return resolve();
}
if (hostTimeline.playerState === 'stopped') {
return resolve();
}
// Check if we need to autoplay
if (
((ourTimeline.state === 'stopped' || !ourTimeline.state)
if (ourTimeline.playerState === 'buffering') {
return;
}
if (
(hostTimeline.playerState === 'stopped' || !hostTimeline.playerState)
&& ourTimeline.state !== 'stopped'
) {
sendNotification('The host pressed stop');
await rootState.chosenClient.pressStop();
return;
}
if (hostTimeline.playerState === 'stopped') {
return;
}
// Check if we need to autoplay
if (
((ourTimeline.state === 'stopped' || !ourTimeline.state)
&& hostTimeline.playerState !== 'stopped')
|| state.rawTitle !== hostTimeline.rawTitle
) {
if (rootState.blockAutoPlay || !hostTimeline.rawTitle) {
return resolve();
}
// We need to autoplay!
if (!rootGetters['settings/GET_AUTOPLAY']) {
return resolve();
}
commit('SET_BLOCK_AUTOPLAY', true, { root: true });
) {
if (rootState.blockAutoPlay || !hostTimeline.rawTitle) {
return;
}
// We need to autoplay!
if (!rootGetters['settings/GET_AUTOPLAY']) {
return;
}
commit('SET_BLOCK_AUTOPLAY', true, { root: true });
const servers = { ...rootState.plex.servers };
rootGetters['settings/GET_BLOCKEDSERVERS'].forEach((id) => {
if (rootState.plex.servers[id]) {
delete servers[id];
const servers = { ...rootState.plex.servers };
rootGetters['settings/GET_BLOCKEDSERVERS'].forEach((id) => {
if (rootState.plex.servers[id]) {
delete servers[id];
}
});
commit('SET_RAW_TITLE', hostTimeline.rawTitle);
sendNotification(`Searching Plex Servers for "${hostTimeline.rawTitle}"`);
await rootState.chosenClient
.playContentAutomatically(
rootState.chosenClient,
hostTimeline,
servers,
hostTimeline.time,
)
.catch(async () => {
const hostServer = rootState.plex.servers[hostTimeline.machineIdentifier];
if (hostServer && hostTimeline.key) {
if (
!rootGetters['settings/GET_BLOCKEDSERVERS'].includes(
hostTimeline.machineIdentifier,
)
) {
await rootState.chosenClient.playMedia({
// TODO: have timeline updates send out more info like mediaIdentifier etc
key: hostTimeline.key,
mediaIndex: 0,
server: rootState.plex.servers[hostTimeline.machineIdentifier],
offset: hostTimeline.time || 0,
}).catch(() => {});
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 15000);
return;
}
}
sendNotification(
`Failed to find a compatible copy of ${hostTimeline.rawTitle}. If you have access to the content try manually playing it.`,
);
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 15000);
});
commit('SET_RAW_TITLE', hostTimeline.rawTitle);
sendNotification(`Searching Plex Servers for "${hostTimeline.rawTitle}"`);
const result = await rootState.chosenClient
.playContentAutomatically(
rootState.chosenClient,
hostTimeline,
servers,
hostTimeline.time,
)
.catch(async () => {
const hostServer = rootState.plex.servers[hostTimeline.machineIdentifier];
if (hostServer && hostTimeline.key) {
if (
!rootGetters['settings/GET_BLOCKEDSERVERS'].includes(
hostTimeline.machineIdentifier,
)
) {
await rootState.chosenClient.playMedia({
// TODO: have timeline updates send out more info like mediaIdentifier etc
key: hostTimeline.key,
mediaIndex: 0,
server: rootState.plex.servers[hostTimeline.machineIdentifier],
offset: hostTimeline.time || 0,
}).catch(() => {});
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 15000);
return resolve();
}
}
sendNotification(
`Failed to find a compatible copy of ${hostTimeline.rawTitle}. If you have access to the content try manually playing it.`,
);
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 15000);
});
await delay(1000);
await delay(1000);
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 10000);
return;
}
setTimeout(() => {
commit('SET_BLOCK_AUTOPLAY', false, { root: true });
}, 10000);
return resolve();
}
if (hostTimeline.playerState === 'playing' && ourTimeline.state === 'paused') {
sendNotification('Resuming..');
return resolve(await rootState.chosenClient.pressPlay());
}
if ((hostTimeline.playerState === 'paused'
if (hostTimeline.playerState === 'playing' && ourTimeline.state === 'paused') {
sendNotification('Resuming..');
resolve(await rootState.chosenClient.pressPlay());
return;
}
if ((hostTimeline.playerState === 'paused'
|| hostTimeline.playerState === 'buffering')
&& ourTimeline.state === 'playing') {
sendNotification('Pausing..');
return resolve(await rootState.chosenClient.pressPause());
}
if (hostTimeline.playerState === 'playing') {
// Add on the delay between us and the SLServer plus the delay between the server and the host
try {
const ourLastDelay = Math.round(
state.commands[Object.keys(state.commands).length - 1].difference,
);
const hostLastDelay = Math.round(hostTimeline.latency);
// console.log('adding delays', { ourLastDelay, hostLastDelay });
if (ourLastDelay && hostLastDelay) {
// console.log(
// 'Adding host delay',
// hostLastDelay,
// 'and our lastDelay',
// ourLastDelay,
// );
data.time = data.time + (ourLastDelay || 0) + (hostLastDelay || 0);
}
} catch (e) {
console.log('Failed to add extra lag time');
}
}
sendNotification('Pausing..');
resolve(await rootState.chosenClient.pressPause());
return;
}
if (hostTimeline.playerState === 'playing') {
// Add on the delay between us and the SLServer plus the delay between the server and the host
try {
await rootState.chosenClient.sync(
data,
rootGetters['settings/GET_SYNCFLEXIBILITY'],
rootGetters['settings/GET_SYNCMODE'],
rootGetters['settings/GET_CLIENTPOLLINTERVAL'],
const ourLastDelay = Math.round(
state.commands[Object.keys(state.commands).length - 1].difference,
);
const hostLastDelay = Math.round(hostTimeline.latency);
// console.log('adding delays', { ourLastDelay, hostLastDelay });
if (ourLastDelay && hostLastDelay) {
// console.log(
// 'Adding host delay',
// hostLastDelay,
// 'and our lastDelay',
// ourLastDelay,
// );
hostUpdateData.time = hostUpdateData.time + (ourLastDelay || 0)
+ (hostLastDelay || 0);
}
} catch (e) {
return resolve();
console.log('Failed to add extra lag time');
}
return resolve();
});
}
try {
await rootState.chosenClient.sync(
hostUpdateData,
rootGetters['settings/GET_SYNCFLEXIBILITY'],
rootGetters['settings/GET_SYNCMODE'],
rootGetters['settings/GET_CLIENTPOLLINTERVAL'],
);
} catch (e) {
resolve();
return;
}
resolve();
};
/* This is data from the host, we should react to this data by potentially changing
what we're playing or seeking to get back in sync with the host.
@ -389,6 +394,8 @@ export default {
if (!rootState.chosenClient.lastTimelineObject) {
console.log('Dont have our first timeline data yet.');
if (rootState.chosenClient.clientIdentifier === 'PTPLAYER9PLUS10') {
// TODO: come back and fix this
// eslint-disable-next-line no-param-reassign
rootState.chosenClient.lastTimelineObject = {
playerState: 'stopped',
};

11
src/utils/xmlutils.js Normal file
View File

@ -0,0 +1,11 @@
import parser from 'fast-xml-parser';
const options = {
attributeNamePrefix: '',
ignoreAttributes: false,
arrayMode: true,
};
export default {
parseXML: (xml) => parser.parse(xml, options),
};