From aacbac4757fabeb6df9960ee5a78941b0bb4016c Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 1 Mar 2012 15:17:15 -0500 Subject: [PATCH] CC-2430: Preview tracks in the library + better preview (ability to jump around in clip) - Updated functionality so user can play a track and in jplayer and jump around it. --- .../application/controllers/ApiController.php | 196 +++--- .../controllers/PlaylistController.php | 3 +- .../public/js/airtime/library/preview.js | 14 +- .../js/airtime/library/preview_jplayer.js | 53 +- .../jplayer.audio-preview.blue.monday.css | 630 ++++++++++++++++++ 5 files changed, 742 insertions(+), 154 deletions(-) create mode 100644 airtime_mvc/public/js/jplayer/skin/jplayer.audio-preview.blue.monday.css diff --git a/airtime_mvc/application/controllers/ApiController.php b/airtime_mvc/application/controllers/ApiController.php index 805bacbef..54d810fa8 100644 --- a/airtime_mvc/application/controllers/ApiController.php +++ b/airtime_mvc/application/controllers/ApiController.php @@ -96,37 +96,6 @@ class ApiController extends Zend_Controller_Action */ public function getMediaAction() { - //Logging::log(print_r($_SERVER, true)); - Logging::log($_SERVER['HTTP_RANGE']); - if (isset($_SERVER['HTTP_RANGE'])) { -/** - if (!preg_match('^bytes=\d*-\d*(,\d*-\d*)*$', $_SERVER['HTTP_RANGE'])) { - header('HTTP/1.1 416 Requested Range Not Satisfiable'); - header('Content-Range: bytes /' . filelength); // Required in 416. - exit; - } -**/ - $ranges = explode(',', substr($_SERVER['HTTP_RANGE'], 6)); - foreach ($ranges as $range) { - $parts = explode('-', $range); - $start = $parts[0]; // If this is empty, this should be 0. - $end = $parts[1]; // If this is empty or greater than than filelength - 1, this should be filelength - 1. - - //the $end shouldn't be specified. - - //if ($start > $end) { - // header('HTTP/1.1 416 Requested Range Not Satisfiable'); - // header('Content-Range: bytes */' );//. filelength); // Required in 416. - // exit; - //} - - // ... - } - Logging::log("the starting point for the download is $start"); - } - - //Logging::log(print_r($_REQUEST, true)); - //Logging::log("in the get media action!"); global $CC_CONFIG; // disable the view and the layout @@ -134,7 +103,7 @@ class ApiController extends Zend_Controller_Action $this->_helper->viewRenderer->setNoRender(true); $api_key = $this->_getParam('api_key'); - $download = ("true" == $this->_getParam('download')); + $logger = Logging::getLogger(); @@ -149,70 +118,121 @@ class ApiController extends Zend_Controller_Action $filename = $this->_getParam("file"); $file_id = substr($filename, 0, strpos($filename, ".")); - if (ctype_alnum($file_id) && strlen($file_id) == 32) { - $media = Application_Model_StoredFile::RecallByGunid($file_id); - if ($media != null && !PEAR::isError($media)) { - $filepath = $media->getFilePath(); - if(is_file($filepath)){ - // possibly use fileinfo module here in the future. - // http://www.php.net/manual/en/book.fileinfo.php - $ext = pathinfo($filename, PATHINFO_EXTENSION); - if ($ext == "ogg") - header("Content-Type: audio/ogg"); - else if ($ext == "mp3") - header("Content-Type: audio/mpeg"); - if ($download){ - //path_info breaks up a file path into seperate pieces of informaiton. - //We just want the basename which is the file name with the path - //information stripped away. We are using Content-Disposition to specify - //to the browser what name the file should be saved as. - // - // By james.moon: - // I'm removing pathinfo() since it strips away UTF-8 characters. - // Using manualy parsing + if (ctype_alnum($file_id) && strlen($file_id) == 32) + { + $media = Application_Model_StoredFile::RecallByGunid($file_id); + if ( $media != null && !PEAR::isError($media)) + { + $filepath = $media->getFilePath(); + + if(is_file($filepath)){ $full_path = $media->getPropelOrm()->getDbFilepath(); $file_base_name = strrchr($full_path, '/'); $file_base_name = substr($file_base_name, 1); - header('Content-Disposition: attachment; filename="'.$file_base_name.'"'); - } - $logger->info("Sending $filepath"); - header("Content-Length: " . filesize($filepath)); - header('Accept-Ranges: bytes'); + // possibly use fileinfo module here in the future. + // http://www.php.net/manual/en/book.fileinfo.php + $ext = pathinfo($filename, PATHINFO_EXTENSION); + //Download user left clicks a track and selects Download. + if ("true" == $this->_getParam('download')){ + //path_info breaks up a file path into seperate pieces of informaiton. + //We just want the basename which is the file name with the path + //information stripped away. We are using Content-Disposition to specify + //to the browser what name the file should be saved as. + // + // By james.moon: + // I'm removing pathinfo() since it strips away UTF-8 characters. + // Using manualy parsing + header('Content-Disposition: attachment; filename="'.$file_base_name.'"'); + }else{ + //user clicks play button for track and downloads it. + header("Content-Disposition: inline; filename=$file_base_name"); + } - // !! binary mode !! - $fp = fopen($filepath, 'rb'); - if (isset($start) && $start != 0){ - Logging::log("updating the start of the file to be sent."); - fseek($fp, $start); - header("Content-Range: bytes $start-".(filesize($filepath)-1).'/'. filesize($filepath)); - Logging::log("done"); + //ini_set('memory_limit', '512M'); + $this->smartReadFile($filepath, $ext); + exit; + }else{ + header ("HTTP/1.1 404 Not Found"); } - //We can have multiple levels of output buffering. Need to - //keep looping until all have been disabled!!! - //http://www.php.net/manual/en/function.ob-end-flush.php - while (@ob_end_flush()); - - fpassthru($fp); - fclose($fp); -Logging::log(print_r($this->getResponse(), true)); - //make sure to exit here so that no other output is sent. - exit; - } else { - $logger->err('Resource in database, but not in storage: "'.$filepath.'"'); } - } else { - $logger->err('$media != null && !PEAR::isError($media)'); - } - } else { - $logger->err('ctype_alnum($file_id) && strlen($file_id) == 32'); - } - header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); - $logger->info("404 Not Found"); - - Logging::log(print_r($this->getResponse())); - return; + } + return; } + /** + * Reads the requested portion of a file and sends its contents to the client with the appropriate headers. + * + * This HTTP_RANGE compatible read file function is necessary for allowing streaming media to be skipped around in. + * + * @param string $location + * @param string $mimeType + * @return void + * + * @link https://groups.google.com/d/msg/jplayer/nSM2UmnSKKA/Hu76jDZS4xcJ + * @link http://php.net/manual/en/function.readfile.php#86244 + */ + function smartReadFile($location, $mimeType = 'audio/mpeg') + { + $size= filesize($location); + $time= date('r', filemtime($location)); + + $fm = @fopen($location, 'rb'); + if (!$fm) + { + header ("HTTP/1.1 505 Internal server error"); + return; + } + + $begin= 0; + $end= $size - 1; + + if (isset($_SERVER['HTTP_RANGE'])) + { + if (preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches)) + { + $begin = intval($matches[1]); + if (!empty($matches[2])) + { + $end = intval($matches[2]); + } + } + } + + if (isset($_SERVER['HTTP_RANGE'])) + { + header('HTTP/1.1 206 Partial Content'); + } + else + { + header('HTTP/1.1 200 OK'); + } + header("Content-Type: $mimeType"); + header('Cache-Control: public, must-revalidate, max-age=0'); + header('Pragma: no-cache'); + header('Accept-Ranges: bytes'); + header('Content-Length:' . (($end - $begin) + 1)); + if (isset($_SERVER['HTTP_RANGE'])) + { + header("Content-Range: bytes $begin-$end/$size"); + } + + header("Content-Transfer-Encoding: binary"); + header("Last-Modified: $time"); + + //We can have multiple levels of output buffering. Need to + //keep looping until all have been disabled!!! + //http://www.php.net/manual/en/function.ob-end-flush.php + while (@ob_end_flush()); + + $cur = $begin; + fseek($fm, $begin, 0); + while(!feof($fm) && $cur <= $end && (connection_status() == 0)) + { + echo fread($fm, min(1024 * 16, ($end - $cur) + 1)); + $cur += 1024 * 16; + } + } + /** * Retrieve the currently playing show as well as upcoming shows. * Number of shows returned and the time interval in which to diff --git a/airtime_mvc/application/controllers/PlaylistController.php b/airtime_mvc/application/controllers/PlaylistController.php index b68a280ff..b1b7292ce 100644 --- a/airtime_mvc/application/controllers/PlaylistController.php +++ b/airtime_mvc/application/controllers/PlaylistController.php @@ -210,8 +210,7 @@ class PlaylistController extends Zend_Controller_Action $this->view->headScript()->appendFile($baseUrl.'/js/airtime/library/preview_jplayer.js?'.filemtime($baseDir.'/js/airtime/library/preview_jplayer.js'),'text/javascript'); $this->view->headScript()->appendFile($baseUrl.'/js/jplayer/jquery.jplayer.min.js?'.filemtime($baseDir.'/js/jplayer/jquery.jplayer.min.js'),'text/javascript'); - //$this->view->headScript()->appendFile($baseUrl.'/js/jplayer/jquery.jplayer.inspector.js?'.filemtime($baseDir.'/js/jplayer/jquery.jplayer.inspector.js'),'text/javascript'); - $this->view->headLink()->appendStylesheet($baseUrl.'/js/jplayer/skin/jplayer.blue.monday.css?'.filemtime($baseDir.'/js/jplayer/skin/jplayer.blue.monday.css')); + $this->view->headLink()->appendStylesheet($baseUrl.'/js/jplayer/skin/jplayer.audio-preview.blue.monday.css?'.filemtime($baseDir.'/js/jplayer/skin/jplayer.audio-preview.blue.monday.css')); $this->_helper->layout->setLayout('audioPlayer'); $logo = Application_Model_Preference::GetStationLogo(); diff --git a/airtime_mvc/public/js/airtime/library/preview.js b/airtime_mvc/public/js/airtime/library/preview.js index 878214854..e02f5a679 100644 --- a/airtime_mvc/public/js/airtime/library/preview.js +++ b/airtime_mvc/public/js/airtime/library/preview.js @@ -44,22 +44,12 @@ function playlistAudioPreviewEditor(filename, elemIndexString){ function open_audio_preview(filename, index) { console.log("hello world 2 "+filename+" help?"); url = 'Playlist/audio-preview-player/filename/'+filename+'/index/'+index; - + //$.post(baseUri+'Playlist/audio-preview-player', {fileName: fileName, cueIn: cueIn, cueOut: cueOut, fadeIn: fadeIn, fadeInFileName: fadeInFileName, fadeOut: fadeOut, fadeOutFileName: fadeOutFileName}) if (audio_preview_window == null || audio_preview_window.closed){ console.log("opening : "+baseUrl+url); audio_preview_window = window.open(url, 'Audio Player', 'width=400,height=95'); - //audio_preview_window.setTitle('Audio Player'); - //$.post(baseUri+'Playlist/audio-preview-player', {fileName: fileName, cueIn: cueIn, cueOut: cueOut, fadeIn: fadeIn, fadeInFileName: fadeInFileName, fadeOut: fadeOut, fadeOutFileName: fadeOutFileName}) -/** - $.post(url, - {format: "json", elementFilename: filename, elementIndex: elemIndexString}, - function(json){ - audio_preview_window.document.open(); - audio_preview_window.document.write(json); - audio_preview_window.document.close(); - }); -**/ + } else if (!audio_preview_window.closed) { console.log("refreshing : "+baseUrl+url); audio_preview_window.play(filename); diff --git a/airtime_mvc/public/js/airtime/library/preview_jplayer.js b/airtime_mvc/public/js/airtime/library/preview_jplayer.js index 14ae81486..ed48c6328 100644 --- a/airtime_mvc/public/js/airtime/library/preview_jplayer.js +++ b/airtime_mvc/public/js/airtime/library/preview_jplayer.js @@ -37,16 +37,13 @@ function audioPreview(filename, elemID){ $(document).ready(function(){ var filename = $(".filename").text(); - //var filename = $(".jp_audio_0").attr("src"); play(filename); - - }); function play(filename){ console.log("in the play function! "+filename); - var uri = "/api/get-media/file/" + filename+"/api_key/H7CSH1RH1YH2W3KFAKCZ"; + var uri = "/api/get-media/file/" + filename; var ext = getFileExt(filename); @@ -72,53 +69,5 @@ function play(filename){ cssSelectorAncestor: '#jp_container_1', wmode: "window" }); - /** - $("#jquery_jplayer_1").jPlayer().bind($.jPlayer.event.play, function(event){ - console.log("playing xxx"); - //console.log(this.htmlElement.media.currentTime) - //$("#jquery_jplayer_1").jPlayer("playHead", event.jPlayer.status.seekPercent); - }); - $("#jquery_jplayer_1").jPlayer().bind($.jPlayer.event.seeking, function(event){ - console.log("hello 123"); - //console.log(this.htmlElement.media.currentTime) - //$("#jquery_jplayer_1").jPlayer("playHead", event.jPlayer.status.seekPercent); - }); - $("#jquery_jplayer_1").jPlayer().bind($.jPlayer.event.seeked, function(event){ - console.log("hello 456"); - //console.log(this.htmlElement.media.currentTime) - //$("#jquery_jplayer_1").jPlayer("playHead", event.jPlayer.status.seekPercent); - }); - $("#jquery_jplayer_1").jPlayer().bind($.jPlayer.event.volumechange, function(event){ - console.log("hello 666"); - //console.log(this.htmlElement.media.currentTime) - //$("#jquery_jplayer_1").jPlayer("playHead", event.jPlayer.status.seekPercent); - }); - $(".jp-seek-bar").click(function(){ - console.log("seek bar clicked"); - console.log($("#currentTime")); - //console.log(this.htmlElement.media.seekable) - //console.log($(".jp-play-bar").attr("style")); - //$("#jquery_jplayer_1").jPlayer("play", 40); - }); - - $(".jp-seek-bar").click(function(){ - console.log("hi"); - //console.log(this.htmlElement.media.seekable) - //console.log($(".jp-play-bar").attr("style")); - //$("#jquery_jplayer_1").jPlayer("playHead", "50%"); - }); - $(".jp-play-bar").click(function(){ - console.log("bye"); - }); - $(".jp-progres").click(function(){ - console.log("no"); - }); - $("#combo-box").change(function(eventObject){ - var elem = $("#combo-box option:selected"); - setjPlayer(elem.attr("data-url"), elem.attr("data-type"), elem.attr("server-type")); - }); - **/ - - } diff --git a/airtime_mvc/public/js/jplayer/skin/jplayer.audio-preview.blue.monday.css b/airtime_mvc/public/js/jplayer/skin/jplayer.audio-preview.blue.monday.css new file mode 100644 index 000000000..61daa3a11 --- /dev/null +++ b/airtime_mvc/public/js/jplayer/skin/jplayer.audio-preview.blue.monday.css @@ -0,0 +1,630 @@ +/* + * Skin for jPlayer Plugin (jQuery JavaScript Library) + * http://www.happyworm.com/jquery/jplayer + * + * Skin Name: Blue Monday + * + * Copyright (c) 2010-2011 Happyworm Ltd + * Dual licensed under the MIT and GPL licenses. + * - http://www.opensource.org/licenses/mit-license.php + * - http://www.gnu.org/copyleft/gpl.html + * + * Author: Silvia Benvenuti + * Skin Version: 4.0 (jPlayer 2.1.0) + * Date: 1st September 2011 + */ + +div.jp-audio, +div.jp-video { + + /* Edit the font-size to counteract inherited font sizing. + * Eg. 1.25em = 1 / 0.8em + */ + + font-size:1em; /* 1.25em for testing in site pages */ /* No parent CSS that can effect the size in the demos ZIP */ + + font-family:Verdana, Arial, sans-serif; + line-height:1.6; + color: #666; + border:1px solid #009be3; + background-color:#eee; + position:relative; +} +div.jp-audio { + width:420px; +} +div.jp-video-270p { + width:480px; +} +div.jp-video-360p { + width:640px; +} +div.jp-video-full { + /* Rules for IE6 (full-screen) */ + width:480px; + height:270px; + /* Rules for IE7 (full-screen) - Otherwise the relative container causes other page items that are not position:static (default) to appear over the video/gui. */ + position:static !important; position:relative +} + +div.jp-video-full div.jp-jplayer { + top: 0; + left: 0; + position: fixed !important; position: relative; /* Rules for IE6 (full-screen) */ + overflow: hidden; + z-index:1000; +} + +div.jp-video-full div.jp-gui { + position: fixed !important; position: static; /* Rules for IE6 (full-screen) */ + top: 0; + left: 0; + width:100%; + height:100%; + z-index:1000; +} + +div.jp-video-full div.jp-interface { + position: absolute !important; position: relative; /* Rules for IE6 (full-screen) */ + bottom: 0; + left: 0; + z-index:1000; +} + +div.jp-interface { + position: relative; + background-color:#eee; + width:100%; +} + +div.jp-audio div.jp-type-single div.jp-interface { + height:80px; +} +div.jp-audio div.jp-type-playlist div.jp-interface { + height:80px; +} + +div.jp-video div.jp-interface { + border-top:1px solid #009be3; +} + +/* Used to hide the filename field **/ +span.filename { + display:none; +} + +/* @group CONTROLS */ + +div.jp-controls-holder { + clear: both; + width:440px; + margin:0 auto; + position: relative; + overflow:hidden; + top:-8px; /* This negative value depends on the size of the text in jp-currentTime and jp-duration */ +} + +div.jp-interface ul.jp-controls { + list-style-type:none; + margin:0; + padding: 0; + overflow:hidden; +} + +div.jp-audio ul.jp-controls { + width: 380px; + padding:20px 20px 0 20px; +} + +div.jp-video div.jp-type-single ul.jp-controls { + width: 78px; + margin-left: 200px; +} + +div.jp-video div.jp-type-playlist ul.jp-controls { + width: 134px; + margin-left: 172px; +} +div.jp-video ul.jp-controls, +div.jp-interface ul.jp-controls li { + display:inline; + float: left; +} + +div.jp-interface ul.jp-controls a { + display:block; + overflow:hidden; + text-indent:-9999px; +} +a.jp-play, +a.jp-pause { + width:40px; + height:40px; +} + +a.jp-play { + background: url("jplayer.blue.monday.jpg") 0 0 no-repeat; +} +a.jp-play:hover { + background: url("jplayer.blue.monday.jpg") -41px 0 no-repeat; +} +a.jp-pause { + background: url("jplayer.blue.monday.jpg") 0 -42px no-repeat; + display: none; +} +a.jp-pause:hover { + background: url("jplayer.blue.monday.jpg") -41px -42px no-repeat; +} + +a.jp-stop, a.jp-previous, a.jp-next { + width:28px; + height:28px; + margin-top:6px; +} + +a.jp-stop { + background: url("jplayer.blue.monday.jpg") 0 -83px no-repeat; + margin-left:10px; +} + +a.jp-stop:hover { + background: url("jplayer.blue.monday.jpg") -29px -83px no-repeat; +} + +a.jp-previous { + background: url("jplayer.blue.monday.jpg") 0 -112px no-repeat; +} +a.jp-previous:hover { + background: url("jplayer.blue.monday.jpg") -29px -112px no-repeat; +} + +a.jp-next { + background: url("jplayer.blue.monday.jpg") 0 -141px no-repeat; +} +a.jp-next:hover { + background: url("jplayer.blue.monday.jpg") -29px -141px no-repeat; +} + +/* @end */ + +/* @group progress bar */ + +div.jp-progress { + overflow:hidden; + background-color: #ddd; +} +div.jp-audio div.jp-progress { + position: absolute; + top:32px; + height:15px; +} +div.jp-audio div.jp-type-single div.jp-progress { + left:110px; + width:186px; +} +div.jp-audio div.jp-type-playlist div.jp-progress { + left:166px; + width:130px; +} +div.jp-video div.jp-progress { + top:0px; + left:0px; + width:100%; + height:10px; +} +div.jp-seek-bar { + background: url("jplayer.blue.monday.jpg") 0 -202px repeat-x; + width:0px; + height:100%; + cursor: pointer; +} +div.jp-play-bar { + background: url("jplayer.blue.monday.jpg") 0 -218px repeat-x ; + width:0px; + height:100%; +} + +/* The seeking class is added/removed inside jPlayer */ +div.jp-seeking-bg { + background: url("jplayer.blue.monday.seeking.gif"); +} + +/* @end */ + +/* @group volume controls */ + + +a.jp-mute, +a.jp-unmute, +a.jp-volume-max { + width:18px; + height:15px; + margin-top:12px; +} + +div.jp-audio div.jp-type-single a.jp-mute, +div.jp-audio div.jp-type-single a.jp-unmute { + margin-left: 210px; +} + +div.jp-audio div.jp-type-playlist a.jp-mute, +div.jp-audio div.jp-type-playlist a.jp-unmute { + margin-left: 154px; +} + +div.jp-audio a.jp-volume-max { + margin-left: 56px; +} + +div.jp-video a.jp-mute, +div.jp-video a.jp-unmute, +div.jp-video a.jp-volume-max { + position: absolute; + top:12px; + margin-top:0; +} + +div.jp-video a.jp-mute, +div.jp-video a.jp-unmute { + left: 50px; +} + + +div.jp-video a.jp-volume-max { + left: 134px; +} + +a.jp-mute { + background: url("jplayer.blue.monday.jpg") 0 -170px no-repeat; +} +a.jp-mute:hover { + background: url("jplayer.blue.monday.jpg") -19px -170px no-repeat; +} +a.jp-unmute { + background: url("jplayer.blue.monday.jpg") -60px -170px no-repeat; + display: none; +} +a.jp-unmute:hover { + background: url("jplayer.blue.monday.jpg") -79px -170px no-repeat; +} + +a.jp-volume-max { + background: url("jplayer.blue.monday.jpg") 0 -186px no-repeat; +} +a.jp-volume-max:hover { + background: url("jplayer.blue.monday.jpg") -19px -186px no-repeat; +} + +div.jp-volume-bar { + position: absolute; + overflow:hidden; + background: url("jplayer.blue.monday.jpg") 0 -250px repeat-x; + width:46px; + height:5px; + cursor: pointer; +} +div.jp-audio div.jp-volume-bar { + top:37px; + left:330px; +} +div.jp-video div.jp-volume-bar { + top:17px; + left:72px; +} +div.jp-volume-bar-value { + background: url("jplayer.blue.monday.jpg") 0 -256px repeat-x; + width:0px; + height:5px; +} + +/* @end */ + +/* @group current time and duration */ + +div.jp-audio div.jp-time-holder { + position:absolute; + top:50px; +} +div.jp-audio div.jp-type-single div.jp-time-holder { + left:110px; + width:186px; +} +div.jp-audio div.jp-type-playlist div.jp-time-holder { + left:166px; + width:130px; +} + +div.jp-current-time, +div.jp-duration { + width:60px; + font-size:.64em; + font-style:oblique; +} +div.jp-current-time { + float: left; + display:inline; +} +div.jp-duration { + float: right; + display:inline; + text-align: right; +} + +div.jp-video div.jp-current-time { + margin-left:20px; +} +div.jp-video div.jp-duration { + margin-right:20px; +} + +/* @end */ + +/* @group playlist */ + +div.jp-title { + font-weight:bold; + text-align:center; +} + +div.jp-title, +div.jp-playlist { + width:100%; + background-color:#ccc; + border-top:1px solid #009be3; +} +div.jp-type-single div.jp-title, +div.jp-type-playlist div.jp-title, +div.jp-type-single div.jp-playlist { + border-top:none; +} +div.jp-title ul, +div.jp-playlist ul { + list-style-type:none; + margin:0; + padding:0 20px; + font-size:.72em; +} + +div.jp-title li { + padding:5px 0; + font-weight:bold; +} +div.jp-playlist li { + padding:5px 0 4px 20px; + border-bottom:1px solid #eee; +} + +div.jp-playlist li div { + display:inline; +} + +/* Note that the first-child (IE6) and last-child (IE6/7/8) selectors do not work on IE */ + +div.jp-type-playlist div.jp-playlist li:last-child { + padding:5px 0 5px 20px; + border-bottom:none; +} +div.jp-type-playlist div.jp-playlist li.jp-playlist-current { + list-style-type:square; + list-style-position:inside; + padding-left:7px; +} +div.jp-type-playlist div.jp-playlist a { + color: #333; + text-decoration: none; +} +div.jp-type-playlist div.jp-playlist a:hover { + color:#0d88c1; +} +div.jp-type-playlist div.jp-playlist a.jp-playlist-current { + color:#0d88c1; +} + +div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove { + float:right; + display:inline; + text-align:right; + margin-right:10px; + font-weight:bold; + color:#666; +} +div.jp-type-playlist div.jp-playlist a.jp-playlist-item-remove:hover { + color:#0d88c1; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media { + float:right; + display:inline; + text-align:right; + margin-right:10px; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media a{ + color:#666; +} +div.jp-type-playlist div.jp-playlist span.jp-free-media a:hover{ + color:#0d88c1; +} +span.jp-artist { + font-size:.8em; + color:#666; +} + +/* @end */ + +div.jp-video-play { + position:absolute; + top:0; + left:0; + width:100%; + cursor:pointer; + background-color:rgba(0,0,0,0); /* Makes IE9 work with the active area over the whole video area. IE6/7/8 only have the button as active area. */ +} +div.jp-video-270p div.jp-video-play { + height:270px; +} +div.jp-video-360p div.jp-video-play { + height:360px; +} +div.jp-video-full div.jp-video-play { + height:100%; + z-index:1000; +} +a.jp-video-play-icon { + position:relative; + display:block; + width: 112px; + height: 100px; + + margin-left:-56px; + margin-top:-50px; + left:50%; + top:50%; + + background: url("jplayer.blue.monday.video.play.png") 0 0 no-repeat; + text-indent:-9999px; +} +div.jp-video-play:hover a.jp-video-play-icon { + background: url("jplayer.blue.monday.video.play.png") 0 -100px no-repeat; +} + + + + + +div.jp-jplayer audio, +div.jp-jplayer { + width:0px; + height:0px; +} + +div.jp-jplayer { + background-color: #000000; +} + + + + + +/* @group TOGGLES */ + +/* The audio toggles are nested inside jp-time-holder */ + +ul.jp-toggles { + list-style-type:none; + padding:0; + margin:0 auto; + overflow:hidden; +} + +div.jp-audio .jp-type-single ul.jp-toggles { + width:25px; +} +div.jp-audio .jp-type-playlist ul.jp-toggles { + width:55px; + margin: 0; + position: absolute; + left: 325px; + top: 50px; +} + +div.jp-video ul.jp-toggles { + margin-top:10px; + width:100px; +} + +ul.jp-toggles li { + display:block; + float:right; +} + +ul.jp-toggles li a { + display:block; + width:25px; + height:18px; + text-indent:-9999px; + line-height:100%; /* need this for IE6 */ +} + +a.jp-full-screen { + background: url("jplayer.blue.monday.jpg") 0 -310px no-repeat; + margin-left: 20px; +} + +a.jp-full-screen:hover { + background: url("jplayer.blue.monday.jpg") -30px -310px no-repeat; +} + +a.jp-restore-screen { + background: url("jplayer.blue.monday.jpg") -60px -310px no-repeat; + margin-left: 20px; +} + +a.jp-restore-screen:hover { + background: url("jplayer.blue.monday.jpg") -90px -310px no-repeat; +} + +a.jp-repeat { + background: url("jplayer.blue.monday.jpg") 0 -290px no-repeat; +} + +a.jp-repeat:hover { + background: url("jplayer.blue.monday.jpg") -30px -290px no-repeat; +} + +a.jp-repeat-off { + background: url("jplayer.blue.monday.jpg") -60px -290px no-repeat; +} + +a.jp-repeat-off:hover { + background: url("jplayer.blue.monday.jpg") -90px -290px no-repeat; +} + +a.jp-shuffle { + background: url("jplayer.blue.monday.jpg") 0 -270px no-repeat; + margin-left: 5px; +} + +a.jp-shuffle:hover { + background: url("jplayer.blue.monday.jpg") -30px -270px no-repeat; +} + +a.jp-shuffle-off { + background: url("jplayer.blue.monday.jpg") -60px -270px no-repeat; + margin-left: 5px; +} + +a.jp-shuffle-off:hover { + background: url("jplayer.blue.monday.jpg") -90px -270px no-repeat; +} + + +/* @end */ + +/* @group NO SOLUTION error feedback */ + +.jp-no-solution { + position:absolute; + width:390px; + margin-left:-202px; + left:50%; + top: 10px; + + padding:5px; + font-size:.8em; + background-color:#eee; + border:2px solid #009be3; + color:#000; + display:none; +} + +.jp-no-solution a { + color:#000; +} + +.jp-no-solution span { + font-size:1em; + display:block; + text-align:center; + font-weight:bold; +} + +/* @end */ \ No newline at end of file