├── favicon.ico ├── intranet ├── images │ ├── hd.png │ ├── sab.png │ ├── sd.png │ ├── tv.png │ ├── couch.png │ ├── tick.png │ ├── wifi.png │ ├── body-bg.gif │ ├── trailers.png │ ├── utorrent.png │ ├── headphones.png │ ├── sickbeard.png │ └── mymanjackie.png ├── screenshots │ ├── screen1.png │ └── screen2.png ├── fonts │ ├── TitilliumText22L001-webfont.eot │ ├── TitilliumText22L001-webfont.ttf │ ├── TitilliumText22L001-webfont.woff │ ├── TitilliumText22L002-webfont.eot │ ├── TitilliumText22L002-webfont.ttf │ ├── TitilliumText22L002-webfont.woff │ ├── TitilliumText22L003-webfont.eot │ ├── TitilliumText22L003-webfont.ttf │ ├── TitilliumText22L003-webfont.woff │ ├── TitilliumText22L004-webfont.eot │ ├── TitilliumText22L004-webfont.ttf │ ├── TitilliumText22L004-webfont.woff │ ├── TitilliumText22L005-webfont.eot │ ├── TitilliumText22L005-webfont.ttf │ ├── TitilliumText22L005-webfont.woff │ ├── TitilliumText22L006-webfont.eot │ ├── TitilliumText22L006-webfont.ttf │ ├── TitilliumText22L006-webfont.woff │ ├── SIL Open Font License 1.1.txt │ ├── TitilliumText22L006-webfont.svg │ ├── TitilliumText22L005-webfont.svg │ ├── TitilliumText22L004-webfont.svg │ ├── TitilliumText22L003-webfont.svg │ ├── TitilliumText22L002-webfont.svg │ └── TitilliumText22L001-webfont.svg ├── js │ └── scripts.js ├── comingseasons.php ├── lib │ ├── utorrent_php_api.php │ ├── functions.php │ └── transmissionrpc.class.php ├── serverconfig-example.php └── style.css ├── README.md └── index.php /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/favicon.ico -------------------------------------------------------------------------------- /intranet/images/hd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/hd.png -------------------------------------------------------------------------------- /intranet/images/sab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/sab.png -------------------------------------------------------------------------------- /intranet/images/sd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/sd.png -------------------------------------------------------------------------------- /intranet/images/tv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/tv.png -------------------------------------------------------------------------------- /intranet/images/couch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/couch.png -------------------------------------------------------------------------------- /intranet/images/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/tick.png -------------------------------------------------------------------------------- /intranet/images/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/wifi.png -------------------------------------------------------------------------------- /intranet/images/body-bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/body-bg.gif -------------------------------------------------------------------------------- /intranet/images/trailers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/trailers.png -------------------------------------------------------------------------------- /intranet/images/utorrent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/utorrent.png -------------------------------------------------------------------------------- /intranet/images/headphones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/headphones.png -------------------------------------------------------------------------------- /intranet/images/sickbeard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/sickbeard.png -------------------------------------------------------------------------------- /intranet/images/mymanjackie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/images/mymanjackie.png -------------------------------------------------------------------------------- /intranet/screenshots/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/screenshots/screen1.png -------------------------------------------------------------------------------- /intranet/screenshots/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/screenshots/screen2.png -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L001-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L001-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L001-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L001-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L001-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L001-webfont.woff -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L002-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L002-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L002-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L002-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L002-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L002-webfont.woff -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L003-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L003-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L003-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L003-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L003-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L003-webfont.woff -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L004-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L004-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L004-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L004-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L004-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L004-webfont.woff -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L005-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L005-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L005-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L005-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L005-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L005-webfont.woff -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L006-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L006-webfont.eot -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L006-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L006-webfont.ttf -------------------------------------------------------------------------------- /intranet/fonts/TitilliumText22L006-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dbaines/Usenet-Intranet-PHP-Homepage/HEAD/intranet/fonts/TitilliumText22L006-webfont.woff -------------------------------------------------------------------------------- /intranet/js/scripts.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | // Current/Recent switcher 4 | $(".downloadPage .go").click(function() { 5 | if($(this).parent(".downloadPage").hasClass("downloadPageCurrent")) { 6 | $(".downloadFrameSlide").animate({ 7 | "marginLeft": "-450px" 8 | }, 500); 9 | } else if ($(this).parent(".downloadPage").hasClass("downloadPageHistory")) { 10 | $(".downloadFrameSlide").animate({ 11 | "marginLeft": "0px" 12 | }, 500); 13 | } 14 | }); 15 | 16 | // Some helper scripts for the show poster 17 | $(".sickbeardShows li").each(function() { 18 | 19 | // Fix for popups on multi-line shows 20 | // Get half of the height of this li 21 | var liHeight = $(this).height() / 2, 22 | // 90 is half of the height of the poseter 23 | newHeight = 90 - liHeight; 24 | popup = $(this).find(".showPopup"); 25 | // Set top postion based on that 26 | popup.css("top",-newHeight); 27 | 28 | }); 29 | $(".sickbeardShows li, .seasonStarts li").hover(function() { 30 | 31 | var popup = $(this).find(".showPopup"); 32 | // reset position 33 | popup.stop().css({ 34 | "left": "-170px", 35 | "opacity": 0 36 | }); 37 | // Animate position 38 | popup.stop().animate({ 39 | "left": "-146px", 40 | "opacity": 1 41 | }, 200); 42 | 43 | }, function() { 44 | 45 | var popup = $(this).find(".showPopup"); 46 | popup.stop().animate({ 47 | "left": "-170px", 48 | "opacity": 0 49 | }, 200); 50 | }); 51 | 52 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Usenet Intranet PHP Homepage 2 | ==== 3 | 4 | A simple little one-page Intranet homepage I created for my home server with the following features: 5 | 6 | * Buttons to go directly to: 7 | * Sickbeard 8 | * CouchPotato 9 | * Headphones 10 | * SABnzbd 11 | * uTorrent WebUI 12 | * Movie Trailers 13 | * Currently downloading on SABnzbd 14 | * Currently downloading on uTorrent (requires web ui to be enabled) 15 | * List of TV Shows that come out today from Sickbeard 16 | * Show Wifi Password 17 | * Completely customisable to show or hide only the features you want 18 | 19 |  20 | 21 | ## Installation 22 | 23 | Ideally this would go on the root of your web server, which is why the intranet resources are in a folder named intranet. The resources are all relative so you can move it in to a subfolder if you like. 24 | 25 | To configure the page you will need to open the intranet folder and rename serverconfig-example.php to serverconfig.php 26 | Open the config file and make the appropriate changes to the IPs, Ports, API Keys, Usernames and Passwords. 27 | 28 | ## Requirements 29 | 30 | Your webserver will require: 31 | 32 | * cURL 33 | * PHP 34 | 35 | ## Updating 36 | 37 | If something goes wrong after updating, please consult the latest serverconfig-example.php file. Odds are something's changed and you'll need to either: 38 | 39 | * Update your serverconfig.php with the changes 40 | * Rename serverconfig-example.php to serverconfig.php and copy in the settings from your old serverconfig.php -------------------------------------------------------------------------------- /intranet/comingseasons.php: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 |
24 |
40 | * $rpc = new TransmissionRPC($rpc_url);
41 | * $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
42 | *
43 | *
44 | */
45 | class TransmissionRPC
46 | {
47 | /**
48 | * User agent used in all http communication
49 | */
50 | const HTTP_UA = 'TransmissionRPC for PHP/0.3';
51 |
52 | /**
53 | * Minimum PHP version required
54 | * 5.2.10 implemented the required http stream ignore_errors option
55 | */
56 | const MIN_PHPVER = '5.2.10';
57 |
58 | /**
59 | * The URL to the bittorent client you want to communicate with
60 | * the port (default: 9091) can be set in you Transmission preferences
61 | * @var string
62 | */
63 | public $url = '';
64 |
65 | /**
66 | * If your Transmission RPC requires authentication, supply username here
67 | * @var string
68 | */
69 | public $username = '';
70 |
71 | /**
72 | * If your Transmission RPC requires authentication, supply password here
73 | * @var string
74 | */
75 | public $password = '';
76 |
77 | /**
78 | * Return results as an array, or an object (default)
79 | * @var bool
80 | */
81 | public $return_as_array = false;
82 |
83 | /**
84 | * Print debugging information, default is off
85 | * @var bool
86 | */
87 | public $debug = false;
88 |
89 | /**
90 | * Transmission RPC version
91 | * @var int
92 | */
93 | protected $rpc_version = 0;
94 |
95 | /**
96 | * Transmission uses a session id to prevent CSRF attacks
97 | * @var string
98 | */
99 | protected $session_id = '';
100 |
101 | /**
102 | * Default values for stream context
103 | * @var array
104 | */
105 | private $default_context_opts = array( 'http' => array(
106 | 'user_agent' => self::HTTP_UA,
107 | // 'timeout' => '5', // Don't want to be too slow
108 | 'ignore_errors' => true, // Leave the error parsing/handling to the code
109 | )
110 | );
111 |
112 | /**
113 | * Constants for torrent status
114 | */
115 | const TR_STATUS_STOPPED = 0;
116 | const TR_STATUS_CHECK_WAIT = 1;
117 | const TR_STATUS_CHECK = 2;
118 | const TR_STATUS_DOWNLOAD_WAIT = 3;
119 | const TR_STATUS_DOWNLOAD = 4;
120 | const TR_STATUS_SEED_WAIT = 5;
121 | const TR_STATUS_SEED = 6;
122 |
123 | const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
124 | const RPC_LT_14_TR_STATUS_CHECK = 2;
125 | const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
126 | const RPC_LT_14_TR_STATUS_SEED = 8;
127 | const RPC_LT_14_TR_STATUS_STOPPED = 16;
128 |
129 | /**
130 | * Start one or more torrents
131 | *
132 | * @param int|array ids A list of transmission torrent ids
133 | */
134 | public function start ( $ids )
135 | {
136 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
137 | $request = array( "ids" => $ids );
138 | return $this->request( "torrent-start", $request );
139 | }
140 |
141 | /**
142 | * Stop one or more torrents
143 | *
144 | * @param int|array ids A list of transmission torrent ids
145 | */
146 | public function stop ( $ids )
147 | {
148 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
149 | $request = array( "ids" => $ids );
150 | return $this->request( "torrent-stop", $request );
151 | }
152 |
153 | /**
154 | * Reannounce one or more torrents
155 | *
156 | * @param int|array ids A list of transmission torrent ids
157 | */
158 | public function reannounce ( $ids )
159 | {
160 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
161 | $request = array( "ids" => $ids );
162 | return $this->request( "torrent-reannounce", $request );
163 | }
164 |
165 | /**
166 | * Verify one or more torrents
167 | *
168 | * @param int|array ids A list of transmission torrent ids
169 | */
170 | public function verify ( $ids )
171 | {
172 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
173 | $request = array( "ids" => $ids );
174 | return $this->request( "torrent-verify", $request );
175 | }
176 |
177 | /**
178 | * Get information on torrents in transmission, if the ids parameter is
179 | * empty all torrents will be returned. The fields array can be used to return certain
180 | * fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
181 | * See https://trac.transmissionbt.com/browser/trunk/doc/rpc-spec.txt for available fields
182 | *
183 | * @param array fields An array of return fields
184 | * @param int|array ids A list of transmission torrent ids
185 | */
186 | public function get ( $ids = array(), $fields = array() )
187 | {
188 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
189 | if ( count( $fields ) == 0 ) $fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); // Defaults
190 | $request = array(
191 | "fields" => $fields,
192 | "ids" => $ids
193 | );
194 | return $this->request( "torrent-get", $request );
195 | }
196 |
197 | /**
198 | * Set properties on one or more torrents, available fields are:
199 | * "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
200 | * "downloadLimit" | number maximum download speed (in K/s)
201 | * "downloadLimited" | boolean true if "downloadLimit" is honored
202 | * "files-wanted" | array indices of file(s) to download
203 | * "files-unwanted" | array indices of file(s) to not download
204 | * "honorsSessionLimits" | boolean true if session upload limits are honored
205 | * "ids" | array torrent list, as described in 3.1
206 | * "location" | string new location of the torrent's content
207 | * "peer-limit" | number maximum number of peers
208 | * "priority-high" | array indices of high-priority file(s)
209 | * "priority-low" | array indices of low-priority file(s)
210 | * "priority-normal" | array indices of normal-priority file(s)
211 | * "seedRatioLimit" | double session seeding ratio
212 | * "seedRatioMode" | number which ratio to use. See tr_ratiolimit
213 | * "uploadLimit" | number maximum upload speed (in K/s)
214 | * "uploadLimited" | boolean true if "uploadLimit" is honored
215 | * See https://trac.transmissionbt.com/browser/trunk/doc/rpc-spec.txt for more information
216 | *
217 | * @param array arguments An associative array of arguments to set
218 | * @param int|array ids A list of transmission torrent ids
219 | */
220 | public function set ( $ids = array(), $arguments = array() )
221 | {
222 | // See https://trac.transmissionbt.com/browser/trunk/doc/rpc-spec.txt for available fields
223 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
224 | if ( !isset( $arguments['ids'] ) ) $arguments['ids'] = $ids; // Any $ids given in $arguments overrides the method parameter
225 | return $this->request( "torrent-set", $arguments );
226 | }
227 |
228 | /**
229 | * Add a new torrent
230 | *
231 | * Available extra options:
232 | * key | value type & description
233 | * ---------------------+-------------------------------------------------
234 | * "download-dir" | string path to download the torrent to
235 | * "filename" | string filename or URL of the .torrent file
236 | * "metainfo" | string base64-encoded .torrent content
237 | * "paused" | boolean if true, don't start the torrent
238 | * "peer-limit" | number maximum number of peers
239 | * "bandwidthPriority" | number torrent's bandwidth tr_priority_t
240 | * "files-wanted" | array indices of file(s) to download
241 | * "files-unwanted" | array indices of file(s) to not download
242 | * "priority-high" | array indices of high-priority file(s)
243 | * "priority-low" | array indices of low-priority file(s)
244 | * "priority-normal" | array indices of normal-priority file(s)
245 | *
246 | * Either "filename" OR "metainfo" MUST be included.
247 | * All other arguments are optional.
248 | *
249 | * @param torrent_location The URL or path to the torrent file
250 | * @param save_path Folder to save torrent in
251 | * @param extra options Optional extra torrent options
252 | */
253 | public function add_file ( $torrent_location, $save_path = '', $extra_options = array() )
254 | {
255 | $extra_options['download-dir'] = $save_path;
256 | $extra_options['filename'] = $torrent_location;
257 |
258 | return $this->request( "torrent-add", $extra_options );
259 | }
260 |
261 | /**
262 | * Add a torrent using the raw torrent data
263 | *
264 | * @param torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
265 | * @param save_path Folder to save torrent in
266 | * @param extra options Optional extra torrent options
267 | */
268 | public function add_metainfo ( $torrent_metainfo, $save_path = '', $extra_options = array() )
269 | {
270 | $extra_options['download-dir'] = $save_path;
271 | $extra_options['metainfo'] = base64_encode( $torrent_metainfo );
272 |
273 | return $this->request( "torrent-add", $extra_options );
274 | }
275 |
276 | /* Add a new torrent using a file path or a URL (For backwards compatibility)
277 | * @param torrent_location The URL or path to the torrent file
278 | * @param save_path Folder to save torrent in
279 | * @param extra options Optional extra torrent options
280 | */
281 | public function add ( $torrent_location, $save_path = '', $extra_options = array() )
282 | {
283 | return $this->add_file( $torrent_location, $save_path, $extra_options );
284 | }
285 |
286 | /**
287 | * Remove torrent from transmission
288 | *
289 | * @param bool delete_local_data Also remove local data?
290 | * @param int|array ids A list of transmission torrent ids
291 | */
292 | public function remove ( $ids, $delete_local_data = false )
293 | {
294 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
295 | $request = array(
296 | "ids" => $ids,
297 | "delete-local-data" => $delete_local_data
298 | );
299 | return $this->request( "torrent-remove", $request );
300 | }
301 |
302 | /**
303 | * Move local storage location
304 | *
305 | * @param int|array ids A list of transmission torrent ids
306 | * @param string target_location The new storage location
307 | * @param string move_existing_data Move existing data or scan new location for available data
308 | */
309 | public function move ( $ids, $target_location, $move_existing_data = true )
310 | {
311 | if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
312 | $request = array(
313 | "ids" => $ids,
314 | "location" => $target_location,
315 | "move" => $move_existing_data
316 | );
317 | return $this->request( "torrent-set-location", $request );
318 | }
319 |
320 | /**
321 | * Retrieve session statistics
322 | *
323 | * @returns array of statistics
324 | */
325 | public function sstats ( )
326 | {
327 | return $this->request( "session-stats", array() );
328 | }
329 |
330 | /**
331 | * Retrieve all session variables
332 | *
333 | * @returns array of session information
334 | */
335 | public function sget ( )
336 | {
337 | return $this->request( "session-get", array() );
338 | }
339 |
340 | /**
341 | * Set session variable(s)
342 | *
343 | * @param array of session variables to set
344 | */
345 | public function sset ( $arguments )
346 | {
347 | return $this->request( "session-set", $arguments );
348 | }
349 |
350 | /**
351 | * Return the interpretation of the torrent status
352 | *
353 | * @param int The integer "torrent status"
354 | * @returns string The translated meaning
355 | */
356 | public function getStatusString ( $intstatus )
357 | {
358 | if($this->rpc_version < 14){
359 | if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT )
360 | return "Waiting to verify local files";
361 | if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK )
362 | return "Verifying local files";
363 | if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD )
364 | return "Downloading";
365 | if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED )
366 | return "Seeding";
367 | if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED )
368 | return "Stopped";
369 | }else{
370 | if( $intstatus == self::TR_STATUS_CHECK_WAIT )
371 | return "Waiting to verify local files";
372 | if( $intstatus == self::TR_STATUS_CHECK )
373 | return "Verifying local files";
374 | if( $intstatus == self::TR_STATUS_DOWNLOAD )
375 | return "Downloading";
376 | if( $intstatus == self::TR_STATUS_SEED )
377 | return "Seeding";
378 | if( $intstatus == self::TR_STATUS_STOPPED )
379 | return "Stopped";
380 | if( $intstatus == self::TR_STATUS_SEED_WAIT )
381 | return "Queued for seeding";
382 | if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT )
383 | return "Queued for download";
384 | }
385 | return "Unknown";
386 | }
387 |
388 |
389 |
390 | /**
391 | * Here be dragons (Internal methods)
392 | */
393 |
394 |
395 |
396 | /**
397 | * Clean up the request array. Removes any empty fields from the request
398 | *
399 | * @param array array The request associative array to clean
400 | * @returns array The cleaned array
401 | */
402 | protected function cleanRequestData ( $array )
403 | {
404 | if ( !is_array( $array ) || count( $array ) == 0 ) return null; // Nothing to clean
405 | setlocale( LC_NUMERIC, 'en_US.utf8' ); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
406 | foreach ( $array as $index => $value )
407 | {
408 | if( is_object( $value ) ) $array[$index] = $value->toArray(); // Convert objects to arrays so they can be JSON encoded
409 | if( is_array( $value ) ) $array[$index] = $this->cleanRequestData( $value ); // Recursion
410 | if( empty( $value ) && $value != 0 ) unset( $array[$index] ); // Remove empty members
411 | if( is_numeric( $value ) ) $array[$index] = $value+0; // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
412 | if( is_bool( $value ) ) $array[$index] = ( $value ? 1 : 0); // Store boolean values as 0 or 1
413 | if( is_string( $value ) ) $array[$index] = utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
414 | }
415 | return $array;
416 | }
417 |
418 | /**
419 | * Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
420 | * and converts any object with any all-digit property names to an array.
421 | *
422 | * @param object The request result to clean
423 | * @returns array The cleaned object
424 | */
425 | protected function cleanResultObject ( $object )
426 | {
427 | // Prepare and cast object to array
428 | $return_as_array = false;
429 | $array = $object;
430 | if ( !is_array( $array ) ) $array = (array) $array;
431 | foreach ( $array as $index => $value )
432 | {
433 | if( is_array( $array[$index] ) || is_object( $array[$index] ) )
434 | {
435 | $array[$index] = $this->cleanResultObject( $array[$index] ); // Recursion
436 | }
437 | if ( strstr( $index, '-' ) )
438 | {
439 | $valid_index = str_replace( '-', '_', $index );
440 | $array[$valid_index] = $array[$index];
441 | unset( $array[$index] );
442 | $index = $valid_index;
443 | }
444 | // Might be an array, check index for digits, if so, an array should be returned
445 | if ( ctype_digit( (string) $index ) ) { $return_as_array = true; }
446 | if ( empty( $value ) ) unset( $array[$index] );
447 | }
448 | // Return array cast to object
449 | return $return_as_array ? $array : (object) $array;
450 | }
451 |
452 | /**
453 | * The request handler method handles all requests to the Transmission client
454 | *
455 | * @param string method The request method to use
456 | * @param array arguments The request arguments
457 | * @returns array The request result
458 | */
459 | protected function request( $method, $arguments )
460 | {
461 | // Check the parameters
462 | if ( !is_scalar( $method ) )
463 | throw new TransmissionRPCException( 'Method name has no scalar value', TransmissionRPCException::E_INVALIDARG );
464 | if ( !is_array( $arguments ) )
465 | throw new TransmissionRPCException( 'Arguments must be given as array', TransmissionRPCException::E_INVALIDARG );
466 |
467 | $arguments = $this->cleanRequestData( $arguments ); // Sanitize input
468 |
469 | // Grab the X-Transmission-Session-Id if we don't have it already
470 | if( !$this->session_id )
471 | if( !$this->GetSessionID() )
472 | throw new TransmissionRPCException( 'Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID );
473 |
474 | // Build (and encode) request array
475 | $data = array(
476 | "method" => $method,
477 | "arguments" => $arguments
478 | );
479 | $data = json_encode( $data );
480 |
481 | // performs the HTTP POST
482 | $contextopts = $this->default_context_opts; // Start with the defaults
483 | $contextopts['http']['method'] = 'POST';
484 | $contextopts['http']['header'] = 'Content-type: application/json'."\r\n".
485 | 'X-Transmission-Session-Id: '.$this->session_id."\r\n";
486 | $contextopts['http']['content'] = $data;
487 |
488 | // Setup authentication (if provided)
489 | if ( $this->username && $this->password )
490 | $contextopts['http']['header'] .= sprintf( "Authorization: Basic %s\r\n", base64_encode( $this->username.':'.$this->password ) );
491 |
492 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: request( method=$method, ...):: Stream context created with options:" .
493 | PHP_EOL . print_r( $contextopts, true );
494 |
495 | $context = stream_context_create( $contextopts ); // Create the context for this request
496 | if ( $fp = fopen( $this->url, 'r', false, $context ) ) { // Open a filepointer to the data, and use fgets to get the result
497 | $response = '';
498 | while( $row = fgets( $fp ) ) {
499 | $response.= trim( $row )."\n";
500 | }
501 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: request( method=$method, ...):: POST Result: ".
502 | PHP_EOL . print_r( $response, true );
503 | } else
504 | throw new TransmissionRPCException( 'Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION );
505 |
506 | // Check the response (headers etc)
507 | $stream_meta = stream_get_meta_data( $fp );
508 | fclose( $fp );
509 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: request( method={$method}, ...):: Stream meta info: ".
510 | PHP_EOL . print_r( $stream_meta, true );
511 | if( $stream_meta['timed_out'] )
512 | throw new TransmissionRPCException( "Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION );
513 | if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" )
514 | throw new TransmissionRPCException( "Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION );
515 | elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" )
516 | throw new TransmissionRPCException( "Invalid X-Transmission-Session-Id. Please try again after calling GetSessionID().", TransmissionRPCException::E_SESSIONID );
517 |
518 | return $this->return_as_array ? json_decode( $response, true ) : $this->cleanResultObject( json_decode( $response ) ); // Return the sanitized result
519 | }
520 |
521 | /**
522 | * Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
523 | * and store it in $this->session_id
524 | *
525 | * @return string
526 | */
527 | public function GetSessionID()
528 | {
529 | if( !$this->url )
530 | throw new TransmissionRPCException( "Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG );
531 |
532 | // Setup the context
533 | $contextopts = $this->default_context_opts; // Start with the defaults
534 |
535 | // Make sure it's blank/empty (reset)
536 | $this->session_id = null;
537 |
538 | // Setup authentication (if provided)
539 | if ( $this->username && $this->password )
540 | $contextopts['http']['header'] = sprintf( "Authorization: Basic %s\r\n", base64_encode( $this->username.':'.$this->password ) );
541 |
542 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:".
543 | PHP_EOL . print_r( $contextopts, true );
544 |
545 | $context = stream_context_create( $contextopts ); // Create the context for this request
546 | if ( ! $fp = @fopen( $this->url, 'r', false, $context ) ) // Open a filepointer to the data, and use fgets to get the result
547 | throw new TransmissionRPCException( 'Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION );
548 |
549 | // Check the response (headers etc)
550 | $stream_meta = stream_get_meta_data( $fp );
551 | fclose( $fp );
552 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ".
553 | PHP_EOL . print_r( $stream_meta, true );
554 | if( $stream_meta['timed_out'] )
555 | throw new TransmissionRPCException( "Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION );
556 | if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" )
557 | throw new TransmissionRPCException( "Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION );
558 | elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" ) // This is what we're hoping to find
559 | {
560 | // Loop through the returned headers and extract the X-Transmission-Session-Id
561 | foreach( $stream_meta['wrapper_data'] as $header )
562 | {
563 | if( strpos( $header, 'X-Transmission-Session-Id: ' ) === 0 )
564 | {
565 | if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ".
566 | PHP_EOL . print_r( $header, true );
567 | $this->session_id = trim( substr( $header, 27 ) );
568 | break;
569 | }
570 | }
571 | if( ! $this->session_id ) { // Didn't find a session_id
572 | throw new TransmissionRPCException( "Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID );
573 | }
574 | } else {
575 | throw new TransmissionRPCException( "Unexpected response from Transmission RPC: ".$stream_meta['wrapper_data'][0] );
576 | }
577 | return $this->session_id;
578 | }
579 |
580 | /**
581 | * Takes the connection parameters
582 | *
583 | * TODO: Sanitize username, password, and URL
584 | *
585 | * @param string $url
586 | * @param string $username
587 | * @param string $password
588 | */
589 | public function __construct( $url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false )
590 | {
591 | // server URL
592 | $this->url = $url;
593 |
594 | // Username & password
595 | $this->username = $username;
596 | $this->password = $password;
597 |
598 | // Return As Array
599 | $this->return_as_array = $return_as_array;
600 |
601 | // Reset X-Transmission-Session-Id so we (re)fetch one
602 | $this->session_id = null;
603 |
604 | // Get the Transmission RPC_version
605 | $this->rpc_version = self::sget()->arguments->rpc_version;
606 | }
607 | }
608 |
609 | /**
610 | * This is the type of exception the TransmissionRPC class will throw
611 | */
612 | class TransmissionRPCException extends Exception
613 | {
614 | /**
615 | * Exception: Invalid arguments
616 | */
617 | const E_INVALIDARG = -1;
618 |
619 | /**
620 | * Exception: Invalid Session-Id
621 | */
622 | const E_SESSIONID = -2;
623 |
624 | /**
625 | * Exception: Error while connecting
626 | */
627 | const E_CONNECTION = -3;
628 |
629 | /**
630 | * Exception: Error 401 returned, unauthorized
631 | */
632 | const E_AUTHENTICATION = -4;
633 |
634 | /**
635 | * Exception constructor
636 | */
637 | public function __construct( $message = null, $code = 0, Exception $previous = null )
638 | {
639 | // PHP version 5.3.0 and above support Exception linking
640 | if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) )
641 | parent::__construct( $message, $code, $previous );
642 | else
643 | parent::__construct( $message, $code );
644 | }
645 | }
646 |
647 | ?>
648 |
--------------------------------------------------------------------------------