mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-03 09:49:30 +02:00
1543 lines
55 KiB
PHP
1543 lines
55 KiB
PHP
<?php
|
|
/* vim:set softtabstop=4 shiftwidth=4 expandtab: */
|
|
/**
|
|
*
|
|
* LICENSE: GNU General Public License, version 2 (GPLv2)
|
|
* Copyright 2001 - 2015 Ampache.org
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; version 2
|
|
* of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Plex Class
|
|
*
|
|
* This class wrap Ampache to Plex API library functions. See http://wiki.plexapp.com/index.php/HTTP_API
|
|
* These are all static calls.
|
|
*
|
|
* @SuppressWarnings("unused")
|
|
*/
|
|
class Plex_Api
|
|
{
|
|
/**
|
|
* constructor
|
|
* This really isn't anything to do here, so it's private
|
|
*/
|
|
private function __construct()
|
|
{
|
|
}
|
|
|
|
protected static function is_local()
|
|
{
|
|
$local = false;
|
|
$local_auth = AmpConfig::get('plex_local_auth');
|
|
if (!$local_auth) {
|
|
$ip = $_SERVER['REMOTE_ADDR'];
|
|
$lip = ip2long($ip);
|
|
$rangs = array(
|
|
array('127.0.0.1', '127.0.0.1'),
|
|
array('10.0.0.1', '10.255.255.254'),
|
|
array('172.16.0.1', '172.31.255.254'),
|
|
array('192.168.0.1', '192.168.255.254'),
|
|
);
|
|
|
|
foreach ($rangs as $rang) {
|
|
$ld = ip2long($rang[0]);
|
|
$lu = ip2long($rang[1]);
|
|
if ($lip <= $lu && $ld <= $lip) {
|
|
debug_event('Access Control', 'Local client ip address (' . $ip . '), bypass authentication.', '3');
|
|
$local = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $local;
|
|
}
|
|
|
|
public static function auth_user()
|
|
{
|
|
$isLocal = self::is_local();
|
|
|
|
$headers = apache_request_headers();
|
|
$myplex_token = $headers['X-Plex-Token'];
|
|
if (empty($myplex_token)) {
|
|
$myplex_token = $_REQUEST['X-Plex-Token'];
|
|
}
|
|
|
|
if (!$isLocal) {
|
|
$match_users = AmpConfig::get('plex_match_email');
|
|
|
|
$myplex_username = $headers['X-Plex-Username'];
|
|
|
|
if (empty($myplex_token)) {
|
|
// Never fail OPTIONS requests
|
|
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
|
|
self::setPlexHeader($headers);
|
|
exit();
|
|
} else {
|
|
debug_event('Access Control', 'Authentication token is missing.', '3');
|
|
self::createError(401);
|
|
}
|
|
}
|
|
|
|
$createSession = false;
|
|
Session::gc();
|
|
$username = "";
|
|
$email = trim(Session::read((string) $myplex_token));
|
|
|
|
if (empty($email)) {
|
|
$createSession = true;
|
|
$xml = self::get_server_authtokens();
|
|
$validToken = false;
|
|
foreach ($xml->access_token as $tk) {
|
|
if ((string) $tk['token'] == $myplex_token) {
|
|
$username = (string) $tk['username'];
|
|
// We should apply filter and access restriction to shared sections only, but that's not easily possible with current Ampache architecture
|
|
$validToken = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!$validToken) {
|
|
debug_event('Access Control', 'Auth-Token ' . $myplex_token . ' invalid for this server.', '3');
|
|
self::createError(401);
|
|
}
|
|
}
|
|
|
|
// Need to get a match between Plex and Ampache users
|
|
if ($match_users) {
|
|
if (!AmpConfig::get('access_control')) {
|
|
debug_event('Access Control', 'Error Attempted to use Plex with Access Control turned off and plex/ampache link enabled.','3');
|
|
self::createError(401);
|
|
}
|
|
|
|
if (empty($email)) {
|
|
$xml = self::get_users_account();
|
|
if ((string) $xml->username == $username) {
|
|
$email = (string) $xml->email;
|
|
} else {
|
|
$xml = self::get_server_friends();
|
|
foreach ($xml->User as $xuser) {
|
|
if ((string) $xuser['username'] == $username) {
|
|
$email = (string) $xuser['email'];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!empty($email)) {
|
|
$user = User::get_from_email($email);
|
|
}
|
|
if (!isset($user) || !$user->id) {
|
|
debug_event('Access Denied', 'Unable to get an Ampache user match for email ' . $email, '3');
|
|
self::createError(401);
|
|
} else {
|
|
$username = $user->username;
|
|
if (!Access::check_network('init-api', $username, 5)) {
|
|
debug_event('Access Denied', 'Unauthorized access attempt to Plex [' . $_SERVER['REMOTE_ADDR'] . ']', '3');
|
|
self::createError(401);
|
|
} else {
|
|
$GLOBALS['user'] = $user;
|
|
$GLOBALS['user']->load_playlist();
|
|
}
|
|
}
|
|
} else {
|
|
$email = $username;
|
|
$username = null;
|
|
|
|
$GLOBALS['user'] = new User();
|
|
$GLOBALS['user']->load_playlist();
|
|
}
|
|
|
|
if ($createSession) {
|
|
// Create an Ampache session from Plex authtoken
|
|
Session::create(array(
|
|
'type' => 'api',
|
|
'sid' => $myplex_token,
|
|
'username' => $username,
|
|
'value' => $email
|
|
));
|
|
}
|
|
} else {
|
|
AmpConfig::set('cookie_path', '/', true);
|
|
$sid = $_COOKIE[AmpConfig::get('session_name')];
|
|
if (!$sid) {
|
|
$sid = $myplex_token;
|
|
if ($sid) {
|
|
session_id($sid);
|
|
Session::create_cookie();
|
|
}
|
|
}
|
|
if (!empty($sid) && Session::exists('api', $sid)) {
|
|
Session::check();
|
|
$GLOBALS['user'] = User::get_from_username($_SESSION['userdata']['username']);
|
|
} else {
|
|
$GLOBALS['user'] = new User();
|
|
$data = array(
|
|
'type' => 'api',
|
|
'sid' => $sid,
|
|
);
|
|
Session::create($data);
|
|
Session::check();
|
|
}
|
|
|
|
$GLOBALS['user']->load_playlist();
|
|
}
|
|
}
|
|
|
|
protected static function check_access($level)
|
|
{
|
|
if (self::is_local()) {
|
|
// Promote all users as content manager if local
|
|
if ($GLOBALS['user']->access < 50) {
|
|
$GLOBALS['user']->access = 50;
|
|
}
|
|
}
|
|
|
|
if (($GLOBALS['user']->access < $level || AmpConfig::get('demo_mode'))) {
|
|
debug_event('plex', 'User ' . $GLOBALS['user']->username . ' is unauthorized to complete the action.', '3');
|
|
self::createError(401);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
public static function setHeader($f)
|
|
{
|
|
header("HTTP/1.1 200 OK", true, 200);
|
|
header("Connection: close", true);
|
|
|
|
header_remove("x-powered-by");
|
|
|
|
if (strtolower($f) == "xml") {
|
|
header("Cache-Control: no-cache", true);
|
|
header("Content-type: text/xml; charset=" . AmpConfig::get('site_charset'), true);
|
|
} elseif (substr(strtolower($f), 0, 6) == "image/") {
|
|
header("Cache-Control: public, max-age=604800", true);
|
|
header("Content-type: " . $f, true);
|
|
} else {
|
|
header("Content-type: " . $f, true);
|
|
}
|
|
}
|
|
|
|
public static function setPlexHeader($reqheaders)
|
|
{
|
|
header("X-Plex-Protocol: 1.0");
|
|
|
|
header('Access-Control-Allow-Origin: *');
|
|
$acm = $reqheaders['Access-Control-Request-Method'];
|
|
if ($acm) {
|
|
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT, HEAD');
|
|
}
|
|
$ach = $reqheaders['Access-Control-Request-Headers'];
|
|
if ($ach) {
|
|
//$filter = explode(',', $ach);
|
|
$filter = null;
|
|
$headers = self::getPlexHeaders(true, $filter);
|
|
$headerkeys = array();
|
|
foreach ($headers as $key => $value) {
|
|
$headerkeys[] = $key;
|
|
}
|
|
header('Access-Control-Allow-Headers: ' . implode(',', $headerkeys));
|
|
}
|
|
|
|
if ($acm || $ach) {
|
|
header('Access-Control-Max-Age: 1209600');
|
|
} else {
|
|
header('Access-Control-Expose-Headers: Location');
|
|
}
|
|
}
|
|
|
|
public static function apiOutputXml($xml)
|
|
{
|
|
// Format xml output
|
|
$dom = new DOMDocument();
|
|
$dom->loadXML($xml);
|
|
$dom->formatOutput = true;
|
|
self::apiOutput($dom->saveXML());
|
|
}
|
|
|
|
public static function apiOutput($string)
|
|
{
|
|
if ($_SERVER['REQUEST_METHOD'] != 'OPTIONS') {
|
|
ob_clean();
|
|
ob_start('ob_gzhandler');
|
|
echo $string;
|
|
ob_end_flush();
|
|
$reqheaders = getallheaders();
|
|
if ($reqheaders['Accept-Encoding']) {
|
|
header("X-Plex-Content-Compressed-Length: " . ob_get_length(), true);
|
|
header("X-Plex-Content-Original-Length: " . strlen($string), true);
|
|
}
|
|
} else {
|
|
header("Content-type: text/plain", true);
|
|
//header("Content-length: 0", true);
|
|
}
|
|
}
|
|
|
|
public static function createError($code)
|
|
{
|
|
$error = "";
|
|
switch ($code) {
|
|
case 404:
|
|
$error = "Not Found";
|
|
break;
|
|
|
|
case 401:
|
|
$error = "Unauthorized";
|
|
break;
|
|
}
|
|
header("Content-type: text/html", true);
|
|
header("HTTP/1.0 ". $code . " " . $error, true, $code);
|
|
|
|
$html = "<html><head><title>" . $error . "</title></head><body><h1>" . $code . " " . $error . "</h1></body></html>";
|
|
self::apiOutput($html);
|
|
exit();
|
|
}
|
|
|
|
public static function validateMyPlex($myplex_username, $myplex_password)
|
|
{
|
|
$options = array(
|
|
CURLOPT_USERPWD => $myplex_username . ':' . $myplex_password,
|
|
//CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
|
|
CURLOPT_POST => true,
|
|
);
|
|
$headers = array(
|
|
'Content-Length: 0'
|
|
);
|
|
$action = 'users/sign_in.xml';
|
|
|
|
$res = self::myPlexRequest($action, $options, $headers);
|
|
;
|
|
return $res['xml']['authenticationToken'];
|
|
}
|
|
|
|
public static function getPublicIp()
|
|
{
|
|
$action = 'pms/:/ip';
|
|
|
|
$res = self::myPlexRequest($action);
|
|
return trim($res['raw']);
|
|
}
|
|
|
|
public static function registerMyPlex($authtoken)
|
|
{
|
|
$headers = array (
|
|
'Content-Type: text/xml'
|
|
);
|
|
$action = 'servers.xml?auth_token=' . $authtoken;
|
|
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setServerInfo($r, Catalog::get_catalogs());
|
|
Plex_XML_Data::setContainerSize($r);
|
|
|
|
$curlopts = array(
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $r->asXML()
|
|
);
|
|
|
|
return self::myPlexRequest($action, $curlopts, $headers, true);
|
|
}
|
|
|
|
public static function publishDeviceConnection($authtoken)
|
|
{
|
|
$headers = array ();
|
|
$action = 'devices/' . Plex_XML_Data::getMachineIdentifier() . '?Connection[][uri]=' . Plex_XML_Data::getServerUri() . '&X-Plex-Token=' . $authtoken;
|
|
$curlopts = array(
|
|
CURLOPT_CUSTOMREQUEST => "PUT"
|
|
);
|
|
|
|
return self::myPlexRequest($action, $curlopts, $headers);
|
|
}
|
|
|
|
public static function unregisterMyPlex($authtoken)
|
|
{
|
|
$headers = array (
|
|
'Content-Type: text/xml'
|
|
);
|
|
$action = 'servers/' . Plex_XML_Data::getMachineIdentifier() . '.xml?auth_token=' . $authtoken;
|
|
$curlopts = array(
|
|
CURLOPT_CUSTOMREQUEST => "DELETE"
|
|
);
|
|
|
|
return self::myPlexRequest($action, $curlopts, $headers);
|
|
}
|
|
|
|
protected static function get_server_authtokens()
|
|
{
|
|
$action = 'servers/' . Plex_XML_Data::getMachineIdentifier() . '/access_tokens.xml?auth_token=' . Plex_XML_Data::getMyPlexAuthToken();
|
|
|
|
$res = self::myPlexRequest($action);
|
|
return $res['xml'];
|
|
}
|
|
|
|
protected static function get_server_friends()
|
|
{
|
|
$action = 'pms/friends/all?auth_token=' . Plex_XML_Data::getMyPlexAuthToken();
|
|
|
|
$res = self::myPlexRequest($action);
|
|
return $res['xml'];
|
|
}
|
|
|
|
protected static function getPlexHeaders($private = false, $filters = null)
|
|
{
|
|
$headers = array(
|
|
'X-Plex-Client-Identifier' => Plex_XML_Data::getClientIdentifier(),
|
|
'X-Plex-Product' => 'Plex Media Server',
|
|
'X-Plex-Version' => Plex_XML_Data::getPlexVersion(),
|
|
'X-Plex-Platform' => Plex_XML_Data::getPlexPlatform(),
|
|
'X-Plex-Platform-Version' => Plex_XML_Data::getPlexPlatformVersion(),
|
|
'X-Plex-Client-Platform' => Plex_XML_Data::getPlexPlatform(),
|
|
'X-Plex-Protocol' => 1.0,
|
|
'X-Plex-Device' => 'Ampache',
|
|
'X-Plex-Device-Name' => 'Ampache',
|
|
'X-Plex-Provides' => 'server'
|
|
);
|
|
|
|
if ($private) {
|
|
if (Plex_XML_Data::getMyPlexUsername()) {
|
|
$headers['X-Plex-Username'] = Plex_XML_Data::getMyPlexUsername();
|
|
}
|
|
if (Plex_XML_Data::getMyPlexUsername()) {
|
|
$headers['X-Plex-Token'] = Plex_XML_Data::getMyPlexAuthToken();
|
|
}
|
|
}
|
|
|
|
if ($filters) {
|
|
$fheaders = array();
|
|
foreach ($headers as $key => $value) {
|
|
if (array_search(strtolower($key), $filters)) {
|
|
$fheaders[$key] = $value;
|
|
}
|
|
}
|
|
$headers = $fheaders;
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
|
|
public static $request_headers = array();
|
|
public static function request_output_header($ch, $header)
|
|
{
|
|
self::$request_headers[] = $header;
|
|
return strlen($header);
|
|
}
|
|
|
|
public static function replay_header($ch, $header)
|
|
{
|
|
$rheader = trim($header);
|
|
$rhpart = explode(':', $rheader);
|
|
if (!empty($rheader) && count($rhpart) > 1) {
|
|
if ($rhpart[0] != "Transfer-Encoding") {
|
|
header($rheader);
|
|
}
|
|
}
|
|
return strlen($header);
|
|
}
|
|
|
|
public static function replay_body($ch, $data)
|
|
{
|
|
if (connection_status() != 0) {
|
|
curl_close($ch);
|
|
debug_event('plex', 'Stream cancelled.', 5);
|
|
exit;
|
|
}
|
|
|
|
echo $data;
|
|
ob_flush();
|
|
|
|
return strlen($data);
|
|
}
|
|
|
|
protected static function myPlexRequest($action, $curlopts = array(), $headers = array(), $proxy = false)
|
|
{
|
|
$server = Plex_XML_Data::getServerUri();
|
|
$allheaders = array();
|
|
if (!$proxy) {
|
|
$allheadersarr = self::getPlexHeaders();
|
|
foreach ($allheadersarr as $key => $value) {
|
|
$allheaders[] = $key . ': ' . $value;
|
|
}
|
|
$allheaders += array(
|
|
'Origin: ' . $server,
|
|
'Referer: ' . $server . '/web/index.html',
|
|
);
|
|
|
|
if (!$curlopts[CURLOPT_POST]) {
|
|
$allheaders[] = 'Content-length: 0';
|
|
}
|
|
}
|
|
$allheaders = array_merge($allheaders, $headers);
|
|
|
|
$url = 'https://my.plexapp.com/' . $action;
|
|
debug_event('plex', 'Calling ' . $url, '5');
|
|
|
|
$options = array(
|
|
CURLOPT_HEADER => false,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_HEADERFUNCTION => array('Plex_Api', 'request_output_header'),
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => false,
|
|
CURLOPT_HTTPHEADER => $allheaders,
|
|
);
|
|
$options += $curlopts;
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, $options);
|
|
$r = curl_exec($ch);
|
|
$res = array();
|
|
$res['status'] = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
$res['headers'] = self::$request_headers;
|
|
$res['raw'] = $r;
|
|
try {
|
|
$res['xml'] = simplexml_load_string($r);
|
|
} catch (Exception $e) {
|
|
// If exception, wrong data returned (Plex API changes?)
|
|
}
|
|
return $res;
|
|
}
|
|
|
|
public static function root()
|
|
{
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setRootContent($r, Catalog::get_catalogs());
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function library($params)
|
|
{
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
Plex_XML_Data::setLibraryContent($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function system($params)
|
|
{
|
|
$r = Plex_XML_Data::createSysContainer();
|
|
Plex_XML_Data::setSystemContent($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function clients($params)
|
|
{
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function channels($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function photos($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function photo($params)
|
|
{
|
|
if (count($params) == 2) {
|
|
if ($params[0] == ':' && $params[1] == 'transcode') {
|
|
$width = $_REQUEST['width'];
|
|
$height = $_REQUEST['height'];
|
|
$url = $_REQUEST['url'];
|
|
|
|
// Replace 32400 request port with the real listening port
|
|
// *** To `Plex Inc`: ***
|
|
// Please allow listening port server configuration for your Plex server
|
|
// and fix your clients to not request resources so hard-coded on 127.0.0.1:32400.
|
|
// May be ok on Apple & UPnP world but that's really ugly for a server...
|
|
// Yes, it's a little hack but it works.
|
|
$localrs = "http://127.0.0.1:32400/";
|
|
$options = Core::requests_options();
|
|
if (strpos($url, $localrs) !== false) {
|
|
$options = array(); // In case proxy is set, no proxy for local addresses
|
|
$url = "http://127.0.0.1:" . Plex_XML_Data::getServerPort() . "/" . substr($url, strlen($localrs));
|
|
}
|
|
|
|
if ($width && $height && $url) {
|
|
$request = Requests::get($url, array(), $options);
|
|
if ($request->status_code == 200) {
|
|
ob_clean();
|
|
$mime = $request->headers['content-type'];
|
|
self::setHeader($mime);
|
|
$art = new Art(0);
|
|
$art->raw = $request->body;
|
|
$thumb = $art->generate_thumb($art->raw, array('width' => $width, 'height' => $height), $mime);
|
|
echo $thumb['thumb'];
|
|
exit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function music($params)
|
|
{
|
|
if (count($params) > 2) {
|
|
if ($params[0] == ':' && $params[1] == 'transcode') {
|
|
if (count($params) == 3) {
|
|
$format = $_REQUEST['format'] ?: pathinfo($params[2], PATHINFO_EXTENSION);
|
|
$url = $_REQUEST['url'];
|
|
$br = $_REQUEST['audioBitrate'];
|
|
if (preg_match("/\/parts\/([0-9]+)\//", $url, $matches)) {
|
|
$song_id = Plex_XML_Data::getAmpacheId($matches[1]);
|
|
}
|
|
} elseif (count($params) == 4 && $params[2] == 'universal') {
|
|
$format = pathinfo($params[3], PATHINFO_EXTENSION);
|
|
$path = $_REQUEST['path'];
|
|
// Should be the maximal allowed bitrate, not necessary the bitrate used but Ampache doesn't support this kind of option yet
|
|
$br = $_REQUEST['maxAudioBitrate'];
|
|
if (preg_match("/\/metadata\/([0-9]+)/", $path, $matches)) {
|
|
$song_id = Plex_XML_Data::getAmpacheId($matches[1]);
|
|
}
|
|
}
|
|
|
|
if (!empty($format) && !empty($song_id)) {
|
|
$urlparams = '&transcode_to=' . $format;
|
|
if (!empty($br)) {
|
|
$urlparams .= '&bitrate=' . $br;
|
|
}
|
|
|
|
$url = Song::play_url($song_id, $urlparams, 'api', true);
|
|
self::stream_url($url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function video($params)
|
|
{
|
|
if (count($params) > 1 && $params[0] == ':' && $params[1] == 'transcode') {
|
|
array_shift($params);
|
|
array_shift($params);
|
|
self::video_transcode($params);
|
|
} else {
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
}
|
|
|
|
private static function video_transcode($params)
|
|
{
|
|
$n = count($params);
|
|
if ($n == 2) {
|
|
$transcode_to = $params[0];
|
|
$action = $params[1];
|
|
$id = '';
|
|
|
|
$path = $_GET['path'];
|
|
$protocol = $_GET['protocol'];
|
|
$offset = $_GET['offset'];
|
|
|
|
// Transcode arguments.
|
|
$videoQuality = $_GET['videoQuality'];
|
|
$videoResolution = $_GET['videoResolution'];
|
|
$maxVideoBitrate = $_GET['maxVideoBitrate'];
|
|
$subtitleSize = $_GET['subtitleSize'];
|
|
$audioBoost = $_GET['audioBoost'];
|
|
|
|
$additional_params = '&vsettings=';
|
|
if ($videoResolution) {
|
|
$additional_params .= 'resolution-' . $videoResolution . '-';
|
|
}
|
|
if ($maxVideoBitrate) {
|
|
$additional_params .= 'maxbitrate-' . $maxVideoBitrate . '-';
|
|
}
|
|
if ($videoQuality) {
|
|
$additional_params .= 'quality-' . $videoQuality . '-';
|
|
}
|
|
|
|
if ($offset) {
|
|
$additional_params .= '&frame=' . $offset;
|
|
}
|
|
|
|
// Several Media and Part per Video is not supported
|
|
//$mediaIndex = $_GET['mediaIndex'];
|
|
//$partIndex = $_GET['partIndex'];
|
|
|
|
// What's that?
|
|
//$fastSeek = $_GET['fastSeek'];
|
|
//$directPlay = $_GET['directPlay'];
|
|
//$directStream = $_GET['directStream'];
|
|
|
|
$uriroot = '/library/metadata/';
|
|
$upos = strrpos($path, $uriroot);
|
|
if ($upos !== false) {
|
|
$id = substr($path, $upos + strlen($uriroot));
|
|
}
|
|
|
|
$session = $_GET['session'];
|
|
if ($action == "stop") {
|
|
// We should kill associated transcode session here
|
|
} elseif (strpos($action, "start") === 0) {
|
|
if (empty($protocol) || $protocol == "hls") {
|
|
header('Content-Type: application/vnd.apple.mpegurl');
|
|
|
|
$videoResolution = $_GET['videoResolution'];
|
|
$maxVideoBitrate = $_GET['maxVideoBitrate'];
|
|
if (!$maxVideoBitrate) {
|
|
$maxVideoBitrate = 8175;
|
|
}
|
|
|
|
echo "#EXTM3U\n";
|
|
echo "#EXT-X-STREAM-INF:PROGRAM-ID=1";
|
|
if ($maxVideoBitrate) {
|
|
echo ",BANDWIDTH=" . ($maxVideoBitrate * 1000);
|
|
}
|
|
if ($videoResolution) {
|
|
echo ",RESOLUTION=" . $videoResolution;
|
|
}
|
|
echo "\n";
|
|
echo "hls.m3u8?" . substr($_SERVER['QUERY_STRING'], strpos($_SERVER['QUERY_STRING'], '&') + 1);
|
|
} elseif ($protocol == "http") {
|
|
$url = null;
|
|
if ($transcode_to == 'universal') {
|
|
$additional_params .= '&transcode_to=webm';
|
|
if (AmpConfig::get('encode_args_webm')) {
|
|
debug_event('plex', 'Universal transcoder requested but `webm` transcode settings not configured. This will probably failed.', 3);
|
|
}
|
|
}
|
|
|
|
if ($id) {
|
|
if (Plex_XML_Data::isSong($id)) {
|
|
$url = Song::play_url(Plex_XML_Data::getAmpacheId($id), $additional_params, 'api');
|
|
} elseif (Plex_XML_Data::isVideo($id)) {
|
|
$url = Video::play_url(Plex_XML_Data::getAmpacheId($id), $additional_params, 'api');
|
|
}
|
|
|
|
if ($url) {
|
|
self::stream_url($url);
|
|
}
|
|
}
|
|
}
|
|
} elseif ($action == "hls.m3u8") {
|
|
if ($id) {
|
|
$pl = new Stream_Playlist();
|
|
|
|
$media = null;
|
|
if (Plex_XML_Data::isSong($id)) {
|
|
$media = array(
|
|
'object_type' => 'song',
|
|
'object_id' => Plex_XML_Data::getAmpacheId($id),
|
|
);
|
|
} elseif (Plex_XML_Data::isVideo($id)) {
|
|
$media = array(
|
|
'object_type' => 'video',
|
|
'object_id' => Plex_XML_Data::getAmpacheId($id),
|
|
);
|
|
}
|
|
|
|
if ($media != null) {
|
|
$pl->add(array($media), $additional_params);
|
|
}
|
|
|
|
$pl->generate_playlist('hls');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function applications($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function library_sections($params)
|
|
{
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
$n = count($params);
|
|
if ($n == 0) {
|
|
Plex_XML_Data::setSections($r, Catalog::get_catalogs());
|
|
} else {
|
|
$key = $params[0];
|
|
$catalog = Catalog::create_from_id($key);
|
|
if (!$catalog) {
|
|
self::createError(404);
|
|
}
|
|
if ($n == 1) {
|
|
Plex_XML_Data::setSectionContent($r, $catalog);
|
|
} elseif ($n == 2) {
|
|
$view = $params[1];
|
|
$gtypes = $catalog->get_gather_types();
|
|
if ($gtypes[0] == 'music') {
|
|
$type = 'artist';
|
|
if ($_GET['type']) {
|
|
$type = Plex_XML_Data::getAmpacheType($_GET['type']);
|
|
}
|
|
|
|
if ($view == "all") {
|
|
switch ($type) {
|
|
case 'artist':
|
|
Plex_XML_Data::setSectionAll_Artists($r, $catalog);
|
|
break;
|
|
case 'album':
|
|
Plex_XML_Data::setSectionAll_Albums($r, $catalog);
|
|
break;
|
|
}
|
|
} elseif ($view == "albums") {
|
|
Plex_XML_Data::setSectionAlbums($r, $catalog);
|
|
} elseif ($view == "recentlyadded") {
|
|
Plex_XML_Data::setCustomSectionView($r, $catalog, Stats::get_recent('album', 25, $key));
|
|
} elseif ($view == "genre") {
|
|
Plex_XML_Data::setSectionTags($r, $catalog, 'song');
|
|
}
|
|
} elseif ($gtypes[0] == "tvshow") {
|
|
$type = 'tvshow';
|
|
if ($_GET['type']) {
|
|
$type = Plex_XML_Data::getAmpacheType($_GET['type']);
|
|
}
|
|
|
|
if ($view == "all") {
|
|
switch ($type) {
|
|
case 'tvshow':
|
|
Plex_XML_Data::setSectionAll_TVShows($r, $catalog);
|
|
break;
|
|
case 'season':
|
|
Plex_XML_Data::setSectionAll_Seasons($r, $catalog);
|
|
break;
|
|
case 'episode':
|
|
Plex_XML_Data::setSectionAll_Episodes($r, $catalog);
|
|
break;
|
|
}
|
|
} elseif ($view == "recentlyadded") {
|
|
Plex_XML_Data::setCustomSectionView($r, $catalog, Stats::get_recent('tvshow_episode', 25, $key));
|
|
} elseif ($view == "genre") {
|
|
Plex_XML_Data::setSectionTags($r, $catalog, 'video');
|
|
}
|
|
} elseif ($gtypes[0] == "movie") {
|
|
$type = 'tvshow';
|
|
if ($_GET['type']) {
|
|
$type = Plex_XML_Data::getAmpacheType($_GET['type']);
|
|
}
|
|
|
|
if ($view == "all") {
|
|
Plex_XML_Data::setSectionAll_Movies($r, $catalog);
|
|
} elseif ($view == "recentlyadded") {
|
|
Plex_XML_Data::setCustomSectionView($r, $catalog, Stats::get_recent('movie', 25, $key));
|
|
} elseif ($view == "genre") {
|
|
Plex_XML_Data::setSectionTags($r, $catalog, 'video');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function library_metadata($params)
|
|
{
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
$n = count($params);
|
|
$litem = null;
|
|
|
|
$createMode = ($_SERVER['REQUEST_METHOD'] == 'POST');
|
|
$editMode = ($_SERVER['REQUEST_METHOD'] == 'PUT');
|
|
|
|
if ($n > 0) {
|
|
$key = $params[0];
|
|
|
|
$id = Plex_XML_Data::getAmpacheId($key);
|
|
if ($editMode) {
|
|
self::check_access(50);
|
|
}
|
|
|
|
if ($n == 1) {
|
|
// Should we check that files still exists here?
|
|
$checkFiles = $_REQUEST['checkFiles'];
|
|
$extra = $_REQUEST['includeExtra'];
|
|
|
|
if (Plex_XML_Data::isArtist($key)) {
|
|
$litem = new Artist($id);
|
|
$litem->format();
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => 'name',
|
|
'summary' => null,
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
Plex_XML_Data::addArtist($r, $litem);
|
|
} elseif (Plex_XML_Data::isAlbum($key)) {
|
|
$litem = new Album($id);
|
|
$litem->format();
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => 'name',
|
|
'year' => null,
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
Plex_XML_Data::addAlbum($r, $litem);
|
|
} elseif (Plex_XML_Data::isTrack($key)) {
|
|
$litem = new Song($id);
|
|
$litem->format();
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => null,
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
Plex_XML_Data::addSong($r, $litem);
|
|
} elseif (Plex_XML_Data::isTVShow($key)) {
|
|
$litem = new TVShow($id);
|
|
$litem->format();
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => 'name',
|
|
'year' => null,
|
|
'summary' => null,
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
Plex_XML_Data::addTVShow($r, $litem);
|
|
} elseif (Plex_XML_Data::isTVShowSeason($key)) {
|
|
$litem = new TVShow_Season($id);
|
|
$litem->format();
|
|
Plex_XML_Data::addTVShowSeason($r, $litem);
|
|
} elseif (Plex_XML_Data::isVideo($key)) {
|
|
$litem = Video::create_from_id($id);
|
|
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => null,
|
|
'year' => null,
|
|
'originallyAvailableAt' => 'release_date',
|
|
'originalTitle' => 'original_name',
|
|
'summary' => null,
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
$litem->format();
|
|
|
|
$subtype = strtolower(get_class($litem));
|
|
if ($subtype == 'tvshow_episode') {
|
|
Plex_XML_Data::addEpisode($r, $litem, true);
|
|
} elseif ($subtype == 'movie') {
|
|
Plex_XML_Data::addMovie($r, $litem, true);
|
|
}
|
|
} elseif (Plex_XML_Data::isPlaylist($key)) {
|
|
$litem = new Playlist($id);
|
|
$litem->format();
|
|
if ($editMode) {
|
|
$dmap = array(
|
|
'title' => 'name',
|
|
);
|
|
$litem->update(self::get_data_from_map($dmap));
|
|
}
|
|
Plex_XML_Data::addPlaylist($r, $litem);
|
|
}
|
|
} else {
|
|
$subact = $params[1];
|
|
if ($subact == "children") {
|
|
if (Plex_XML_Data::isArtist($key)) {
|
|
$litem = new Artist($id);
|
|
$litem->format();
|
|
Plex_XML_Data::setArtistRoot($r, $litem);
|
|
} else {
|
|
if (Plex_XML_Data::isAlbum($key)) {
|
|
$litem = new Album($id);
|
|
$litem->format();
|
|
Plex_XML_Data::setAlbumRoot($r, $litem);
|
|
} else {
|
|
if (Plex_XML_Data::isTVShow($key)) {
|
|
$litem = new TVShow($id);
|
|
$litem->format();
|
|
Plex_XML_Data::setTVShowRoot($r, $litem);
|
|
} else {
|
|
if (Plex_XML_Data::isTVShowSeason($key)) {
|
|
$litem = new TVShow_Season($id);
|
|
$litem->format();
|
|
Plex_XML_Data::setTVShowSeasonRoot($r, $litem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} elseif ($subact == "thumbs" || $subact == "posters" || $subact == "arts" || $subact == 'backgrounds') {
|
|
$kind = Plex_XML_Data::getPhotoKind($subact);
|
|
if ($createMode) {
|
|
// Upload art
|
|
$litem = Plex_XML_Data::createLibraryItem($key);
|
|
if ($litem != null) {
|
|
$uri = Plex_XML_Data::getMetadataUri($key) . '/' . Plex_XML_Data::getPhotoPlexKind($kind) . '/' . $key;
|
|
if (is_a($litem, 'video')) {
|
|
$type = 'video';
|
|
} else {
|
|
$type = get_class($litem);
|
|
}
|
|
|
|
$art = new Art($litem->id, $type, $kind);
|
|
$raw = file_get_contents("php://input");
|
|
$art->insert($raw);
|
|
|
|
header('Content-Type: text/html');
|
|
echo $uri;
|
|
exit;
|
|
}
|
|
}
|
|
Plex_XML_Data::addPhotos($r, $key, $kind);
|
|
} elseif ($subact == "thumb" || $subact == "poster" || $subact == "art" || $subact == "background") {
|
|
if ($n == 3) {
|
|
$kind = Plex_XML_Data::getPhotoKind($subact);
|
|
// Ignore art id as we can only have 1 thumb
|
|
$art = null;
|
|
if (Plex_XML_Data::isArtist($key)) {
|
|
$art = new Art($id, "artist", $kind);
|
|
} else {
|
|
if (Plex_XML_Data::isAlbum($key)) {
|
|
$art = new Art($id, "album", $kind);
|
|
} else {
|
|
if (Plex_XML_Data::isTrack($key)) {
|
|
$art = new Art($id, "song", $kind);
|
|
} else {
|
|
if (Plex_XML_Data::isTVShow($key)) {
|
|
$art = new Art($id, "tvshow", $kind);
|
|
} else {
|
|
if (Plex_XML_Data::isTVShowSeason($key)) {
|
|
$art = new Art($id, "tvshow_season", $kind);
|
|
} else {
|
|
if (Plex_XML_Data::isVideo($key)) {
|
|
$art = new Art($id, "video", $kind);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($art != null) {
|
|
$art->get_db();
|
|
|
|
ob_clean();
|
|
if (!isset($size)) {
|
|
self::setHeader($art->raw_mime);
|
|
echo $art->raw;
|
|
} else {
|
|
$dim = array();
|
|
$dim['width'] = $size;
|
|
$dim['height'] = $size;
|
|
$thumb = $art->get_thumb($dim);
|
|
self::setHeader($art->thumb_mime);
|
|
echo $thumb['thumb'];
|
|
}
|
|
exit();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($litem != null) {
|
|
$catalog_ids = $litem->get_catalogs();
|
|
if (count($catalog_ids) > 0) {
|
|
$catalog = Catalog::create_from_id($catalog_ids[0]);
|
|
Plex_XML_Data::addCatalogIdentity($r, $catalog);
|
|
}
|
|
}
|
|
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
protected static function stream_url($url)
|
|
{
|
|
// header("Location: " . $url);
|
|
set_time_limit(0);
|
|
ob_end_clean();
|
|
|
|
$headers = apache_request_headers();
|
|
$reqheaders = array();
|
|
if (isset($headers['Range'])) {
|
|
$reqheaders[] = "Range: " . $headers['Range'];
|
|
}
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, array(
|
|
CURLOPT_HTTPHEADER => $reqheaders,
|
|
CURLOPT_HEADER => false,
|
|
CURLOPT_CONNECTTIMEOUT => 2,
|
|
CURLOPT_RETURNTRANSFER => false,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_WRITEFUNCTION => array('Plex_Api', 'replay_body'),
|
|
CURLOPT_HEADERFUNCTION => array('Plex_Api', 'replay_header'),
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => false,
|
|
CURLOPT_TIMEOUT => 0
|
|
));
|
|
if (curl_exec($ch) === false) {
|
|
debug_event('plex-api', 'Curl error: ' . curl_error($ch),1);
|
|
}
|
|
curl_close($ch);
|
|
}
|
|
|
|
public static function library_parts($params)
|
|
{
|
|
$n = count($params);
|
|
|
|
if ($n > 0) {
|
|
$key = $params[0];
|
|
if ($n == 2) {
|
|
$file = $params[1];
|
|
|
|
$id = Plex_XML_Data::getAmpacheId($key);
|
|
if (Plex_XML_Data::isSong($key)) {
|
|
$media = new Song($id);
|
|
if ($media->id) {
|
|
$url = Song::play_url($id, '', 'api', true);
|
|
self::stream_url($url);
|
|
} else {
|
|
self::createError(404);
|
|
}
|
|
} elseif (Plex_XML_Data::isVideo($key)) {
|
|
$media = new Video($id);
|
|
if ($media->id) {
|
|
$url = Video::play_url($id, '', 'api', true);
|
|
self::stream_url($url);
|
|
} else {
|
|
self::createError(404);
|
|
}
|
|
}
|
|
} elseif ($n == 1) {
|
|
if ($_SERVER['REQUEST_METHOD'] == 'PUT') {
|
|
if (isset($_GET['subtitleStreamID'])) {
|
|
$lang_code = dechex(hex2bin(substr($_GET['subtitleStreamID'], 0, 2)));
|
|
$_SESSION['iframe']['subtitle'] = $lang_code;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function library_recentlyadded($params)
|
|
{
|
|
$data = array();
|
|
$data['album'] = Stats::get_newest('album', 25);
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
Plex_XML_Data::setCustomView($r, $data);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function library_ondeck($params)
|
|
{
|
|
$data = array();
|
|
$data['album'] = Stats::get_recent('album', 25);
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
Plex_XML_Data::setCustomView($r, $data);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function system_library_sections($params)
|
|
{
|
|
$r = Plex_XML_Data::createSysContainer();
|
|
Plex_XML_Data::setSysSections($r, Catalog::get_catalogs());
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function manage_frameworks_ekspinner_resources($params)
|
|
{
|
|
// Image file used to 'ping' the server
|
|
if ($params[0] == "small_black_7.png") {
|
|
header("Content-type: image/png", true);
|
|
echo file_get_contents(AmpConfig::get('prefix') . '/plex/resources/small_black_7.png');
|
|
exit;
|
|
}
|
|
}
|
|
|
|
public static function myplex_account($params)
|
|
{
|
|
$r = Plex_XML_Data::createMyPlexAccount();
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function system_agents($params)
|
|
{
|
|
$r = Plex_XML_Data::createSysContainer();
|
|
$addcontributors = false;
|
|
$mediaType = $_REQUEST['mediaType'];
|
|
if (count($params) >= 3 && $params[1] == 'config') {
|
|
$mediaType = $params[2];
|
|
$addcontributors = true;
|
|
}
|
|
|
|
if ($mediaType) {
|
|
switch ($mediaType) {
|
|
case '1':
|
|
Plex_XML_Data::setSysMovieAgents($r);
|
|
break;
|
|
case '2':
|
|
Plex_XML_Data::setSysTVShowAgents($r);
|
|
break;
|
|
case '13':
|
|
Plex_XML_Data::setSysPhotoAgents($r);
|
|
break;
|
|
case '8':
|
|
Plex_XML_Data::setSysMusicAgents($r);
|
|
break;
|
|
case '9':
|
|
Plex_XML_Data::setSysMusicAgents($r, 'Albums');
|
|
break;
|
|
default:
|
|
self::createError(404);
|
|
break;
|
|
}
|
|
} else {
|
|
Plex_XML_Data::setSysAgents($r);
|
|
}
|
|
if ($addcontributors) {
|
|
Plex_XML_Data::setAgentsContributors($r, $mediaType, 'com.plexapp.agents.none');
|
|
}
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function system_agents_contributors($params)
|
|
{
|
|
$mediaType = $_REQUEST['mediaType'];
|
|
$primaryAgent = $_REQUEST['primaryAgent'];
|
|
|
|
$r = Plex_XML_Data::createSysContainer();
|
|
Plex_XML_Data::setAgentsContributors($r, $mediaType, $primaryAgent);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function system_agents_attribution($params)
|
|
{
|
|
$identifier = $_REQUEST['identifier'];
|
|
|
|
self::createError(404);
|
|
}
|
|
|
|
public static function system_scanners($params)
|
|
{
|
|
if (count($params) > 0) {
|
|
$type = $params[0];
|
|
$r = Plex_XML_Data::createSysContainer();
|
|
Plex_XML_Data::setScanners($r, $type);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
} else {
|
|
self::createError(404);
|
|
}
|
|
}
|
|
|
|
public static function system_appstore($params)
|
|
{
|
|
$r = Plex_XML_Data::createAppStore();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function accounts($params)
|
|
{
|
|
$userid = '';
|
|
if (isset($params[0])) {
|
|
$userid = $params[0];
|
|
}
|
|
// Not supported yet
|
|
if ($userid > 1) {
|
|
self::createError(404);
|
|
}
|
|
|
|
$r = Plex_XML_Data::createAccountContainer();
|
|
Plex_XML_Data::setAccounts($r, $userid);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function status($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setStatus($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function status_sessions($params)
|
|
{
|
|
self::createError(403);
|
|
}
|
|
|
|
public static function prefs($params)
|
|
{
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setPrefs($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function help($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function plexonline($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function plugins($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function services($params)
|
|
{
|
|
$r = Plex_XML_Data::createPluginContainer();
|
|
Plex_XML_Data::setServices($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function services_browse($params)
|
|
{
|
|
self::check_access(75);
|
|
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setBrowseService($r, $params[0]);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function timeline($params)
|
|
{
|
|
$ratingKey = $_REQUEST['ratingKey'];
|
|
$key = $_REQUEST['key'];
|
|
$state = $_REQUEST['state'];
|
|
$time = $_REQUEST['time'];
|
|
$duration = $_REQUEST['duration'];
|
|
|
|
// Not supported right now (maybe in a future for broadcast?)
|
|
header('Content-Type: text/html');
|
|
}
|
|
|
|
public static function rate($params)
|
|
{
|
|
$id = $_REQUEST['key'];
|
|
$identifier = $_REQUEST['identifier'];
|
|
$rating = $_REQUEST['rating'];
|
|
|
|
if ($identifier == 'com.plexapp.plugins.library') {
|
|
$robj = new Rating(Plex_XML_Data::getAmpacheId($id), Plex_XML_Data::getLibraryItemType($id));
|
|
$robj->set_rating($rating / 2);
|
|
}
|
|
}
|
|
|
|
protected static function get_users_account($authtoken='')
|
|
{
|
|
if (empty($authtoken)) {
|
|
$authtoken = Plex_XML_Data::getMyPlexAuthToken();
|
|
}
|
|
|
|
$action = 'users/account?auth_token=' . $authtoken;
|
|
$res = self::myPlexRequest($action);
|
|
return $res['xml'];
|
|
}
|
|
|
|
public static function users_account($params)
|
|
{
|
|
$xml = self::get_users_account();
|
|
Plex_XML_Data::setMyPlexSubscription($xml);
|
|
self::apiOutput($xml->asXML());
|
|
}
|
|
|
|
/**
|
|
myPlex server function to grant plexpass access dynamically.
|
|
Used for testing purpose only.
|
|
*/
|
|
public static function users($params)
|
|
{
|
|
if ($params[0] == 'sign_in.xml') {
|
|
$curlopts = array();
|
|
$headers = array();
|
|
$res = self::myPlexRequest('users/sign_in.xml', $curlopts, $headers, true);
|
|
|
|
foreach ($res['headers'] as $header) {
|
|
header($header);
|
|
}
|
|
|
|
if ($res['status'] == '201') {
|
|
Plex_XML_Data::setMyPlexSubscription($res['xml']);
|
|
self::apiOutput($res['xml']->asXML());
|
|
} else {
|
|
self::createError($res['status']);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function servers($params)
|
|
{
|
|
$r = Plex_XML_Data::createContainer();
|
|
Plex_XML_Data::setLocalServerInfo($r);
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function playlists($params)
|
|
{
|
|
$r = Plex_XML_Data::createContainer();
|
|
$n = count($params);
|
|
|
|
$createMode = ($_SERVER['REQUEST_METHOD'] == 'POST');
|
|
$editMode = ($_SERVER['REQUEST_METHOD'] == 'PUT');
|
|
$delMode = ($_SERVER['REQUEST_METHOD'] == 'DELETE');
|
|
if ($createMode || $editMode || $delMode) {
|
|
self::check_access(50);
|
|
}
|
|
|
|
if ($n <= 1) {
|
|
$plid = 0;
|
|
if ($n == 0 && $createMode) {
|
|
// Create a new playlist
|
|
//$type = $_GET['type'];
|
|
$title = $_GET['title'];
|
|
//$smart = $_GET['smart'];
|
|
//$summary = $_GET['summary'];
|
|
$uri = $_GET['uri'];
|
|
|
|
$plid = Playlist::create($title, 'public');
|
|
$playlist = new Playlist($plid);
|
|
$key = Plex_XML_Data::getKeyFromFullUri($uri);
|
|
$id = Plex_XML_Data::getKeyFromMetadataUri($key);
|
|
if ($id) {
|
|
$item = Plex_XML_Data::createLibraryItem($id);
|
|
$medias = $item->get_medias();
|
|
$playlist->add_medias($medias);
|
|
}
|
|
$plid = Plex_XML_Data::getPlaylistId($plid);
|
|
} else {
|
|
if ($n == 1 && $params[0] != "all") {
|
|
$plid = $params[0];
|
|
}
|
|
}
|
|
|
|
if ($plid) {
|
|
if (Plex_XML_Data::isPlaylist($plid)) {
|
|
$playlist = new Playlist(Plex_XML_Data::getAmpacheId($plid));
|
|
if ($playlist->id) {
|
|
if ($delMode) {
|
|
// Delete playlist
|
|
$playlist->delete();
|
|
} else {
|
|
// Display playlist information
|
|
Plex_XML_Data::addPlaylist($r, $playlist);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// List all playlists
|
|
Plex_XML_Data::setPlaylists($r);
|
|
}
|
|
} elseif ($n >= 2) {
|
|
$plid = $params[0];
|
|
if (Plex_XML_Data::isPlaylist($plid) && $params[1] == "items") {
|
|
$playlist = new Playlist(Plex_XML_Data::getAmpacheId($plid));
|
|
if ($playlist->id) {
|
|
if ($n == 2) {
|
|
if ($editMode) {
|
|
// Add a new item to playlist
|
|
$uri = $_GET['uri'];
|
|
$key = Plex_XML_Data::getKeyFromFullUri($uri);
|
|
$id = Plex_XML_Data::getKeyFromMetadataUri($key);
|
|
if ($id) {
|
|
$item = Plex_XML_Data::createLibraryItem($id);
|
|
$medias = $item->get_medias();
|
|
$playlist->add_medias($medias);
|
|
Plex_XML_Data::addPlaylist($r, $playlist);
|
|
}
|
|
} else {
|
|
Plex_XML_Data::setPlaylistItems($r, $playlist);
|
|
}
|
|
} elseif ($n == 3) {
|
|
$index = intval($params[2]);
|
|
if ($delMode) {
|
|
$playlist->delete_track_number($index);
|
|
$playlist->regenerate_track_numbers();
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
public static function playqueues($params)
|
|
{
|
|
$n = count($params);
|
|
$r = Plex_XML_Data::createLibContainer();
|
|
|
|
if ($n == 1) {
|
|
$playlistID = $params[0];
|
|
Plex_XML_Data::setTmpPlayQueue($r, $playlistID);
|
|
} else {
|
|
$type = $_GET['type'];
|
|
$playlistID = $_GET['playlistID'];
|
|
$uri = $_GET['uri'];
|
|
$key = $_GET['key'];
|
|
$shuffle = $_GET['shuffle'];
|
|
|
|
Plex_XML_Data::setPlayQueue($r, $type, $playlistID, $uri, $key, $shuffle);
|
|
}
|
|
|
|
Plex_XML_Data::setContainerSize($r);
|
|
self::apiOutputXml($r->asXML());
|
|
}
|
|
|
|
private static function get_data_from_map($dmap)
|
|
{
|
|
$data = array();
|
|
|
|
foreach ($dmap as $key=>$value) {
|
|
if (isset($_GET[$key])) {
|
|
if ($value == null) {
|
|
$value = $key;
|
|
}
|
|
|
|
$data[$value] = $_GET[$key];
|
|
}
|
|
}
|
|
|
|
if (isset($_GET['genre'])) {
|
|
$data['edit_tags'] = implode(',', $_GET['genre']);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
}
|