Working on quality change
This commit is contained in:
parent
8d10add379
commit
e99a5ed1e3
42
index.html
42
index.html
|
@ -40,10 +40,12 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let hls;
|
||||||
|
|
||||||
app.ports.initVideo.subscribe(function(arg) {
|
app.ports.initVideo.subscribe(function(arg) {
|
||||||
const video = document.getElementById('video');
|
const video = document.getElementById('video');
|
||||||
if (Hls.isSupported()) {
|
if (Hls.isSupported()) {
|
||||||
const hls = new Hls();
|
hls = new Hls();
|
||||||
hls.loadSource(arg);
|
hls.loadSource(arg);
|
||||||
|
|
||||||
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
||||||
|
@ -54,16 +56,10 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
hls.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
|
hls.on(Hls.Events.LEVEL_SWITCHED, function (event, data) {
|
||||||
// var span = document.querySelector(".plyr__menu__container [data-plyr='quality'][value='0'] span");
|
app.ports.nowHasQuality.send({
|
||||||
// if (hls.autoLevelEnabled) {
|
auto: hls.autoLevelEnabled,
|
||||||
// span.innerHTML = "Auto (" + hls.levels[data.level].height + "p)";
|
height: hls.levels[data.level].height
|
||||||
// } 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(video);
|
hls.attachMedia(video);
|
||||||
|
@ -93,6 +89,30 @@
|
||||||
app.ports.exitFullscreen.subscribe(function() {
|
app.ports.exitFullscreen.subscribe(function() {
|
||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.ports.setPlaybackRate.subscribe(function(arg) {
|
||||||
|
const video = document.getElementById('video');
|
||||||
|
video.playbackRate = arg;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.ports.setQuality.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.nowHasQuality.send({
|
||||||
|
auto: hls.autoLevelEnabled,
|
||||||
|
height: hls.currentLevel === -1 ? 0 : hls.levels[hls.currentLevel].height
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
module Icons exposing
|
module Icons exposing (..)
|
||||||
( maximize
|
|
||||||
, minimize
|
|
||||||
, pause
|
|
||||||
, play
|
|
||||||
, playCircle
|
|
||||||
)
|
|
||||||
|
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
import Html exposing (Html)
|
import Html exposing (Html)
|
||||||
|
@ -69,3 +63,18 @@ playCircle =
|
||||||
[ Svg.circle [ cx "12", cy "12", r "10" ] []
|
[ Svg.circle [ cx "12", cy "12", r "10" ] []
|
||||||
, Svg.polygon [ points "10 8 16 12 10 16 10 8" ] []
|
, Svg.polygon [ points "10 8 16 12 10 16 10 8" ] []
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
settings : Bool -> Element msg
|
||||||
|
settings =
|
||||||
|
svgFeatherIcon "settings"
|
||||||
|
[ Svg.circle [ cx "12", cy "12", r "3" ] []
|
||||||
|
, Svg.path [ d "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" ] []
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
check : Bool -> Element msg
|
||||||
|
check =
|
||||||
|
svgFeatherIcon "check"
|
||||||
|
[ Svg.polyline [ points "20 6 9 17 4 12" ] []
|
||||||
|
]
|
||||||
|
|
218
src/Main.elm
218
src/Main.elm
|
@ -3,7 +3,7 @@ port module Main exposing (..)
|
||||||
import Browser
|
import Browser
|
||||||
import Browser.Events
|
import Browser.Events
|
||||||
import DOM as Dom
|
import DOM as Dom
|
||||||
import Element exposing (Element, alignRight, centerY, el, fill, padding, rgb255, row, spacing, text, width)
|
import Element exposing (Element)
|
||||||
import Element.Background as Background
|
import Element.Background as Background
|
||||||
import Element.Border as Border
|
import Element.Border as Border
|
||||||
import Element.Font as Font
|
import Element.Font as Font
|
||||||
|
@ -13,6 +13,7 @@ import Html.Attributes
|
||||||
import Html.Events
|
import Html.Events
|
||||||
import Icons
|
import Icons
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
|
import Quality
|
||||||
import Simple.Animation as Animation exposing (Animation)
|
import Simple.Animation as Animation exposing (Animation)
|
||||||
import Simple.Animation.Animated as Animated
|
import Simple.Animation.Animated as Animated
|
||||||
import Simple.Animation.Property as P
|
import Simple.Animation.Property as P
|
||||||
|
@ -28,6 +29,15 @@ main =
|
||||||
\_ ->
|
\_ ->
|
||||||
Sub.batch
|
Sub.batch
|
||||||
[ nowHasQualities NowHasQualities
|
[ nowHasQualities NowHasQualities
|
||||||
|
, nowHasQuality
|
||||||
|
(\x ->
|
||||||
|
case Decode.decodeValue Quality.decode x of
|
||||||
|
Ok s ->
|
||||||
|
NowHasQuality s
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Noop
|
||||||
|
)
|
||||||
, Browser.Events.onAnimationFrameDelta AnimationFrameDelta
|
, Browser.Events.onAnimationFrameDelta AnimationFrameDelta
|
||||||
, Browser.Events.onResize (\x y -> NowHasWindowSize ( x, y ))
|
, Browser.Events.onResize (\x y -> NowHasWindowSize ( x, y ))
|
||||||
]
|
]
|
||||||
|
@ -45,18 +55,32 @@ type alias Model =
|
||||||
, volume : Float
|
, volume : Float
|
||||||
, muted : Bool
|
, muted : Bool
|
||||||
, isFullscreen : Bool
|
, isFullscreen : Bool
|
||||||
|
, quality : Maybe Quality.Quality
|
||||||
, qualities : List Int
|
, qualities : List Int
|
||||||
, showBar : Bool
|
, showBar : Bool
|
||||||
, animationFrame : Float
|
, animationFrame : Float
|
||||||
, videoSize : ( Int, Int )
|
, videoSize : ( Int, Int )
|
||||||
, screenSize : ( Int, Int )
|
, screenSize : ( Int, Int )
|
||||||
|
, playbackRate : Float
|
||||||
|
, settings : Settings
|
||||||
|
, showSettings : Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Settings
|
||||||
|
= All
|
||||||
|
| Speed
|
||||||
|
| Quality
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= Noop
|
= Noop
|
||||||
| PlayPause
|
| PlayPause
|
||||||
| Seek Float
|
| Seek Float
|
||||||
|
| ToggleSettings
|
||||||
|
| SetSettings Settings
|
||||||
|
| SetPlaybackRate Float
|
||||||
|
| SetQuality Quality.Quality
|
||||||
| RequestFullscreen
|
| RequestFullscreen
|
||||||
| ExitFullscreen
|
| ExitFullscreen
|
||||||
| AnimationFrameDelta Float
|
| AnimationFrameDelta Float
|
||||||
|
@ -69,8 +93,10 @@ type Msg
|
||||||
| NowLoaded (List ( Float, Float ))
|
| NowLoaded (List ( Float, Float ))
|
||||||
| NowIsFullscreen Bool
|
| NowIsFullscreen Bool
|
||||||
| NowHasQualities (List Int)
|
| NowHasQualities (List Int)
|
||||||
|
| NowHasQuality Quality.Quality
|
||||||
| NowHasVideoSize ( Int, Int )
|
| NowHasVideoSize ( Int, Int )
|
||||||
| NowHasWindowSize ( Int, Int )
|
| NowHasWindowSize ( Int, Int )
|
||||||
|
| NowHasPlaybackRate Float
|
||||||
|
|
||||||
|
|
||||||
init : Decode.Value -> ( Model, Cmd Msg )
|
init : Decode.Value -> ( Model, Cmd Msg )
|
||||||
|
@ -97,11 +123,15 @@ init flags =
|
||||||
1.0
|
1.0
|
||||||
False
|
False
|
||||||
False
|
False
|
||||||
|
Nothing
|
||||||
[]
|
[]
|
||||||
True
|
True
|
||||||
0
|
0
|
||||||
( 0, 0 )
|
( 0, 0 )
|
||||||
( width, height )
|
( width, height )
|
||||||
|
1.0
|
||||||
|
All
|
||||||
|
False
|
||||||
, initVideo url
|
, initVideo url
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,13 +148,29 @@ update msg model =
|
||||||
Seek ratio ->
|
Seek ratio ->
|
||||||
( model, seek (ratio * model.duration) )
|
( model, seek (ratio * model.duration) )
|
||||||
|
|
||||||
|
SetPlaybackRate rate ->
|
||||||
|
( { model | showSettings = False, settings = All }, setPlaybackRate rate )
|
||||||
|
|
||||||
|
ToggleSettings ->
|
||||||
|
( { model | showSettings = not model.showSettings }, Cmd.none )
|
||||||
|
|
||||||
|
SetSettings s ->
|
||||||
|
( { model | settings = s }, Cmd.none )
|
||||||
|
|
||||||
RequestFullscreen ->
|
RequestFullscreen ->
|
||||||
( model, requestFullscreen () )
|
( model, requestFullscreen () )
|
||||||
|
|
||||||
ExitFullscreen ->
|
ExitFullscreen ->
|
||||||
( model, exitFullscreen () )
|
( model, exitFullscreen () )
|
||||||
|
|
||||||
|
SetQuality q ->
|
||||||
|
( { model | showSettings = False, settings = All }, setQuality q )
|
||||||
|
|
||||||
AnimationFrameDelta delta ->
|
AnimationFrameDelta delta ->
|
||||||
|
if model.animationFrame + delta > 3500 then
|
||||||
|
( { model | animationFrame = model.animationFrame + delta, showSettings = False, settings = All }, Cmd.none )
|
||||||
|
|
||||||
|
else
|
||||||
( { model | animationFrame = model.animationFrame + delta }, Cmd.none )
|
( { model | animationFrame = model.animationFrame + delta }, Cmd.none )
|
||||||
|
|
||||||
MouseMove ->
|
MouseMove ->
|
||||||
|
@ -154,12 +200,18 @@ update msg model =
|
||||||
NowHasQualities qualities ->
|
NowHasQualities qualities ->
|
||||||
( { model | qualities = qualities }, Cmd.none )
|
( { model | qualities = qualities }, Cmd.none )
|
||||||
|
|
||||||
|
NowHasQuality quality ->
|
||||||
|
( { model | quality = Just quality }, Cmd.none )
|
||||||
|
|
||||||
NowHasVideoSize size ->
|
NowHasVideoSize size ->
|
||||||
( { model | videoSize = size }, Cmd.none )
|
( { model | videoSize = size }, Cmd.none )
|
||||||
|
|
||||||
NowHasWindowSize size ->
|
NowHasWindowSize size ->
|
||||||
( { model | screenSize = size }, Cmd.none )
|
( { model | screenSize = size }, Cmd.none )
|
||||||
|
|
||||||
|
NowHasPlaybackRate rate ->
|
||||||
|
( { model | playbackRate = rate }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Browser.Document Msg
|
view : Model -> Browser.Document Msg
|
||||||
view model =
|
view model =
|
||||||
|
@ -220,9 +272,13 @@ video model =
|
||||||
[ Element.width Element.fill, Element.height Element.fill ]
|
[ Element.width Element.fill, Element.height Element.fill ]
|
||||||
(Element.column
|
(Element.column
|
||||||
[ Element.width Element.fill
|
[ Element.width Element.fill
|
||||||
, Element.padding 10
|
|
||||||
, Element.alignBottom
|
, Element.alignBottom
|
||||||
, Font.color (Element.rgba 1 1 1 0.85)
|
, Font.color (Element.rgba 1 1 1 0.85)
|
||||||
|
]
|
||||||
|
[ settings model
|
||||||
|
, Element.column
|
||||||
|
[ Element.width Element.fill
|
||||||
|
, Element.padding 10
|
||||||
, Background.gradient { angle = 0, steps = [ Element.rgba 0 0 0 0.75, Element.rgba 0 0 0 0 ] }
|
, Background.gradient { angle = 0, steps = [ Element.rgba 0 0 0 0.75, Element.rgba 0 0 0 0 ] }
|
||||||
]
|
]
|
||||||
[ Element.row
|
[ Element.row
|
||||||
|
@ -266,7 +322,8 @@ video model =
|
||||||
[ playPauseButton model.playing
|
[ playPauseButton model.playing
|
||||||
, Element.el [ Element.moveDown 2.5 ] (Element.text (formatTime model.position ++ " / " ++ formatTime model.duration))
|
, Element.el [ Element.moveDown 2.5 ] (Element.text (formatTime model.position ++ " / " ++ formatTime model.duration))
|
||||||
, Element.row [ Element.spacing 10, Element.alignRight ]
|
, Element.row [ Element.spacing 10, Element.alignRight ]
|
||||||
[ fullscreenButton model.isFullscreen ]
|
[ settingsButton, fullscreenButton model.isFullscreen ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -318,6 +375,134 @@ video model =
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
settings : Model -> Element Msg
|
||||||
|
settings model =
|
||||||
|
let
|
||||||
|
makeMenuButton : Settings -> Element Msg -> Element Msg -> Element Msg
|
||||||
|
makeMenuButton s key value =
|
||||||
|
Input.button [ Element.width Element.fill, Element.paddingXY 0 10 ]
|
||||||
|
{ label =
|
||||||
|
Element.row [ Element.width Element.fill, Element.spacing 20 ]
|
||||||
|
[ Element.el [ Font.bold, Element.alignLeft ] key
|
||||||
|
, Element.el [ Element.alignRight ] value
|
||||||
|
]
|
||||||
|
, onPress = Just (SetSettings s)
|
||||||
|
}
|
||||||
|
|
||||||
|
speedButton =
|
||||||
|
makeMenuButton Speed (Element.text "Speed") (Element.text ("x" ++ String.fromFloat model.playbackRate))
|
||||||
|
|
||||||
|
qualityButton =
|
||||||
|
case model.quality of
|
||||||
|
Just q ->
|
||||||
|
makeMenuButton Quality (Element.text "Quality") (Element.text (Quality.toString q))
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Element.none
|
||||||
|
|
||||||
|
returnButton =
|
||||||
|
Input.button
|
||||||
|
[ Element.width Element.fill
|
||||||
|
, Element.paddingXY 0 10
|
||||||
|
, Border.widthEach
|
||||||
|
{ bottom = 1
|
||||||
|
, top = 0
|
||||||
|
, left = 0
|
||||||
|
, right = 0
|
||||||
|
}
|
||||||
|
, Border.color (Element.rgba 0.5 0.5 0.5 0.75)
|
||||||
|
]
|
||||||
|
{ label = Element.text "Return"
|
||||||
|
, onPress = Just (SetSettings All)
|
||||||
|
}
|
||||||
|
|
||||||
|
speedOptions =
|
||||||
|
[ 0.5, 0.75, 1, 1.5, 2 ]
|
||||||
|
|> List.map
|
||||||
|
(\x ->
|
||||||
|
Input.button [ Element.width Element.fill, Element.paddingXY 0 10 ]
|
||||||
|
{ label =
|
||||||
|
Element.row [ Element.width Element.fill ]
|
||||||
|
[ if x == model.playbackRate 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 ("x" ++ String.fromFloat x))
|
||||||
|
]
|
||||||
|
, onPress = Just (SetPlaybackRate x)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> (\x -> returnButton :: x)
|
||||||
|
|
||||||
|
qualityOptions =
|
||||||
|
model.qualities
|
||||||
|
|> List.map
|
||||||
|
(\x ->
|
||||||
|
Input.button [ Element.width Element.fill, Element.paddingXY 0 10 ]
|
||||||
|
{ label =
|
||||||
|
Element.row [ Element.width Element.fill ]
|
||||||
|
[ if Quality.isSameOption (Just { auto = False, height = x }) model.quality 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 (Quality.toString { auto = False, height = x }))
|
||||||
|
]
|
||||||
|
, onPress = Just (SetQuality { auto = x == 0, height = x })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|> (\x -> returnButton :: x)
|
||||||
|
|
||||||
|
buttons =
|
||||||
|
case model.settings of
|
||||||
|
All ->
|
||||||
|
[ speedButton, qualityButton ]
|
||||||
|
|
||||||
|
Speed ->
|
||||||
|
speedOptions
|
||||||
|
|
||||||
|
Quality ->
|
||||||
|
qualityOptions
|
||||||
|
in
|
||||||
|
animatedEl
|
||||||
|
(if model.showSettings then
|
||||||
|
fadeIn
|
||||||
|
|
||||||
|
else
|
||||||
|
fadeOut
|
||||||
|
)
|
||||||
|
[ Element.padding 10
|
||||||
|
, Element.width Element.fill
|
||||||
|
, Element.height Element.fill
|
||||||
|
, Element.moveDown 20
|
||||||
|
]
|
||||||
|
(Element.column
|
||||||
|
[ Background.color (Element.rgba 0.2 0.2 0.2 0.75)
|
||||||
|
, Element.alignRight
|
||||||
|
, Element.paddingXY 20 10
|
||||||
|
, Border.rounded 10
|
||||||
|
]
|
||||||
|
buttons
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
playPauseButton : Bool -> Element Msg
|
playPauseButton : Bool -> Element Msg
|
||||||
playPauseButton playing =
|
playPauseButton playing =
|
||||||
let
|
let
|
||||||
|
@ -349,6 +534,14 @@ fullscreenButton isFullscreen =
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
settingsButton : Element Msg
|
||||||
|
settingsButton =
|
||||||
|
Input.button []
|
||||||
|
{ label = Icons.settings False
|
||||||
|
, onPress = Just ToggleSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
playerEvents : List (Element.Attribute Msg)
|
playerEvents : List (Element.Attribute Msg)
|
||||||
playerEvents =
|
playerEvents =
|
||||||
List.map Element.htmlAttribute
|
List.map Element.htmlAttribute
|
||||||
|
@ -367,6 +560,7 @@ videoEvents =
|
||||||
, Html.Events.on "volumechange" decodeVolumeChange
|
, Html.Events.on "volumechange" decodeVolumeChange
|
||||||
, Html.Events.on "progress" decodeProgress
|
, Html.Events.on "progress" decodeProgress
|
||||||
, Html.Events.on "resize" decodeVideoResize
|
, Html.Events.on "resize" decodeVideoResize
|
||||||
|
, Html.Events.on "ratechange" decodePlaybackRateChange
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -422,7 +616,7 @@ decodeTimeRanges =
|
||||||
|
|
||||||
decodeTimeRange : Decode.Decoder ( Float, Float )
|
decodeTimeRange : Decode.Decoder ( Float, Float )
|
||||||
decodeTimeRange =
|
decodeTimeRange =
|
||||||
Decode.map2 (\x y -> ( x, y ))
|
Decode.map2 Tuple.pair
|
||||||
(Decode.field "start" Decode.float)
|
(Decode.field "start" Decode.float)
|
||||||
(Decode.field "end" Decode.float)
|
(Decode.field "end" Decode.float)
|
||||||
|
|
||||||
|
@ -445,6 +639,13 @@ decodeVideoResize =
|
||||||
(Decode.field "videoHeight" Decode.int)
|
(Decode.field "videoHeight" Decode.int)
|
||||||
|
|
||||||
|
|
||||||
|
decodePlaybackRateChange : Decode.Decoder Msg
|
||||||
|
decodePlaybackRateChange =
|
||||||
|
Dom.target <|
|
||||||
|
Decode.map NowHasPlaybackRate
|
||||||
|
(Decode.field "playbackRate" Decode.float)
|
||||||
|
|
||||||
|
|
||||||
every : Float -> List ( Float, Float ) -> List ( Float, Float, Bool )
|
every : Float -> List ( Float, Float ) -> List ( Float, Float, Bool )
|
||||||
every duration input =
|
every duration input =
|
||||||
everyAux duration 0.0 [] input |> List.reverse |> List.filter (\( x, y, _ ) -> x /= y)
|
everyAux duration 0.0 [] input |> List.reverse |> List.filter (\( x, y, _ ) -> x /= y)
|
||||||
|
@ -517,9 +718,18 @@ port requestFullscreen : () -> Cmd msg
|
||||||
port exitFullscreen : () -> Cmd msg
|
port exitFullscreen : () -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port setPlaybackRate : Float -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
|
port setQuality : Quality.Quality -> Cmd msg
|
||||||
|
|
||||||
|
|
||||||
port nowHasQualities : (List Int -> msg) -> Sub msg
|
port nowHasQualities : (List Int -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
|
port nowHasQuality : (Decode.Value -> msg) -> Sub msg
|
||||||
|
|
||||||
|
|
||||||
fadeIn : Animation
|
fadeIn : Animation
|
||||||
fadeIn =
|
fadeIn =
|
||||||
Animation.fromTo
|
Animation.fromTo
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
module Quality exposing (Quality, decode, isSameOption, toString)
|
||||||
|
|
||||||
|
import Json.Decode as Decode
|
||||||
|
|
||||||
|
|
||||||
|
type alias Quality =
|
||||||
|
{ auto : Bool
|
||||||
|
, height : Int
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toString : Quality -> String
|
||||||
|
toString { auto, height } =
|
||||||
|
if height == 0 then
|
||||||
|
"Auto"
|
||||||
|
|
||||||
|
else if auto then
|
||||||
|
"Auto (" ++ String.fromInt height ++ "p)"
|
||||||
|
|
||||||
|
else
|
||||||
|
String.fromInt height ++ "p"
|
||||||
|
|
||||||
|
|
||||||
|
isSameOption : Maybe Quality -> Maybe Quality -> Bool
|
||||||
|
isSameOption quality1 quality2 =
|
||||||
|
case ( quality1, quality2 ) of
|
||||||
|
( Just q1, Just q2 ) ->
|
||||||
|
autoHeight q1 == autoHeight q2
|
||||||
|
|
||||||
|
( Nothing, Nothing ) ->
|
||||||
|
True
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
False
|
||||||
|
|
||||||
|
|
||||||
|
autoHeight : Quality -> Int
|
||||||
|
autoHeight { auto, height } =
|
||||||
|
if auto then
|
||||||
|
0
|
||||||
|
|
||||||
|
else
|
||||||
|
height
|
||||||
|
|
||||||
|
|
||||||
|
decode : Decode.Decoder Quality
|
||||||
|
decode =
|
||||||
|
Decode.map2 Quality
|
||||||
|
(Decode.field "auto" Decode.bool)
|
||||||
|
(Decode.field "height" Decode.int)
|
Loading…
Reference in New Issue