Adds support for captions, more shortcuts
This commit is contained in:
parent
3193f3c382
commit
80ef34d43b
110
index.html
110
index.html
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue