Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Radioplayer button with an in-page player #356

Open
wants to merge 10 commits into
base: development
Choose a base branch
from
2 changes: 2 additions & 0 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
odName = "On Tap"

publicMyRadioAPIKey = "DFm1QGGyDZXHvGjjKYxv72ItpZe5oPiNvqTKEJLC2CmPYvdbYQ591DNKhpXwb1U9NVIgBuQ4XOBdSAKbaGzliqHm7pu4H4PxmO7mrH4JvKV6dBZx5n32obnEE2pE9vWC"
liveAudioUrl = "https://audio.ury.org.uk/live-high"
icecastStatusUrl = "https://audio.ury.org.uk/status-json.xsl"

[pageContext.indexCountdown]
enabled = true
Expand Down
5 changes: 3 additions & 2 deletions controllers/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,16 @@ func (ic *IndexController) Post(w http.ResponseWriter, r *http.Request) {
if err != nil {
// Set prompt if send fails
data.MsgBoxError = true
// Indicate to the client that this was an error
w.WriteHeader(400)
}

ic.render(w, data)

}

func (ic *IndexController) render(w http.ResponseWriter, data RenderData) {
// Render page
err := utils.RenderTemplate(w, ic.config.PageContext, data, "index.tmpl", "elements/current_and_next.tmpl", "elements/banner.tmpl", "elements/message_box.tmpl", "elements/index_countdown.tmpl")
err := utils.RenderTemplate(w, ic.config.PageContext, data, "index.tmpl", "elements/current_and_next.tmpl", "elements/banner.tmpl", "elements/message_box.tmpl", "elements/index_countdown.tmpl", "elements/live_player.tmpl")
if err != nil {
log.Println(err)
return
Expand Down
2 changes: 1 addition & 1 deletion controllers/people.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (pc *PeopleController) Get(w http.ResponseWriter, r *http.Request) {
CurrentAndNext: currentAndNext,
}

err = utils.RenderTemplate(w, pc.config.PageContext, data, "people.tmpl", "elements/current_and_next.tmpl")
err = utils.RenderTemplate(w, pc.config.PageContext, data, "people.tmpl", "elements/current_and_next.tmpl", "elements/live_player.tmpl")
if err != nil {
log.Println(err)
return
Expand Down
2 changes: 1 addition & 1 deletion controllers/schedule_week.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ func (sc *ScheduleWeekController) makeAndRenderWeek(w http.ResponseWriter, year,
Subtypes: subtypes,
}

err = utils.RenderTemplate(w, sc.config.PageContext, data, "schedule_week.tmpl", "elements/current_and_next.tmpl")
err = utils.RenderTemplate(w, sc.config.PageContext, data, "schedule_week.tmpl", "elements/current_and_next.tmpl", "elements/live_player.tmpl")
if err != nil {
log.Println(err)
return
Expand Down
10 changes: 5 additions & 5 deletions public/js/currentAndNext.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ function success(data) {
' title="Show currently on air: ' +
data.payload.current.title +
'">' +
'<h2 class="font-weight-bold">Now</h2>' +
'<h2 class="font-weight-bold">Live now</h2>' +
makeContent(data.payload.current) +
'</a>',
)
$('#studiomessage *').attr('disabled', false)
} else {
$('.current-and-next-now').replaceWith(
'<div class="current-and-next-now p-2 pt-3 px-3 p-sm-3 p-lg-4 " title="View the show now on air.">' +
'<h2 class="font-weight-bold">Now</h2>' +
'<h2 class="font-weight-bold">Live now</h2>' +
makeContent(data.payload.current) +
'</a>',
)
Expand All @@ -121,7 +121,7 @@ function success(data) {
// There is no next show (e.g. we're off air)
$('.current-and-next-next').replaceWith(
'<div class="current-and-next-next p-2 pt-3 px-3 p-sm-3 p-lg-4 " title="View the show up next.">' +
'<h2 class="font-weight-bold">Next</h2>' +
'<h2 class="font-weight-bold">Up next</h2>' +
'<div class="h3 show-title ellipsis">There\'s nothing up next yet.</div>' +
'</a>',
)
Expand All @@ -132,14 +132,14 @@ function success(data) {
' title="Show on air next: ' +
data.payload.next.title +
'.">' +
'<h2 class="font-weight-bold">Next</h2>' +
'<h2 class="font-weight-bold">Up next</h2>' +
makeContent(data.payload.next) +
'</a>',
)
} else {
$('.current-and-next-next').replaceWith(
'<div class="current-and-next-next p-2 pt-3 px-3 p-sm-3 p-lg-4 " title="View the show up next.">' +
'<h2 class="font-weight-bold">Next</h2>' +
'<h2 class="font-weight-bold">Up next</h2>' +
makeContent(data.payload.next) +
'</a>',
)
Expand Down
160 changes: 160 additions & 0 deletions public/js/live-player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
export function makePlayer(config) {
const { idPrefix, audioUrl, icecastStatusUrl } = config;
let player = new Audio();
player.preload = 'none';
const playPause = document.getElementById(`${idPrefix}-play`);
const volume = document.getElementById(`${idPrefix}-volume`);
const currentTrackTitle = document.getElementById(`${idPrefix}-track-title`);
const currentTrackArtist = document.getElementById(`${idPrefix}-track-artist`);
const currentTrackContainer = document.getElementById(`${idPrefix}-track-container`);

function updateButton() {
if (player.paused) {
playPause.innerHTML = '<i class="fa fa-play"></i>';
} else {
playPause.innerHTML = '<i class="fa fa-stop"></i>';
}
}

let playbackError = false;

function markLoading() {
playPause.innerHTML = '<span class="player-load-dots" title="loading"><span>&bull;</span><span>&bull;</span><span>&bull;</span></span>';
}

function markError() {
playbackError = true;
playPause.disabled = true;
playPause.innerHTML = '<i class="fa fa-exclamation-triangle"></i>';
}

function setNowPlaying(title, artist) {
currentTrackTitle.innerText = title;
currentTrackArtist.innerText = artist;
if (title === 'URY' && artist === null) {
currentTrackContainer.style.display = 'none';
} else {
currentTrackContainer.style.display = 'block';
}
}

let nowPlayingUpdate = null;

function fetchNowPlaying() {
fetch(icecastStatusUrl)
.then((resp) => {
if (!resp.ok) {
console.error('failed to fetch current track', resp.status, resp.statusText);
nowPlayingUpdate = setTimeout(fetchNowPlaying, 10_000);
return;
} else {
return resp.json();
}
})
.then((resp) => {
const stream = resp.icestats.source.filter((s) => s.listenurl.indexOf('/live-high-ogg') !== -1)[0];
const { artist, title } = stream;

setNowPlaying(title, artist);

// Update every 10s
nowPlayingUpdate = setTimeout(fetchNowPlaying, 10_000);
}).catch((e) => {
console.error('failed to fetch now playing', e);
});
}

const playbackControls = {
play() {
if (playbackError) {
console.log('playback error');
return;
}
if (!nowPlayingUpdate) {
fetchNowPlaying();
}

if (!this.playing) {
player.src = audioUrl;
player.play();
}
},

pause() {
if (playbackError) {
console.error('playback error');
return;
}
if (nowPlayingUpdate) {
clearTimeout(nowPlayingUpdate);
nowPlayingUpdate = null;
}

player.src = null;
player.srcObject = null;

updateButton();
},

setVolume(level) {
player.volume = level;
},

get playing() {
return player.src !== null && !player.paused;
}
};

player.addEventListener('waiting', () => {
if (playbackError) return;
markLoading();
})

player.addEventListener('pause', () => {
if (playbackError) return;
updateButton();
});

player.addEventListener('play', () => {
if (playbackError) return;
updateButton();
});

player.addEventListener('playing', () => {
if (playbackError) return;
updateButton();
});

player.addEventListener('ended', () => {
if (playbackError) return;
console.log('retrying load');
player.load();
});

player.addEventListener('error', (ev) => {
console.log(ev.error);
markError();
});

playPause.addEventListener('click', () => {
if (player.paused) {
playbackControls.play();
} else {
playbackControls.pause();
}
});

playbackControls.setVolume(parseInt(volume.value) / 11.0);
volume.addEventListener('input', () => {
playbackControls.setVolume(parseInt(volume.value) / 11.0);
});

window.onbeforeunload = () => {
console.log('before unload');
if (playbackControls.playing) {
return '';
}
};

return playbackControls;
}
17 changes: 16 additions & 1 deletion sass/elements/_currentAndNext.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.current-next {
background: url("/images/bg-banner-1.jpg") center center no-repeat;
background: url("/images/bg-banner-1.jpg") center no-repeat;
background-size: cover;
color: white;
box-sizing: content-box;
Expand Down Expand Up @@ -75,4 +75,19 @@
background: $current-next-next-bg;
}

.current-and-next-player {
transition: max-height 500ms ease;
overflow: hidden;
max-height: 256px;

&.closed {
transition: max-height 500ms ease;
max-height: 0;
@include media-breakpoint-down(sm) {
// silly hack since the closed player has a height of 1px,
// even though i tell it that it can have a max height of 0
background-color: red;
}
}
}
}
3 changes: 2 additions & 1 deletion sass/elements/_elements.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
@import "snow";
@import "aprilFools";
@import "show";
@import "faq";
@import "faq";
@import "livePlayer";
73 changes: 73 additions & 0 deletions sass/elements/_livePlayer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.live-container {
gap: 1.5rem;
}

.live-now-playing p {
margin: 0;
}

@include media-breakpoint-down(md) {
.mobile-column {
flex-direction: column;
align-items: center !important;
justify-content: left !important;
text-align: center;
gap: 1rem;
}
}

.play-pause-button {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 99999px;
background-color: $ury-blue-color;
color: white;
border: none;
outline: none;
width: 48px;
height: 48px;
box-shadow: 0 0.25rem 1rem rgba(0,0,0,0.5);
transition: transform ease 200ms, box-shadow ease 200ms;
&:hover {
transform: scale(1.1);
box-shadow: 0 0.25rem 1rem rgba(0,0,0,0.75);
}
&:focus {
outline: none;
}
&:active {
transform: scale(0.9);
box-shadow: 0 0.25rem 1rem rgba(0,0,0,0.5);
}

.player-load-dots span {
animation: pulse 2000ms ease infinite;

&:nth-child(1) {
animation-delay: 0ms;
}
&:nth-child(2) {
animation-delay: 250ms;
}
&:nth-child(3) {
animation-delay: 500ms;
}
}
}

@keyframes pulse {
from {
opacity: 1;
}
25% {
opacity: 0;
}
75% {
opacity: 0;
}
to {
opacity: 1;
}
}
8 changes: 5 additions & 3 deletions structs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type PageContext struct {
CINLive string `toml:"cinLive"`
IndexCountdown *IndexCountdownConfig `toml:"indexCountdown"`
CacheBuster string `toml:"cacheBuster"`
LiveAudioURL string `toml:"liveAudioUrl"`
IcecastStatusURL string `toml:"icecastStatusUrl"`
Pages []Page
Youtube youtube
Gmaps gmaps
Expand Down Expand Up @@ -92,9 +94,9 @@ type Page struct {
}

type youtube struct {
APIKey string `toml:"apiKey"`
CINPlaylistID string `toml:"cinPlaylistID"`
ChannelURL string `toml:"channelURL"`
APIKey string `toml:"apiKey"`
CINPlaylistID string `toml:"cinPlaylistID"`
ChannelURL string `toml:"channelURL"`
}

type gmaps struct {
Expand Down
Loading