PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/modules/upnp/upnpplayer.class.php

https://gitlab.com/x33n/ampache
PHP | 384 lines | 205 code | 58 blank | 121 comment | 15 complexity | a78bea00f968d4ad6b892fd53e9a7d9e MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * LICENSE: GNU General Public License, version 2 (GPLv2)
  5. * Copyright 2001 - 2015 Ampache.org
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License v2
  9. * as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. *
  20. */
  21. /**
  22. * UPnPPlayer Class
  23. *
  24. * This player controls an instance of UPnP player
  25. *
  26. */
  27. class UPnPPlayer
  28. {
  29. /* @var UPnPPlaylist $object */
  30. private $_playlist = null;
  31. /* @var UPnPDevice $object */
  32. private $_device;
  33. private $_description_url = null;
  34. // 0 - stopped, 1 - playing
  35. private $_intState = 0;
  36. /**
  37. * Lazy initialization for UPNP device property
  38. * @return UPnPDevice
  39. */
  40. private function Device()
  41. {
  42. if (is_null($this->_device))
  43. $this->_device = new UPnPDevice($this->_description_url);
  44. return $this->_device;
  45. }
  46. /**
  47. * Lazy initialization for UPNP playlist property
  48. * @return UPnPPlaylist
  49. */
  50. private function Playlist()
  51. {
  52. if (is_null($this->_playlist))
  53. $this->_playlist = new UPnPPlaylist($this->_description_url);
  54. return $this->_playlist;
  55. }
  56. /**
  57. * UPnPPlayer
  58. * This is the constructor,
  59. */
  60. public function UPnPPlayer($name = "noname", $description_url = "http://localhost")
  61. {
  62. require_once AmpConfig::get('prefix') . '/modules/upnp/upnpdevice.php';
  63. require_once AmpConfig::get('prefix') . '/modules/upnp/upnpplaylist.php';
  64. debug_event('upnpPlayer', 'constructor: ' . $name . ' | ' . $description_url, 5);
  65. $this->_description_url = $description_url;
  66. $this->ReadIndState();
  67. }
  68. /**
  69. * add
  70. * append a song to the playlist
  71. * $name Name to be shown in the playlist
  72. * $link URL of the song
  73. */
  74. public function PlayListAdd($name, $link)
  75. {
  76. $this->Playlist()->Add($name, $link);
  77. return true;
  78. }
  79. /**
  80. * delete_pos
  81. * This deletes a specific track
  82. */
  83. public function PlaylistRemove($track)
  84. {
  85. $this->Playlist()->RemoveTrack($track);
  86. return true;
  87. }
  88. public function PlaylistClear()
  89. {
  90. $this->Playlist()->Clear();
  91. return true;
  92. }
  93. /**
  94. * GetPlayListItems
  95. * This returns a delimited string of all of the filenames
  96. * current in your playlist, only url's at the moment
  97. */
  98. public function GetPlaylistItems()
  99. {
  100. return $this->Playlist()->AllItems();
  101. }
  102. public function GetCurrentItem()
  103. {
  104. return $this->Playlist()->CurrentItem();
  105. }
  106. public function GetState()
  107. {
  108. $response = $this->Device()->instanceOnly('GetTransportInfo');
  109. $responseXML = simplexml_load_string($response);
  110. list($state) = $responseXML->xpath('//CurrentTransportState');
  111. //!!debug_event('upnpPlayer', 'GetState = ' . $state, 5);
  112. return $state;
  113. }
  114. /**
  115. * next
  116. * go to next song
  117. */
  118. public function Next($forcePlay = true)
  119. {
  120. // get current internal play state, for case if someone has changed it
  121. if (! $forcePlay)
  122. $this->ReadIndState();
  123. if (($forcePlay || ($this->_intState == 1)) && ($this->Playlist()->Next()))
  124. {
  125. $this->Play();
  126. return true;
  127. }
  128. return false;
  129. }
  130. /**
  131. * prev
  132. * go to previous song
  133. */
  134. public function Prev()
  135. {
  136. if ($this->Playlist()->Prev()) {
  137. $this->Play();
  138. return true;
  139. }
  140. return false;
  141. }
  142. /**
  143. * skip
  144. * This skips to POS in the playlist
  145. */
  146. public function Skip($pos)
  147. {
  148. if ($this->Playlist()->Skip($pos)) {
  149. $this->Play();
  150. return true;
  151. }
  152. return false;
  153. }
  154. private function prepareURIRequest($song, $prefix)
  155. {
  156. if ($song == null) return null;
  157. $songUrl = $song['link'];
  158. $songId = preg_replace('/(.+)\/oid\/(\d+)\/(.+)/i', '${2}', $songUrl);
  159. $song = new song($songId);
  160. $song->format();
  161. $songItem = Upnp_Api::_itemSong($song, '');
  162. $domDIDL = Upnp_Api::createDIDL($songItem);
  163. $xmlDIDL = $domDIDL->saveXML();
  164. return array(
  165. 'InstanceID' => 0,
  166. $prefix . 'URI' => $songUrl,
  167. $prefix . 'URIMetaData' => htmlentities($xmlDIDL)
  168. );
  169. }
  170. private function CallAsyncURL($url) {
  171. $ch = curl_init();
  172. curl_setopt( $ch, CURLOPT_URL, $url );
  173. curl_setopt( $ch, CURLOPT_FRESH_CONNECT, true );
  174. curl_setopt( $ch, CURLOPT_HEADER, false );
  175. curl_exec( $ch );
  176. curl_close( $ch );
  177. }
  178. /**
  179. * play
  180. * play the current song
  181. */
  182. public function Play()
  183. {
  184. //!!$this->Stop();
  185. $this->SetIntState(1);
  186. $currentSongArgs = $this->prepareURIRequest($this->Playlist()->CurrentItem(), "Current");
  187. $response = $this->Device()->sendRequestToDevice('SetAVTransportURI', $currentSongArgs, 'AVTransport');
  188. $args = array( 'InstanceID' => 0, 'Speed' => 1);
  189. $response = $this->Device()->sendRequestToDevice('Play', $args, 'AVTransport');
  190. //!! UPNP subscription work not for all renderers, and works strange
  191. //!! so now is not used
  192. //$sid = $this->Device()->Subscribe();
  193. //$_SESSION['upnp_SID'] = $sid;
  194. // launch special page in background for periodically check play status
  195. $url = AmpConfig::get('local_web_path') . "/upnp/playstatus.php";
  196. $this->CallAsyncURL($url);
  197. return true;
  198. }
  199. /**
  200. * Stop
  201. * stops the current song amazing!
  202. */
  203. public function Stop()
  204. {
  205. $this->SetIntState(0);
  206. $response = $this->Device()->instanceOnly('Stop');
  207. //!! UPNP subscription work not for all renderers, and works strange
  208. //!! so now is not used
  209. //$sid = $_SESSION['upnp_SID'];
  210. //$_SESSION['upnp_SID'] = "";
  211. //$this->Device()->UnSubscribe($sid);
  212. return true;
  213. }
  214. /**
  215. * pause
  216. * toggle pause mode on current song
  217. */
  218. public function Pause()
  219. {
  220. $state = $this->GetState();
  221. debug_event('upnpPlayer', 'Pause. prev state = ' . $state, 5);
  222. if ($state == 'PLAYING') {
  223. $response = $this->Device()->instanceOnly('Pause');
  224. }
  225. else {
  226. $args = array( 'InstanceID' => 0, 'Speed' => 1);
  227. $response = $this->Device()->sendRequestToDevice('Play', $args, 'AVTransport');
  228. }
  229. return true;
  230. }
  231. /**
  232. * Repeat
  233. * This toggles the repeat state
  234. */
  235. public function Repeat($value)
  236. {
  237. //!! TODO not implemented yet
  238. return true;
  239. }
  240. /**
  241. * Random
  242. * this toggles the random state
  243. */
  244. public function Random($value)
  245. {
  246. //!! TODO not implemented yet
  247. return true;
  248. }
  249. /**
  250. *
  251. *
  252. */
  253. public function FullState()
  254. {
  255. //!! TODO not implemented yet
  256. return "";
  257. }
  258. /**
  259. * VolumeUp
  260. * increases the volume
  261. */
  262. public function VolumeUp()
  263. {
  264. $volume = $this->GetVolume() + 2;
  265. return $this->SetVolume($volume);
  266. }
  267. /**
  268. * VolumeDown
  269. * decreases the volume
  270. */
  271. public function VolumeDown()
  272. {
  273. $volume = $this->GetVolume() - 2;
  274. return $this->SetVolume($volume);
  275. }
  276. /**
  277. * SetVolume
  278. */
  279. public function SetVolume($value)
  280. {
  281. $desiredVolume = Max(0, Min(100, $value));
  282. $instanceId = 0;
  283. $channel = 'Master';
  284. $response = $this->Device()->sendRequestToDevice( 'SetVolume', array(
  285. 'InstanceID' => $instanceId,
  286. 'Channel' => $channel,
  287. 'DesiredVolume' => $desiredVolume
  288. ));
  289. return true;
  290. }
  291. /**
  292. * GetVolume
  293. */
  294. public function GetVolume()
  295. {
  296. $instanceId = 0;
  297. $channel = 'Master';
  298. $response = $this->Device()->sendRequestToDevice( 'GetVolume', array(
  299. 'InstanceID' => $instanceId,
  300. 'Channel' => $channel
  301. ));
  302. $responseXML = simplexml_load_string($response);
  303. list($volume) = ($responseXML->xpath('//CurrentVolume'));
  304. debug_event('upnpPlayer', 'GetVolume:' . $volume, 5);
  305. return $volume;
  306. }
  307. private function SetIntState($state)
  308. {
  309. $this->_intState = $state;
  310. $sid = 'upnp_ply_' . $this->_description_url;
  311. $data = serialize($this->_intState);
  312. if (! Session::exists('api', $sid))
  313. Session::create(array('type' => 'api', 'sid' => $sid, 'value' => $data ));
  314. else
  315. Session::write($sid, $data);
  316. debug_event('upnpPlayer', 'SetIntState:' . $this->_intState, 5);
  317. }
  318. private function ReadIndState()
  319. {
  320. $sid = 'upnp_ply_' . $this->_description_url;
  321. $data = Session::read($sid);
  322. $this->_intState = unserialize($data);
  323. debug_event('upnpPlayer', 'ReadIndState:' . $this->_intState, 5);
  324. }
  325. } // End UPnPPlayer Class
  326. ?>