├── .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 | ?>
--------------------------------------------------------------------------------