Sign in/Urls: Sign in now adds all servers; however refresh guide/refresh channels must be edited through the json. Now uses local/remote server https url instead of local 34440 port url with no cert. Video Playback: Remove code not enforcing time limit for streams. Make default stream protocol http instread of hls which was used previously. Add option to choose. Add option to specify video codecs (which is prone to user error). Added mpeg2video to default video codecs. Add option to specify direct stream/transcode media buffer size. Not sure how much of a difference this makes. Add in safeguard to ffmpeg's kill so failed streams don't crash the application M3Us: Add group-title="PseudoTV" for easier management in xteve
176 lines
8.7 KiB
JavaScript
176 lines
8.7 KiB
JavaScript
const Plex = require('../../src/plex');
|
|
const fetch = require('node-fetch');
|
|
|
|
module.exports = function ($http, $window, $interval) {
|
|
return {
|
|
login: async () => {
|
|
const headers = {
|
|
'Accept': 'application/json',
|
|
'X-Plex-Product': 'PseudoTV',
|
|
'X-Plex-Version': 'Plex OAuth',
|
|
'X-Plex-Client-Identifier': 'rg14zekk3pa5zp4safjwaa8z',
|
|
'X-Plex-Model': 'Plex OAuth'
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
$http({
|
|
method: 'POST',
|
|
url: 'https://plex.tv/api/v2/pins?strong=true',
|
|
headers: headers
|
|
}).then((res) => {
|
|
$window.open('https://app.plex.tv/auth/#!?clientID=rg14zekk3pa5zp4safjwaa8z&context[device][version]=Plex OAuth&context[device][model]=Plex OAuth&code=' + res.data.code + '&context[device][product]=Plex Web')
|
|
let limit = 120000 // 2 minute time out limit
|
|
let poll = 2000 // check every 2 seconds for token
|
|
let interval = $interval(() => {
|
|
$http({
|
|
method: 'GET',
|
|
url: `https://plex.tv/api/v2/pins/${res.data.id}`,
|
|
headers: headers
|
|
}).then(async (r2) => {
|
|
limit -= poll
|
|
if (limit <= 0) {
|
|
$interval.cancel(interval)
|
|
reject('Timed Out. Failed to sign in a timely manner (2 mins)')
|
|
}
|
|
if (r2.data.authToken !== null) {
|
|
$interval.cancel(interval)
|
|
try {
|
|
headers['X-Plex-Token'] = r2.data.authToken
|
|
let res_servers = []
|
|
const getServers = await fetch(`https://plex.tv/api/v2/resources?includeHttps=1`, {
|
|
method: 'GET', headers: headers
|
|
});
|
|
const servers = await getServers.json();
|
|
|
|
servers.forEach((server) => {
|
|
// not pms, skip
|
|
if (server.provides != `server`)
|
|
return;
|
|
|
|
// true = local server, false = remote
|
|
const i = (server.publicAddressMatches == true) ? 0 : 2
|
|
server.uri = server.connections[i].uri
|
|
server.protocol = server.connections[i].protocol
|
|
server.address = server.connections[i].address
|
|
server.port = server.connections[i].port
|
|
|
|
res_servers.push(server);
|
|
});
|
|
|
|
res.servers = res_servers
|
|
resolve(res)
|
|
} catch (err) {
|
|
reject(err)
|
|
}
|
|
}
|
|
}, (err) => {
|
|
$interval.cancel(interval)
|
|
reject(err)
|
|
})
|
|
|
|
}, poll)
|
|
}, (err) => {
|
|
reject(err)
|
|
})
|
|
})
|
|
},
|
|
getLibrary: async (server) => {
|
|
var client = new Plex(server)
|
|
const res = await client.Get('/library/sections')
|
|
var sections = []
|
|
for (let i = 0, l = typeof res.Directory !== 'undefined' ? res.Directory.length : 0; i < l; i++)
|
|
if (res.Directory[i].type === 'movie' || res.Directory[i].type === 'show')
|
|
sections.push({
|
|
title: res.Directory[i].title,
|
|
key: `/library/sections/${res.Directory[i].key}/all`,
|
|
icon: `${server.uri}${res.Directory[i].composite}?X-Plex-Token=${server.accessToken}`,
|
|
type: res.Directory[i].type
|
|
})
|
|
return sections
|
|
},
|
|
getPlaylists: async (server) => {
|
|
var client = new Plex(server)
|
|
const res = await client.Get('/playlists')
|
|
var playlists = []
|
|
for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++)
|
|
if (res.Metadata[i].playlistType === 'video')
|
|
playlists.push({
|
|
title: res.Metadata[i].title,
|
|
key: res.Metadata[i].key,
|
|
icon: `${server.uri}${res.Metadata[i].composite}?X-Plex-Token=${server.accessToken}`,
|
|
duration: res.Metadata[i].duration
|
|
})
|
|
return playlists
|
|
},
|
|
getStreams: async (server, key) => {
|
|
var client = new Plex(server)
|
|
return client.Get(key).then((res) => {
|
|
let streams = res.Metadata[0].Media[0].Part[0].Stream
|
|
for (let i = 0, l = streams.length; i < l; i++) {
|
|
if (typeof streams[i].key !== 'undefined') {
|
|
streams[i].key = `${server.uri}${streams[i].key}?X-Plex-Token=${server.accessToken}`
|
|
}
|
|
}
|
|
return streams
|
|
})
|
|
},
|
|
getNested: async (server, key) => {
|
|
var client = new Plex(server)
|
|
const res = await client.Get(key)
|
|
var nested = []
|
|
for (let i = 0, l = typeof res.Metadata !== 'undefined' ? res.Metadata.length : 0; i < l; i++) {
|
|
// Skip any videos (movie or episode) without a duration set...
|
|
if (typeof res.Metadata[i].duration === 'undefined' && (res.Metadata[i].type === "episode" || res.Metadata[i].type === "movie"))
|
|
continue
|
|
if (res.Metadata[i].duration <= 0 && (res.Metadata[i].type === "episode" || res.Metadata[i].type === "movie"))
|
|
continue
|
|
var program = {
|
|
title: res.Metadata[i].title,
|
|
key: res.Metadata[i].key,
|
|
ratingKey: res.Metadata[i].ratingKey,
|
|
server: server,
|
|
icon: `${server.uri}${res.Metadata[i].thumb}?X-Plex-Token=${server.accessToken}`,
|
|
type: res.Metadata[i].type,
|
|
duration: res.Metadata[i].duration,
|
|
actualDuration: res.Metadata[i].duration,
|
|
durationStr: msToTime(res.Metadata[i].duration),
|
|
subtitle: res.Metadata[i].subtitle,
|
|
summary: res.Metadata[i].summary,
|
|
rating: res.Metadata[i].contentRating,
|
|
date: res.Metadata[i].originallyAvailableAt,
|
|
year: res.Metadata[i].year,
|
|
}
|
|
if (program.type === 'episode') {
|
|
program.showTitle = res.Metadata[i].grandparentTitle
|
|
program.episode = res.Metadata[i].index
|
|
program.season = res.Metadata[i].parentIndex
|
|
program.icon = `${server.uri}${res.Metadata[i].grandparentThumb}?X-Plex-Token=${server.accessToken}`
|
|
program.episodeIcon = `${server.uri}${res.Metadata[i].thumb}?X-Plex-Token=${server.accessToken}`
|
|
program.seasonIcon = `${server.uri}${res.Metadata[i].parentThumb}?X-Plex-Token=${server.accessToken}`
|
|
program.showIcon = `${server.uri}${res.Metadata[i].grandparentThumb}?X-Plex-Token=${server.accessToken}`
|
|
}
|
|
else if (program.type === 'movie') {
|
|
program.showTitle = res.Metadata[i].title
|
|
program.episode = 1
|
|
program.season = 1
|
|
}
|
|
nested.push(program)
|
|
}
|
|
return nested
|
|
}
|
|
}
|
|
}
|
|
|
|
function msToTime(duration) {
|
|
var milliseconds = parseInt((duration % 1000) / 100),
|
|
seconds = Math.floor((duration / 1000) % 60),
|
|
minutes = Math.floor((duration / (1000 * 60)) % 60),
|
|
hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
|
|
|
|
hours = (hours < 10) ? "0" + hours : hours;
|
|
minutes = (minutes < 10) ? "0" + minutes : minutes;
|
|
seconds = (seconds < 10) ? "0" + seconds : seconds;
|
|
|
|
return hours + ":" + minutes + ":" + seconds + "." + milliseconds;
|
|
}
|