switch over to MediaElement.js so we can add skip-30-second buttons

This commit is contained in:
2021-10-18 23:08:58 -07:00
parent cf9988c367
commit 4642c685fa
7 changed files with 156 additions and 70 deletions

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "getid3"] [submodule "getid3"]
path = getid3 path = getid3
url = https://github.com/JamesHeinrich/getID3 url = https://github.com/JamesHeinrich/getID3
[submodule "mediaelement-plugins"]
path = mediaelement-plugins
url = https://github.com/mediaelement/mediaelement-plugins

10
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9000
}
]
}

View File

@@ -7,4 +7,5 @@ ENV CHANNELS "${CHANNELS:-1}"
ENV BITRATE "${BITRATE:-32}" ENV BITRATE "${BITRATE:-32}"
RUN (cd /var/www/html && curl -L https://github.com/JamesHeinrich/getID3/archive/refs/tags/v1.9.21.tar.gz | tar xzf - && mv getID3-* getid3) RUN (cd /var/www/html && curl -L https://github.com/JamesHeinrich/getID3/archive/refs/tags/v1.9.21.tar.gz | tar xzf - && mv getID3-* getid3)
RUN (cd /var/www/html && curl -L https://github.com/mediaelement/mediaelement/archive/refs/tags/5.0.1.tar.gz | tar xzf - && mv mediaelement-* mediaelement) RUN (cd /var/www/html && curl -L https://github.com/mediaelement/mediaelement/archive/refs/tags/5.0.1.tar.gz | tar xzf - && mv mediaelement-* mediaelement)
RUN (cd /var/www/html && curl -L https://github.com/mediaelement/mediaelement-plugins/archive/refs/tags/2.6.1.tar.gz | tar xzf - && mv mediaelement-plugins-* mediaelement-plugins)
VOLUME /var/www/html/ VOLUME /var/www/html/

View File

@@ -3,6 +3,8 @@ StreamShifter
Capture streaming audio from your favorite sources to build a custom podcast stream. I used this for a number of years on a bog-standard Linux/Nginx/PHP-FPM server, and have recently containerized it for use with Docker. It'll record live audio captured from a URL, transcoding it into a common format along the way. You can listen to the most recent stream as it records with HTTP live streaming, or you can listen later to the podcast. Capture streaming audio from your favorite sources to build a custom podcast stream. I used this for a number of years on a bog-standard Linux/Nginx/PHP-FPM server, and have recently containerized it for use with Docker. It'll record live audio captured from a URL, transcoding it into a common format along the way. You can listen to the most recent stream as it records with HTTP live streaming, or you can listen later to the podcast.
StreamShifter actually uses MediaElement.js now...it wasn't previously. Buttons to skip forward and back 30 seconds have been added to the player, which are convenient for skipping past commercials.
Standalone Instructions Standalone Instructions
----------------------- -----------------------

141
index.php
View File

@@ -1,78 +1,79 @@
<html> <!DOCTYPE html>
<html lang="en">
<head> <head>
<title>Live Audio and Podcasts</title> <title>Live Audio and Podcasts</title>
<meta name="viewport" content="width=320"> <meta charset="UTF-8">
<script src="mediaelement/build/jquery.js"></script> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<script src="mediaelement/build/mediaelement-and-player.min.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<link rel="stylesheet" href="mediaelement/build/mediaelementplayer.min.css" />
<script type="text/javascript">
function LoadAudio(file)
{
document.getElementById("player").style.visibility="visible";
document.getElementById("video").style.visibility="hidden";
document.getElementById("player").innerHTML="<audio src=\"audio/"+file+"\" type=\"audio/m4a\" controls=\"controls\" width=\"500px\" />"; <link rel="stylesheet" href="mediaelement/build/mediaelementplayer.css">
document.getElementById("filename").innerHTML=file; <link rel="stylesheet" href="mediaelement-plugins/dist/jump-forward/jump-forward.css">
$('audio').mediaelementplayer(); <link rel="stylesheet" href="mediaelement-plugins/dist/skip-back/skip-back.css">
}
function LoadM3U8(file)
{
document.getElementById("player").style.visibility="hidden";
document.getElementById("video").style.visibility="visible";
var video=document.getElementById("video");
if (Hls.isSupported())
{
var hls=new Hls();
hls.loadSource(file);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function() {video.play();});
}
else if (video.canPlayType("application/vnd.apple.mpegurl"))
{
video.src=file;
video.addEventListener("loadedmetadata", function() {video.play();});
}
document.getElementById("filename").innerHTML=file;
}
</script>
</head> </head>
<body> <body>
<h1>Live Audio and Podcasts</h1> <h1>Live Audio and Podcasts</h1>
<ul> <ul>
<?php <?php
$arr=[]; $arr=[];
$c=0; $c=0;
if ($h=opendir("audio")) if ($h=opendir("audio"))
{
while (($e=readdir($h))!==false)
{ {
if (strlen($e)>4) while (($e=readdir($h))!==false)
if (substr_compare($e,".m4a",-4,4)===0) {
$arr[$c++]="<li><a href=\"audio/".$e."\">".$e."</a> (<a href=\"#\" onclick=\"LoadAudio('".$e."')\">Load in Player</a>)</li>\n"; if (strlen($e)>4)
if (substr_compare($e,".m4a",-4,4)===0)
$arr[$c++]="<li><a href=\"audio/".$e."\">".$e."</a> (<a href=\"#\" onclick=\"Load('audio/".$e."', 'audio/m4a')\">Load in Player</a>)</li>\n";
}
closedir($h);
} }
closedir($h); if ($h=opendir("."))
}
if ($h=opendir("."))
{
while (($e=readdir($h))!==false)
{ {
if (strlen($e)>5) while (($e=readdir($h))!==false)
if (substr_compare($e,".m3u8",-5,5)===0) {
$arr[$c++]="<li><a href=\"".$e."\">".$e."</a> (<a href=\"#\" onclick=\"LoadM3U8('".$e."')\">Load in Player</a>)</li>\n"; if (strlen($e)>5)
if (substr_compare($e,".m3u8",-5,5)===0)
$arr[$c++]="<li><a href=\"".$e."\">".$e."</a> (<a href=\"#\" onclick=\"Load('".$e."', 'application/x-mpegURL')\">Load in Player</a>)</li>\n";
}
closedir($h);
} }
closedir($h); sort($arr);
} foreach ($arr as $item)
sort($arr); echo $item;
foreach ($arr as $item) ?>
echo $item; <li><a href="rss.php">RSS Feed</a></li>
?> </ul>
<li><a href="rss.php">RSS Feed</a></li> <br />
</ul> <div class="media-wrapper">
<p> <audio id="player1" width="400" height="45" controls preload="none"></audio>
<span id="player" style="visibility: hidden;"></span> </div>
<video id="video" controls autoplay style="width: 500px; height: 50px; visibility: hidden;"></video>
<br /><span id="filename"></span></p> <br /><span id="filename"></span>
</body></html>
<script src="mediaelement/build/mediaelement-and-player.min.js"></script>
<script src="mediaelement-plugins/dist/jump-forward/jump-forward.js"></script>
<script src="mediaelement-plugins/dist/skip-back/skip-back.js"></script>
<script>
var mediaElements = document.querySelectorAll('video, audio');
for (var i = 0, total = mediaElements.length; i < total; i++) {
var features = ['playpause', 'current', 'skipback', 'progress', 'jumpforward', 'duration'];
new MediaElementPlayer(mediaElements[i], {
autoRewind: false,
features: features,
});
}
function Load(src, mime_type)
{
document.getElementById("player1").src=src;
document.getElementById("player1").type=mime_type;
document.getElementById("player1").load();
document.getElementById("player1").play();
document.getElementById("filename").innerHTML=src;
}
</script>
</body>
</html>

1
mediaelement-plugins Submodule

Submodule mediaelement-plugins added at 9651e3f626

68
mejs-controls.svg Normal file
View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="icon-captions" viewBox="0 0 18 14" xmlns="http://www.w3.org/2000/svg">
<g fill="#fff">
<path d="M-.67-4.08h14a2,2,0,0,1,2,2v10a2,2,0,0,1-2,2h-14a2,2,0,0,1-2-2v-10A2,2,0,0,1-.67-4.08Z" transform="translate(2.67 4.08)"/>
</g>
<g fill="#000">
<path d="M2.88,6.67a2.81,2.81,0,0,1-2.1-1A3.91,3.91,0,0,1,.88.87c.5-.6,2-1.7,4.6.2l-.6.8c-1.4-1-2.6-1.1-3.3-.3a3,3,0,0,0-.1,3.5c.7.9,1.9.8,3.4-.1l.5.9a5.28,5.28,0,0,1-2.5.8Zm7.5,0a2.81,2.81,0,0,1-2.1-1,3.91,3.91,0,0,1,.1-4.8c.5-.6,2-1.7,4.6.2l-.5.8c-1.4-1-2.6-1.1-3.3-.3a3,3,0,0,0-.1,3.5c.7.9,1.9.8,3.4-.1l.5.9a6.07,6.07,0,0,1-2.6.8Z" transform="translate(2.67 4.08)"/>
</g>
</symbol>
<symbol id="icon-chapters" viewBox="0 0 16.6 13" xmlns="http://www.w3.org/2000/svg"><path d="M1.5,0A1.54,1.54,0,0,1,3,1.5,1.54,1.54,0,0,1,1.5,3,1.54,1.54,0,0,1,0,1.5,1.47,1.47,0,0,1,1.5,0ZM6.6,0h8.5a1.47,1.47,0,0,1,1.5,1.5A1.54,1.54,0,0,1,15.1,3H6.6A1.47,1.47,0,0,1,5.1,1.5,1.37,1.37,0,0,1,6.6,0ZM1.5,5A1.54,1.54,0,0,1,3,6.5,1.54,1.54,0,0,1,1.5,8,1.54,1.54,0,0,1,0,6.5,1.47,1.47,0,0,1,1.5,5ZM6.6,5h8.5a1.47,1.47,0,0,1,1.5,1.5A1.54,1.54,0,0,1,15.1,8H6.6A1.47,1.47,0,0,1,5.1,6.5,1.37,1.37,0,0,1,6.6,5ZM1.5,10a1.5,1.5,0,0,1,0,3A1.54,1.54,0,0,1,0,11.5,1.47,1.47,0,0,1,1.5,10Zm5.1,0h8.5a1.47,1.47,0,0,1,1.5,1.5A1.54,1.54,0,0,1,15.1,13H6.6a1.47,1.47,0,0,1-1.5-1.5A1.37,1.37,0,0,1,6.6,10Z"/></symbol>
<symbol id="icon-fullscreen" viewBox="0 0 17.8 17.8" xmlns="http://www.w3.org/2000/svg"><path d="M0,1A.94.94,0,0,1,1,0H6.4c.6,0,.7.3.3.7l-6,6C.3,7.1,0,7,0,6.4ZM0,16.8a.94.94,0,0,0,1,1H6.4c.6,0,.7-.3.3-.7l-6-6c-.4-.4-.7-.3-.7.3ZM17.8,1a.94.94,0,0,0-1-1H11.4c-.6,0-.7.3-.3.7l6,6c.4.4.7.3.7-.3Zm0,15.8a.94.94,0,0,1-1,1H11.4c-.6,0-.7-.3-.3-.7l6-6c.4-.4.7-.3.7.3Z"/></symbol>
<symbol id="icon-unfullscreen" viewBox="0 0 17.8 17.8" xmlns="http://www.w3.org/2000/svg"><path d="M11.74,4.64a.94.94,0,0,0,1,1h4.1c.6,0,.7-.3.3-.7L12.44.24c-.4-.4-.7-.3-.7.3Zm-7.1,1a.94.94,0,0,0,1-1V.54c0-.6-.3-.7-.7-.3L.24,4.94c-.4.4-.3.7.3.7Zm1,7.1a.94.94,0,0,0-1-1H.54c-.6,0-.7.3-.3.7l4.7,4.7c.4.4.7.3.7-.3Zm7.1-1a.94.94,0,0,0-1,1v4.1c0,.5.3.7.7.3l4.7-4.7c.4-.4.3-.7-.3-.7Z"/></symbol>
<symbol id="icon-mute" viewBox="0 0 17.55 17.03" xmlns="http://www.w3.org/2000/svg">
<g stroke="none">
<path d="M6.21,4.36a3,3,0,0,1-1.8.6H1.21a.94.94,0,0,0-1,1v5.7a.94.94,0,0,0,1,1h4.2c.3.2.5.4.8.6l3.5,2.6a.47.47,0,0,0,.8-.4V2.06a.47.47,0,0,0-.8-.4Z"/>
</g>
<g fill="none" stroke-linecap="round" stroke-width="1.5px">
<path d="M13.11,1.18S17,.38,17,8.88s-3.9,7.8-3.9,7.8"/>
<path d="M11.81,5.08s2.6-.4,2.6,3.8-2.6,3.9-2.6,3.9"/>
</g>
</symbol>
<symbol id="icon-unmute" viewBox="0 0 18.2 17" xmlns="http://www.w3.org/2000/svg">
<g stroke="none">
<path d="M6.21,4.36a3,3,0,0,1-1.8.6H1.21a.94.94,0,0,0-1,1v5.7a.94.94,0,0,0,1,1h4.2c.3.2.5.4.8.6l3.5,2.6a.47.47,0,0,0,.8-.4V2.06a.47.47,0,0,0-.8-.4Z"/>
</g>
<g fill="none" stroke-linecap="round" stroke-width="2px">
<path d="M12,5.55l5.4,5.4M12,11l5.4-5.4"/>
</g>
</symbol>
<symbol id="icon-play" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M16.5 8.5c.3.1.4.5.2.8-.1.1-.1.2-.2.2l-11.4 7c-.5.3-.8.1-.8-.5V2c0-.5.4-.8.8-.5l11.4 7z"/></symbol>
<symbol id="icon-pause" viewBox="0 0 14 16" xmlns="http://www.w3.org/2000/svg"><path d="M1,0H3.2a.94.94,0,0,1,1,1V15a.94.94,0,0,1-1,1H1a.94.94,0,0,1-1-1V1A1,1,0,0,1,1,0Zm9.8,0H13a.94.94,0,0,1,1,1V15a.94.94,0,0,1-1,1H10.8a.94.94,0,0,1-1-1V1A1,1,0,0,1,10.8,0Z"/></symbol>
<symbol id="icon-replay" viewBox="0 0 17.52 15.97" xmlns="http://www.w3.org/2000/svg"><path d="M16.7,7.27a.81.81,0,0,1-.9.7c-.1,0-.2,0-.2-.1l-4.9-1.8c-.5-.2-.6-.6-.1-.8l6.2-3.6c.5-.3.8-.1.7.5l-.8,5.1Z"/><path d="M8.1,13.77a5.92,5.92,0,0,1-2.9-.7A5.77,5.77,0,0,1,2,7.87a6.21,6.21,0,0,1,6.3-6A6.15,6.15,0,0,1,13.9,6l.1-.1L16.1,7A8.1,8.1,0,0,0,7,.07,8.24,8.24,0,0,0,0,8a8.06,8.06,0,0,0,4.3,7,10.21,10.21,0,0,0,3.8,1,8.24,8.24,0,0,0,6.6-3.3l-1.8-.9a7,7,0,0,1-4.8,2Z"/></symbol>
<symbol id="icon-overlay-play" viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg">
<g fill="#333" stroke="#fff" stroke-width="5px">
<path d="M2.5,40A37.5,37.5,0,1,1,40,77.5,37.51,37.51,0,0,1,2.5,40Z"/>
</g>
<g fill="#fff">
<path d="M60.3,38a1,1,0,0,1,.6,1.4.9.9,0,0,1-.6.6L30,57.5c-1,.6-1.7.1-1.7-1v-35c0-1.1.8-1.5,1.7-1Z"/>
</g>
</symbol>
<symbol id="icon-loading-spinner" viewBox="0 0 75.8 77.9" xmlns="http://www.w3.org/2000/svg">
<g fill="#fff">
<circle cx="38.8" cy="8.1" r="8.1"/>
<g opacity="0.4">
<circle cx="70.8" cy="40" r="5"/>
</g>
<g opacity="0.6">
<circle cx="38.8" cy="71.9" r="6"/>
</g>
<g opacity="0.8">
<circle cx="7" cy="40" r="7"/>
</g>
<g opacity="0.9">
<circle cx="15.1" cy="17.3" r="7.5"/>
</g>
<g opacity="0.3">
<circle cx="63.2" cy="17.1" r="4.5"/>
</g>
<g opacity="0.5">
<circle cx="62.7" cy="63.8" r="5.5"/>
</g>
<g opacity="0.7">
<circle cx="15.1" cy="63.8" r="6.5"/>
</g>
</g>
</symbol>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB