Adds support for captions, more shortcuts

This commit is contained in:
Thomas Forgione 2021-06-14 16:33:30 +02:00
parent 3193f3c382
commit 80ef34d43b
4 changed files with 194 additions and 107 deletions

View File

@ -14,112 +14,14 @@
<div id="container"></div>
<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/ports.js"></script>
<script>
Object.defineProperty(TimeRanges.prototype, "asArray", {
get: function() {
var ret = [];
for (var i = 0; i < this.length; i++) {
ret.push({start: this.start(i), end: this.end(i)});
}
return ret;
}
});
Object.defineProperty(HTMLElement.prototype, "document", {
get: function() {
return document;
}
});
const app = Elm.Main.init({
node: document.getElementById('container'),
flags: {
url: "video/manifest.m3u8",
width: window.innerWidth,
height: window.innerHeight
}
});
let hls;
app.ports.polymnyVideoInit.subscribe(function(arg) {
const video = document.getElementById('video');
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(arg);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
// Transform available levels into an array of integers (height values).
const availableQualities = hls.levels.map((l) => l.height);
availableQualities.unshift(0);
app.ports.polymnyVideoNowHasQualities.send(availableQualities);
});
hls.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
app.ports.polymnyVideoNowHasQuality.send({
auto: hls.autoLevelEnabled,
height: hls.levels[data.level].height
});
})
hls.attachMedia(video);
} else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = arg;
}
});
app.ports.polymnyVideoPlayPause.subscribe(function() {
const video = document.getElementById('video');
if (video.paused) {
video.play();
} else {
video.pause();
}
});
app.ports.polymnyVideoSeek.subscribe(function(arg) {
const video = document.getElementById('video');
console.log(arg);
video.currentTime = arg;
});
app.ports.polymnyVideoRequestFullscreen.subscribe(function() {
document.getElementById('full').requestFullscreen();
});
app.ports.polymnyVideoExitFullscreen.subscribe(function() {
document.exitFullscreen();
});
app.ports.polymnyVideoSetPlaybackRate.subscribe(function(arg) {
const video = document.getElementById('video');
video.playbackRate = arg;
});
app.ports.polymnyVideoSetQuality.subscribe(function(arg) {
var old = hls.currentLevel;
if (arg.auto) {
hls.currentLevel = -1;
} else {
hls.levels.forEach((level, levelIndex) => {
if (level.height === arg.height) {
hls.currentLevel = levelIndex;
}
});
}
if (old === hls.currentLevel) {
app.ports.polymnyVideoNowHasQuality.send({
auto: hls.autoLevelEnabled,
height: hls.currentLevel === -1 ? 0 : hls.levels[hls.currentLevel].height
});
}
});
app.ports.polymnyVideoSetVolume.subscribe(function(arg) {
const video = document.getElementById('video');
video.volume = arg.volume;
video.muted = arg.muted;
embed({
node: document.getElementById("container"),
url: "video/manifest.m3u8",
});
</script>
<script>
</script>
</body>
</html>

View File

@ -21,6 +21,24 @@ subs model =
Ok s ->
Video.NowHasQuality s
_ ->
Video.Noop
)
, Video.nowHasSubtitles
(\x ->
case Decode.decodeValue decodeSubtitles x of
Ok s ->
Video.NowHasSubtitles s
_ ->
Video.Noop
)
, Video.nowHasSubtitleTrack
(\x ->
case Decode.decodeValue decodeMaybeSubtitleTrack x of
Ok t ->
Video.NowHasSubtitleTrack t
_ ->
Video.Noop
)
@ -185,6 +203,67 @@ decodeKeyDown model =
Video.RequestFullscreen
)
-- 0 key
48 ->
Decode.succeed (Video.Seek 0)
-- 1 key
49 ->
Decode.succeed (Video.Seek (0.1 * model.duration))
-- 2 key
50 ->
Decode.succeed (Video.Seek (0.2 * model.duration))
-- 3 key
51 ->
Decode.succeed (Video.Seek (0.3 * model.duration))
-- 4 key
52 ->
Decode.succeed (Video.Seek (0.4 * model.duration))
-- 5 key
53 ->
Decode.succeed (Video.Seek (0.5 * model.duration))
-- 6 key
54 ->
Decode.succeed (Video.Seek (0.6 * model.duration))
-- 7 key
55 ->
Decode.succeed (Video.Seek (0.7 * model.duration))
-- 8 key
56 ->
Decode.succeed (Video.Seek (0.8 * model.duration))
-- 9 key
57 ->
Decode.succeed (Video.Seek (0.9 * model.duration))
_ ->
Decode.fail ("no shortcut for code " ++ String.fromInt x)
)
decodeSubtitleTrack : Decode.Decoder Video.SubtitleTrack
decodeSubtitleTrack =
Decode.map6 Video.SubtitleTrack
(Decode.field "name" Decode.string)
(Decode.field "groupId" Decode.string)
(Decode.field "type" Decode.string)
(Decode.field "autoselect" Decode.bool)
(Decode.field "default" Decode.bool)
(Decode.field "forced" Decode.bool)
decodeMaybeSubtitleTrack : Decode.Decoder (Maybe Video.SubtitleTrack)
decodeMaybeSubtitleTrack =
Decode.nullable decodeSubtitleTrack
decodeSubtitles : Decode.Decoder (List Video.SubtitleTrack)
decodeSubtitles =
Decode.list decodeSubtitleTrack

View File

@ -1,4 +1,16 @@
port module Video exposing (Msg(..), Settings(..), Video, fromUrl, init, nowHasQualities, nowHasQuality, update)
port module Video exposing
( Msg(..)
, Settings(..)
, SubtitleTrack
, Video
, fromUrl
, init
, nowHasQualities
, nowHasQuality
, nowHasSubtitleTrack
, nowHasSubtitles
, update
)
import Json.Decode as Decode
import Quality exposing (Quality)
@ -21,6 +33,8 @@ type alias Video =
, playbackRate : Float
, settings : Settings
, showSettings : Bool
, subtitles : List SubtitleTrack
, subtitleTrack : Maybe SubtitleTrack
}
@ -42,6 +56,8 @@ fromUrl url =
, playbackRate = 1
, settings = All
, showSettings = False
, subtitles = []
, subtitleTrack = Nothing
}
@ -49,6 +65,17 @@ type Settings
= All
| Speed
| Quality
| Subtitles
type alias SubtitleTrack =
{ name : String
, groupdId : String
, ty : String
, autoselect : Bool
, default : Bool
, forced : Bool
}
type Msg
@ -59,6 +86,7 @@ type Msg
| SetSettings Settings
| SetPlaybackRate Float
| SetQuality Quality.Quality
| SetSubtitleTrack Int
| SetVolume Float Bool
| RequestFullscreen
| ExitFullscreen
@ -75,6 +103,8 @@ type Msg
| NowHasQuality Quality.Quality
| NowHasSize ( Int, Int )
| NowHasPlaybackRate Float
| NowHasSubtitles (List SubtitleTrack)
| NowHasSubtitleTrack (Maybe SubtitleTrack)
update : Msg -> Video -> ( Video, Cmd Msg )
@ -107,6 +137,9 @@ update msg model =
SetQuality q ->
( { model | showSettings = False, settings = All }, setQuality q )
SetSubtitleTrack t ->
( { model | showSettings = False, settings = All }, setSubtitleTrack t )
SetVolume v m ->
( model, setVolume { volume = v, muted = m } )
@ -153,6 +186,12 @@ update msg model =
NowHasPlaybackRate rate ->
( { model | playbackRate = rate }, Cmd.none )
NowHasSubtitles tracks ->
( { model | subtitles = tracks }, Cmd.none )
NowHasSubtitleTrack track ->
( { model | subtitleTrack = track }, Cmd.none )
port polymnyVideoInit : String -> Cmd msg
@ -210,6 +249,14 @@ setQuality =
polymnyVideoSetQuality
port polymnyVideoSetSubtitleTrack : Int -> Cmd msg
setSubtitleTrack : Int -> Cmd msg
setSubtitleTrack =
polymnyVideoSetSubtitleTrack
port polymnyVideoSetVolume : { volume : Float, muted : Bool } -> Cmd msg
@ -232,3 +279,19 @@ port polymnyVideoNowHasQuality : (Decode.Value -> msg) -> Sub msg
nowHasQuality : (Decode.Value -> msg) -> Sub msg
nowHasQuality =
polymnyVideoNowHasQuality
port polymnyVideoNowHasSubtitles : (Decode.Value -> msg) -> Sub msg
nowHasSubtitles : (Decode.Value -> msg) -> Sub msg
nowHasSubtitles =
polymnyVideoNowHasSubtitles
port polymnyVideoNowHasSubtitleTrack : (Decode.Value -> msg) -> Sub msg
nowHasSubtitleTrack : (Decode.Value -> msg) -> Sub msg
nowHasSubtitleTrack =
polymnyVideoNowHasSubtitleTrack

View File

@ -65,7 +65,7 @@ embed screenSize model =
else
fadeOut
)
[ Element.width Element.fill, Element.height Element.fill ]
[ Element.width Element.fill, Element.alignBottom ]
(Element.column
[ Element.width Element.fill
, Element.alignBottom
@ -197,6 +197,17 @@ settings model =
_ ->
Element.none
subtitlesButton =
case ( model.subtitleTrack, model.subtitles ) of
( Just t, _ :: _ ) ->
makeMenuButton Video.Subtitles (Element.text "Subtitles") (Element.text t.name)
( _, _ :: _ ) ->
makeMenuButton Video.Subtitles (Element.text "Subtitles") (Element.text "Disabled")
_ ->
Element.none
returnButton =
Input.button
[ Element.width Element.fill
@ -267,16 +278,48 @@ settings model =
)
|> (\x -> returnButton :: x)
subtitleOptions =
model.subtitles
|> List.indexedMap (\i x -> ( i, Just x ))
|> (\x -> ( -1, Nothing ) :: x)
|> List.map
(\( i, x ) ->
Input.button [ Element.width Element.fill, Element.paddingXY 0 10 ]
{ label =
Element.row [ Element.width Element.fill ]
[ if Maybe.map .name model.subtitleTrack == Maybe.map .name x then
Icons.check False
else
Element.el [ Font.color (Element.rgba 0 0 0 0) ] (Icons.check False)
, Element.el
[ Element.paddingEach
{ left = 10
, right = 0
, top = 0
, bottom = 0
}
]
(Element.text (Maybe.withDefault "Disabled" (Maybe.map .name x)))
]
, onPress = Just (Video.SetSubtitleTrack i)
}
)
|> (\x -> returnButton :: x)
buttons =
case model.settings of
Video.All ->
[ speedButton, qualityButton ]
[ speedButton, qualityButton, subtitlesButton ]
Video.Speed ->
speedOptions
Video.Quality ->
qualityOptions
Video.Subtitles ->
subtitleOptions
in
animatedEl
(if model.showSettings then