First attempt with plyr

This commit is contained in:
Thomas Forgione 2021-05-29 15:38:59 +02:00
parent df821e07af
commit 0201bcd4a9
3 changed files with 91 additions and 74 deletions

View File

@ -5,14 +5,90 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/ico" href="/favicon.ico"/> <link rel="icon" type="image/ico" href="/favicon.ico"/>
<link href="css/video-js.css" rel="stylesheet"> <link rel="stylesheet" href="https://cdn.plyr.io/3.6.7/plyr.css" />
<link href="css/spinner.css" rel="stylesheet"> <link href="css/spinner.css" rel="stylesheet">
<style>
video, .plyr, .plyr__video-wrapper {
height: 100%;
max-height: 100%;
}
</style>
</head> </head>
<body> <body>
<div id="container"></div> <div id="container"></div>
<script src="js/vd.js"></script> <script src="https://cdn.plyr.io/3.6.7/plyr.polyfilled.js"></script>
<script src="https://cdn.rawgit.com/video-dev/hls.js/18bb552/dist/hls.min.js"></script>
<script src="js/main.js"></script> <script src="js/main.js"></script>
<script> <script>
customElements.define('plyr-video',
class PlyrVideo extends HTMLElement {
constructor() {
super();
this.videoElement = document.createElement('video');
this.videoElement.setAttribute('controls', 'true');
}
static get observedAttributes() { return []; }
connectedCallback() {
this.appendChild(this.videoElement)
const hls = new Hls();
hls.loadSource(this.getAttribute('src'));
hls.on(Hls.Events.MANIFEST_PARSED, (event, data) => {
// Transform available levels into an array of integers (height values).
const availableQualities = hls.levels.map((l) => l.height);
availableQualities.unshift(0);
// Initialize here
new Plyr(this.videoElement, {
quality: {
default: 0,
options: availableQualities,
// this ensures Plyr to use Hls to update quality level
forced: true,
onChange: updateQuality,
debug: true,
},
fullscreen: {
enabled: true,
fallback: true,
},
});
});
hls.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
var span = document.querySelector(".plyr__menu__container [data-plyr='quality'][value='0'] span");
if (hls.autoLevelEnabled) {
span.innerHTML = "Auto (" + hls.levels[data.level].height + "p)";
} else {
span.innerHTML = "Auto";
}
var x = document.querySelectorAll(".plyr__menu__container [data-plyr='settings'] span")[3];
if (x.innerHTML.startsWith("Auto") || x.innerHTML === "0p") {
x.innerHTML = span.innerHTML;
}
})
hls.attachMedia(this.videoElement);
function updateQuality(newQuality) {
if (newQuality === 0) {
hls.currentLevel = -1;
} else {
hls.levels.forEach((level, levelIndex) => {
if (level.height === newQuality) {
hls.currentLevel = levelIndex;
}
});
}
}
}
}
);
function isDarkMode(e) { function isDarkMode(e) {
var darkMode = JSON.parse(localStorage.getItem('darkMode')); var darkMode = JSON.parse(localStorage.getItem('darkMode'));
@ -37,30 +113,9 @@
} }
}); });
var lastId, player; var lastId;
if (app.ports !== undefined) { if (app.ports !== undefined) {
if (app.ports.registerVideo !== undefined) {
app.ports.registerVideo.subscribe(function(args) {
window.scrollTo(0, 0);
var time = vd.parseTime(args[2]) || undefined;
requestAnimationFrame(function() {
if (args[0] !== lastId) {
lastId = args[0];
player = vd.setup(args[0], {
v: args[1] + "/manifest.m3u8",
t: time,
focus: true
});
} else if (time !== undefined ){
player.currentTime(time);
}
});
});
}
if (app.ports.setDarkMode !== undefined) { if (app.ports.setDarkMode !== undefined) {
app.ports.setDarkMode.subscribe(function(arg) { app.ports.setDarkMode.subscribe(function(arg) {
if (arg === null) { if (arg === null) {
@ -73,19 +128,9 @@
} }
} }
if (app.ports !== undefined) { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
if (app.ports.eraseVideo !== undefined) { app.ports.darkMode.send(isDarkMode(e));
app.ports.eraseVideo.subscribe(function() { });
window.scrollTo(0, 0);
lastId = undefined;
});
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
app.ports.darkMode.send(isDarkMode(e));
});
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -231,28 +231,18 @@ update msg model =
_ -> _ ->
Nothing Nothing
( page, cmd ) = page =
case ( playlist, video ) of case ( playlist, video ) of
( Just p, Just v ) -> ( Just p, Just v ) ->
( Video p v Nothing Video p v Nothing
, Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time )
)
( Just p, Nothing ) -> ( Just p, Nothing ) ->
( Playlist p Nothing, Cmd.none ) Playlist p Nothing
_ -> _ ->
( Home Nothing, Cmd.none ) Home Nothing
extraCmd =
case page of
Video _ _ _ ->
Cmd.none
_ ->
Ports.eraseVideo ()
in in
( { model | page = page }, Cmd.batch [ cmd, extraCmd ] ) ( { model | page = page }, Cmd.none )
UrlRequested u -> UrlRequested u ->
case u of case u of

View File

@ -381,28 +381,10 @@ videoView darkMode device zone currentDate time hover playlist video =
, Element.alignTop , Element.alignTop
, Element.height Element.fill , Element.height Element.fill
] ]
[ Keyed.el [ Element.html
[ Element.width Element.fill (Html.node "plyr-video"
, Element.height (Element.px 0) [ Html.Attributes.attribute "src" ("videos/" ++ playlist.url ++ video.url ++ "manifest.m3u8") ]
, Element.htmlAttribute (Html.Attributes.style "padding-top" "56.25%") []
, Element.htmlAttribute (Html.Attributes.style "position" "relative")
]
( video.url
, Element.html
(Html.video
[ Html.Attributes.id (Twitch.videoId video)
, Html.Attributes.class "video-js"
, Html.Attributes.class "vjs-default-skin"
, Html.Attributes.class "wf"
, Html.Attributes.property "data-setup" (Encode.string "{\"fluid\": true}")
, Html.Attributes.style "position" "absolute"
, Html.Attributes.style "top" "0"
, Html.Attributes.style "height" "100%"
, Html.Attributes.controls True
, Html.Attributes.autoplay True
]
[]
)
) )
, Element.paragraph , Element.paragraph
[ Font.size Consts.homeFontSize [ Font.size Consts.homeFontSize