PVL Mobile Initial Launch

This commit is contained in:
Buster Neece 2014-03-05 07:08:24 -06:00
parent 6e87864b78
commit 4ff2003a05
17 changed files with 414 additions and 149 deletions

View File

@ -88,6 +88,12 @@ $affiliates = array(
'image' => \DF\Url::content('affiliates/lpp.png'),
),
array(
'name' => 'HoofSounds',
'url' => 'https://hoofsounds.little.my/',
'description' => 'Pony Radio in Your Browser!',
'image' => \DF\Url::content('affiliates/hoofsounds.png'),
),
);
shuffle($affiliates);

View File

@ -8,7 +8,6 @@ echo $this->doctype();
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="keywords" content="Pony, Brony, Radio, Video, Podcast, Multimedia, Convention, Ponyville, Live, MLP, MLP:FiM" />
<meta name="description" content="Equestria's newest Brony multimedia network, featuring 24/7 pony radio, videos, podcasts, convention coverage, and more." />
@ -78,7 +77,7 @@ ga('send', 'pageview');
<!-- End Google Universal Analytics -->
<!-- Header -->
<div data-role="header" data-position="fixed" data-theme="a" data-add-back-btn="true">
<div data-role="header" data-position="fixed" data-tap-toggle="false" data-theme="a">
<a id="btn_back" href="#" data-rel="back" class="ui-btn-left ui-alt-icon ui-nodisc-icon ui-btn ui-icon-carat-l ui-btn-icon-notext ui-corner-all" data-role="button" role="button">Back</a>
<h1><?=$title ?></h1>
</div>
@ -91,10 +90,10 @@ ga('send', 'pageview');
</div>
<!-- Footer Nav -->
<div data-role="footer" data-position="fixed" data-theme="a">
<div data-role="footer" data-position="fixed" data-tap-toggle="false" data-theme="a">
<div id="player_controls" data-role="controlgroup">
<a id="btn_stop" class="ui-btn ui-btn-left ui-corner-all ui-btn-icon-notext" id="player_pause" href="#">Stop</a>
<input type="range" class="volume_slider" name="player_volume" id="player_volume" min="0" max="100" value="50" data-highlight="true">
<a class="ui-corner-all ui-btn ui-btn-left" id="player_pause">Stop</a>
<input type="range" class="volume_slider" name="player_volume" id="player_volume" min="0" max="100" value="50" data-role="none" data-highlight="true">
</div>
<div data-role="navbar">
@ -111,7 +110,7 @@ ga('send', 'pageview');
<span class="icon"><i class="fa fa-rss"></i></span>
<span class="tab-label">Shows</span>
</a></li>
<li><a href="<?=$this->route(array('module' => 'mobile', 'controller' => 'station', 'action' => 'request')) ?>">
<li><a href="<?=$this->route(array('module' => 'mobile', 'controller' => 'requests')) ?>">
<span class="icon"><i class="fa fa-comments"></i></span>
<span class="tab-label">Requests</span>
</a></li>

View File

@ -1,5 +1,7 @@
<?php
use \Entity\Station;
use \Entity\Podcast;
use \Entity\Schedule;
class Mobile_IndexController extends \PVL\Controller\Action\Mobile
{
@ -37,14 +39,44 @@ class Mobile_IndexController extends \PVL\Controller\Action\Mobile
case "video":
$this->view->category = $this->categories['video'];
$this->render('view_video');
$this->view->headTitle('Video Streams');
$this->render('view_station');
break;
case "radio":
default:
$this->view->category = $this->categories['audio'];
$this->render('view_radio');
$this->view->headTitle('Radio Stations');
$this->render('view_station');
break;
}
}
public function stationAction()
{
$id = (int)$this->_getParam('id');
$station = Station::find($id);
if (!($station instanceof Station))
throw new \DF\Exception\DisplayOnly('Not found!');
$this->view->station = $station;
$threshold = time()+86400*7;
$this->view->events = Schedule::getEventsInRange($station->id, time(), $threshold);
}
public function podcastAction()
{
$id = (int)$this->_getParam('id');
$podcast = Podcast::find($id);
if (!($podcast instanceof Podcast))
throw new \DF\Exception\DisplayOnly('Not found!');
$this->view->podcast = $podcast;
$this->view->episodes = $podcast->episodes;
}
}

View File

@ -0,0 +1,14 @@
<?php
use \Entity\Station;
use \Entity\StationMedia;
class Mobile_RequestsController extends \PVL\Controller\Action\Mobile
{
public function indexAction()
{
$stations_supporting_requests = $this->em->createQuery('SELECT s FROM Entity\Station s WHERE s.requests_enabled = 1 ORDER BY s.weight ASC')
->getArrayResult();
$this->view->stations = $stations_supporting_requests;
}
}

View File

@ -1,16 +0,0 @@
<?php
use \Entity\Station;
class Mobile_StationController extends \PVL\Controller\Action\Mobile
{
public function indexAction()
{
$id = (int)$this->_getParam('id');
$station = Station::find($id);
if (!($station instanceof Station))
throw new \DF\Exception\DisplayOnly('Not found!');
$this->view->station = $station;
}
}

View File

@ -16,7 +16,7 @@ $this->headTitle('Ponyville Live!');
<a class="ui-btn ui-corner-all" href="<?=$this->route(array('module' => 'mobile', 'action' => 'view', 'type' => 'show')) ?>">
<i class="fa fa-rss"></i> Show Episodes
</a>
<a class="ui-btn ui-corner-all" href="<?=$this->route(array('module' => 'mobile', 'controller' => 'station', 'action' => 'request')) ?>">
<a class="ui-btn ui-corner-all" href="<?=$this->route(array('module' => 'mobile', 'controller' => 'requests', 'action' => 'index')) ?>">
<i class="fa fa-comments"></i> Song Requests
</a>
</div>

View File

@ -0,0 +1,45 @@
<?php
$this->headTitle($this->podcast->name);
?>
<div class="podcast">
<div class="podcast-header clearfix">
<img src="<?=\DF\Url::content($this->podcast['image_url']) ?>">
<h4>
<?=$this->podcast['name'] ?>
<span class="genre-info"><?=$this->podcast['description'] ?></span>
</h4>
</div>
<a class="ui-btn ui-btn-b ui-corner-all" href="<?=$this->podcast['web_url'] ?>" target="_blank" rel="external">
<i class="fa fa-globe"></i> Visit Homepage
</a>
<? if (count($this->podcast->stations) > 0): ?>
<div class="panel nowplaying-onair ui-corner-all custom-corners" style="display: none;">
<div class="ui-bar ui-bar-a">
<h3>Airs on Stations</h3>
</div>
<div class="ui-body ui-body-a">
<? foreach($this->podcast->stations as $station): ?>
<div><?=$station->name ?></div>
<? endforeach; ?>
</div>
</div>
<? endif; ?>
<div class="panel nowplaying-info ui-corner-all custom-corners">
<div class="ui-bar ui-bar-a">
<h3>Recent Episodes</h3>
</div>
<div class="ui-body ui-body-a">
<? foreach($this->episodes as $ep): ?>
<div class="vertical-padded">
<small><?=date('F j, Y g:ia', $ep['timestamp']) ?></small><br>
<a href="<?=$ep['web_url'] ?>" target="_blank"><?=$ep['title'] ?></a>
</div>
<? endforeach; ?>
</div>
</div>
</div>

View File

@ -0,0 +1,84 @@
<?
$station = $this->station;
$this->headTitle($station->name);
$tz_info = \PVL\Timezone::getInfo();
$tz_name = $tz_info['abbr'];
?>
<div class="station" id="station_<?=$station->short_name ?>"
data-autoplay="true"
data-id="<?=$station['id'] ?>"
data-name="<?=$station['name'] ?>"
data-type="<?=$station['type'] ?>"
data-stream="<?=$station['stream_url'] ?>"
data-inactive="<? if ($station['hide_if_inactive']): ?>hide<? else: ?>show<? endif; ?>">
<div class="station-header clearfix">
<img src="<?=\DF\Url::content($station['image_url']) ?>">
<h4>
<?=$station['name'] ?>
<span class="nowplaying-info">
<span class="nowplaying-listeners"></span>
</span>
<? if ($station['genre']): ?>
<span class="genre-info"><?=$station['genre'] ?></span>
<? endif; ?>
</h4>
</div>
<? if ($station['category'] == 'audio'): ?>
<a class="btn_tunein ui-btn ui-btn-b ui-corner-all" href="#">
<i class="fa fa-music"></i> Tune In Now
</a>
<? else: ?>
<a class="ui-btn ui-btn-b ui-corner-all" href="<?=$station['stream_url'] ?>" target="_blank" rel="external">
<i class="fa fa-video-camera"></i> Tune In Now
</a>
<? endif; ?>
<div class="panel nowplaying-onair ui-corner-all custom-corners" style="display: none;">
<div class="ui-bar ui-bar-a">
<h3>Now On Air</h3>
</div>
<div class="ui-body ui-body-a">
<span class="nowplaying-onair-inner"></span>
</div>
</div>
<div class="panel nowplaying-info ui-corner-all custom-corners">
<div class="ui-bar ui-bar-a">
<h3>Now Playing</h3>
</div>
<div class="ui-body ui-body-a">
<div class="nowplaying-artist">Loading...</div>
<div class="nowplaying-title">Loading...</div>
</div>
</div>
<div class="panel station-history-panel ui-corner-all custom-corners">
<div class="ui-bar ui-bar-a">
<h3>Recently Played</h3>
</div>
<div class="ui-body ui-body-a">
<div class="station-history"></div>
</div>
</div>
<? if ($this->events): ?>
<div class="panel station-history-panel ui-corner-all custom-corners">
<div class="ui-bar ui-bar-a">
<h3>On the Schedule (<?=$tz_name ?>)</h3>
</div>
<div class="ui-body ui-body-a">
<? foreach($this->events as $event): ?>
<div class="vertical-padded">
<small><?=$event['range'] ?>:</small><br>
<b><?=\Entity\Schedule::formatName($event['title']) ?></b>
</div>
<? endforeach; ?>
</div>
</div>
<? endif; ?>
</div>

View File

@ -0,0 +1,25 @@
<?
$this->headTitle('Show Episodes');
?>
<ul data-role="listview" class="podcast_list">
<? foreach($this->podcasts as $podcast_info): ?>
<?
$podcast = $podcast_info['record'];
$ep = $podcast_info['episodes'][0];
?>
<li class="podcast">
<a href="<?=$this->routeFromHere(array('action' => 'podcast', 'id' => $podcast['id'])) ?>">
<img src="<?=\DF\Url::content($podcast['image_url']) ?>">
<h4>
<?=$podcast['name'] ?>
<span class="genre-info"><?=$podcast['description'] ?></span>
</h4>
<span class="newest-episode-intro">Newest Episode (<?=date('F j, Y', $ep['timestamp']) ?>):</span>
<span class="newest-episode-name"><?=$ep['title'] ?></span>
</a>
</li>
<? endforeach; ?>
</ul>

View File

@ -1,7 +1,3 @@
<?php
$this->headTitle('Radio Stations');
?>
<ul data-role="listview" class="station_list" id="station_list">
<? foreach($this->category['stations'] as $station): ?>
<li class="station" id="station_<?=\Entity\Station::getStationShortName($station['name']) ?>"
@ -9,7 +5,7 @@ $this->headTitle('Radio Stations');
data-name="<?=$station['name'] ?>"
data-type="<?=$station['type'] ?>"
data-inactive="<? if ($station['hide_if_inactive']): ?>hide<? else: ?>show<? endif; ?>">
<a href="<?=$this->routeFromHere(array('controller' => 'station', 'action' => 'index', 'id' => $station['id'])) ?>">
<a href="<?=$this->routeFromHere(array('action' => 'station', 'id' => $station['id'])) ?>">
<img src="<?=\DF\Url::content($station['image_url']) ?>">
<h4>
@ -21,7 +17,7 @@ $this->headTitle('Radio Stations');
<span class="genre-info"><?=$station['genre'] ?></span>
<? endif; ?>
</h4>
<span class="nowplaying-onair" style="display: none;"></span>
<span class="nowplaying-onair" style="display: none;"><span class="nowplaying-onair-inner"></span></span>
<span class="nowplaying-artist">Loading...</span>
<span class="nowplaying-title">Loading...</span>
</a>

View File

@ -0,0 +1,13 @@
<?
$this->headTitle('Song Requests');
?>
<ul data-role="listview">
<li data-role="list-divider">Stations Supporting Requests</li>
<? foreach($this->stations as $station): ?>
<li><a href="<?=$this->route(array('controller' => 'station', 'action' => 'request', 'id' => $station['id'] )) ?>" target="_blank" rel="external">
<?=$station['name'] ?><br>
<small><?=$station['genre'] ?></small>
</a></li>
<? endforeach; ?>
</ul>

View File

@ -1,27 +0,0 @@
<?
$station = $this->station;
$this->headTitle($station->name);
?>
<div class="station" id="station_<?=$station->short_name ?>"
data-autoplay="true"
data-id="<?=$station['id'] ?>"
data-name="<?=$station['name'] ?>"
data-type="<?=$station['type'] ?>"
data-stream="<?=$station['stream_url'] ?>"
data-inactive="<? if ($station['hide_if_inactive']): ?>hide<? else: ?>show<? endif; ?>">
<img src="<?=\DF\Url::content($station['image_url']) ?>">
<h4>
<?=$station['name'] ?>
<span class="nowplaying-info">
<span class="nowplaying-listeners"></span>
</span>
<? if ($station['genre']): ?>
<span class="genre-info"><?=$station['genre'] ?></span>
<? endif; ?>
</h4>
<span class="nowplaying-onair" style="display: none;"></span>
<span class="nowplaying-artist">Loading...</span>
<span class="nowplaying-title">Loading...</span>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@ -18,6 +18,35 @@ body,
font-weight: normal;
}
.clearfix:after {
content: " "; /* Older browser do not support empty content */
visibility: hidden;
display: block;
height: 0;
clear: both;
}
.custom-corners .ui-bar {
-webkit-border-top-left-radius: inherit;
border-top-left-radius: inherit;
-webkit-border-top-right-radius: inherit;
border-top-right-radius: inherit;
}
.custom-corners .ui-body {
border-top-width: 0;
-webkit-border-bottom-left-radius: inherit;
border-bottom-left-radius: inherit;
-webkit-border-bottom-right-radius: inherit;
border-bottom-right-radius: inherit;
}
div.vertical-padded {
padding: 5px 0 0 0;
}
div.vertical-padded:nth-child(1) {
padding: 0;
}
/**
* Homepage
*/
@ -60,83 +89,155 @@ input.volume_slider {
* Station Listings
*/
#page .station a {
#page li.station a,
#page li.podcast a {
padding-top: 10px;
padding-bottom: 10px;
padding-left: 70px;
min-height: 50px;
}
#page .station a img {
#page li.station a img,
#page li.podcast a img {
width: 50px;
top: 10px;
left: 10px;
}
#page .station h4,
#page .station div,
#page .station span {
#page li.station h4,
#page li.station div,
#page li.station span,
#page li.podcast h4,
#page li.podcast div,
#page li.podcast span {
margin: 0;
padding: 0;
}
#page .station span {
#page li.station span,
#page li.podcast span {
display: block;
}
#page li.podcast span {
white-space: normal;
}
#page .station.offline,
#page .station.offline h4 small {
#page li.station.offline,
#page li.station.offline h4 small {
opacity: 0.6;
}
#page .station.live,
#page .station.live h4 small {
#page li.station.live,
#page li.station.live h4 small {
color: #2C7FD2;
}
#page .station.playing,
#page .station.playing h4 small {
#page li.station.playing,
#page li.station.playing h4 small {
color: #5699DC;
}
/**
* Player Skin for PVL
*/
#page .station h4,
#page .podcast h4 {
#page li.station h4,
#page li.podcast h4 {
font-size: 16px;
}
#page .station h4 span,
#page .podcast h4 span {
#page li.station h4 span,
#page li.podcast h4 span {
display: inline;
font-weight: normal;
font-size: 12px;
color: #999;
}
#page .station span.genre-info,
#page .podcast span.genre-info {
#page li.station span.genre-info,
#page li.podcast span.genre-info {
font-size: 13px;
display: block;
}
#page .station span.nowplaying-onair {
#page li.station span.nowplaying-onair {
font-size: 12px;
line-height: 15px;
color: #AAA;
}
#page .station span.nowplaying-artist {
#page li.station span.nowplaying-artist,
#page li.podcast span.newest-episode-name {
font-size: 14px;
line-height: 15px;
font-weight: bold;
}
#page .station span.nowplaying-title {
#page li.station span.nowplaying-title,
#page li.podcast span.newest-episode-intro {
font-size: 12px;
line-height: 13px;
font-weight: normal;
}
#page .station.playing,
#page .station.playing h4 small {
#page li.station.playing,
#page li.station.playing h4 small {
color: #5699DC;
}
/**
* Station Detail Page
*/
#page div.station img,
#page div.podcast img {
width: 50px;
float: left;
padding-right: 5px;
}
#page div.station .station-header,
#page div.podcast .podcast-header {
padding-bottom: 5px;
}
#page div.station h4,
#page div.podcast h4 {
margin: 0;
padding: 6px 0;
font-size: 20px;
line-height: 18px;
}
#page div.station h4 span,
#page div.podcast h4 span {
display: inline;
font-weight: normal;
font-size: 15px;
color: #999;
}
#page div.station span.genre-info,
#page div.podcast span.genre-info {
display: block;
}
#page div.podcast span.genre-info {
white-space: normal;
}
#page div.station .panel {
padding-top: 10px;
}
#page div.station .ui-bar,
#page div.station .ui-body {
padding: 8px;
}
#page div.station .nowplaying-artist {
font-size: 16px;
line-height: 17px;
font-weight: bold;
}
#page div.station .nowplaying-title {
font-size: 14px;
line-height: 15px;
font-weight: normal;
}
#page div.station .ui-body {
font-size: 14px;
line-height: 14px;
}

View File

@ -2,33 +2,64 @@
* Mobile Custom JS
*/
var is_playing = false;
var is_first_load = true;
var volume = 100;
var nowplaying_last_run = 0;
var nowplaying_timeout;
var nowplaying_interval;
$(function() {
$('#btn_back').hide();
$('#player_controls').hide();
$(document).on("pageinit", function() {
if (is_first_load)
{
$('#btn_back').hide();
$('input[type="slider"]').slider();
$("#pvl-jplayer").jPlayer({
swfPath: DF_ContentPath+'/jplayer/jplayer.swf',
solution: (canPlayMp3()) ? 'html, flash' : 'flash',
supplied: 'mp3',
preload: 'none',
volume: (volume / 100),
muted: false,
backgroundColor: '#000000',
cssSelectorAncestor: '#pvl-jplayer-controls',
errorAlerts: false,
warningAlerts: false
});
$('#player_controls input.volume_slider').attr('value', volume);
$('#player_controls input.volume_slider').on("slidestop", function(event, ui) {
volume = parseInt(ui.value);
$('#player_controls').hide();
$('#pvl-jplayer').jPlayer('volume', (volume / 100));
});
$('#player_volume').attr('value', volume);
$('#player_volume').slider({
stop: function(event, ui) {
volume = parseInt(event.target.value);
$("[data-role='navbar']").navbar();
$("[data-role='header'], [data-role='footer']").toolbar();
nowplaying_interval = setInterval('verifyNowPlaying()', 30000);
if (is_playing)
$('#pvl-jplayer').jPlayer('volume', (volume / 100));
}
});
$('#player_controls #player_pause').on("click", function(event, ui) {
stopAllPlayers();
});
$('body').on('click', '.btn_tunein', function(event) {
playStation($(this).closest('.station').attr('id'));
});
$("[data-role='navbar']").navbar();
$("[data-role='header'], [data-role='footer']").toolbar();
nowplaying_interval = setInterval('verifyNowPlaying()', 30000);
}
});
$(window).on("pagecontainershow", function(event) {
$('#btn_back').show();
if (!is_first_load)
$('#btn_back').show();
is_first_load = false;
// Force old page to be deleted.
$("[data-role='page']:not(.ui-page-active)").remove();
@ -49,21 +80,9 @@ $(window).on("pagecontainershow", function(event) {
});
// Detect autoplay station.
checkAutoPlay();
checkNowPlaying(true);
});
function checkAutoPlay()
{
$('[data-role="page"].ui-page-active').find('.station').each(function() {
var autoplay = Boolean($(this).data('autoplay'));
if (autoplay)
playStation($(this).attr('id'));
});
}
// Ensure now-playing is being checked, in spite of any interruptions.
function verifyNowPlaying()
{
@ -139,16 +158,16 @@ function checkNowPlaying(force_update)
if (station_info.event)
{
var event_info = station_info.event;
station.find('.nowplaying-onair').show().html('<i class="fa fa-star"></i>&nbsp;On Air: '+event_info.title);
station.find('.nowplaying-onair').show().find('.nowplaying-onair-inner').html('<i class="fa fa-star"></i>&nbsp;On Air: '+event_info.title);
}
else if (station_info.event_upcoming)
{
var event_info = station_info.event_upcoming;
station.find('.nowplaying-onair').show().html('<i class="fa fa-star"></i>&nbsp;In '+event_info.minutes_until+' mins: '+event_info.title);
station.find('.nowplaying-onair').show().find('.nowplaying-onair-inner').html('<i class="fa fa-star"></i>&nbsp;In '+event_info.minutes_until+' mins: '+event_info.title);
}
else
{
station.find('.nowplaying-onair').empty().hide();
station.find('.nowplaying-onair').hide();
}
// Set station history.
@ -208,39 +227,7 @@ function playStation(id)
}
else
{
$("#pvl-jplayer").jPlayer({
ready: function (event) {
ready = true;
startPlayer(stream_url);
},
pause: function() {
$(this).jPlayer("clearMedia");
},
error: function(event) {
var error_details = event.jPlayer.error;
console.log('Error: '+error_details.message+' - '+error_details.hint);
setTimeout(function() {
console.log('Retrying playback...');
startPlayer(stream_url);
}, 200);
},
volumechange: function(event) {
volume = Math.round(event.jPlayer.options.volume * 100);
},
swfPath: DF_ContentPath+'/jplayer/jplayer.swf',
solution: (canPlayMp3()) ? 'html, flash' : 'flash',
supplied: 'mp3',
preload: 'none',
volume: volume,
muted: false,
backgroundColor: '#000000',
cssSelectorAncestor: '#pvl-jplayer-controls',
errorAlerts: false,
warningAlerts: false
});
$('#player_controls').show();
startPlayer(stream_url);
// Trigger an immediate now-playing check.
checkNowPlaying(true);
@ -258,7 +245,11 @@ function startPlayer(stream_url)
mp3: stream_url
};
is_playing = true;
$('.btn_tunein').hide();
$("#pvl-jplayer").jPlayer("setMedia", stream).jPlayer("play");
$('#player_controls').show();
}
function stopAllPlayers()
@ -273,12 +264,14 @@ function stopAllPlayers()
try
{
$('#pvl-jplayer').jPlayer("clearMedia").jPlayer("destroy");
$('#pvl-jplayer').jPlayer("clearMedia");
}
catch(e) {}
}
$('#pvl-jplayer').empty();
is_playing = false;
$('.btn_tunein').show();
$('#player_controls').hide();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB