├── README.md ├── composer.json └── uTorrent ├── Api.php └── model └── Filter.php /README.md: -------------------------------------------------------------------------------- 1 | What? 2 | ---- 3 | This library lets you control uTorrent using PHP. 4 | 5 | * List Torrents 6 | * Add Torrents 7 | * Start / Stop / Pause torrents 8 | * Add / edit RSS favourites 9 | 10 | How? 11 | ---- 12 | Enable the web UI in utorrent: http://www.utorrent.com/help/guides/webui 13 | 14 | $utorrent = new \uTorrent\Api($host, $port, $user, $pass); 15 | $utorrent->getTorrents(); 16 | 17 | Who? 18 | ---- 19 | * Originally by Miyanokouji http://forum.utorrent.com/viewtopic.php?id=27414&p=1 20 | * Upgraded by Ultima http://forum.utorrent.com/viewtopic.php?pid=320049#p320049 21 | 22 | I've added this to github to aid any future collaboration, add composer support and make it easier for people to find. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tboothman/utorrent-api", 3 | "autoload": { 4 | "psr-0": {"uTorrent\\": ""} 5 | } 6 | } -------------------------------------------------------------------------------- /uTorrent/Api.php: -------------------------------------------------------------------------------- 1 | host = $host; 51 | $this->port = $port; 52 | $this->user = $user; 53 | $this->pass = $pass; 54 | 55 | if (!$this->getToken()) { 56 | //handle error here, don't know how to best do this yet 57 | die('could not get token'); 58 | } 59 | } 60 | 61 | // performs request 62 | private function makeRequest($request, $decode = true, $options = array()) { 63 | $request = preg_replace('/^\?/', '?token='.$this->token . '&', $request); 64 | 65 | $ch = curl_init(); 66 | curl_setopt_array($ch, $options); 67 | curl_setopt($ch, CURLOPT_URL, sprintf(self::$base, $this->host, $this->port, $request)); 68 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 69 | curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass); 70 | curl_setopt($ch, CURLOPT_COOKIE, "GUID=".$this->guid); 71 | 72 | $req = curl_exec($ch); 73 | curl_close($ch); 74 | 75 | return ($decode ? json_decode($req, true) : $req); 76 | } 77 | 78 | // implodes given parameter with glue, whether it is an array or not 79 | private function paramImplode($glue, $param) { 80 | return $glue.implode($glue, is_array($param) ? $param : array($param)); 81 | } 82 | 83 | // gets token, returns true on success 84 | private function getToken() { 85 | $ch = curl_init(); 86 | curl_setopt($ch, CURLOPT_URL, sprintf(self::$base, $this->host, $this->port, 'token.html')); 87 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 88 | curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass); 89 | curl_setopt($ch, CURLOPT_HEADER, true); 90 | 91 | $output = curl_exec($ch); 92 | $info = curl_getinfo($ch); 93 | curl_close($ch); 94 | 95 | $headers = substr($output, 0, $info['header_size']); 96 | 97 | if (preg_match("@Set-Cookie: GUID=([^;]+);@i", $headers, $matches)) { 98 | $this->guid = $matches[1]; 99 | } 100 | 101 | if (preg_match('/
(.*)<\/div>/', $output, $m)) { 102 | $this->token = $m[1]; 103 | return true; 104 | } 105 | return false; 106 | } 107 | 108 | // returns the uTorrent build number 109 | public function getBuild(){ 110 | $json = $this->makeRequest("?"); 111 | return $json['build']; 112 | } 113 | 114 | // returns an array of files for the specified torrent hash 115 | // TODO: 116 | // - (when implemented in API) allow multiple hashes to be specified 117 | public function getFiles($hash) { 118 | $json = $this->makeRequest("?action=getfiles&hash=".$hash); 119 | return $json['files']; 120 | } 121 | 122 | // returns an array of all labels 123 | public function getLabels(){ 124 | $json = $this->makeRequest("?list=1"); 125 | return $json['label']; 126 | } 127 | 128 | // returns an array of the properties for the specified torrent hash 129 | // TODO: 130 | // - (when implemented in API) allow multiple hashes to be specified 131 | public function getProperties($hash) { 132 | $json = $this->makeRequest("?action=getprops&hash=".$hash); 133 | return $json['props']; 134 | } 135 | 136 | // returns an array of all settings 137 | public function getSettings() { 138 | $json = $this->makeRequest("?action=getsettings"); 139 | return $json['settings']; 140 | } 141 | 142 | // returns an array of all torrent jobs and related information 143 | public function getTorrents() { 144 | $json = $this->makeRequest("?list=1"); 145 | return $json['torrents']; 146 | } 147 | 148 | /** 149 | * Get all the RSS favourites/filters 150 | * @return model\Filter[] 151 | */ 152 | public function getRSSFilters() { 153 | $json = $this->makeRequest("?list=1"); 154 | $filters = array(); 155 | foreach ($json['rssfilters'] as $filter) { 156 | $filters[] = model\Filter::fromData($filter); 157 | } 158 | return $filters; 159 | } 160 | 161 | /** 162 | * Update an RSS filter as retrieved from getRSSFilters 163 | * @param \uTorrent\model\Filter $filter 164 | */ 165 | public function setRSSFilter(model\Filter $filter) { 166 | $request = array_merge(array('action' => 'filter-update'), $filter->toParams()); 167 | return $this->makeRequest('?'.http_build_query($request)); 168 | } 169 | 170 | /** 171 | * Add a new RSS filter 172 | * Requires a utorrent > 2.2.1 (not sure which version exactly) 173 | * @param \uTorrent\model\Filter $filter 174 | * @return int ID of the new filter 175 | */ 176 | public function addRSSFilter(model\Filter $filter) { 177 | $filter->filterId = -1; 178 | $resp = $this->setRSSFilter($filter); 179 | if (!empty($resp['filter_ident'])) { 180 | return $resp['filter_ident']; 181 | } else { 182 | return 0; 183 | } 184 | } 185 | 186 | // returns true if WebUI server is online and enabled, false otherwise 187 | public function is_online() { 188 | return is_array($this->makeRequest("?")); 189 | } 190 | 191 | // sets the properties for the specified torrent hash 192 | // TODO: 193 | // - allow multiple hashes, properties, and values to be set simultaneously 194 | public function setProperties($hash, $property, $value) { 195 | $this->makeRequest("?action=setprops&hash=".$hash."&s=".$property."&v=".$value, false); 196 | } 197 | 198 | // sets the priorities for the specified files in the specified torrent hash 199 | public function setPriority($hash, $files, $priority) { 200 | $this->makeRequest("?action=setprio&hash=".$hash."&p=".$priority.$this->paramImplode("&f=", $files), false); 201 | } 202 | 203 | // sets the settings 204 | // TODO: 205 | // - allow multiple settings and values to be set simultaneously 206 | public function setSetting($setting, $value) { 207 | $this->makeRequest("?action=setsetting&s=".$setting."&v=".$value, false); 208 | } 209 | 210 | // add a file to the list 211 | public function torrentAdd($filename, &$estring = false) { 212 | $split = explode(":", $filename, 2); 213 | if (count($split) > 1 && (stristr("|http|https|file|magnet|", "|".$split[0]."|") !== false)) { 214 | $this->makeRequest("?action=add-url&s=".urlencode($filename), false); 215 | } 216 | elseif (file_exists($filename)) { 217 | $json = $this->makeRequest("?action=add-file", true, array(CURLOPT_POSTFIELDS => array("torrent_file" => "@".realpath($filename)))); 218 | 219 | if (isset($json['error'])) { 220 | if ($estring !== false) $estring = $json['error']; 221 | return false; 222 | } 223 | return true; 224 | } 225 | else { 226 | if ($estring !== false) $estring = "File doesn't exist!"; 227 | return false; 228 | } 229 | } 230 | 231 | // force start the specified torrent hashes 232 | public function torrentForceStart($hash) { 233 | $this->makeRequest("?action=forcestart".$this->paramImplode("&hash=", $hash), false); 234 | } 235 | 236 | // pause the specified torrent hashes 237 | public function torrentPause($hash) { 238 | $this->makeRequest("?action=pause".$this->paramImplode("&hash=", $hash), false); 239 | } 240 | 241 | // recheck the specified torrent hashes 242 | public function torrentRecheck($hash) { 243 | $this->makeRequest("?action=recheck".$this->paramImplode("&hash=", $hash), false); 244 | } 245 | 246 | // start the specified torrent hashes 247 | public function torrentStart($hash) { 248 | $this->makeRequest("?action=start".$this->paramImplode("&hash=", $hash), false); 249 | } 250 | 251 | // stop the specified torrent hashes 252 | public function torrentStop($hash) { 253 | $this->makeRequest("?action=stop".$this->paramImplode("&hash=", $hash), false); 254 | } 255 | 256 | // remove the specified torrent hashes (and data, if $data is set to true) 257 | public function torrentRemove($hash, $data = false) { 258 | $this->makeRequest("?action=".($data ? "removedata" : "remove").$this->paramImplode("&hash=", $hash), false); 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /uTorrent/model/Filter.php: -------------------------------------------------------------------------------- 1 | filterId = $data[0]; 84 | // Bitmask for settings: 85 | // 1 = enabled. Not sure how to disable it. UI doesn't seem to support it 86 | $filter->origname = (bool)(2 & $data[1]); 87 | $filter->prio = (bool)(4 & $data[1]); 88 | $filter->smartEpFilter = (bool)(8 & $data[1]); 89 | $filter->addStopped = (bool)(16 & $data[1]); 90 | 91 | $filter->name = $data[2]; 92 | $filter->filter = $data[3]; 93 | $filter->notFilter = $data[4]; 94 | $filter->saveIn = $data[5]; 95 | $filter->feedId = $data[6]; 96 | $filter->quality = $data[7]; 97 | $filter->label = $data[8]; 98 | return $filter; 99 | } 100 | 101 | /** 102 | * Turn model into an array of parameters for the api 103 | * @return array 104 | */ 105 | public function toParams() { 106 | $params = array(); 107 | foreach (array('filterId', 'origname', 'prio', 'smartEpFilter', 'addStopped', 108 | 'name', 'filter', 'notFilter', 'saveIn', 'feedId', 'quality', 'label') as $field) { 109 | if ($this->$field !== null) { 110 | $paramName = preg_replace_callback('/[A-Z]/', function($match){ 111 | return '-'.strtolower($match[0]); 112 | }, $field); 113 | $params[$paramName] = $this->$field; 114 | } 115 | } 116 | return $params; 117 | } 118 | } --------------------------------------------------------------------------------