Autoplay rework (WIP)
This commit is contained in:
parent
d37bb26661
commit
0b96fa563a
|
@ -205,6 +205,10 @@ export default {
|
|||
window.EventBus.$on('NEW_TIMELINE', timeline => {
|
||||
this.$store.dispatch('NEW_TIMELINE', timeline)
|
||||
})
|
||||
window.EventBus.$on('PLAYBACK_CHANGE', data => {
|
||||
console.log('Playback change event', data)
|
||||
this.$store.dispatch('PLAYBACK_CHANGE', data)
|
||||
})
|
||||
if (!window['localStorage'].getItem('plexuser')) {
|
||||
console.log('Token doesnt exist', window['localStorage'].getItem('plexuser'))
|
||||
this.$router.push('/signin')
|
||||
|
|
|
@ -133,7 +133,7 @@ export default {
|
|||
return data.callback(playerdata)
|
||||
}
|
||||
if (data.command === '/player/playback/play') {
|
||||
this.eventbus.$emit('player-press-play', function (res) {
|
||||
this.eventbus.$emit('player-press-play', (res) => {
|
||||
return data.callback(res)
|
||||
})
|
||||
return
|
||||
|
@ -557,7 +557,6 @@ export default {
|
|||
if (changeItem || !this.playingMetadata) {
|
||||
this.playingMetadata = null
|
||||
this.chosenServer.getMediaByRatingKey(this.chosenKey).then(result => {
|
||||
console.log(result)
|
||||
this.playingMetadata = result.MediaContainer.Metadata[0]
|
||||
req()
|
||||
})
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
<template>
|
||||
<li v-if="object" class="mdc-list-item avatar ptuser mainUser"
|
||||
style="height: 100%; margin-bottom: 1%; overflow-y:hidden">
|
||||
<span class="mdc-list-item__start-detail" style="height:3em; width: 3em">
|
||||
<img v-bind:src="object.avatarUrl" class="circle plex-gamboge-border" style="height:3em; width: 3em">
|
||||
</span>
|
||||
<span class="mdc-list-item__text" style="width: 89%; line-height: 1.2">
|
||||
<span class="mdc-list-item__text__primary">
|
||||
<div class="ptuser-username">{{ object.username }} </div>
|
||||
<small class="ptuser-title"><i class="material-icons" v-if="playerState"
|
||||
style="font-size:smaller">{{ playerState }}</i> {{ getTitle }}</small>
|
||||
<i class="material-icons plex-gamboge-text ptuser-role right" v-bind:style="{ display: isHost }">
|
||||
star
|
||||
</i>
|
||||
</span>
|
||||
<span class="mdc-list-item__text__secondary" id="metaDropdownDiv">
|
||||
<div class="progress" style="margin-bottom: 0;background-color: rgba(242, 243, 244, 0.11)">
|
||||
<div class="determinate ptuser-percent" v-bind:style="{ width: percent }"></div>
|
||||
</div>
|
||||
<div>
|
||||
<small style="float: left" class="ptuser-time">{{ getCurrent }}</small>
|
||||
<small style="float: right" class="ptuser-maxTime">{{ getMax }}</small>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['object'],
|
||||
name: 'ptuser',
|
||||
methods: {
|
||||
getTimeFromMs (ms) {
|
||||
var hours = ms / (1000 * 60 * 60)
|
||||
var absoluteHours = Math.floor(hours)
|
||||
var h = absoluteHours > 9 ? absoluteHours : '0' + absoluteHours
|
||||
var minutes = (hours - absoluteHours) * 60
|
||||
var absoluteMinutes = Math.floor(minutes)
|
||||
var m = absoluteMinutes > 9 ? absoluteMinutes : '0' + absoluteMinutes
|
||||
var seconds = (minutes - absoluteMinutes) * 60
|
||||
var absoluteSeconds = Math.floor(seconds)
|
||||
var s = absoluteSeconds > 9 ? absoluteSeconds : '0' + absoluteSeconds
|
||||
return (h + ':' + m + ':' + s)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isHost: function () {
|
||||
if (this.object.role == 'host') {
|
||||
return 'block'
|
||||
}
|
||||
return 'none'
|
||||
},
|
||||
percent: function () {
|
||||
let perc = (parseInt(this.object.time) / parseInt(this.object.maxTime)) * 100
|
||||
if (isNaN(perc)) {
|
||||
perc = 0
|
||||
}
|
||||
return perc + '%'
|
||||
},
|
||||
getCurrent: function () {
|
||||
if (isNaN(this.object.time)) {
|
||||
return ''
|
||||
}
|
||||
return this.getTimeFromMs(this.object.time)
|
||||
},
|
||||
getMax: function () {
|
||||
if (isNaN(this.object.maxTime)) {
|
||||
return ''
|
||||
}
|
||||
return this.getTimeFromMs(this.object.maxTime)
|
||||
},
|
||||
getTitle: function () {
|
||||
if (this.object.title && this.object.title.length > 0) {
|
||||
return this.object.title
|
||||
}
|
||||
return 'Nothing'
|
||||
},
|
||||
playerState: function () {
|
||||
if (this.object.playerState) {
|
||||
if (this.object.playerState == 'stopped') {
|
||||
return 'pause'
|
||||
}
|
||||
if (this.object.playerState == 'paused') {
|
||||
return 'pause'
|
||||
}
|
||||
if (this.object.playerState == 'playing') {
|
||||
return 'play_arrow'
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -50,6 +50,7 @@ window.EventBus.$on('command', (data) => {
|
|||
}
|
||||
})
|
||||
}
|
||||
return data.callback(true)
|
||||
})
|
||||
|
||||
Vue.mixin({
|
||||
|
|
|
@ -82,10 +82,8 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ptuser from './components/application/ptuser.vue'
|
||||
export default {
|
||||
components: {
|
||||
ptuser
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -338,7 +338,8 @@ const actions = {
|
|||
},
|
||||
NEW_TIMELINE ({ commit, state, dispatch }, data) {
|
||||
// return true
|
||||
let [client, timeline, mediaContainer] = data
|
||||
let timeline = data
|
||||
let client = state.chosenClient
|
||||
// console.log(state)
|
||||
if (!state.chosenClient || (client.clientIdentifier !== state.chosenClient.clientIdentifier)) {
|
||||
console.log('Invalid client')
|
||||
|
|
|
@ -219,4 +219,5 @@ export default {
|
|||
console.log('Updating timeline for', client, 'with', timeline)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,113 +1,111 @@
|
|||
var axios = require('axios');
|
||||
var parseXMLString = require('xml2js').parseString;
|
||||
const EventEmitter = require('events');
|
||||
var _PlexAuth = require('./PlexAuth.js');
|
||||
var PlexAuth = new _PlexAuth();
|
||||
var axios = require('axios')
|
||||
var parseXMLString = require('xml2js').parseString
|
||||
const EventEmitter = require('events')
|
||||
var _PlexAuth = require('./PlexAuth.js')
|
||||
var PlexAuth = new _PlexAuth()
|
||||
|
||||
module.exports = function PlexClient() {
|
||||
this.commandId = 0;
|
||||
this.name = null;
|
||||
this.product = null;
|
||||
this.productVersion = null;
|
||||
this.platform = null;
|
||||
this.platformVersion = null;
|
||||
this.device = null;
|
||||
this.clientIdentifier = null;
|
||||
this.createdAt = null;
|
||||
this.lastSeenAt = null;
|
||||
this.provides = null;
|
||||
this.owned = null;
|
||||
this.publicAddressMatches = null;
|
||||
this.presence = null;
|
||||
this.plexConnections = null;
|
||||
this.chosenConnection = null;
|
||||
this.httpServer = null;
|
||||
this.tempId = null;
|
||||
this.events = new EventEmitter();
|
||||
module.exports = function PlexClient () {
|
||||
this.commandId = 0
|
||||
this.name = null
|
||||
this.product = null
|
||||
this.productVersion = null
|
||||
this.platform = null
|
||||
this.platformVersion = null
|
||||
this.device = null
|
||||
this.clientIdentifier = null
|
||||
this.createdAt = null
|
||||
this.lastSeenAt = null
|
||||
this.provides = null
|
||||
this.owned = null
|
||||
this.publicAddressMatches = null
|
||||
this.presence = null
|
||||
this.plexConnections = null
|
||||
this.chosenConnection = null
|
||||
this.httpServer = null
|
||||
this.tempId = null
|
||||
this.events = new EventEmitter()
|
||||
|
||||
this.userData = null;
|
||||
this.userData = null
|
||||
|
||||
// Latest objects for reference in the future
|
||||
this.lastRatingKey = null;
|
||||
this.lastTimelineObject = null;
|
||||
this.oldTimelineObject = null;
|
||||
this.lastTimeline = null;
|
||||
this.oldTimeline = null;
|
||||
this.clientPlayingMetadata = null;
|
||||
this.lastSubscribe = 0;
|
||||
this.connectedstatus = 'fresh';
|
||||
this.lastRatingKey = null
|
||||
this.lastTimelineObject = null
|
||||
this.oldTimelineObject = null
|
||||
this.lastTimeline = null
|
||||
this.oldTimeline = null
|
||||
this.clientPlayingMetadata = null
|
||||
this.lastSubscribe = 0
|
||||
this.connectedstatus = 'fresh'
|
||||
|
||||
this.eventbus = window.EventBus; // We will use this to communicate with the SLPlayer
|
||||
this.commit = null;
|
||||
this.dispatch = null;
|
||||
this.eventbus = window.EventBus // We will use this to communicate with the SLPlayer
|
||||
this.commit = null
|
||||
this.dispatch = null
|
||||
|
||||
let previousTimeline = {};
|
||||
let previousTimeline = {}
|
||||
|
||||
this.setValue = function (key, value) {
|
||||
this[key] = value;
|
||||
this.commit('PLEX_CLIENT_SET_VALUE', [this, key, value]);
|
||||
};
|
||||
this[key] = value
|
||||
this.commit('PLEX_CLIENT_SET_VALUE', [this, key, value])
|
||||
}
|
||||
|
||||
this.generateGuid = function () {
|
||||
function s4() {
|
||||
function s4 () {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
.substring(1)
|
||||
}
|
||||
|
||||
return s4() + s4() + '-' + s4();
|
||||
};
|
||||
this.uuid = this.generateGuid();
|
||||
return s4() + s4() + '-' + s4()
|
||||
}
|
||||
this.uuid = this.generateGuid()
|
||||
|
||||
this.hitApi = function (command, params, connection) {
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (this.clientIdentifier == 'PTPLAYER9PLUS10') {
|
||||
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
// We are using the SyncLounge Player
|
||||
|
||||
let data = {
|
||||
command: command,
|
||||
params: params,
|
||||
callback: (resultData) => {
|
||||
console.log('Result from player', resultData);
|
||||
if (command === '/player/timeline/poll') {
|
||||
this.updateTimelineObject(resultData);
|
||||
this.updateTimelineObject(resultData)
|
||||
}
|
||||
resolve(resultData, 0, 200, 'PTPLAYER');
|
||||
console.log('Heard back from PTPLAYER', resultData)
|
||||
resolve(resultData)
|
||||
}
|
||||
};
|
||||
this.eventbus.$emit('command', data);
|
||||
|
||||
}
|
||||
this.eventbus.$emit('command', data)
|
||||
} else {
|
||||
const doRequest = () => {
|
||||
if (!connection) {
|
||||
connection = this.chosenConnection;
|
||||
connection = this.chosenConnection
|
||||
}
|
||||
if (!connection) {
|
||||
return reject('No connection specified');
|
||||
return reject(new Error('No connection specified'))
|
||||
}
|
||||
var query = '';
|
||||
var query = ''
|
||||
Object.assign(params, {
|
||||
type: 'video',
|
||||
'X-Plex-Device-Name': 'SyncLounge'
|
||||
});
|
||||
})
|
||||
// console.log(params)
|
||||
for (let key in params) {
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
|
||||
}
|
||||
query = query + 'commandID=' + this.commandId;
|
||||
if (connection.uri.charAt(connection.uri.length - 1) == '/') {
|
||||
query = query + 'commandID=' + this.commandId
|
||||
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);
|
||||
connection.uri = connection.uri.slice(0, connection.uri.length - 1)
|
||||
}
|
||||
var _url = connection.uri + command + '?' + query;
|
||||
this.setValue('commandId', this.commandId + 1);
|
||||
var options = PlexAuth.getClientApiOptions(_url, this.clientIdentifier, this.uuid, 5000);
|
||||
var _url = connection.uri + command + '?' + query
|
||||
this.setValue('commandId', this.commandId + 1)
|
||||
var options = PlexAuth.getClientApiOptions(_url, this.clientIdentifier, this.uuid, 5000)
|
||||
// console.log('sending api request', options)
|
||||
|
||||
if (window.localStorage.getItem('EXTAVAILABLE')) {
|
||||
console.log('Extension is available');
|
||||
chrome.runtime.sendMessage('mlmjjfdcbemagmnjahllphjnohbmhcnf', {
|
||||
console.log('Extension is available')
|
||||
window.chrome.runtime.sendMessage('mlmjjfdcbemagmnjahllphjnohbmhcnf', {
|
||||
command: 'client',
|
||||
data: {
|
||||
url: _url,
|
||||
|
@ -120,316 +118,225 @@ module.exports = function PlexClient() {
|
|||
// console.log('SyncLoungePlus response', response)
|
||||
if (response) {
|
||||
parseXMLString(response, (err, result) => {
|
||||
resolve(result);
|
||||
if (command === '/player/timeline/poll') {
|
||||
this.updateTimelineObject(result);
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
return;
|
||||
});
|
||||
resolve(result)
|
||||
if (command === '/player/timeline/poll') {
|
||||
this.updateTimelineObject(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
axios.get(connection.uri + command, {
|
||||
params,
|
||||
headers: options.headers
|
||||
})
|
||||
.then((response) => {
|
||||
resolve(response);
|
||||
resolve(response)
|
||||
if (command === '/player/timeline/poll') {
|
||||
this.updateTimelineObject(response);
|
||||
this.updateTimelineObject(response)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
reject(error)
|
||||
})
|
||||
}
|
||||
// request(options, (error, response, body) => {
|
||||
// // console.log('response data', response)
|
||||
// if (error) {
|
||||
// return reject(error)
|
||||
|
||||
// } else {
|
||||
// parseXMLString(body, function (err, result) {
|
||||
// if (err || (response.statusCode != 200 && response.statusCode != 201)) {
|
||||
// return reject(err)
|
||||
// }
|
||||
// return resolve(result)
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
if ((new Date().getTime() - this.lastSubscribe) > 29000) {
|
||||
// We need to subscribe first!
|
||||
|
||||
try {
|
||||
// await this.subscribe(connection)
|
||||
doRequest();
|
||||
doRequest()
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
reject(e)
|
||||
}
|
||||
return;
|
||||
|
||||
} else {
|
||||
doRequest();
|
||||
return;
|
||||
doRequest()
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getTimeline = async function (callback) {
|
||||
// Get the timeline object from the client
|
||||
let data;
|
||||
let data
|
||||
try {
|
||||
data = await this.hitApi('/player/timeline/poll', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection);
|
||||
data = await this.hitApi('/player/timeline/poll', { 'wait': 0 }, this.chosenConnection)
|
||||
if (data) {
|
||||
return this.updateTimelineObject(data);
|
||||
return this.updateTimelineObject(data)
|
||||
} else {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e)
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.updateTimelineObject = function (result) {
|
||||
|
||||
// this.events.emit('new_timeline', result)
|
||||
// console.log('New timeline', result)
|
||||
// Check if we are the SLPlayer
|
||||
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
// SLPLAYER
|
||||
let tempObj = {
|
||||
MediaContainer: {
|
||||
timelines: [result]
|
||||
}
|
||||
};
|
||||
result = tempObj;
|
||||
// this.events.emit('new_timeline', result);
|
||||
// var clonetimeline = this.lastTimelineObject;
|
||||
|
||||
// if (!this.oldTimelineObject) {
|
||||
// if (!this.lastTimelineObject.ratingKey) {
|
||||
// this.events.emit('playback_change', null);
|
||||
// } else {
|
||||
// this.events.emit('playback_change', this.lastTimelineObject.ratingKey);
|
||||
// }
|
||||
// this.setValue('oldTimelineObject', result);
|
||||
// // this.oldTimelineObject = result
|
||||
// return callback(result);
|
||||
// }
|
||||
// this.setValue('oldTimelineObject', clonetimeline);
|
||||
// if (this.oldTimelineObject.ratingKey != this.lastTimelineObject.ratingKey) {
|
||||
// if (!this.lastTimelineObject.ratingKey) {
|
||||
// this.events.emit('playback_change', null);
|
||||
// } else {
|
||||
// this.events.emit('playback_change', this.lastTimelineObject.ratingKey);
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
}
|
||||
|
||||
if (!result.MediaContainer.Timeline) {
|
||||
// Not a valid timeline object
|
||||
return false;
|
||||
}
|
||||
// Valid timeline data
|
||||
// Standard player
|
||||
let timelines = result.MediaContainer.Timeline;
|
||||
let videoTimeline = {};
|
||||
for (let i = 0; i < timelines.length; i++) {
|
||||
let _timeline = timelines[i]['$'];
|
||||
if (_timeline.type == 'video') {
|
||||
videoTimeline = _timeline;
|
||||
console.log('Does', videoTimeline.ratingKey + ' equal ' + previousTimeline.ratingKey);
|
||||
if (videoTimeline.ratingKey !== previousTimeline.ratingKey) {
|
||||
window.EventBus.$emit('PLAYBACK_CHANGE', [this, videoTimeline.ratingKey, result.MediaContainer]);
|
||||
Timeline: [result]
|
||||
}
|
||||
}
|
||||
// if ((_timeline.state && _timeline.state != 'stopped') || i == (timelines.length - 1)) {
|
||||
// this.events.emit('new_timeline', timelines[i]['$'])
|
||||
// var clonetimeline = this.lastTimelineObject
|
||||
// this.lastTimelineObject = timelines[i]['$']
|
||||
// this.setValue('lastTimelineObject', timelines[i]['$'])
|
||||
// if (!this.oldTimelineObject) {
|
||||
// // First time we've got data!
|
||||
// if (!this.lastTimelineObject.ratingKey) {
|
||||
// this.events.emit('playback_change', null)
|
||||
// } else {
|
||||
// this.events.emit('playback_change', this.lastTimelineObject.ratingKey)
|
||||
// }
|
||||
// this.setValue('oldTimelineObject', timelines[i]['$'])
|
||||
// // this.oldTimelineObject = timelines[i]['$']
|
||||
// return timelines[i]['$']
|
||||
// }
|
||||
// this.setValue('oldTimelineObject', clonetimeline)
|
||||
// this.oldTimelineObject = clonetimeline
|
||||
// if (this.oldTimelineObject.ratingKey != result.ratingKey) {
|
||||
// if (!this.lastTimelineObject.ratingKey) {
|
||||
// this.events.emit('playback_change', null)
|
||||
// } else {
|
||||
// this.events.emit('playback_change', result.ratingKey)
|
||||
// }
|
||||
// }
|
||||
// return timelines[i]['$']
|
||||
// }
|
||||
result = tempObj
|
||||
if (!previousTimeline.MediaContainer || result.MediaContainer.Timeline[0].ratingKey !== previousTimeline.MediaContainer.Timeline[0].ratingKey) {
|
||||
window.EventBus.$emit('PLAYBACK_CHANGE', [this, result.MediaContainer.Timeline[0].ratingKey, result.MediaContainer.Timeline[0]])
|
||||
}
|
||||
previousTimeline = result
|
||||
window.EventBus.$emit('NEW_TIMELINE', result.MediaContainer.Timeline[0])
|
||||
return result
|
||||
}
|
||||
window.EventBus.$emit('NEW_TIMELINE', [this, videoTimeline, result.MediaContainer]);
|
||||
previousTimeline = videoTimeline;
|
||||
// Standard player
|
||||
let timelines = result.MediaContainer.Timeline
|
||||
let videoTimeline = {}
|
||||
for (let i = 0; i < timelines.length; i++) {
|
||||
let _timeline = timelines[i]['$']
|
||||
if (_timeline.type === 'video') {
|
||||
videoTimeline = _timeline
|
||||
console.log('Does', videoTimeline.ratingKey + ' equal ' + previousTimeline.ratingKey)
|
||||
if (videoTimeline.ratingKey !== previousTimeline.ratingKey) {
|
||||
window.EventBus.$emit('PLAYBACK_CHANGE', [this, videoTimeline.ratingKey, result.MediaContainer])
|
||||
}
|
||||
window.EventBus.$emit('NEW_TIMELINE', videoTimeline)
|
||||
}
|
||||
}
|
||||
window.EventBus.$emit('NEW_TIMELINE', [this, videoTimeline, result.MediaContainer])
|
||||
previousTimeline = videoTimeline
|
||||
// this.setValue('lastTimelineObject', videoTimeline)
|
||||
return videoTimeline;
|
||||
};
|
||||
return videoTimeline
|
||||
}
|
||||
this.pressPlay = function (callback) {
|
||||
// Press play on the client
|
||||
this.hitApi('/player/playback/play', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result, responseTime) => {
|
||||
this.hitApi('/player/playback/play', { 'wait': 0 }, this.chosenConnection).then((result, responseTime) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
return callback(result, responseTime);
|
||||
// Valid response back from the client
|
||||
return callback(result, responseTime)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.pressPause = function (callback) {
|
||||
// Press pause on the client
|
||||
this.hitApi('/player/playback/pause', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result, responseTime) => {
|
||||
this.hitApi('/player/playback/pause', { 'wait': 0 }, this.chosenConnection).then((result, responseTime) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
return callback(result, responseTime);
|
||||
// Valid response back from the client
|
||||
return callback(result, responseTime)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.pressStop = function (callback) {
|
||||
// Press pause on the client
|
||||
this.hitApi('/player/playback/stop', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result, responseTime) => {
|
||||
this.hitApi('/player/playback/stop', { 'wait': 0 }, this.chosenConnection).then((result, responseTime) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
return callback(result, responseTime);
|
||||
// Valid response back from the client
|
||||
return callback(result, responseTime)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.seekTo = function (time, callback) {
|
||||
// Seek to a time (in ms)
|
||||
this.hitApi('/player/playback/seekTo', {
|
||||
'wait': 0,
|
||||
'offset': time
|
||||
}, this.chosenConnection, function (result, responseTime) {
|
||||
this.hitApi('/player/playback/seekTo', { 'wait': 0, 'offset': time }, this.chosenConnection, function (result, responseTime) {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
return callback(result, responseTime);
|
||||
// Valid response back from the client
|
||||
return callback(result, responseTime)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getRatingKey = function (callback) {
|
||||
// Get the ratingKey, aka the mediaId, of the item playing
|
||||
this.hitApi('/player/timeline/poll', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result) => {
|
||||
this.hitApi('/player/timeline/poll', { 'wait': 0 }, this.chosenConnection).then((result) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline;
|
||||
// Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline
|
||||
for (var i in allTimelines) {
|
||||
var timeline = allTimelines[i]['$'];
|
||||
//We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type == 'video') {
|
||||
return callback(timeline.ratingKey);
|
||||
var timeline = allTimelines[i]['$']
|
||||
// We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type === 'video') {
|
||||
return callback(timeline.ratingKey)
|
||||
}
|
||||
}
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getServerId = function (callback) {
|
||||
// Get the machineId of the server we're playing from'
|
||||
this.hitApi('/player/timeline/poll', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result) => {
|
||||
this.hitApi('/player/timeline/poll', { 'wait': 0 }, this.chosenConnection).then((result) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline;
|
||||
// Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline
|
||||
for (var i in allTimelines) {
|
||||
var timeline = allTimelines[i]['$'];
|
||||
//We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type == 'video') {
|
||||
return callback(timeline.machineIdentifier);
|
||||
var timeline = allTimelines[i]['$']
|
||||
// We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type === 'video') {
|
||||
return callback(timeline.machineIdentifier)
|
||||
}
|
||||
}
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getPlayerState = function (callback) {
|
||||
// Get the Player State (playing, paused or stopped)
|
||||
this.hitApi('/player/timeline/poll', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result) => {
|
||||
this.hitApi('/player/timeline/poll', { 'wait': 0 }, this.chosenConnection).then((result) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline;
|
||||
// Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline
|
||||
for (var i in allTimelines) {
|
||||
var timeline = allTimelines[i]['$'];
|
||||
//We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type == 'video') {
|
||||
return callback(timeline.state);
|
||||
var timeline = allTimelines[i]['$']
|
||||
// We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type === 'video') {
|
||||
return callback(timeline.state)
|
||||
}
|
||||
}
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
} else {
|
||||
return callback(null);
|
||||
return callback(null)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getPlayerTime = function (callback) {
|
||||
// Get the current playback time in ms
|
||||
this.hitApi('/player/timeline/poll', {
|
||||
'wait': 0
|
||||
}, this.chosenConnection).then((result, responseTime, code) => {
|
||||
this.hitApi('/player/timeline/poll', { 'wait': 0 }, this.chosenConnection).then((result, responseTime, code) => {
|
||||
if (result) {
|
||||
//Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline;
|
||||
// Valid response back from the client
|
||||
var allTimelines = result.MediaContainer.Timeline
|
||||
for (var i in allTimelines) {
|
||||
var timeline = allTimelines[i]['$'];
|
||||
//We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type == 'video') {
|
||||
return callback(timeline.time, responseTime);
|
||||
var timeline = allTimelines[i]['$']
|
||||
// We only want the rating key of whatever is playing in the video timeline
|
||||
if (timeline.type === 'video') {
|
||||
return callback(timeline.time, responseTime)
|
||||
}
|
||||
}
|
||||
return callback(null, responseTime);
|
||||
return callback(null, responseTime)
|
||||
} else {
|
||||
return callback(null, responseTime);
|
||||
return callback(null, responseTime)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.playMedia = async function (data) {
|
||||
// Play a media item given a mediaId key and a server to play from
|
||||
|
@ -438,60 +345,63 @@ module.exports = function 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) => {
|
||||
console.log('Autoplaying from client', data)
|
||||
const send = async () => {
|
||||
let command = '/player/playback/playMedia'
|
||||
let mediaId = '/library/metadata/' + data.ratingKey
|
||||
let offset = data.offset || 0
|
||||
let serverId = data.server.clientIdentifier
|
||||
let address = data.server.chosenConnection.address
|
||||
let port = data.server.chosenConnection.port
|
||||
let protocol = data.server.chosenConnection.protocol
|
||||
let path = data.server.chosenConnection.uri + mediaId
|
||||
|
||||
const send = async () => {
|
||||
let command = '/player/playback/playMedia';
|
||||
let mediaId = '/library/metadata/' + data.ratingKey;
|
||||
let offset = data.offset || 0;
|
||||
let serverId = data.server.clientIdentifier;
|
||||
let address = data.server.chosenConnection.address;
|
||||
let port = data.server.chosenConnection.port;
|
||||
let protocol = data.server.chosenConnection.protocol;
|
||||
let path = data.server.chosenConnection.uri + mediaId;
|
||||
let params = {
|
||||
'X-Plex-Client-Identifier': 'SyncLounge',
|
||||
'key': mediaId,
|
||||
'offset': offset,
|
||||
'machineIdentifier': serverId,
|
||||
'address': address,
|
||||
'port': port,
|
||||
'protocol': protocol,
|
||||
'path': path,
|
||||
'wait': 0,
|
||||
'token': data.server.accessToken
|
||||
}
|
||||
|
||||
let params = {
|
||||
'X-Plex-Client-Identifier': 'SyncLounge',
|
||||
'key': mediaId,
|
||||
'offset': offset,
|
||||
'machineIdentifier': serverId,
|
||||
'address': address,
|
||||
'port': port,
|
||||
'protocol': protocol,
|
||||
'path': path,
|
||||
'wait': 0,
|
||||
'token': data.server.accessToken
|
||||
};
|
||||
if (data.mediaIndex !== undefined || data.mediaIndex !== null) {
|
||||
params.mediaIndex = data.mediaIndex
|
||||
}
|
||||
|
||||
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
|
||||
console.log('Sending command')
|
||||
await this.hitApi(command, params, this.chosenConnection)
|
||||
console.log('PlayMedia DONE')
|
||||
resolve(true)
|
||||
}
|
||||
|
||||
// Now that we've built our params, it's time to hit the client api
|
||||
return this.hitApi(command, params, this.chosenConnection);
|
||||
|
||||
};
|
||||
|
||||
if (this.clientIdentifier == 'PTPLAYER9PLUS10') {
|
||||
return send();
|
||||
} else {
|
||||
await this.mirrorContent(data.ratingKey, data.server);
|
||||
return send();
|
||||
}
|
||||
};
|
||||
if (this.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
return send()
|
||||
} else {
|
||||
await this.mirrorContent(data.ratingKey, data.server)
|
||||
return send()
|
||||
}
|
||||
})
|
||||
}
|
||||
this.mirrorContent = function (key, serverObject, callback) {
|
||||
// Mirror a media item given a mediaId key and a server to play from
|
||||
// We need the following variables to build our paramaters:
|
||||
// MediaId Key, Offset (0 for simplicity), server MachineId,
|
||||
// Server Ip, Server Port, Server Protocol, Path
|
||||
|
||||
let command = '/player/mirror/details';
|
||||
let mediaId = '/library/metadata/' + key;
|
||||
let offset = 0;
|
||||
let serverId = serverObject.clientIdentifier;
|
||||
let address = serverObject.chosenConnection.address;
|
||||
let port = serverObject.chosenConnection.port;
|
||||
let protocol = serverObject.chosenConnection.protocol;
|
||||
let path = serverObject.chosenConnection.uri + mediaId;
|
||||
let command = '/player/mirror/details'
|
||||
let mediaId = '/library/metadata/' + key
|
||||
let serverId = serverObject.clientIdentifier
|
||||
let address = serverObject.chosenConnection.address
|
||||
let port = serverObject.chosenConnection.port
|
||||
let protocol = serverObject.chosenConnection.protocol
|
||||
let path = serverObject.chosenConnection.uri + mediaId
|
||||
|
||||
let params = {
|
||||
'X-Plex-Client-Identifier': 'SyncLounge',
|
||||
|
@ -503,95 +413,181 @@ module.exports = function PlexClient() {
|
|||
'path': path,
|
||||
'wait': 0,
|
||||
'token': serverObject.accessToken
|
||||
};
|
||||
}
|
||||
// Now that we've built our params, it's time to hit the client api
|
||||
return this.hitApi(command, params, this.chosenConnection);
|
||||
};
|
||||
return this.hitApi(command, params, this.chosenConnection)
|
||||
}
|
||||
this.subscribe = function (connection, commit) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const doRequest = () => {
|
||||
// Already have a valid http server running, lets send the request
|
||||
if (!connection) {
|
||||
// It is possible to try to subscribe before we've found a working connection
|
||||
connection = this.chosenConnection;
|
||||
connection = this.chosenConnection
|
||||
}
|
||||
var command = '/player/timeline/subscribe';
|
||||
var command = '/player/timeline/subscribe'
|
||||
var params = {
|
||||
'port': '8555',
|
||||
'protocol': 'http',
|
||||
'X-Plex-Device-Name': 'SyncLounge'
|
||||
};
|
||||
// Now that we've built our params, it's time to hit the client api
|
||||
var query = '';
|
||||
}
|
||||
// Now that we've built our params, it's time to hit the client api
|
||||
var query = ''
|
||||
for (let key in params) {
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
|
||||
}
|
||||
query = query + 'commandID=' + this.commandId;
|
||||
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);
|
||||
query = query + 'commandID=' + this.commandId
|
||||
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)
|
||||
}
|
||||
|
||||
var _url = connection.uri + command + '?' + query;
|
||||
var _url = connection.uri + command + '?' + query
|
||||
// console.log('subscription url: ' + _url)
|
||||
this.commandId = this.commandId + 1;
|
||||
this.setValue('commandId', this.commandId + 1);
|
||||
var options = PlexAuth.getClientApiOptions(_url, this.clientIdentifier, this.uuid, 5000);
|
||||
console.log(options);
|
||||
request(options, (error, response, body) => {
|
||||
// console.log('subscription result', response)
|
||||
this.setValue('lastSubscribe', new Date().getTime());
|
||||
if (error) {
|
||||
return reject(error);
|
||||
} else {
|
||||
return resolve(true);
|
||||
this.commandId = this.commandId + 1
|
||||
this.setValue('commandId', this.commandId + 1)
|
||||
var options = PlexAuth.getClientApiOptions(_url, this.clientIdentifier, this.uuid, 5000)
|
||||
console.log(options)
|
||||
resolve()
|
||||
// request(options, (error, response, body) => {
|
||||
// // console.log('subscription result', response)
|
||||
// this.setValue('lastSubscribe', new Date().getTime())
|
||||
// if (error) {
|
||||
// return reject(error)
|
||||
// } else {
|
||||
// return resolve(true)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
doRequest()
|
||||
})
|
||||
}
|
||||
// this.unsubscribe = function (callback) {
|
||||
// // var that = this
|
||||
// // doRequest()
|
||||
|
||||
// // function doRequest () {
|
||||
// // // Already have a valid http server running, lets send the request
|
||||
// // let tempId = 'SyncLoungeWeb'
|
||||
// // var command = '/player/timeline/unsubscribe'
|
||||
// // var params = {
|
||||
// // 'port': '8555',
|
||||
// // 'protocol': 'http',
|
||||
// // 'X-Plex-Device-Name': 'SyncLounge'
|
||||
// // }
|
||||
// // // Now that we've built our params, it's time to hit the client api
|
||||
|
||||
// // var query = ''
|
||||
// // for (let key in params) {
|
||||
// // query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
|
||||
// // }
|
||||
// // query = query + 'commandID=' + that.commandId
|
||||
// // 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)
|
||||
// // }
|
||||
// // if (that.chosenConnection == null) {
|
||||
// // // It is possible to try to subscribe before we've found a working connection
|
||||
// // console.log('Chosen connection has not been set yet.')
|
||||
// // return (callback(false))
|
||||
// // }
|
||||
// // var _url = that.chosenConnection.uri + command + '?' + query
|
||||
// // // console.log('subscription url: ' + _url)
|
||||
// // that.commandId = that.commandId + 1
|
||||
// // var options = PlexAuth.getClientApiOptions(_url, that.clientIdentifier, that.uuid, 5000)
|
||||
// // request(options, function (error, response, body) {
|
||||
// // if (!error) {
|
||||
// // return callback(true, that)
|
||||
// // } else {
|
||||
// // return callback(false, that)
|
||||
// // }
|
||||
// // })
|
||||
// // }
|
||||
// },
|
||||
this.playContentAutomatically = function (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 = []
|
||||
console.log('Autoplay', client, hostData, servers, offset)
|
||||
let serversArr = []
|
||||
for (let i in servers) {
|
||||
serversArr.push(servers[i])
|
||||
}
|
||||
await Promise.all(serversArr.map(async (server) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if (!server.chosenConnection) {
|
||||
return resolve()
|
||||
}
|
||||
});
|
||||
};
|
||||
doRequest();
|
||||
});
|
||||
|
||||
};
|
||||
this.unsubscribe = function (callback) {
|
||||
var that = this;
|
||||
doRequest();
|
||||
|
||||
function doRequest() {
|
||||
// Already have a valid http server running, lets send the request
|
||||
let tempId = 'SyncLoungeWeb';
|
||||
var command = '/player/timeline/unsubscribe';
|
||||
var params = {
|
||||
'port': '8555',
|
||||
'protocol': 'http',
|
||||
'X-Plex-Device-Name': 'SyncLounge'
|
||||
};
|
||||
//Now that we've built our params, it's time to hit the client api
|
||||
|
||||
var query = '';
|
||||
for (key in params) {
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';
|
||||
}
|
||||
query = query + 'commandID=' + that.commandId;
|
||||
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);
|
||||
}
|
||||
if (that.chosenConnection == null) {
|
||||
// It is possible to try to subscribe before we've found a working connection
|
||||
console.log('Chosen connection has not been set yet.');
|
||||
return (callback(false));
|
||||
}
|
||||
var _url = that.chosenConnection.uri + command + '?' + query;
|
||||
// console.log('subscription url: ' + _url)
|
||||
that.commandId = that.commandId + 1;
|
||||
var options = PlexAuth.getClientApiOptions(_url, that.clientIdentifier, that.uuid, 5000);
|
||||
request(options, function (error, response, body) {
|
||||
if (!error) {
|
||||
return callback(true, that);
|
||||
} else {
|
||||
return callback(false, that);
|
||||
let results = await server.search(hostData.rawTitle)
|
||||
console.log(server.name, 'found', results.length, 'results')
|
||||
for (var k = 0; k < results.length; k++) {
|
||||
// Now we need to check the result
|
||||
if (checkResult(results[k], hostData)) {
|
||||
// Its a match!
|
||||
playables.push({
|
||||
'server': server,
|
||||
'result': results[k]
|
||||
})
|
||||
}
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
}))
|
||||
const start = async (index) => {
|
||||
// Now lets try and play our items one by one
|
||||
if (playables.length === 0) {
|
||||
return reject(new Error('Didnt find any playable items'))
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
var server = playables[index].server
|
||||
var ratingKey = playables[index].result.ratingKey
|
||||
let data = {
|
||||
ratingKey: ratingKey,
|
||||
mediaIndex: null,
|
||||
server: server,
|
||||
offset: offset || 0
|
||||
}
|
||||
if (client.clientIdentifier !== 'PTPLAYER9PLUS10') {
|
||||
await client.subscribe()
|
||||
}
|
||||
let res = await this.playMedia(data).catch(() => {
|
||||
start(parseInt(parseInt(index) + 1))
|
||||
})
|
||||
console.log('Autoplayer await result', res)
|
||||
return resolve()
|
||||
}
|
||||
start(0)
|
||||
|
||||
function checkResult (data, hostData) {
|
||||
console.log('Checking compatibility for this item', data, hostData)
|
||||
// Do a series of checks to see if this result is OK
|
||||
// Check if rawTitle matches
|
||||
if (data.title !== hostData.rawTitle) {
|
||||
// global.renderLog.info('wrong title')
|
||||
return false
|
||||
}
|
||||
// Check if length is close enough
|
||||
if (Math.abs(parseInt(data.duration) - parseInt(hostData.maxTime)) > 5000 || !data.duration) {
|
||||
// global.renderLog.info('wrong time')
|
||||
return false
|
||||
}
|
||||
if (data.type === 'movie') {
|
||||
// We're good!
|
||||
console.log('FOUND A PLAYABLE MOVIE')
|
||||
return true
|
||||
}
|
||||
if (data.type === 'episode') {
|
||||
// Check if the show name is the same
|
||||
console.log('FOUND A PLAYABLE TV EPISODE')
|
||||
return true
|
||||
}
|
||||
if (data.type === 'track') {
|
||||
// We're good!
|
||||
console.log('FOUND A PLAYABLE track')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
module.exports = function PlexConnection() {
|
||||
this.protocol = null;
|
||||
this.address = null;
|
||||
this.port = null;
|
||||
this.uri = null;
|
||||
this.local = null;
|
||||
};
|
||||
module.exports = function PlexConnection () {
|
||||
this.protocol = null
|
||||
this.address = null
|
||||
this.port = null
|
||||
this.uri = null
|
||||
this.local = null
|
||||
}
|
||||
|
|
|
@ -1,240 +1,239 @@
|
|||
var request = require('request');
|
||||
var safeParse = require('safe-json-parse/callback');
|
||||
var _PlexAuth = require('./PlexAuth.js');
|
||||
var PlexAuth = new _PlexAuth();
|
||||
var request = require('request')
|
||||
var safeParse = require('safe-json-parse/callback')
|
||||
var _PlexAuth = require('./PlexAuth.js')
|
||||
var PlexAuth = new _PlexAuth()
|
||||
|
||||
module.exports = function PlexServer() {
|
||||
this.name;
|
||||
this.product;
|
||||
this.productVersion;
|
||||
this.platform;
|
||||
this.platformVersion;
|
||||
this.device;
|
||||
this.clientIdentifier;
|
||||
this.createdAt;
|
||||
this.lastSeenAt;
|
||||
this.provides;
|
||||
this.owned;
|
||||
this.httpsRequired;
|
||||
this.ownerId;
|
||||
this.home;
|
||||
this.accessToken;
|
||||
this.sourceTitle;
|
||||
this.synced;
|
||||
this.relay;
|
||||
this.publicAddressMatches;
|
||||
this.presence;
|
||||
this.plexConnections;
|
||||
this.chosenConnection = null;
|
||||
module.exports = function PlexServer () {
|
||||
this.name = ''
|
||||
this.product = ''
|
||||
this.productVersion = ''
|
||||
this.platform = ''
|
||||
this.platformVersion = ''
|
||||
this.device = ''
|
||||
this.clientIdentifier = ''
|
||||
this.createdAt = ''
|
||||
this.lastSeenAt = ''
|
||||
this.provides = ''
|
||||
this.owned = ''
|
||||
this.httpsRequired = ''
|
||||
this.ownerId = ''
|
||||
this.home = ''
|
||||
this.accessToken = ''
|
||||
this.sourceTitle = ''
|
||||
this.synced = ''
|
||||
this.relay = ''
|
||||
this.publicAddressMatches = ''
|
||||
this.presence = ''
|
||||
this.plexConnections = ''
|
||||
this.chosenConnection = null
|
||||
|
||||
this.commit;
|
||||
this.commit = null
|
||||
|
||||
this.setValue = function (key, value) {
|
||||
this[key] = value;
|
||||
this.commit('PLEX_SERVER_SET_VALUE', [this, key, value]);
|
||||
};
|
||||
this[key] = value
|
||||
this.commit('PLEX_SERVER_SET_VALUE', [this, key, value])
|
||||
}
|
||||
|
||||
// Functions
|
||||
this.hitApi = function (command, params) {
|
||||
return new Promise(async(resolve, reject) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
let query = '';
|
||||
//console.log('Query params: ' + JSON.stringify(params))
|
||||
let query = ''
|
||||
// console.log('Query params: ' + JSON.stringify(params))
|
||||
for (let key in params) {
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&';
|
||||
query += encodeURIComponent(key) + '=' + encodeURIComponent(params[key]) + '&'
|
||||
}
|
||||
if (!this.chosenConnection) {
|
||||
let result = await this.findConnection();
|
||||
let result = await this.findConnection()
|
||||
if (!result) {
|
||||
return reject('Failed to find a connection');
|
||||
return reject(new Error('Failed to find a connection'))
|
||||
}
|
||||
}
|
||||
var _url = this.chosenConnection.uri + command + '?' + query;
|
||||
var options = PlexAuth.getApiOptions(_url, this.accessToken, 15000, 'GET');
|
||||
var _url = this.chosenConnection.uri + command + '?' + query
|
||||
var options = PlexAuth.getApiOptions(_url, this.accessToken, 15000, 'GET')
|
||||
request(options, (error, response, body) => {
|
||||
if (!error) {
|
||||
let parsed = JSON.parse(body);
|
||||
console.log('Metadata request result', parsed);
|
||||
this.handleMetadata(parsed);
|
||||
return resolve(parsed);
|
||||
} else return reject(false, error);
|
||||
});
|
||||
let parsed = JSON.parse(body)
|
||||
console.log('Metadata request result', parsed)
|
||||
this.handleMetadata(parsed)
|
||||
return resolve(parsed)
|
||||
} else return reject(error)
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
reject(e);
|
||||
console.log(e)
|
||||
reject(e)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.hitApiTestConnection = async function (command, connection) {
|
||||
return new Promise(async(resolve, reject) => {
|
||||
var _url = connection.uri + command;
|
||||
var options = PlexAuth.getApiOptions(_url, this.accessToken, 7500, 'GET');
|
||||
return new Promise(async (resolve, reject) => {
|
||||
var _url = connection.uri + command
|
||||
var options = PlexAuth.getApiOptions(_url, this.accessToken, 7500, 'GET')
|
||||
request(options, function (error, response, body) {
|
||||
if (!error) {
|
||||
safeParse(body, function (err, json) {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
return reject(err)
|
||||
}
|
||||
return resolve(json);
|
||||
});
|
||||
return resolve(json)
|
||||
})
|
||||
} else {
|
||||
return reject(null);
|
||||
return reject(error)
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
})
|
||||
})
|
||||
}
|
||||
this.setChosenConnection = function (con) {
|
||||
this.chosenConnection = con;
|
||||
return;
|
||||
};
|
||||
this.chosenConnection = con
|
||||
}
|
||||
this.findConnection = function () {
|
||||
// This function iterates through all available connections and
|
||||
// if any of them return a valid response we'll set that connection
|
||||
// as the chosen connection for future use.
|
||||
let resolved = false;
|
||||
let resolved = false
|
||||
|
||||
return new Promise(async(resolve, reject) => {
|
||||
await Promise.all(this.plexConnections.map(async(connection, index) => {
|
||||
return new Promise(async(_resolve, _reject) => {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await Promise.all(this.plexConnections.map(async (connection, index) => {
|
||||
/*eslint-disable */
|
||||
return new Promise(async (_resolve, _reject) => {
|
||||
try {
|
||||
let result = await this.hitApiTestConnection('', connection);
|
||||
let result = await this.hitApiTestConnection('', connection)
|
||||
if (result) {
|
||||
resolved = true;
|
||||
//console.log('Succesfully connected to', server, 'via', connection)
|
||||
this.setValue('chosenConnection', connection);
|
||||
resolve(true);
|
||||
resolved = true
|
||||
// console.log('Succesfully connected to', server, 'via', connection)
|
||||
this.setValue('chosenConnection', connection)
|
||||
resolve(true)
|
||||
}
|
||||
_resolve(false);
|
||||
_resolve(false)
|
||||
} catch (e) {
|
||||
_resolve(false);
|
||||
_resolve(false)
|
||||
}
|
||||
});
|
||||
}));
|
||||
})
|
||||
/* eslint-enable */
|
||||
}))
|
||||
if (!resolved) {
|
||||
reject('Unable to find a connection');
|
||||
reject(new Error('Unable to find a connection'))
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
//Functions for dealing with media
|
||||
// Functions for dealing with media
|
||||
this.search = async function (searchTerm) {
|
||||
//This function hits the PMS using the /search endpoint and returns what the server returns if valid
|
||||
let result = await this.hitApi('/search', {
|
||||
query: searchTerm
|
||||
});
|
||||
let validResults = [];
|
||||
if (result && result.MediaContainer) {
|
||||
if (result.MediaContainer.Metadata) {
|
||||
for (let i = 0; i < result.MediaContainer.Metadata.length; i++) {
|
||||
validResults.push(result.MediaContainer.Metadata[i]);
|
||||
// This function hits the PMS using the /search endpoint and returns what the server returns if valid
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let result = await this.hitApi('/search', { query: searchTerm })
|
||||
let validResults = []
|
||||
if (result && result.MediaContainer) {
|
||||
if (result.MediaContainer.Metadata) {
|
||||
for (let i = 0; i < result.MediaContainer.Metadata.length; i++) {
|
||||
validResults.push(result.MediaContainer.Metadata[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return validResults;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return resolve(validResults)
|
||||
})
|
||||
}
|
||||
|
||||
this.getMediaByRatingKey = async function (ratingKey) {
|
||||
//This function hits the PMS and returns the item at the ratingKey
|
||||
// This function hits the PMS and returns the item at the ratingKey
|
||||
try {
|
||||
let data = await this.hitApi('/library/metadata/' + ratingKey, {});
|
||||
let data = await this.hitApi('/library/metadata/' + ratingKey, {})
|
||||
if (data && data.MediaContainer.librarySectionID) {
|
||||
this.commit('SET_LIBRARYCACHE', [data.MediaContainer.librarySectionID, this.clientIdentifier, data.MediaContainer.librarySectionTitle]);
|
||||
this.commit('SET_LIBRARYCACHE', [data.MediaContainer.librarySectionID, this.clientIdentifier, data.MediaContainer.librarySectionTitle])
|
||||
}
|
||||
return data;
|
||||
return data
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
console.log(e)
|
||||
return false
|
||||
}
|
||||
// return this.handleMetadata(data)
|
||||
};
|
||||
}
|
||||
this.markWatchedByRatingKey = function (ratingKey) {
|
||||
return this.hitApi('/:/scrobble', {
|
||||
identifier: 'com.plexapp.plugins.library',
|
||||
key: ratingKey
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.getUrlForLibraryLoc = function (location, width, height, blur) {
|
||||
if (!(blur > 0)) {
|
||||
blur = 0;
|
||||
blur = 0
|
||||
}
|
||||
return this.chosenConnection.uri + '/photo/:/transcode?url=' + location + '&X-Plex-Token=' + this.accessToken + '&height=' + Math.floor(height) + '&width=' + Math.floor(width) + '&blur=' + blur;
|
||||
};
|
||||
return this.chosenConnection.uri + '/photo/:/transcode?url=' + location + '&X-Plex-Token=' + this.accessToken + '&height=' + Math.floor(height) + '&width=' + Math.floor(width) + '&blur=' + blur
|
||||
}
|
||||
this.getRandomItem = async function () {
|
||||
try {
|
||||
let data = await this.getAllLibraries();
|
||||
let data = await this.getAllLibraries()
|
||||
if (!data || !data.MediaContainer || !data.MediaContainer.Directory) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
let libraries = data.MediaContainer.Directory;
|
||||
let library = libraries[Math.floor(Math.random() * libraries.length)];
|
||||
let libraries = data.MediaContainer.Directory
|
||||
let library = libraries[Math.floor(Math.random() * libraries.length)]
|
||||
|
||||
let result = await this.getLibraryContents(library.key, 0, 50);
|
||||
let result = await this.getLibraryContents(library.key, 0, 50)
|
||||
if (!result) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
let items = result.MediaContainer.Metadata;
|
||||
let item = items[Math.floor(Math.random() * items.length)];
|
||||
return item;
|
||||
|
||||
let items = result.MediaContainer.Metadata
|
||||
let item = items[Math.floor(Math.random() * items.length)]
|
||||
return item
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new Error(e);
|
||||
console.log(e)
|
||||
throw new Error(e)
|
||||
}
|
||||
};
|
||||
}
|
||||
this.getAllLibraries = async function () {
|
||||
try {
|
||||
let data = await this.hitApi('/library/sections', {});
|
||||
console.log('Library data', data);
|
||||
let data = await this.hitApi('/library/sections', {})
|
||||
console.log('Library data', data)
|
||||
if (data && data.MediaContainer) {
|
||||
data.MediaContainer.Directory.forEach((library) => {
|
||||
this.commit('SET_LIBRARYCACHE', [library.key, this.clientIdentifier, library.title]);
|
||||
});
|
||||
this.commit('SET_LIBRARYCACHE', [library.key, this.clientIdentifier, library.title])
|
||||
})
|
||||
}
|
||||
return data;
|
||||
return data
|
||||
} catch (e) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
};
|
||||
}
|
||||
this.getLibraryContents = async function (key, start, size) {
|
||||
try {
|
||||
let data = await this.hitApi('/library/sections/' + key + '/all', {
|
||||
'X-Plex-Container-Start': start,
|
||||
'X-Plex-Container-Size': size,
|
||||
'excludeAllLeaves': 1
|
||||
});
|
||||
})
|
||||
for (let i = 0; i < data.MediaContainer.Metadata.length; i++) {
|
||||
data.MediaContainer.Metadata[i].librarySectionID = key;
|
||||
// this.commit('SET_ITEMCACHE', [data.MediaContainer.Metadata[i].ratingKey,
|
||||
// data.MediaContainer.Metadata[i]])
|
||||
data.MediaContainer.Metadata[i].librarySectionID = key
|
||||
// this.commit('SET_ITEMCACHE', [data.MediaContainer.Metadata[i].ratingKey,
|
||||
// data.MediaContainer.Metadata[i]])
|
||||
}
|
||||
return data;
|
||||
return data
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
console.log(e)
|
||||
return false
|
||||
}
|
||||
};
|
||||
}
|
||||
this.getRelated = function (key, count) {
|
||||
return this.hitApi('/hubs/metadata/' + key + '/related', {
|
||||
'count': count || 10
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.getRecentlyAddedAll = function (start, size) {
|
||||
return this.hitApi('/library/recentlyAdded', {});
|
||||
};
|
||||
return this.hitApi('/library/recentlyAdded', {})
|
||||
}
|
||||
this.getOnDeck = function (start, size) {
|
||||
return this.hitApi('/library/onDeck', {
|
||||
'X-Plex-Container-Start': start,
|
||||
'X-Plex-Container-Size': size,
|
||||
});
|
||||
};
|
||||
'X-Plex-Container-Size': size
|
||||
})
|
||||
}
|
||||
this.getRelated = function (ratingKey, size) {
|
||||
ratingKey = ratingKey.replace('/library/metadata/', '');
|
||||
ratingKey = ratingKey.replace('/library/metadata/', '')
|
||||
return this.hitApi('/hubs/metadata/' + ratingKey + '/related', {
|
||||
excludeFields: 'summary',
|
||||
count: 12
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
this.getSeriesData = function (ratingKey) {
|
||||
return this.hitApi('/library/metadata/' + ratingKey, {
|
||||
includeConcerts: 1,
|
||||
|
@ -244,8 +243,8 @@ module.exports = function PlexServer() {
|
|||
asyncCheckFiles: 1,
|
||||
asyncRefreshAnalysis: 1,
|
||||
asyncRefreshLocalMediaAgent: 1
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
this.getSeriesChildren = async function (ratingKey, start, size, excludeAllLeaves, library) {
|
||||
try {
|
||||
|
@ -253,38 +252,38 @@ module.exports = function PlexServer() {
|
|||
'X-Plex-Container-Start': start,
|
||||
'X-Plex-Container-Size': size,
|
||||
excludeAllLeaves: excludeAllLeaves
|
||||
});
|
||||
})
|
||||
if (library) {
|
||||
for (let i = 0; i < data.MediaContainer.Metadata.length; i++) {
|
||||
data.MediaContainer.Metadata[i].librarySectionID = library;
|
||||
// this.commit('SET_ITEMCACHE', [data.MediaContainer.Metadata[i].ratingKey,
|
||||
// data.MediaContainer.Metadata[i]])
|
||||
data.MediaContainer.Metadata[i].librarySectionID = library
|
||||
// this.commit('SET_ITEMCACHE', [data.MediaContainer.Metadata[i].ratingKey,
|
||||
// data.MediaContainer.Metadata[i]])
|
||||
}
|
||||
}
|
||||
return data;
|
||||
return data
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
console.log(e)
|
||||
return false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.handleMetadata = function (result) {
|
||||
if (result) {
|
||||
if (result.MediaContainer && result.MediaContainer.Metadata && result.MediaContainer.Metadata.length > 0) {
|
||||
for (let i = 0; i < result.MediaContainer.Metadata.length; i++) {
|
||||
result.MediaContainer.Metadata[i].machineIdentifier = this.clientIdentifier;
|
||||
result.MediaContainer.Metadata[i].machineIdentifier = this.clientIdentifier
|
||||
if (result.MediaContainer.Metadata[i].ratingKey) {
|
||||
this.commit('SET_ITEMCACHE', [result.MediaContainer.Metadata[i].ratingKey,
|
||||
result.MediaContainer.Metadata[i]
|
||||
]);
|
||||
])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (result.MediaContainer.ratingKey) {
|
||||
this.commit('SET_ITEMCACHE', [result.MediaContainer.ratingKey, result.MediaContainer]);
|
||||
this.commit('SET_ITEMCACHE', [result.MediaContainer.ratingKey, result.MediaContainer])
|
||||
}
|
||||
}
|
||||
return result.MediaContainer.Metadata;
|
||||
return result.MediaContainer.Metadata
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,385 +10,377 @@ var _PlexAuth = require('./PlexAuth.js')
|
|||
var PlexAuth = new _PlexAuth()
|
||||
|
||||
module.exports = function () {
|
||||
this.signedin = false
|
||||
this.plextvdata = {}
|
||||
this.gotDevices = false
|
||||
|
||||
this.signedin = false
|
||||
this.plextvdata = {}
|
||||
this.gotDevices = false
|
||||
this.user = {}
|
||||
this.username = null
|
||||
this.all_devices = []
|
||||
this.servers = []
|
||||
this.clients = []
|
||||
this.checkedClients = []
|
||||
|
||||
this.user = {}
|
||||
this.username = null
|
||||
this.all_devices = []
|
||||
this.servers = []
|
||||
this.clients = []
|
||||
this.checkedClients = []
|
||||
this.chosenClient = null
|
||||
this.chosenServer = null
|
||||
|
||||
this.chosenClient = null
|
||||
this.chosenServer = null
|
||||
this.httpServer = null
|
||||
this.httpServerPort = 1
|
||||
|
||||
this.httpServer = null
|
||||
this.httpServerPort = 1
|
||||
// Functions
|
||||
this.getPort = function () {
|
||||
this.httpServerPort = 8082
|
||||
}
|
||||
this.loginUserPass = function (_username, _password, callback) {
|
||||
var that = this
|
||||
this.doStandardLogin(_username, _password, function (result) {
|
||||
if (result === 200 || result === 201) {
|
||||
// Login successful!
|
||||
that.signedin = true
|
||||
return (callback(true))
|
||||
}
|
||||
return (callback(false))
|
||||
})
|
||||
}
|
||||
this.loginToken = function (token, callback) {
|
||||
var that = this
|
||||
this.doTokenLogin(token, function (result) {
|
||||
if (result === 200 || result === 201) {
|
||||
// Login successful!
|
||||
that.signedin = true
|
||||
return (callback(true))
|
||||
}
|
||||
return (callback(false))
|
||||
})
|
||||
}
|
||||
|
||||
//Functions
|
||||
this.getPort = function () {
|
||||
this.httpServerPort = 8082
|
||||
}
|
||||
this.loginUserPass = function (_username, _password, callback) {
|
||||
var that = this
|
||||
this.doStandardLogin(_username, _password, function (result) {
|
||||
if (result == 200 || result == 201) {
|
||||
//Login successful!
|
||||
that.signedin = true
|
||||
return (callback(true))
|
||||
}
|
||||
return (callback(false))
|
||||
})
|
||||
}
|
||||
this.loginToken = function (token, callback) {
|
||||
var that = this
|
||||
this.doTokenLogin(token, function (result) {
|
||||
if (result == 200 || result == 201) {
|
||||
//Login successful!
|
||||
that.signedin = true
|
||||
return (callback(true))
|
||||
}
|
||||
return (callback(false))
|
||||
})
|
||||
}
|
||||
this.getDevices = function (callback) {
|
||||
// Retrieve all clients from the plex.tv/api/resources endpoint
|
||||
|
||||
this.getDevices = function (callback) {
|
||||
//Retrieve all clients from the plex.tv/api/resources endpoint
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.user == null) {
|
||||
console.log('Must be logged in to retrieve devices!')
|
||||
return (callback(false))
|
||||
}
|
||||
this.gotDevices = false
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.user == null) {
|
||||
console.log('Must be logged in to retrieve devices!')
|
||||
return (callback(false))
|
||||
}
|
||||
this.gotDevices = false
|
||||
this.servers = []
|
||||
this.clients = []
|
||||
console.log('Retrieving devices for ' + this.user.username)
|
||||
var options = PlexAuth.getApiOptions('https://plex.tv/api/resources?includeHttps=1', this.user.authToken, 5000, 'GET')
|
||||
request(options, (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
// Valid response
|
||||
parseXMLString(body, async (err, result) => {
|
||||
this.servers = []
|
||||
this.clients = []
|
||||
console.log('Retrieving devices for ' + this.user.username)
|
||||
var options = PlexAuth.getApiOptions('https://plex.tv/api/resources?includeHttps=1', this.user.authToken, 5000, 'GET')
|
||||
request(options, (error, response, body) => {
|
||||
if (!error && response.statusCode == 200) {
|
||||
//Valid response
|
||||
parseXMLString(body, async (err, result) => {
|
||||
this.servers = []
|
||||
this.clients = []
|
||||
for (var index in result.MediaContainer.Device) {
|
||||
//Handle the individual device
|
||||
let device = result.MediaContainer.Device[index]['$']
|
||||
//Each device can have multiple network connections
|
||||
//Any of them can be viable routes to interacting with the device
|
||||
let connections = result.MediaContainer.Device[index]['Connection']
|
||||
let tempConnectionsArray = []
|
||||
//Create a temporary array of object:PlexConnection
|
||||
for (var i in connections) {
|
||||
let connection = connections[i]['$']
|
||||
//Exclude local IPs starting with 169.254
|
||||
if (!connection.address.startsWith('169.254')) {
|
||||
let tempConnection = new PlexConnection()
|
||||
for (var key in connection) {
|
||||
tempConnection[key] = connection[key]
|
||||
}
|
||||
tempConnectionsArray.push(tempConnection)
|
||||
}
|
||||
}
|
||||
this.all_devices.push(device)
|
||||
if (device.provides.indexOf('player') != -1) {
|
||||
//This is a Client
|
||||
//Create a new PlexClient object
|
||||
var tempClient = new PlexClient()
|
||||
for (var key in device) {
|
||||
tempClient[key] = device[key]
|
||||
}
|
||||
tempClient.plexConnections = tempConnectionsArray
|
||||
tempClient.subscribePort = this.httpServerPort
|
||||
tempClient.userData = this.user
|
||||
this.clients.push(tempClient)
|
||||
} else {
|
||||
//This is a Server
|
||||
//Create a new PlexServer object
|
||||
let tempServer = new PlexServer()
|
||||
for (var key in device) {
|
||||
tempServer[key] = device[key]
|
||||
}
|
||||
tempServer.plexConnections = tempConnectionsArray
|
||||
if (tempServer['accessToken'] == null) {
|
||||
tempServer['accessToken'] = this.user.authToken
|
||||
}
|
||||
//that.servers.push(tempServer)
|
||||
tempServer.findConnection().then((result) => {
|
||||
if (result) {
|
||||
this.servers.push(tempServer)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
let ptplayer = new PlexClient()
|
||||
ptplayer.provides = 'player'
|
||||
ptplayer.clientIdentifier = 'PTPLAYER9PLUS10'
|
||||
ptplayer.platform = 'Web'
|
||||
ptplayer.device = 'Web'
|
||||
ptplayer.product = 'SyncLounge'
|
||||
ptplayer.name = 'SyncLounge Player (BETA)'
|
||||
ptplayer.lastSeenAt = Math.round((new Date).getTime() / 1000)
|
||||
|
||||
this.clients.push(ptplayer)
|
||||
this.clients.sort(function (a, b) {
|
||||
return parseInt(b.lastSeenAt) - parseInt(a.lastSeenAt)
|
||||
})
|
||||
|
||||
// Setup our PTPlayer
|
||||
console.log('Succesfully retrieved all Plex Devices')
|
||||
this.gotDevices = true
|
||||
return resolve(true)
|
||||
})
|
||||
} else {
|
||||
//Invalid response
|
||||
this.gotDevices = true
|
||||
return reject(false)
|
||||
for (var index in result.MediaContainer.Device) {
|
||||
// Handle the individual device
|
||||
let device = result.MediaContainer.Device[index]['$']
|
||||
// Each device can have multiple network connections
|
||||
// Any of them can be viable routes to interacting with the device
|
||||
let connections = result.MediaContainer.Device[index]['Connection']
|
||||
let tempConnectionsArray = []
|
||||
// Create a temporary array of object:PlexConnection
|
||||
for (var i in connections) {
|
||||
let connection = connections[i]['$']
|
||||
// Exclude local IPs starting with 169.254
|
||||
if (!connection.address.startsWith('169.254')) {
|
||||
let tempConnection = new PlexConnection()
|
||||
for (var key in connection) {
|
||||
tempConnection[key] = connection[key]
|
||||
}
|
||||
tempConnectionsArray.push(tempConnection)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
this.doTokenLogin = function (token, callback) {
|
||||
var that = this
|
||||
//Login via a token, this is the normal login path after
|
||||
// the initial setup
|
||||
var options = PlexAuth.getApiOptions('https://plex.tv/users/sign_in.json', token, 5000, 'POST')
|
||||
var that = this
|
||||
request(options, function (error, response, body) {
|
||||
if (!error && (response.statusCode == 200 || response.statusCode == 201)) {
|
||||
safeParse(body, function (err, json) {
|
||||
if (err) {
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
that.signedin = true
|
||||
that.user = json.user
|
||||
return (callback(true, response, body))
|
||||
}
|
||||
this.all_devices.push(device)
|
||||
if (device.provides.indexOf('player') != -1) {
|
||||
// This is a Client
|
||||
// Create a new PlexClient object
|
||||
var tempClient = new PlexClient()
|
||||
for (var key in device) {
|
||||
tempClient[key] = device[key]
|
||||
}
|
||||
tempClient.plexConnections = tempConnectionsArray
|
||||
tempClient.subscribePort = this.httpServerPort
|
||||
tempClient.userData = this.user
|
||||
this.clients.push(tempClient)
|
||||
} else {
|
||||
// This is a Server
|
||||
// Create a new PlexServer object
|
||||
let tempServer = new PlexServer()
|
||||
for (var key in device) {
|
||||
tempServer[key] = device[key]
|
||||
}
|
||||
tempServer.plexConnections = tempConnectionsArray
|
||||
if (tempServer['accessToken'] == null) {
|
||||
tempServer['accessToken'] = this.user.authToken
|
||||
}
|
||||
// that.servers.push(tempServer)
|
||||
tempServer.findConnection().then((result) => {
|
||||
if (result) {
|
||||
this.servers.push(tempServer)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
var code = response.statusCode
|
||||
if (code == 401) {
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
}
|
||||
})
|
||||
let ptplayer = new PlexClient()
|
||||
ptplayer.provides = 'player'
|
||||
ptplayer.clientIdentifier = 'PTPLAYER9PLUS10'
|
||||
ptplayer.platform = 'Web'
|
||||
ptplayer.device = 'Web'
|
||||
ptplayer.product = 'SyncLounge'
|
||||
ptplayer.name = 'SyncLounge Player (BETA)'
|
||||
ptplayer.lastSeenAt = Math.round((new Date()).getTime() / 1000)
|
||||
|
||||
}
|
||||
this.doStandardLogin = function (username, password, _callback) {
|
||||
//Sign in to Plex.tv via plex.tv/users/sign_in.json via POST
|
||||
var base64encoded = new Buffer(username + ':' + password).toString('base64')
|
||||
var options = {
|
||||
url: 'https://plex.tv/users/sign_in.json',
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + base64encoded,
|
||||
'X-Plex-Client-Identifier': global.constants.X_PLEX_CLIENT_IDENTIFIER
|
||||
},
|
||||
method: 'POST'
|
||||
this.clients.push(ptplayer)
|
||||
this.clients.sort(function (a, b) {
|
||||
return parseInt(b.lastSeenAt) - parseInt(a.lastSeenAt)
|
||||
})
|
||||
|
||||
// Setup our PTPlayer
|
||||
console.log('Succesfully retrieved all Plex Devices')
|
||||
this.gotDevices = true
|
||||
return resolve(true)
|
||||
})
|
||||
} else {
|
||||
// Invalid response
|
||||
this.gotDevices = true
|
||||
return reject(false)
|
||||
}
|
||||
var that = this
|
||||
request(options, function (error, response, body) {
|
||||
if (!error && (response.statusCode == 200 || response.statusCode == 201)) {
|
||||
safeParse(body, function (err, json) {
|
||||
that.user = json.user
|
||||
that.signedin = true
|
||||
return (_callback(response.statusCode))
|
||||
})
|
||||
} else {
|
||||
var code = response.statusCode
|
||||
if (code == 401) {
|
||||
that.signedin = false
|
||||
return (_callback(response.statusCode))
|
||||
}
|
||||
that.signedin = false
|
||||
return (_callback(response.statusCode))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
this.doTokenLogin = function (token, callback) {
|
||||
var that = this
|
||||
// Login via a token, this is the normal login path after
|
||||
// the initial setup
|
||||
var options = PlexAuth.getApiOptions('https://plex.tv/users/sign_in.json', token, 5000, 'POST')
|
||||
var that = this
|
||||
request(options, function (error, response, body) {
|
||||
if (!error && (response.statusCode == 200 || response.statusCode == 201)) {
|
||||
safeParse(body, function (err, json) {
|
||||
if (err) {
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
that.signedin = true
|
||||
that.user = json.user
|
||||
return (callback(true, response, body))
|
||||
})
|
||||
} else {
|
||||
var code = response.statusCode
|
||||
if (code == 401) {
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
that.signedin = false
|
||||
return (callback(false, response, body))
|
||||
}
|
||||
})
|
||||
}
|
||||
this.doStandardLogin = function (username, password, _callback) {
|
||||
// Sign in to Plex.tv via plex.tv/users/sign_in.json via POST
|
||||
var base64encoded = new Buffer(username + ':' + password).toString('base64')
|
||||
var options = {
|
||||
url: 'https://plex.tv/users/sign_in.json',
|
||||
headers: {
|
||||
'Authorization': 'Basic ' + base64encoded,
|
||||
'X-Plex-Client-Identifier': global.constants.X_PLEX_CLIENT_IDENTIFIER
|
||||
},
|
||||
method: 'POST'
|
||||
}
|
||||
this.getClientById = function (clientId, callback) {
|
||||
for (var i in this.clients) {
|
||||
var client = this.clients[i]
|
||||
if (client.clientIdentifier == clientId) {
|
||||
return callback(client)
|
||||
var that = this
|
||||
request(options, function (error, response, body) {
|
||||
if (!error && (response.statusCode == 200 || response.statusCode == 201)) {
|
||||
safeParse(body, function (err, json) {
|
||||
that.user = json.user
|
||||
that.signedin = true
|
||||
return (_callback(response.statusCode))
|
||||
})
|
||||
} else {
|
||||
var code = response.statusCode
|
||||
if (code == 401) {
|
||||
that.signedin = false
|
||||
return (_callback(response.statusCode))
|
||||
}
|
||||
that.signedin = false
|
||||
return (_callback(response.statusCode))
|
||||
}
|
||||
})
|
||||
}
|
||||
this.getClientById = function (clientId, callback) {
|
||||
for (var i in this.clients) {
|
||||
var client = this.clients[i]
|
||||
if (client.clientIdentifier == clientId) {
|
||||
return callback(client)
|
||||
}
|
||||
}
|
||||
return callback(null)
|
||||
}
|
||||
this.getServerById = function (clientId) {
|
||||
for (var i in this.servers) {
|
||||
var server = this.servers[i]
|
||||
if (server.clientIdentifier == clientId) {
|
||||
return server
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
this.getRandomThumb = function (callback) {
|
||||
let ticker = (failures) => {
|
||||
setTimeout(() => {
|
||||
if (failures == 10) {
|
||||
return callback(false)
|
||||
}
|
||||
let validServers = this.servers.filter((server) => {
|
||||
if (server.chosenConnection) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if (validServers.length > 1) {
|
||||
let randomServer = validServers[Math.floor(Math.random() * validServers.length)]
|
||||
randomServer.getRandomItem((result) => {
|
||||
console.log('Random item result', result)
|
||||
if (!result) {
|
||||
return callback(false)
|
||||
}
|
||||
return callback(randomServer.getUrlForLibraryLoc(result.thumb, 900, 900, 8))
|
||||
})
|
||||
} else {
|
||||
ticker(failures + 1)
|
||||
}
|
||||
return callback(null)
|
||||
}, 100)
|
||||
}
|
||||
this.getServerById = function (clientId) {
|
||||
for (var i in this.servers) {
|
||||
var server = this.servers[i]
|
||||
if (server.clientIdentifier == clientId) {
|
||||
return server
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
this.getRandomThumb = function (callback) {
|
||||
let ticker = (failures) => {
|
||||
setTimeout( () => {
|
||||
if (failures == 10){
|
||||
return callback(false)
|
||||
}
|
||||
let validServers = this.servers.filter( (server) => {
|
||||
if (server.chosenConnection){
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
if (validServers.length > 1){
|
||||
let randomServer = validServers[Math.floor(Math.random()*validServers.length)]
|
||||
randomServer.getRandomItem((result) => {
|
||||
console.log('Random item result',result)
|
||||
if (!result){
|
||||
return callback(false)
|
||||
}
|
||||
return callback(randomServer.getUrlForLibraryLoc(result.thumb,900 ,900 ,8))
|
||||
})
|
||||
} else {
|
||||
ticker(failures+1)
|
||||
}
|
||||
},100)
|
||||
}
|
||||
ticker(0)
|
||||
|
||||
}
|
||||
this.playContentAutomatically = function (client, hostData, blockedServers, offset, callback) {
|
||||
ticker(0)
|
||||
}
|
||||
this.playContentAutomatically = function (client, hostData, blockedServers, offset, callback) {
|
||||
// Automatically play content on the client searching all servers based on the title
|
||||
var that = this
|
||||
var that = this
|
||||
|
||||
// First lets find all of our playable items
|
||||
let playables = []
|
||||
let j = 0
|
||||
// First lets find all of our playable items
|
||||
let playables = []
|
||||
let j = 0
|
||||
|
||||
let validServers = this.servers.length
|
||||
if (blockedServers){
|
||||
for (let i = 0; i < blockedServers.length; i++ ){
|
||||
if (this.servers[blockedServers[i]]){
|
||||
validServers--
|
||||
}
|
||||
}
|
||||
let validServers = this.servers.length
|
||||
if (blockedServers) {
|
||||
for (let i = 0; i < blockedServers.length; i++) {
|
||||
if (this.servers[blockedServers[i]]) {
|
||||
validServers--
|
||||
}
|
||||
if (validServers == 0){
|
||||
return callback(false)
|
||||
}
|
||||
for (let i = 0; i < this.servers.length; i++) {
|
||||
var server = this.servers[i]
|
||||
let blocked = false
|
||||
if (blockedServers){
|
||||
for (let i = 0; i < blockedServers.length; i++ ){
|
||||
if (blockedServers[i] == server.clientIdentifier){
|
||||
console.log('Server: ' + server.name + ' is blocked - not searching')
|
||||
blocked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blocked){
|
||||
continue
|
||||
}
|
||||
server.search(hostData.rawTitle, function (results, _server) {
|
||||
j++
|
||||
console.log('Heard back from ' + j + ' servers')
|
||||
if (results != null) {
|
||||
for (var k = 0; k < results.length; k++) {
|
||||
//Now we need to check the result
|
||||
if (checkResult(results[k], hostData)) {
|
||||
//Its a match!
|
||||
playables.push({
|
||||
'server': _server,
|
||||
'result': results[k]
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j == validServers) {
|
||||
console.log('Found ' + playables.length + ' playable items')
|
||||
start(playables, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function start (playables, index) {
|
||||
// Now lets try and play our items one by one
|
||||
if (playables.length == 0) {
|
||||
console.log('We didnt find any playables.')
|
||||
return callback(false)
|
||||
}
|
||||
console.log('Going to try playing the next playable: Index ' + index)
|
||||
var server = playables[index].server
|
||||
console.log('Attempting to play from ')
|
||||
console.log(server)
|
||||
var ratingKey = playables[index].result.ratingKey
|
||||
let data = {
|
||||
ratingKey: ratingKey,
|
||||
mediaIndex: null,
|
||||
server: server,
|
||||
offset: offset || 0,
|
||||
callback: function(playResult, code){
|
||||
console.log('Play result: ' + code)
|
||||
if (code == 200) {
|
||||
return callback(true)
|
||||
} else {
|
||||
|
||||
return start(playables, parseInt(parseInt(index) + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client.clientIdentifier == 'PTPLAYER9PLUS10') {
|
||||
client.playMedia(data)
|
||||
} else {
|
||||
// Standard Plex Player
|
||||
client.subscribe(function (result) {
|
||||
client.playMedia(data)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function checkResult (data, hostData) {
|
||||
console.log('Checking compatibility for this item')
|
||||
console.log(data)
|
||||
console.log(hostData)
|
||||
//Do a series of checks to see if this result is OK
|
||||
//Check if rawTitle matches
|
||||
if (data.title != hostData.rawTitle) {
|
||||
//global.renderLog.info('wrong title')
|
||||
return false
|
||||
}
|
||||
//Check if length is close enough
|
||||
if (Math.abs(parseInt(data.duration) - parseInt(hostData.maxTime)) > 5000 || !data.duration) {
|
||||
//global.renderLog.info('wrong time')
|
||||
return false
|
||||
}
|
||||
if (data.type == 'movie') {
|
||||
//We're good!
|
||||
console.log('FOUND A PLAYABLE MOVIE')
|
||||
return true
|
||||
}
|
||||
if (data.type == 'episode') {
|
||||
//Check if the show name is the same
|
||||
console.log('FOUND A PLAYABLE TV EPISODE')
|
||||
return true
|
||||
|
||||
}
|
||||
if (data.type == 'track') {
|
||||
//We're good!
|
||||
console.log('FOUND A PLAYABLE track')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
this.createHttpServer = function () {
|
||||
|
||||
}
|
||||
}
|
||||
this.getPort()
|
||||
if (validServers == 0) {
|
||||
return callback(false)
|
||||
}
|
||||
for (let i = 0; i < this.servers.length; i++) {
|
||||
var server = this.servers[i]
|
||||
let blocked = false
|
||||
if (blockedServers) {
|
||||
for (let i = 0; i < blockedServers.length; i++) {
|
||||
if (blockedServers[i] == server.clientIdentifier) {
|
||||
console.log('Server: ' + server.name + ' is blocked - not searching')
|
||||
blocked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
continue
|
||||
}
|
||||
server.search(hostData.rawTitle, function (results, _server) {
|
||||
j++
|
||||
console.log('Heard back from ' + j + ' servers')
|
||||
if (results != null) {
|
||||
for (var k = 0; k < results.length; k++) {
|
||||
// Now we need to check the result
|
||||
if (checkResult(results[k], hostData)) {
|
||||
// Its a match!
|
||||
playables.push({
|
||||
'server': _server,
|
||||
'result': results[k]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (j == validServers) {
|
||||
console.log('Found ' + playables.length + ' playable items')
|
||||
start(playables, 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function start (playables, index) {
|
||||
// Now lets try and play our items one by one
|
||||
if (playables.length == 0) {
|
||||
console.log('We didnt find any playables.')
|
||||
return callback(false)
|
||||
}
|
||||
console.log('Going to try playing the next playable: Index ' + index)
|
||||
var server = playables[index].server
|
||||
console.log('Attempting to play from ')
|
||||
console.log(server)
|
||||
var ratingKey = playables[index].result.ratingKey
|
||||
let data = {
|
||||
ratingKey: ratingKey,
|
||||
mediaIndex: null,
|
||||
server: server,
|
||||
offset: offset || 0,
|
||||
callback: function (playResult, code) {
|
||||
console.log('Play result: ' + code)
|
||||
if (code == 200) {
|
||||
return callback(true)
|
||||
} else {
|
||||
return start(playables, parseInt(parseInt(index) + 1))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (client.clientIdentifier == 'PTPLAYER9PLUS10') {
|
||||
client.playMedia(data)
|
||||
} else {
|
||||
// Standard Plex Player
|
||||
client.subscribe(function (result) {
|
||||
client.playMedia(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function checkResult (data, hostData) {
|
||||
console.log('Checking compatibility for this item')
|
||||
console.log(data)
|
||||
console.log(hostData)
|
||||
// Do a series of checks to see if this result is OK
|
||||
// Check if rawTitle matches
|
||||
if (data.title != hostData.rawTitle) {
|
||||
// global.renderLog.info('wrong title')
|
||||
return false
|
||||
}
|
||||
// Check if length is close enough
|
||||
if (Math.abs(parseInt(data.duration) - parseInt(hostData.maxTime)) > 5000 || !data.duration) {
|
||||
// global.renderLog.info('wrong time')
|
||||
return false
|
||||
}
|
||||
if (data.type == 'movie') {
|
||||
// We're good!
|
||||
console.log('FOUND A PLAYABLE MOVIE')
|
||||
return true
|
||||
}
|
||||
if (data.type == 'episode') {
|
||||
// Check if the show name is the same
|
||||
console.log('FOUND A PLAYABLE TV EPISODE')
|
||||
return true
|
||||
}
|
||||
if (data.type == 'track') {
|
||||
// We're good!
|
||||
console.log('FOUND A PLAYABLE track')
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
this.createHttpServer = function () {
|
||||
|
||||
}
|
||||
this.getPort()
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ export default {
|
|||
type: 'alert'
|
||||
})
|
||||
})
|
||||
state._socket.on('host-update', (data) => {
|
||||
state._socket.on('host-update', async (data) => {
|
||||
/* 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.
|
||||
|
||||
|
@ -254,7 +254,11 @@ export default {
|
|||
}
|
||||
if (!rootState.chosenClient.lastTimelineObject) {
|
||||
console.log('Dont have our first timeline data yet.')
|
||||
return
|
||||
if (rootState.chosenClient.clientIdentifier === 'PTPLAYER9PLUS10') {
|
||||
rootState.chosenClient.lastTimelineObject = {}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check previous timeline data age
|
||||
let timelineAge = new Date().getTime() - rootState.chosenClient.lastTimelineObject.recievedAt
|
||||
|
@ -266,7 +270,7 @@ export default {
|
|||
decisionMaker(timelineAge)
|
||||
}
|
||||
|
||||
function decisionMaker (timelineAge) {
|
||||
async function decisionMaker (timelineAge) {
|
||||
let ourTimeline = rootState.chosenClient.lastTimelineObject
|
||||
let hostTimeline = data
|
||||
|
||||
|
@ -295,39 +299,42 @@ export default {
|
|||
state.decisionBlocked = true
|
||||
|
||||
let blockedServers = rootState.BLOCKEDSERVERS
|
||||
let validServers = rootState.plex.servers.length
|
||||
let servers = Object.assign({}, rootState.plex.servers)
|
||||
if (blockedServers) {
|
||||
for (let i = 0; i < blockedServers.length; i++) {
|
||||
if (rootState.plex.servers[blockedServers[i]]) {
|
||||
validServers--
|
||||
delete servers[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendNotification('Searching ' + validServers + ' Plex Servers for "' + hostTimeline.rawTitle + '"')
|
||||
rootState.plex.playContentAutomatically(rootState.chosenClient, hostTimeline, blockedServers, hostTimeline.time, function (result) {
|
||||
console.log('Auto play result: ' + result)
|
||||
if (!result) {
|
||||
sendNotification('Failed to find a compatible copy of ' + hostTimeline.rawTitle)
|
||||
}
|
||||
state.decisionBlocked = false
|
||||
sendNotification('Searching Plex Servers for "' + hostTimeline.rawTitle + '"')
|
||||
let result = await rootState.chosenClient.playContentAutomatically(rootState.chosenClient, hostTimeline, servers, hostTimeline.time).catch((e) => {
|
||||
sendNotification('Failed to find a compatible copy of ' + hostTimeline.rawTitle)
|
||||
setTimeout(function () {
|
||||
rootState.blockAutoPlay = false
|
||||
}, 15000)
|
||||
state.decisionBlocked = false
|
||||
})
|
||||
console.log('Auto play result: ' + result)
|
||||
state.decisionBlocked = false
|
||||
await new Promise((resolve, reject) => {
|
||||
setTimeout(() => resolve(), 10000)
|
||||
})
|
||||
rootState.blockAutoPlay = false
|
||||
return
|
||||
}
|
||||
let difference = Math.abs((parseInt(ourTimeline.time) + parseInt(timelineAge)) - parseInt(hostTimeline.time))
|
||||
|
||||
if (hostTimeline.playerState === 'playing' && ourTimeline.state === 'paused') {
|
||||
sendNotification('The host pressed play')
|
||||
sendNotification('Resuming..')
|
||||
rootState.chosenClient.pressPlay(() => {
|
||||
checkForSeek()
|
||||
})
|
||||
return
|
||||
}
|
||||
if (hostTimeline.playerState === 'paused' && ourTimeline.state === 'playing') {
|
||||
sendNotification('The host pressed pause')
|
||||
sendNotification('Pausing..')
|
||||
rootState.chosenClient.pressPause(() => {
|
||||
checkForSeek()
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue