├── .gitattributes ├── .gitignore ├── .htaccess ├── README.md ├── config-sample-channels.php ├── config-sample-ssl.php ├── functions.php └── index.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | sftp-config.json 217 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | RewriteBase / 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteCond %{REQUEST_FILENAME} !-d 6 | RewriteRule ^(.*)$ index.php?/$1 [L] 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | RunTellThat (for Plex) 2 | ============ 3 | 4 | 5 | # NOTE: This repo is currently no longer being actively developed. However, please leave comments (issues) and questions. The more people that are interested the more likely it is I will pick this back up. 6 | 7 | Distributed computing implementation for plex. In-depth documentation/explanation including load balancing discussion is [here] (https://docs.google.com/drawings/d/1gv_1zGANUaoiXvKYSVKILGWWNoUYZ9ajWIo3Z76XEps/edit?usp=sharing) 8 | 9 | #Current support: 10 | Currently RTT only supports proxy of requests to a designated PMS. This is best suited for sharing channels or providing an super user experience for a shared user. This is helpful for an application like [Serenity] (https://forums.plex.tv/index.php/forum/135-serenity-for-android/) which does not support myPlex shares.
11 | [Here are instructions] (https://github.com/rxsegrxup/RunTellThat/wiki/Setup-Guide-to-Share-Channels) 12 | 13 | #What this is: 14 | - A MitM webserver to distribute or proxy access to arbitrary PMS slaves; A must-have for load-balancing shared access to a PMS cluster [which this hack facilitates]. 15 | - A proxy to delegate channel access w/o permanently binding PMS to myPlex. 16 | - The foundation of a decent plex load balancer. 17 | - A new paradigm for plex sharing. 18 | - A special hack that indeed will probably never be "officially" supported. 19 | 20 | #What this is **not**: 21 | - A hacking tool to steal someone's plex share or server. 22 | - A Plex Channel. 23 | - Chuck Norris's PMS farm 24 | 25 | ##Please see the [FAQ] (https://github.com/rxsegrxup/RunTellThat/wiki/FAQ) 26 | 27 | #Requirements 28 | - Apache 2.x 29 | - curl 30 | - ifstat (load balancing) 31 | - python 2.7 (load balancing/PMS) 32 | 33 | #Pre-Installation 34 | A few things are assumed about your setup & usage of Plex prior to utilizing RTT: 35 | - You ideally have 3 or more separate servers, with/without identical instances of PMS (in terms of library sections & channels installed) that you would like to incorporate in a distributed computing model where: 'server1' (let's call this the Master), recieves and funnels requests to 'server2' OR 'server3', depending on the current CPU/Network load of these machines (known as 'Slaves'). 36 | - You have # or more Plex.tv accounts ____ 37 | 38 | #Installation 39 | Clone this repo into an apache webserver folder on your Master/proxy server. 40 | ``` 41 | webuser@master:/var/www/plex# git clone https://github.com/rxsegrxup/RunTellThat.git 42 | webuser@master:/var/www/plex# mv RunTellThat/* ./ 43 | ``` 44 | Utilize the following VirtualHost 45 | ``` 46 | 47 | ServerAdmin webmaster@localhost 48 | DocumentRoot /var/www/plex 49 | 50 | Options FollowSymLinks 51 | AllowOverride All 52 | 53 | 54 | ErrorLog ${APACHE_LOG_DIR}/rtterror.log 55 | LogLevel warn 56 | CustomLog ${APACHE_LOG_DIR}/rttaccess.log combined 57 | 58 | ``` 59 | Be sure to enable Listening on port 32402 (or whatever port you like) 60 | (NOTE: PMS will run on 32400 AND 32401 61 | ``` 62 | #apache2.conf OR ports.conf 63 | NameVirtualHost *:32402 64 | Listen 32402 65 | ``` 66 | Set the values in the config file. (More help below) 67 | Enable mod_rewrite 68 | ``` 69 | a2enmod rewrite 70 | ``` 71 | Start up apache and you're OFF! 72 | When installation is finished, verify that apache is running and actively listening on incoming port 32402. 73 | 74 | #How to use 75 | There are two modes that this is accessible in. Lets call them IP mode, and Ghost mode.
76 |
77 | **IP Mode**: 78 |
79 | IP mode is the simplest way to utilize this. 80 | This is done by manually configuring a plex server and for the hostname/ip use that of the RunTellThat server. 81 | Or, simply navigate to RunTellThat's server and log in as if you would normally access a plex web server. 82 |
83 | **Ghost Mode** 84 |
85 | Ghost mode is a lot more complex as it requires a PMS server having the same IP as the RunTellThat (host) server to be owned by all end-users. The idea, is that by RunTellThat having the same IP as PMS Plex clients will know to look for a PMS server at this IP and when it finds RTT, RTT will forwards it to the destination. To do this, you must install PMS on the master/proxy server and map it to every user that will be accessing your content via RTT. 86 | 87 | #Ghost mapping 88 | **DummyPMS mapping on windows** 89 |
90 | * Quit PMS 91 | * start -> regedit.exe (search or use cmd) 92 | * navigate to and delete HKEY_CURRENT_USER\Software\Plex, Inc.\Plex Media Server 93 | * (leave regedit open) 94 | * start pms 95 | * Accept EULA 96 | * set friendlyName to DummyPMS 97 | * map pms server to a user at manual port 32402 98 | * retrive and store username=>machineid combo by accessing http://localhost:32400/ or use the registry key ProcessedMachineIdentifier 99 | * export (save) HKEY_CURRENT_USER\Software\Plex, Inc.\Plex Media Server as usernamePrefs.reg 100 | * repeat 101 | 102 | **DummyPMS mapping on linux** 103 |
104 | * stop plexmediaserver 105 | * delete /var/lib/plexmediaserver/Library/Application Support/Preferences.xml 106 | * start pms 107 | * Accept EULA 108 | * set friendlyName to DummyPMS 109 | * map pms server to a user at manual port 32402 110 | * retrive and store username=>machineid combo by accessing http://localhost:32400/ or use the new Preferences.xml 111 | * backup the Preferences.xml as username.Preferences.xml 112 | * repeat 113 | 114 | #Using the config file 115 | ``` 116 | //config.php 117 | 118 | 119 | //user name of the destination server's myPlex owner 120 | //Can be found in Preferences.xml 121 | $username = ""; 122 | 123 | //authToken of the destination server's myPlex owner 124 | //Can be found in Preferences.xml 125 | $token = ""; 126 | 127 | //global ip address:port of the current RTT server 128 | $lbip = "123.123.123.123:32402"; 129 | 130 | //ipaddress:port of the destination server 131 | $destserver = "123.123.123.321:32400"; 132 | 133 | //ipaddress:port of the destination server for RTT client streaming redirect 134 | //not used/documented 135 | $destserverre = "123.123.123.321:32400"; 136 | 137 | //friendlyName of dummyPMS server 138 | $lbname = "DummyPMS"; 139 | 140 | //friendlyName of destination PMS server 141 | $destname = "PMSSlave"; 142 | $lbuuidkvp = array( 143 | "username"=>"uuid", 144 | "username2"=>"uuid2" 145 | ); 146 | //machineIdentifier of the destination pms slave server 147 | //FUTURE: array of "friendlyName"=>"uuid" pairs 148 | $destuuid = ""; 149 | 150 | //DO NOT allow access to sections. 151 | $blocksections = TRUE; 152 | 153 | //configure logging 154 | $log = "/var/log/rtt/rtt.log"; 155 | $loggingenabled = TRUE; 156 | $loghistorylength = 5; 157 | $maxlogsize = 1* 1024 * 1024; //1MB 158 | ``` 159 | -------------------------------------------------------------------------------- /config-sample-channels.php: -------------------------------------------------------------------------------- 1 | "ProcessedMachineIdentifier1", 24 | "PlexOnlineMail2" => "ProcessedMachineIdentifier2", 25 | "PlexOnlineMail3" => "ProcessedMachineIdentifier3", 26 | $username => $destuuid); 27 | //Get the values below from the /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Preferences.xml 28 | //Server requests are being proxied to 29 | 30 | // Config 31 | $log = "/path/to/logs/changeup.log"; 32 | $loggingenabled = TRUE; 33 | $loghistorylength = 5; 34 | $maxlogsize = 1* 1024 * 1024; //1MB 35 | 36 | // RTT Client Mode 37 | // RTT Client mode will allow proxied media playback 38 | $swapidentity = TRUE; //Should the identity of the user be swapped with ther owner. Disable for general proxy 39 | $namedusersonly = TRUE; //Only allow users within the $lbuuidkvp array through. 40 | //When swapidentity is FALSE, disable to let PMS manage permissions. 41 | //When $enableanonymousaccess is TRUE, enable to limit user access. 42 | $blocksections = TRUE; //DO NOT allow access to sections. (TRUE to disable client mode || FALSE to enable client mode) 43 | $streamsize=10485760; //5MB. RTT Client streaming size. Not yet documented 44 | $clientstreaming=TRUE; //Used with Client mode. Will stream media instead of redirecting. 45 | // DANGER: redirecting is dangerous when used with swapidentity. This WILL 100% expose the super user account 46 | 47 | // DANGER ZONE 48 | $enableanonymousaccess=FALSE; //DANGER! DANGER! this will essentially open up the proxied server to ANY and EVERY myPlex user that knows the ip. 49 | //Enable only for debugging or if you know what you are doing. 50 | ?> -------------------------------------------------------------------------------- /config-sample-ssl.php: -------------------------------------------------------------------------------- 1 | :443"; //REMOTEIP:port of RTT 10 | $lbname = ""; //Friendly name of the Dummy PMS server having the same ip as RTT 11 | 12 | // Destination PMS 13 | $destserver = ""; //IP:port of the PMS server that requests are proxied to 14 | //$destserver = "localhost:32400" //If running RTT and PMS on the same server (use lan address if same lan) 15 | $destserverre = $destserver; //REMOTE IP:port of the PMS server that requests are proxied to 16 | //$destserverre = "REMOTEIP:REMOTEPORT" //Only use this if using lan ip for $destserver 17 | //Get the values below from the /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Preferences.xml 18 | $destname = ""; //Friendly name of the PMS server that requests are proxied to 19 | $destuuid = "ProcessedMachineIdentifier"; 20 | 21 | // Proxied Users 22 | $lbuuidkvp = array( 23 | "PlexOnlineMail1" => "ProcessedMachineIdentifier1", 24 | "PlexOnlineMail2" => "ProcessedMachineIdentifier2", 25 | "PlexOnlineMail3" => "ProcessedMachineIdentifier3", 26 | $username => $destuuid); 27 | //Get the values below from the /var/lib/plexmediaserver/Library/Application Support/Plex Media Server/Preferences.xml 28 | //Server requests are being proxied to 29 | 30 | // Config 31 | $log = "/path/to/logs/changeup.log"; 32 | $loggingenabled = TRUE; 33 | $loghistorylength = 5; 34 | $maxlogsize = 1* 1024 * 1024; //1MB 35 | 36 | // RTT Client Mode 37 | // RTT Client mode will allow proxied media playback 38 | $swapidentity = FALSE; //Should the identity of the user be swapped with ther owner. Disable for general proxy 39 | $namedusersonly = FALSE; //Only allow users within the $lbuuidkvp array through. 40 | //When swapidentity is FALSE, disable to let PMS manage permissions. 41 | //When $enableanonymousaccess is TRUE, enable to limit user access. 42 | $blocksections = FALSE; //DO NOT allow access to sections. (TRUE to disable client mode || FALSE to enable client mode) 43 | $streamsize=10485760; //5MB. RTT Client streaming size. Not yet documented 44 | $clientstreaming=TRUE; //Used with Client mode. Will stream media instead of redirecting. 45 | // DANGER: redirecting is dangerous when used with swapidentity. This WILL 100% expose the super user account 46 | 47 | // DANGER ZONE 48 | $enableanonymousaccess=FALSE; //DANGER! DANGER! this will essentially open up the proxied server to ANY and EVERY myPlex user that knows the ip. 49 | //Enable only if you know what you are doing. 50 | ?> -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | ") != FALSE){ 12 | $st = strpos($output,"") + strlen(""); 13 | $ed = strpos($output,""); 14 | $usernm=substr($output,$st,($ed-$st)); 15 | logger("Got username $usernm from plex.tv"); 16 | return $usernm; 17 | } 18 | else{ 19 | logger("Username tag not found"); 20 | return ""; 21 | } 22 | 23 | } 24 | 25 | function setupLogging(){ 26 | global $log,$loggingenabled,$maxlogsize, $logdir,$logfilename,$loghistorylength;; 27 | if($loggingenabled && !touch($log)){ 28 | $loggingenabled = FALSE; 29 | } 30 | elseif($loggingenabled){ 31 | $logdir = dirname($log); 32 | $logfilename = basename($log); 33 | if(!is_int($loghistorylength)){ 34 | $tmp = $loghistorylength; 35 | $loghistorylength = 5; 36 | //resulting to default max length 37 | logger("got incorrect log max lenth $tmp using default 5"); 38 | unset($tmp); 39 | } 40 | if(!is_int($maxlogsize)){ 41 | $maxlogsize = 1 * 1024 * 1024; //1MB 42 | } 43 | logger("============================================="); 44 | } 45 | } 46 | function logger($data){ 47 | global $log,$loggingenabled,$maxlogsize; 48 | if(!$loggingenabled){ 49 | return; 50 | } 51 | $today = date("Y-m-d H:i:s"); 52 | file_put_contents($log,"[$today]: $data\r\n",FILE_APPEND); 53 | if(filesize($log) > $maxlogsize){ //set this in config. Default is 1 MB 54 | logrotate(); 55 | } 56 | } 57 | function logrotate(){ 58 | global $logdir,$logfilename,$log,$loghistorylength; 59 | $logfiles = scandir($logdir); 60 | natsort($logfiles); 61 | $logfiles = array_reverse($logfiles); 62 | $postlog=array(); 63 | foreach ($logfiles as $file) { 64 | if(stripos($file, $logfilename) !== FALSE && $file != $logfilename){ 65 | $clognum = str_replace($logfilename.".","",$file); 66 | $clognum = intval($clognum); 67 | $postlog[] = "Current log is $clognum"; 68 | if($clognum >= $loghistorylength){ 69 | unlink("$logdir/$file"); 70 | $postlog[] = "removing log file $logdir/$file"; 71 | } 72 | elseif($clognum == 0 || $clognum == "0"){ 73 | } 74 | else{ 75 | rename("$logdir/$file", "$log.".($clognum+1)); 76 | $postlog[]= "renaming log from $logdir/$file to $log.".($clognum+1); 77 | } 78 | } 79 | } 80 | rename($log, "$log.1"); 81 | touch($log); 82 | foreach($postlog as $plogl){ 83 | logger($plogl); 84 | } 85 | } 86 | 87 | function replaceCookie($value, $repl){ 88 | $cookies = explode(";",$value); 89 | $toreplace = ""; 90 | foreach ($cookies as $cookie) { 91 | $kvp = explode("=", trim($cookie)); 92 | if($kvp[0] == "SESSION-GUID"){ 93 | $toreplace = $kvp[1]; 94 | break; 95 | } 96 | } 97 | if($toreplace != ""){ 98 | return str_replace($toreplace, $repl, $value); 99 | } 100 | else{ 101 | return $value; 102 | } 103 | } 104 | 105 | function fixRequestUri($uri){ 106 | global $token, $username, $lbip, $destserver, $unm,$utoken;//, $client 107 | $uriparts = explode("?", $uri); 108 | if(count($uriparts)>1){ 109 | $newuri = $uriparts[0]."?"; 110 | $uriq = $uriparts[1]; 111 | } 112 | else{ 113 | return $uri; 114 | } 115 | 116 | $queryparts = explode("&", $uriq); 117 | $firstrun = TRUE; 118 | foreach ($queryparts as $query) { 119 | $kvp = explode("=", trim($query)); 120 | switch ($kvp[0]) { 121 | case 'X-Plex-Token': 122 | $utoken = $kvp[1]; 123 | logger("found user token $utoken"); 124 | $kvp[1] = $token; 125 | logger("fixing url part ".$kvp[0]."=".$kvp[1]); 126 | break; 127 | case 'X-Plex-Username': 128 | $unm = $kvp[1]; 129 | logger("found username $unm"); 130 | $kvp[1] = $username; 131 | logger("fixing url part ".$kvp[0]."=".$kvp[1]); 132 | break; 133 | case 'Referer': 134 | case 'Host': 135 | case 'Origin': 136 | $kvp[1] = str_replace($lbip,$destserver, $kvp[1]); 137 | logger("fixing url part ".$kvp[0]."=".$kvp[1]); 138 | break; 139 | /* 140 | //In my tests I found that the client identifier doesnt much matter. 141 | //Only the username and token needs to be changed 142 | case 'Cookie': 143 | $value = replaceCookie($value, $client); 144 | break; 145 | case 'X-Plex-Client-Identifier': 146 | $value = $client; 147 | break;*/ 148 | } 149 | if($firstrun === TRUE){ 150 | $firstrun = FALSE; 151 | $newuri .= $kvp[0]."=".$kvp[1]; 152 | } 153 | else{ 154 | $newuri .= "&".$kvp[0]."=".$kvp[1]; 155 | } 156 | } 157 | return $newuri; 158 | } 159 | 160 | function swapBackBody($body){ 161 | global $destname, $lbname, $destuuid, $lbuuidkvp, $username, $token, $utoken, $unm; 162 | if(strpos($body,'friendlyName="'.$destname.'"') !== FALSE){ 163 | $body = str_replace('friendlyName="'.$destname.'"', 'friendlyName="'.$lbname.'"', $body); 164 | logger("trying to swap back friendlyname from $destname to $lbname"); 165 | } 166 | if(strpos($body,'machineIdentifier="'.$destuuid.'"') !== FALSE){ 167 | $body = str_replace('machineIdentifier="'.$destuuid.'"', 'machineIdentifier="'.$lbuuidkvp[urldecode($unm)].'"', $body); 168 | logger("trying to swap back machineID from $destuuid to ".$lbuuidkvp[urldecode($unm)]); 169 | } 170 | if(strpos($body,'myPlexUsername="'.$username.'"') !== FALSE){ 171 | $body = str_replace('myPlexUsername="'.$username.'"', 'myPlexUsername="'.$unm.'"', $body); 172 | logger("trying to swap back username from $username to $unm"); 173 | } 174 | if(strpos($body,'username="'.$username.'"') !== FALSE){ 175 | $body = str_replace('username="'.$username.'"', 'username="'.$unm.'"', $body); 176 | logger("trying to swap back username from $username to $unm"); 177 | } 178 | if(strpos($body,'authToken="'.$token.'"') !== FALSE){ 179 | $body = str_replace('authToken="'.$token.'"', 'authToken="'.$utoken.'"', $body); 180 | logger("trying to swap back token from $token to $utoken"); 181 | } 182 | return $body; 183 | } 184 | 185 | function checkBlockUrls($ouri, $originalurl){ 186 | global $blocksections; 187 | if( stripos($ouri, "/:/prefs") !== FALSE || 188 | (stripos($ouri, "/:/plugins") !== FALSE && stripos($ouri, "/prefs") !== FALSE) || 189 | stripos($ouri, "/system/appstore") !== FALSE || 190 | stripos($ouri, "/status/sessions") !== FALSE || 191 | ($blocksections === TRUE && stripos($ouri, "/library/") !== FALSE ) ){ 192 | //block 193 | // /:/plugins//prefs 194 | // /:/prefs 195 | // status/sessions 196 | // /library/ IF $blocksections is set 197 | //allow 198 | // /video/ 199 | // /music/ 200 | // /photo/ 201 | logger("blocking $ouri"); 202 | header("Location: $originalurl"); 203 | exit; 204 | } 205 | } 206 | ?> -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | $value) { 38 | $ovalue = $value; 39 | if( $swapidentity === TRUE || $enableanonymousaccess === FALSE){ 40 | switch ($name) { 41 | case 'X-Plex-Token': 42 | logger("fixing $name"); 43 | $uhtoken = $value; 44 | logger("found user token $uhtoken"); 45 | $value = $token; 46 | logger("adding $name $value"); 47 | break; 48 | case 'X-Plex-Username': 49 | logger("fixing $name"); 50 | $unm = $value; 51 | logger("found username $unm"); 52 | $value = $username; 53 | logger("adding $name $value"); 54 | break; 55 | case 'Referer': 56 | case 'Host': 57 | case 'Origin': 58 | logger("fixing $name"); 59 | $value = str_replace($lbip,$destserver, $value); 60 | logger("adding $name $value"); 61 | break; 62 | case 'Range': 63 | if($clientstreaming === TRUE && stripos($finalurl,"/library/parts/") !== FALSE){ 64 | logger("checking range $value"); 65 | $valsplit = explode("-",$value); 66 | $valsplit[0] = explode("=",$valsplit[0])[1]; 67 | if(count($valsplit) > 1 && intval($valsplit[1])-intval($valsplit[0])>$streamsize){ 68 | logger('fixing range'); 69 | $newend=intval($valsplit[0])+$streamsize; 70 | $value = "bytes=".$valsplit[0]."-$newend"; 71 | } 72 | elseif(count($valsplit) == 1 || (count($valsplit) ==2 && $valsplit[1]=="") ){ 73 | logger('fixing range'); 74 | $newend=intval($valsplit[0])+$streamsize; 75 | $value = "bytes=".$valsplit[0]."-$newend"; 76 | } 77 | logger("adding $name $value"); 78 | } 79 | break; 80 | /* 81 | //In my tests I found that the client identifier doesnt much matter. 82 | //Only the username and token needs to be changed 83 | case 'Cookie': 84 | $value = replaceCookie($value, $client); 85 | break; 86 | case 'X-Plex-Client-Identifier': 87 | $value = $client; 88 | break;*/ 89 | } 90 | } 91 | if(stripos($name,"Accept") !== FALSE && stripos($value,"gzip") !== FALSE){ 92 | //Detect gzip encoding 93 | $acceptr=TRUE; 94 | } 95 | 96 | $headers[] = "$name: $value"; 97 | $userheaders[] = "$name: $ovalue"; 98 | } 99 | 100 | 101 | if($swapidentity === TRUE && $enableanonymousaccess === FALSE 102 | && $unm == "" && ($utoken != "" || $uhtoken != "" ) ){ 103 | if($uhtoken == ""){ 104 | $uhtoken = $utoken; 105 | $userheaders[] = "X-Plex-Token: $uhtoken"; 106 | } 107 | elseif($utoken == ""){ 108 | $utoken = $uhtoken; 109 | } 110 | logger("looking up username with token $utoken"); 111 | $unm = getUsername($userheaders); 112 | } 113 | if( $namedusersonly === TRUE && !isset($lbuuidkvp[$unm]) ){ 114 | logger("blocking user $unm"); 115 | header("Location: $originalurl"); 116 | exit; 117 | } 118 | $ch = curl_init($finalurl); 119 | curl_setopt($ch,CURLOPT_ENCODING, ''); 120 | curl_setopt($ch,CURLOPT_RETURNTRANSFER,true); 121 | curl_setopt($ch,CURLOPT_VERBOSE,1); 122 | curl_setopt($ch, CURLOPT_HEADER, true); 123 | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); 124 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']); 125 | $output = curl_exec($ch); 126 | 127 | //Get the response code before closing curl 128 | $httpCode = curl_getinfo ( $ch, CURLINFO_HTTP_CODE ); 129 | curl_close($ch); 130 | 131 | //Split the response to retreive headers 132 | list($header, $body) = explode("\r\n\r\n", $output, 2); 133 | $header = explode("\r\n",$header); 134 | 135 | //Send response code 136 | http_response_code(intval($httpCode)); 137 | $firstrun = TRUE; 138 | 139 | logger("Server response code $httpCode"); 140 | foreach($header as $value){ 141 | if($firstrun === TRUE){ 142 | //The first header is always HTTP 1/1 CODE 143 | //Sending this messes things up 144 | $firstrun = FALSE; 145 | continue; 146 | } 147 | if(stripos($value,"Content") !== FALSE && stripos($value,"gzip") !== FALSE){ 148 | //Detect gzip encoding 149 | $accepteh=TRUE; 150 | } 151 | else if(stripos($value,$destserver) !== FALSE){ 152 | logger("Fixing ip from $destserver to $lbip"); 153 | $value = str_replace($destserver, $lbip, $value); 154 | } 155 | header("$value"); 156 | } 157 | if( $swapidentity === TRUE && $enableanonymousaccess === FALSE ){ 158 | $body = swapBackBody($body); 159 | } 160 | 161 | if(($accepteh === TRUE && $acceptr === TRUE)){ 162 | echo gzencode($body); 163 | } 164 | else{ 165 | echo $body; 166 | } 167 | 168 | exit; 169 | ?> --------------------------------------------------------------------------------