confirming when user leaves site while audio is playing

This commit is contained in:
Rudis Muiznieks 2023-04-09 19:27:47 -05:00
parent 3ebf5cbcd3
commit ff3636da55
Signed by: rudism
GPG key ID: CABF2F86EF7884F9
7 changed files with 125 additions and 94 deletions

View file

@ -94,7 +94,10 @@
<div id='overlay'></div>
<div id='queue-container'>
<h2>Playlist</h2>
<h3>Up Next:</h3>
<div>
<h3>Up Next:</h3>
<a href='#' id='clear-playlist'>Clear Playlist</a>
</div>
<ol>
</ol>
</div>

View file

@ -5,88 +5,32 @@
</svg>
Radiostasis
</h2>
<p>The home of streaming on-demand Old Time Radio.</p>
<h3>What is Old Time Radio?</h3>
<p>Old Time Radio shows are commercial audio programs that were broadcast over the air during the "Golden Age of Radio," an era that spans from the 1920s through the 1950s. Audio shows that were broadcast in the USA prior to 1972 were not subject to federal copyright protections, so as a result many of these old shows are available in the public domain.</p>
<p>To give you a concrete example, here are a few of my favorite episodes that you can listen to right now:</p>
<div class='seriesList'>
<section class='episode'
title='The Green Hills of Earth'
data-cover='/cover/sm/x-minus-one.jpg'
data-series='X Minus One'
data-file='otr/x-minus-one/00008.mp3'>
<img alt='cover image' title='The Green Hils of Earth'
src='/cover/sm/x-minus-one.jpg'>
<div>
<ul>
<li>Sci-Fi</li>
</ul>
<label>The Green Hills of Earth</label>
<aside>
<span>X-Minus One</span>
<span>1955-07-07</span>
</div>
</section>
<section class='episode'
title='Snow White and the Seven Dwarves'
data-cover='/cover/sm/the-jack-benny-program.jpg'
data-series='The Jack Benny Program'
data-file='otr/the-jack-benny-program/00318.mp3'>
<img alt='cover image' title='Snow White and the Seven Dwarves'
src='/cover/sm/the-jack-benny-program.jpg'>
<div>
<ul>
<li>Comedy</li>
</ul>
<label>Snow White and the Seven Dwarves</label>
<aside>
<span>The Jack Benny Program</span>
<span>1939-01-08</span>
</div>
</section>
<section class='episode'
title='Stage Smash'
data-cover='/cover/sm/gunsmoke.jpg'
data-series='Gunsmoke'
data-file='otr/gunsmoke/00434.mp3'>
<img alt='cover image' title='The Maltese Falcon'
src='/cover/sm/gunsmoke.jpg'>
<div>
<ul>
<li>Adventure</li>
<li>Western</li>
</ul>
<label>Stage Smash</label>
<aside>
<span>Gunsmoke</span>
<span>1960-07-31</span>
</aside>
</div>
</section>
<section class='episode'
title='The Maltese Falcon'
data-cover='/cover/sm/academy-award-theater.jpg'
data-series='Academy Award Theater'
data-file='otr/academy-award-theater/00015.mp3'>
<img alt='cover image' title='The Maltese Falcon'
src='/cover/sm/academy-award-theater.jpg'>
<div>
<ul>
<li>Drama</li>
</ul>
<label>The Maltese Falcon</label>
<aside>
<span>Academy Award Theater</span>
<span>1946-07-03</span>
</aside>
</div>
</section>
</div>
<h3>What is Radiostasis?</h3>
<p>Radiostasis started as an Android application back in 2017, but has since been shut down and rebooted here as a web-based Old Time Radio player. All of the episodes have been sourced from the <a href='https://archive.org' target='_blank'>Internet Archive</a>, cataloged, and made available for on-demand playing. Click the <a href='#' hx-get='/partial/series.html' hx-push-url='/series' hx-target='main' hx-swap='innerHTML show:top'>All Series</a> link in the sidebar, or select a specific genre to begin exploring the library.</p>
<p>Hello and a warm welcome to all of you, beloved radio enthusiasts and curious newcomers alike! We're delighted to have you here at Radiostasis. Our mission is to preserve and celebrate the Golden Age of Radio by offering you <em>free access</em> to a vast collection of old time radio shows that you can stream at your leisure.</p>
<p>We know how special these radio shows are to so many of you, and we're proud to provide a one-stop destination for a trip down memory lane. Our diverse library caters to all tastes, spanning from thrilling crime dramas, gripping mysteries and adventures, to side-splitting comedies and touching family sagas. Whether you're reliving cherished moments from your past or discovering the magic of Old Time Radio for the first time, you're sure to find something you love in our extensive collection.</p>
<p>We take pride in ensuring that our website is friendly and easy to navigate for users of all ages and abilities. If you require any assistance, please don't hesitate to contact our friendly customer support team, who are more than happy to help at <a href='mailto:support@radiostasis.com'>support@radiostasis.com</a>.</p>
<p>To begin your journey, simply visit the <a href='#' hx-get='/partial/series.html' hx-push-url='/series' hx-target='main' hx-swap='innerHTML show:top'>All Series</a> page, or browse by genre from the menu. Enjoy, and thank you for joining us at Radiostasis!</p>
<h3>About Radiostasis</h3>
<table>
<tr>
<td>Number of Series:</td>
<td>271</td>
</tr>
<tr>
<td>Number of Episodes:</td>
<td>29,548</td>
</tr>
<tr>
<td>Total Playtime:</td>
<td>13,551 hours</td>
</tr>
</table>
<p>Radiostasis started as an Android application back in 2017, but has since been shut down and rebooted here as a web-based Old Time Radio player. All of the episodes have been sourced from the <a href='https://archive.org' target='_blank'>Internet Archive</a>, cataloged, and made available for on-demand playing.</p>
<p>Radiostasis is an <a href='https://code.sitosis.com/rudism/radiostasis' target='_blank'>open source</a> passion project by Rudis Muiznieks, a software engineer who happens to love Old Time Radio. Radiostasis is a constant work in progress, with many improvements planned for the future. Currently the site is completely free to use&mdash;Rudis foots all the bills to host this site on his own&mdash;but premium features may be introduced in future updates to help offset some of the costs.</p>
<p>If you have any questions, suggestions, or just want to say hello, feel free to email Rudis at <a href='mailto:support@radiostasis.com'>support@radiostasis.com</a>.</p>
<p>Please send questions, bug reports, suggestions, or just say hello to Rudis at <a href='mailto:support@radiostasis.com'>support@radiostasis.com</a>.</p>
<h3>Change Log</h3>
<h4>2023-04-09</h4>
<p>Added a playlist system and the ability to queue up multiple episodes or entire series to play in order.</p>
<p>Also updated the landing page.</p>
<h4>2023-04-05</h4>
<p>Added series descriptions to the details pages. Descriptions were generated by ChatGPT as summaries of available information on Wikipedia, <a href='https://otrr.org' target='_blank'>OTRR</a> write ups on <a href='https://archive.org/details/OTRR_Home_Page' target='_blank'>archive.org</a>, the <a href='https://otrcat.com' target='_blank'>Old Time Radio Catalog</a>, and other websites.</p>
<p>Also added a search field to series list pages, and fixed some bugs that would happen when you use your browser's forward and back buttons to navigate.</p>

View file

@ -149,6 +149,12 @@ var Player = /** @class */ (function () {
_this.setErrorUI('Fetch episode error');
});
};
Player.prototype.isPlaying = function () {
if (this.howl) {
return this.howl.playing();
}
return false;
};
Player.prototype.setErrorUI = function (message) {
this.stopPlaybackAndResetUi();
this.seriesName.innerHTML = 'Error playing episode';
@ -317,8 +323,11 @@ var Playlist = /** @class */ (function () {
this.queueTab = getOrThrow(this.queueContainer.getElementsByTagName('h2').item(0));
this.queueList = getOrThrow(this.queueContainer.getElementsByTagName('ol').item(0));
this.overlay = getOrThrow(document.getElementById('overlay'));
this.clearButton = getOrThrow(document.getElementById('clear-playlist'));
// wire up global playlist controls
this.queueTab.addEventListener('click', function () { return _this.toggleQueueUI(); });
this.overlay.addEventListener('click', function () { return _this.toggleQueueUI(); });
this.clearButton.addEventListener('click', function () { return _this.clearPlaylist(); });
}
Playlist.prototype.addPlaylistChangedHandler = function (handler) {
this.changedHandlers.push(handler);
@ -379,6 +388,12 @@ var Playlist = /** @class */ (function () {
Playlist.prototype.triggerPlaylistChanged = function () {
this.playlistChanged();
};
Playlist.prototype.clearPlaylist = function () {
this.episodes.length = 0;
this.episodeHash.clear();
this.queueList.innerHTML = '';
this.playlistChanged();
};
Playlist.prototype.playlistChanged = function () {
for (var _i = 0, _a = this.changedHandlers; _i < _a.length; _i++) {
var handler = _a[_i];
@ -450,6 +465,13 @@ var Radiostasis = /** @class */ (function () {
.item(0)) === null || _a === void 0 ? void 0 : _a.addEventListener('htmx:afterSwap', function () { return _this.wireLoadedFragment(); });
// set up playlist changed handler
this.playlist.addPlaylistChangedHandler(function () { return _this.episodeStateChanged(); });
// set up prompt to prevent accidentally leaving the page while playing
window.addEventListener('beforeunload', function (e) {
if (!_this.player.isPlaying())
return undefined;
e.preventDefault();
e.returnValue = '';
});
}
Radiostasis.prototype.initialize = function () {
var path = location.pathname == '/'

View file

@ -193,11 +193,6 @@ section img {
flex: 1 1 20rem;
}
.detail article {
font-size: 1.2rem;
line-height: 120%;
}
.detail article > p:not(:last-child) {
margin-bottom: 1rem;
}
@ -234,7 +229,7 @@ aside {
}
section div aside {
font-size: 0.8em;
font-size: 0.8rem;
}
aside :first-child {
@ -292,6 +287,14 @@ ol > li:not(:last-child) {
margin: 0 1ch;
}
#queue-container > div {
display: flex;
align-items: baseline;
padding-bottom: 1ex;
margin: 1rem 1rem 0 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}
#queue-container h2 {
cursor: pointer;
position: absolute;
@ -321,9 +324,14 @@ ol > li:not(:last-child) {
font-family: Avenir, 'Avenir Next LT Pro', Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif;
font-size: 1rem;
font-weight: 600;
padding-bottom: 1ex;
margin: 1rem 1rem 0 1rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
flex-basis: 100%;
}
#queue-container > div a {
font-size: 0.8rem;
font-family: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif;
white-space: nowrap;
background-image: url('/icon-trash.svg');
}
#queue-container ol {
@ -486,15 +494,40 @@ button:disabled, button:active:disabled {
margin: 0 auto;
}
.home table {
font-family: Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif;
}
.home table td {
padding: 0.5rem;
}
.home table tr:not(:first-child) {
border-top: 1px solid rgba(0, 0, 0, 0.25);
}
.home table tr td:first-child {
text-align: right;
}
.home table tr td:last-child {
font-weight: 600;
}
p {
font-family: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif;
line-height: 150%;
font-size: 1.2rem;
line-height: 120%;
}
p strong {
font-weight: 600;
}
p em {
font-style: italic;
}
.home > * + * {
margin-block-start: 1.5rem;
}
@ -543,14 +576,14 @@ h2 svg {
.controls {
margin: 0.25rem 0;
font-size: 0.8em;
font-size: 0.8rem;
font-family: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif;
display: flex;
flex-wrap: nowrap;
gap: 0.5rem;
}
.controls a, .playing .controls a:hover {
.controls a, .playing .controls a:hover, #queue-container div a {
display: block;
padding: 0.25rem 0.5rem 0.25rem 1.25rem;
border: 1px solid black;
@ -562,7 +595,7 @@ h2 svg {
background-position: 0 50%;
}
.controls a:hover {
.controls a:hover, #queue-container div a:hover {
text-shadow: none;
color: #c50;
box-shadow: 0.125rem 0.125rem 0.25rem rgba(0, 0, 0, 0.75);
@ -617,3 +650,4 @@ h2 svg {
padding-right: 0.5rem;
}
}
}

View file

@ -150,6 +150,13 @@ class Player {
});
}
public isPlaying(): boolean {
if (this.howl) {
return this.howl.playing();
}
return false;
}
private setErrorUI(message: string): void {
this.stopPlaybackAndResetUi();
this.seriesName.innerHTML = 'Error playing episode';

View file

@ -3,6 +3,8 @@ class Playlist {
private readonly queueTab: HTMLElement;
private readonly overlay: HTMLElement;
private readonly queueList: HTMLOListElement;
private readonly clearButton: HTMLAnchorElement;
private readonly queueInitialHeight: string;
private readonly queueExpandedHeight = 'calc(100% - 6ex)';
@ -26,9 +28,14 @@ class Playlist {
this.queueContainer.getElementsByTagName('ol').item(0)
);
this.overlay = getOrThrow(document.getElementById('overlay'));
this.clearButton = getOrThrow(
<HTMLAnchorElement>document.getElementById('clear-playlist')
);
// wire up global playlist controls
this.queueTab.addEventListener('click', () => this.toggleQueueUI());
this.overlay.addEventListener('click', () => this.toggleQueueUI());
this.clearButton.addEventListener('click', () => this.clearPlaylist());
}
public addPlaylistChangedHandler(handler: () => void): void {
@ -95,6 +102,13 @@ class Playlist {
this.playlistChanged();
}
public clearPlaylist(): void {
this.episodes.length = 0;
this.episodeHash.clear();
this.queueList.innerHTML = '';
this.playlistChanged();
}
private playlistChanged(): void {
for (const handler of this.changedHandlers) {
handler();

View file

@ -26,6 +26,13 @@ class Radiostasis {
// set up playlist changed handler
this.playlist.addPlaylistChangedHandler(() => this.episodeStateChanged());
// set up prompt to prevent accidentally leaving the page while playing
window.addEventListener('beforeunload', (e) => {
if (!this.player.isPlaying()) return undefined;
e.preventDefault();
e.returnValue = '';
});
}
public initialize(): void {