archive_audio.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <?php
  2. /**
  3. * @file
  4. * This include processes archive.org audio files for use with Embedded Media Field.
  5. */
  6. /**
  7. * This is the main URL for your provider.
  8. */
  9. define('EMAUDIO_ARCHIVE_MAIN_URL', 'http://www.archive.org/');
  10. /**
  11. * This is the URL to the API of your provider, if this exists.
  12. */
  13. define('EMAUDIO_ARCHIVE_API_URL', 'http://www.google.com/search?q=archive.org+api');
  14. /**
  15. * This defines the version of the content data array that we serialize
  16. * in emaudio_archive_data(). If we change the expected keys of that array,
  17. * we must increment this value, which will allow older content to be updated
  18. * to the new version automatically.
  19. */
  20. define('EMAUDIO_ARCHIVE_DATA_VERSION', 2);
  21. /**
  22. * hook emaudio_PROVIDER_info
  23. * This returns information relevant to a specific 3rd party audio provider.
  24. *
  25. * @return
  26. * A keyed array of strings requested by various admin and other forms.
  27. * 'provider' => The machine name of the provider. This must be the same as
  28. * the base name of this filename, before the .inc extension.
  29. * 'name' => The translated name of the provider.
  30. * 'url' => The url to the main page for the provider.
  31. * 'settings_description' => A description of the provider that will be
  32. * posted in the admin settings form.
  33. * 'supported_features' => An array of rows describing the state of certain
  34. * supported features by the provider. These will be rendered in a table,
  35. * with the columns being 'Feature', 'Supported', 'Notes'. In general,
  36. * the 'Feature' column will give the name of the feature, 'Supported'
  37. * will be Yes or No, and 'Notes' will give an optional description or
  38. * caveats to the feature.
  39. */
  40. function emaudio_archive_audio_info() {
  41. $features = array(
  42. array(t('Thumbnails'), t('No'), ''),
  43. array(t('Autoplay'), t('Yes'), ''),
  44. array(t('RSS attachment'), t('No'), ''),
  45. );
  46. return array(
  47. 'provider' => 'archive_audio',
  48. 'name' => t('Archive'),
  49. 'url' => EMAUDIO_ARCHIVE_MAIN_URL,
  50. 'settings_description' => t('These settings specifically affect audio played from !archive. You can also read more about its !api.', array('!archive' => l(t('Archive.com'), EMAUDIO_ARCHIVE_MAIN_URL), '!api' => l(t("developer's API"), EMAUDIO_ARCHIVE_API_URL))),
  51. 'supported_features' => $features,
  52. );
  53. }
  54. /**
  55. * hook emaudio_PROVIDER_settings
  56. * This should return a subform to be added to the emaudio_settings() admin
  57. * settings page.
  58. *
  59. * Note that a form field set will already be provided at $form['archive'],
  60. * so if you want specific provider settings within that field set, you should
  61. * add the elements to that form array element.
  62. */
  63. function emaudio_archive_audio_settings() {
  64. /* No Settings for this provider */
  65. }
  66. /**
  67. * hook emaudio_PROVIDER_extract
  68. *
  69. * This is called to extract the audio code from a pasted URL or embed code.
  70. *
  71. * We'll be passed a URL or the embed code from a audio when an editor pastes
  72. * that in the field's textfield. We'll need to either pass back an array of
  73. * regex expressions to match, or do the matching ourselves and return the
  74. * resulting audio code.
  75. *
  76. * @param $parse
  77. * An optional string with the pasted URL or embed code.
  78. * @return
  79. * Either an array of regex expressions to be tested, or a string with the
  80. * audio code to be used. If the hook tests the code itself, it should
  81. * return either the string of the audio code (if matched), or an empty
  82. * array. Otherwise, the calling function will handle testing the embed code
  83. * against each regex string in the returned array.
  84. */
  85. function emaudio_archive_audio_extract($parse = '') {
  86. // Here we assume that a URL will be passed in the form of
  87. // http://www.archive.org/details/text-audio-title
  88. // or embed code in the form of
  89. //<embed ... "playlist":[{"url":"http://www.archive.org/download/text-audio-title/...
  90. // We'll simply return an array of regular expressions for Embedded Media
  91. // Field to handle for us.
  92. return array(
  93. //In the URL the archive.org item id will appear after "details/" in the URL
  94. '@archive\.org\/details\/([^\"\&]+)@i',
  95. // Now we test for embedded audio code, which is similar in this case to
  96. // the above expression, except the item id will appear after "download/"
  97. '@archive\.org/download/([^/]+)@i',
  98. );
  99. }
  100. /**
  101. * Implement hook emaudio_PROVIDER_data_version().
  102. */
  103. function emaudio_archive_audio_data_version() {
  104. return EMAUDIO_ARCHIVE_DATA_VERSION;
  105. }
  106. /**
  107. * hook emaudio_PROVIDER_data
  108. *
  109. * Provides an array to be serialised and made available with $item elsewhere.
  110. *
  111. * This data can be used to store any extraneous information available
  112. * specifically to the archive provider.
  113. */
  114. function emaudio_archive_audio_data($field, $item, $error_field = '') {
  115. // Initialize the data array.
  116. $data = array();
  117. // Create some version control. Thus if we make changes to the data array
  118. // down the road, we can respect older content. If allowed by Embedded Media
  119. // Field, any older content will automatically update this array as needed.
  120. // In any case, you should account for the version if you increment it.
  121. $data['emaudio_archive_version'] = EMAUDIO_ARCHIVE_DATA_VERSION;
  122. // Construct a base item url
  123. $item_url = "http://www.archive.org/details/". $item['value'];
  124. // We will leverage the JSON archive.org api to get details about this item
  125. // See http://www.archive.org/help/json.php
  126. $xml_meta_url = $item_url .'&output=json';
  127. $xml_meta = media_archive_request_xml('archive', $xml_meta_url, array(), TRUE, TRUE, $item['value'] .'_meta', FALSE, TRUE);
  128. if ($xml_meta['stat'] == 'error' || empty($xml_meta)) {
  129. drupal_set_message('This item\'s details cannot be retrieved. The audio can not be displayed.');
  130. return $data;
  131. }
  132. else {
  133. $data['details'] = $xml_meta;
  134. $server_url = $data['details']['server'] . $data['details']['dir'];
  135. }
  136. $item_files = $data['details']['files'];
  137. if (!$item_files) {
  138. form_set_error($error_field, 'The list of files for the item at archive.org could not be retrieved. The audio can not be displayed.');
  139. return $data;
  140. }
  141. //Putting the $key (which is actually the file name) as an element of the
  142. //array too for convenience later.
  143. foreach ($item_files as $key => $value) {
  144. $item_files[$key]['name'] = $key;
  145. }
  146. //Get playlist files only (.m3u files)
  147. $files_playlists = array_filter($item_files, "_archive_audio_isplaylist");
  148. //If there is no playlist then fail
  149. if (is_null($files_playlists)) {
  150. form_set_error($error_field, 'This archive.org item does not appear to have a playlist.');
  151. return $data;
  152. }
  153. //We'll custom sort the array so we'll get first VBR playlists, then other
  154. //playlists sorted by highest bitrate. Goal is to present the highest
  155. //bitrate playlist offered for this item.
  156. $data['playlists'] = $files_playlists;
  157. $worked = uasort($files_playlists, "_archive_audio_playlistsort");
  158. $candidate_playlist = array_shift($files_playlists);
  159. $data['playlist_file_used'] = $candidate_playlist;
  160. //Retrieve the playlist
  161. $result = drupal_http_request('http://' . $server_url . $candidate_playlist['name']);
  162. if (!empty($result->error)) {
  163. form_set_error($error_field, 'The playlist for the item at archive.org could not be retrieved. The audio can not be displayed.');
  164. return $data;
  165. }
  166. $playlist = $result->data;
  167. //We'll store it in the data element as it is used in our theme function to
  168. //output the player later.
  169. $data['playlist'] = explode("\n", trim($playlist));
  170. return $data;
  171. }
  172. /**
  173. * Sort playlist files
  174. */
  175. function _archive_audio_playlistsort($file1, $file2) {
  176. $components1 = explode(" ", $file1['format']);
  177. $components2 = explode(" ", $file2['format']);
  178. if ($components1[0] == $components2[0]) {
  179. return 0;
  180. }
  181. //We'll prefer the Variable Bitrate (VBR)
  182. elseif ($components1[0] == "VBR") {
  183. return -1;
  184. }
  185. elseif ($components2[0] == "VBR") {
  186. return 1;
  187. }
  188. //Otherwise we'll look for the highest bitrate playlist
  189. $br1 = (int)$components1[0];
  190. $br2 = (int)$components2[0];
  191. if ($br1 > $br2) {
  192. return -1;
  193. }
  194. else {
  195. return 1;
  196. }
  197. }
  198. /**
  199. * Is this file a playlist (.m3u) file?
  200. */
  201. function _archive_audio_isplaylist($file) {
  202. if (substr($file['format'], -3, 3) == "M3U") {
  203. return TRUE;
  204. }
  205. else {
  206. return FALSE;
  207. }
  208. }
  209. /**
  210. * hook emfield_PROVIDER_rss
  211. */
  212. function emaudio_archive_audio_rss($item, $teaser = NULL) {
  213. // Get size and mime type of first playlist file
  214. $url = $item['data']['playlist'][0];
  215. $response = emfield_request_header('archive_audio', $url, $cached = FALSE);
  216. if ($response->code == 200) {
  217. $data['size'] = $response->headers['Content-Length'];
  218. $data['mime'] = $response->headers['Content-Type'];
  219. }
  220. if ($data['size']) {
  221. $file = array();
  222. $file['filepath'] = $url;
  223. $file['filesize'] = $data['size'];
  224. $file['filemime'] = $data['mime'];
  225. }
  226. return $file;
  227. }
  228. /**
  229. * hook emaudio_PROVIDER_embedded_link($audio_code)
  230. * returns a link to view the audio at the provider's site.
  231. * @param $audio_code
  232. * The string containing the audio item.
  233. * @return
  234. * A string containing the URL the audio item at the original provider's site.
  235. */
  236. function emaudio_archive_audio_embedded_link($audio_code) {
  237. return 'http://www.archive.org/details/'. $audio_code;
  238. }
  239. /**
  240. * Implementation of hook emaudio_archive_audio_audio().
  241. *
  242. * This actually displays the full/normal-sized audio we want, usually on the default page view.
  243. *
  244. * @param $embed
  245. * The audio code for the audio to embed.
  246. * @param $width
  247. * The width to display the audio.
  248. * @param $height
  249. * The height to display the audio.
  250. * @param $field
  251. * The field info from the requesting node.
  252. * @param $item
  253. * The actual content from the field.
  254. * @return
  255. * The html of the embedded audio.
  256. */
  257. function emaudio_archive_audio_audio($embed = NULL, $width = 0, $height = 0, $field = NULL, $item, $node, $autoplay) {
  258. $output = theme('emaudio_archive_audio_flash', $embed, $width, $height, $field, $item, $node, $autoplay);
  259. return $output;
  260. }
  261. /**
  262. * The embedded flash displaying the archive audio.
  263. */
  264. function theme_emaudio_archive_audio_flash($embed, $width, $height, $field, $item, $node, $autoplay) {
  265. $output = '';
  266. if ($item) {
  267. $flowplayerplaylist = _media_archive_flowplayer_playlist($item['data']['playlist']);
  268. $controlplaylist = (count($item['data']['playlist'])>1) ? "true" : "false";
  269. $clipautoplay = $autoplay ? 'true' : 'false';
  270. $output = <<<EOD
  271. <embed type="application/x-shockwave-flash" width="$width" height="$height" allowfullscreen="true"
  272. allowscriptaccess="always" src="http://www.archive.org/flow/flowplayer.commercial-3.0.5.swf" w3c="true"
  273. flashvars='config={
  274. "key":"#\$b6eb72a0f2f1e29f3d4",
  275. "playlist":[
  276. {$flowplayerplaylist}
  277. ],
  278. "clip":{
  279. "autoPlay":{$clipautoplay}
  280. },
  281. "canvas":{
  282. "backgroundColor":"0x000000",
  283. "backgroundGradient":"none"
  284. },
  285. "plugins":{
  286. "audio":{
  287. "url":"http://www.archive.org/flow/flowplayer.audio-3.0.3-dev.swf"
  288. },
  289. "controls":{
  290. "playlist":{$controlplaylist},
  291. "fullscreen":false,
  292. "gloss":"high",
  293. "backgroundColor":"0x000000",
  294. "backgroundGradient":"medium",
  295. "sliderColor":"0x777777",
  296. "progressColor":"0x777777",
  297. "timeColor":"0xeeeeee",
  298. "durationColor":"0x01DAFF",
  299. "buttonColor":"0x333333",
  300. "buttonOverColor":"0x505050"
  301. }
  302. },
  303. "contextMenu":[
  304. {
  305. "Item {$item['value']} at archive.org":"function()"
  306. },
  307. "-",
  308. "Flowplayer 3.0.5"
  309. ]
  310. }'>
  311. </embed>
  312. EOD;
  313. }
  314. return $output;
  315. }
  316. /**
  317. * Implementation of hook_emfield_subtheme().
  318. */
  319. function emaudio_archive_audio_emfield_subtheme() {
  320. return array(
  321. 'emaudio_archive_audio_flash' => array(
  322. 'arguments' => array('embed' => NULL, 'width' => NULL, 'height' => NULL, 'field' => NULL, 'data' => NULL, 'node' => NULL, 'autoplay' => NULL),
  323. 'file' => 'providers/emaudio/archive_audio.inc',
  324. 'path' => drupal_get_path('module', 'media_archive'),
  325. )
  326. );
  327. }