play around with "reusable modules"
parent
7b1ee76d48
commit
b8e3633ae9
1
Makefile
1
Makefile
|
@ -14,6 +14,7 @@ prod-build:
|
|||
upload: prod-build
|
||||
mc cp public/index.html $(REMOTE)/$(BUCKET)/$(BASE_PATH)/index.html
|
||||
mc cp public/application.js $(REMOTE)/$(BUCKET)/$(BASE_PATH)/application.js
|
||||
mc cp public/application.css $(REMOTE)/$(BUCKET)/$(BASE_PATH)/application.css
|
||||
|
||||
.PHONY: gen-videos-json
|
||||
gen-videos-json:
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,75 @@
|
|||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #161616;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
video {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
select,
|
||||
footer,
|
||||
.downloadList {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: #333;
|
||||
color: #ccc;
|
||||
border: 1px solid #555;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
footer > a {
|
||||
font-size: 0.8em;
|
||||
margin-left: 1em;
|
||||
padding: 0.1em 0.8em;
|
||||
background-color: #60b5cc;
|
||||
color: #333;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer > a:hover {
|
||||
background-color: #cc60b6;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.downloadList:before {
|
||||
content: "Direct Download";
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.downloadList {
|
||||
margin-top: 2em;
|
||||
width: 500px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.downloadList > a {
|
||||
color: #60b5cc;
|
||||
display: list-item;
|
||||
margin-top: 0.2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.downloadList > a:hover {
|
||||
color: #ccc;
|
||||
}
|
|
@ -3,83 +3,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Minimalistic WebPlayer</title>
|
||||
<style>
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #161616;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
video {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
select,
|
||||
footer,
|
||||
.downloadList {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
width: 960px;
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: #333;
|
||||
color: #ccc;
|
||||
border: 1px solid #555;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
footer > a {
|
||||
font-size: 0.8em;
|
||||
margin-left: 1em;
|
||||
padding: 0.1em 0.8em;
|
||||
background-color: #60b5cc;
|
||||
color: #333;
|
||||
border-radius: 3px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
footer > a:hover {
|
||||
background-color: #cc60b6;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.downloadList:before {
|
||||
content: "Direct Download";
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
margin-bottom: 1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.downloadList {
|
||||
margin-top: 2em;
|
||||
width: 500px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.downloadList > a {
|
||||
color: #60b5cc;
|
||||
display: list-item;
|
||||
margin-top: 0.2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.downloadList > a:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="application.css">
|
||||
<script type="text/javascript" src="application.js"></script>
|
||||
</head>
|
||||
|
||||
|
@ -93,22 +17,19 @@
|
|||
<script>
|
||||
const app = Elm.Main.init({
|
||||
node: document.getElementById('elm'),
|
||||
flags: {
|
||||
debug: true,
|
||||
extentions:
|
||||
[ { extention: "mkv", mime: "video/x-matroska" }
|
||||
, { extention: "mp4", mime: "video/mp4" }
|
||||
]
|
||||
}
|
||||
flags:
|
||||
{ debug: true
|
||||
, withDownload: true
|
||||
, extentions:
|
||||
[ { extention: "mkv", mime: "video/x-matroska" }
|
||||
, { extention: "mp4", mime: "video/mp4" }
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
app.ports.reloadVideoSource.subscribe(function() {
|
||||
document.querySelector("video").load();
|
||||
});
|
||||
|
||||
app.ports.reloadVideoSource = function(a) {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
module Dropdown exposing (..)
|
||||
|
||||
import Html
|
||||
import Html.Attributes
|
||||
import Html.Events as Events
|
||||
|
||||
type alias Option =
|
||||
{ value : String
|
||||
, display : String
|
||||
}
|
||||
|
||||
type alias Config msg =
|
||||
{ options : List Option
|
||||
, default : Option
|
||||
, selectEvent : String -> msg
|
||||
}
|
||||
|
||||
view : Config msg -> Html.Html msg
|
||||
view config = Html.select [ Events.onInput config.selectEvent ] <|
|
||||
makeOption config.default
|
||||
:: List.map makeOption config.options
|
||||
|
||||
makeOption : Option -> Html.Html msg
|
||||
makeOption option =
|
||||
Html.option
|
||||
[ Html.Attributes.value option.value ]
|
||||
[ Html.text option.display ]
|
|
@ -0,0 +1,19 @@
|
|||
module Flags exposing (..)
|
||||
|
||||
import Video
|
||||
|
||||
type alias Flags =
|
||||
{ debug : Bool
|
||||
, withDownload : Bool
|
||||
, extentions : List Video.FileFormat
|
||||
}
|
||||
|
||||
|
||||
debugEnabled : Flags -> Bool
|
||||
debugEnabled flags =
|
||||
flags.debug
|
||||
|
||||
|
||||
withDownload : Flags -> Bool
|
||||
withDownload flags =
|
||||
flags.withDownload
|
18
src/Main.elm
18
src/Main.elm
|
@ -3,12 +3,13 @@ module Main exposing (..)
|
|||
import Browser
|
||||
import Msg
|
||||
import Request
|
||||
import Types
|
||||
import Model exposing (Model)
|
||||
import Update
|
||||
import View
|
||||
import Flags exposing (Flags)
|
||||
|
||||
|
||||
main : Program Types.Flags Types.Model Msg.Msg
|
||||
main : Program Flags Model Msg.Msg
|
||||
main =
|
||||
Browser.application
|
||||
{ init = init
|
||||
|
@ -20,15 +21,6 @@ main =
|
|||
}
|
||||
|
||||
|
||||
init : Types.Flags -> a -> b -> ( Types.Model, Cmd Msg.Msg )
|
||||
init : Flags -> a -> b -> ( Model, Cmd Msg.Msg )
|
||||
init flags _ _ =
|
||||
( initialState flags, Request.videoList )
|
||||
|
||||
|
||||
initialState : Types.Flags -> Types.Model
|
||||
initialState flags =
|
||||
{ selected = Nothing
|
||||
, videos = []
|
||||
, extensions = flags.extentions
|
||||
, withDownload = Types.withDownload flags
|
||||
}
|
||||
( Model.initialState flags, Request.videoList )
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
module Model exposing (..)
|
||||
|
||||
import Flags exposing (Flags, withDownload)
|
||||
import Video
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ selected : Maybe String
|
||||
, videos : List Video.Video
|
||||
, extensions : List Video.FileFormat
|
||||
, withDownload : Bool
|
||||
}
|
||||
|
||||
|
||||
initialState : Flags -> Model
|
||||
initialState flags =
|
||||
{ selected = Nothing
|
||||
, videos = []
|
||||
, extensions = flags.extentions
|
||||
, withDownload = withDownload flags
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
module Types exposing (..)
|
||||
|
||||
import Video
|
||||
|
||||
|
||||
type alias Flags =
|
||||
{ debug : Bool
|
||||
, extentions : List Video.FileFormat
|
||||
}
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ selected : Maybe String
|
||||
, videos : List Video.Video
|
||||
, extensions : List Video.FileFormat
|
||||
, withDownload : Bool
|
||||
}
|
||||
|
||||
|
||||
videos : Model -> List Video.Video
|
||||
videos model =
|
||||
model.videos
|
||||
|
||||
|
||||
|
||||
-- These functions may seem useless, but that way it's easier to swap out
|
||||
-- from flags without changing the init function.
|
||||
|
||||
|
||||
debugEnabled : Flags -> Bool
|
||||
debugEnabled flags =
|
||||
flags.debug
|
||||
|
||||
|
||||
withDownload : Flags -> Bool
|
||||
withDownload flags =
|
||||
flags.debug
|
|
@ -1,12 +1,14 @@
|
|||
port module Update exposing (..)
|
||||
|
||||
import Msg
|
||||
import Types
|
||||
import Model exposing (Model)
|
||||
|
||||
|
||||
update : Msg.Msg -> Types.Model -> ( Types.Model, Cmd Msg.Msg )
|
||||
update : Msg.Msg -> Model -> ( Model, Cmd Msg.Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
Msg.VideoSelected "" ->
|
||||
( { model | selected = Nothing }, reloadVideoSource () )
|
||||
Msg.VideoSelected path ->
|
||||
( { model | selected = Just path }, reloadVideoSource () )
|
||||
|
||||
|
|
|
@ -50,3 +50,38 @@ downloadLink path format =
|
|||
filename : String -> FileFormat -> String
|
||||
filename path format =
|
||||
(Maybe.withDefault "unknown" <| List.head <| List.drop 1 <| String.split "/" path) ++ "." ++ format.extention
|
||||
|
||||
|
||||
type alias ListConfig msg =
|
||||
{ selected : Maybe String
|
||||
, extensions : List FileFormat
|
||||
, attrs : List (Html.Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
downloadList : ListConfig msg -> Html.Html msg
|
||||
downloadList config =
|
||||
case config.selected of
|
||||
Nothing ->
|
||||
Html.div [] []
|
||||
|
||||
Just path ->
|
||||
Html.div config.attrs <|
|
||||
List.map (\e -> downloadLink path e) config.extensions
|
||||
|
||||
|
||||
type alias PlayerConfig msg =
|
||||
{ path : Maybe String
|
||||
, extensions : List FileFormat
|
||||
, attrs : List (Html.Attribute msg)
|
||||
}
|
||||
|
||||
|
||||
makePlayer : PlayerConfig msg -> Html.Html msg
|
||||
makePlayer config =
|
||||
case config.path of
|
||||
Nothing ->
|
||||
Html.div [] []
|
||||
|
||||
Just path ->
|
||||
Html.video config.attrs (List.map (htmlSourceElem path) config.extensions)
|
||||
|
|
82
src/View.elm
82
src/View.elm
|
@ -1,21 +1,21 @@
|
|||
module View exposing (..)
|
||||
|
||||
import Browser exposing (Document)
|
||||
import Dropdown
|
||||
import Html
|
||||
import Html.Attributes
|
||||
import Html.Events as Events
|
||||
import Msg
|
||||
import Types
|
||||
import Model exposing (Model)
|
||||
import Video
|
||||
|
||||
|
||||
view : Types.Model -> Document Msg.Msg
|
||||
view : Model -> Document Msg.Msg
|
||||
view model =
|
||||
{ title = "Minimal WebPlayer"
|
||||
, body =
|
||||
[ makeDropDown model.videos
|
||||
, makePlayer model
|
||||
, downloadList model
|
||||
[ dropdown model.videos
|
||||
, player model
|
||||
, download model
|
||||
, Html.footer []
|
||||
[ Html.text "Handmade with love (and Vim and elm) ;)"
|
||||
, Html.a
|
||||
|
@ -28,59 +28,41 @@ view model =
|
|||
}
|
||||
|
||||
|
||||
empty : Html.Html Msg.Msg
|
||||
empty =
|
||||
Html.div [] []
|
||||
|
||||
|
||||
makeDropDown : List Video.Video -> Html.Html Msg.Msg
|
||||
makeDropDown videos =
|
||||
dropdown : List Video.Video -> Html.Html Msg.Msg
|
||||
dropdown videos =
|
||||
if List.isEmpty videos then
|
||||
empty
|
||||
Html.div [] [ Html.text "Please wait..." ]
|
||||
|
||||
else
|
||||
Html.select [ Events.onInput Msg.VideoSelected ] <|
|
||||
makeOption { title = "Select one please ;)", path = "" }
|
||||
:: List.map makeOption videos
|
||||
Dropdown.view
|
||||
{ options = List.map (\v -> { value = v.path, display = v.title }) videos
|
||||
, selectEvent = Msg.VideoSelected
|
||||
, default = { value = "", display = "Select one please ;)" }
|
||||
}
|
||||
|
||||
|
||||
makeOption : Video.Video -> Html.Html Msg.Msg
|
||||
makeOption video =
|
||||
Html.option
|
||||
[ Html.Attributes.value video.path ]
|
||||
[ Html.text video.title ]
|
||||
player : Model -> Html.Html Msg.Msg
|
||||
player model =
|
||||
Video.makePlayer
|
||||
{ path = model.selected
|
||||
, extensions = model.extensions
|
||||
, attrs =
|
||||
[ Html.Attributes.width 960
|
||||
, Html.Attributes.height 650
|
||||
, Html.Attributes.controls True
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
makePlayer : Types.Model -> Html.Html Msg.Msg
|
||||
makePlayer model =
|
||||
case model.selected of
|
||||
Nothing ->
|
||||
empty
|
||||
|
||||
Just path ->
|
||||
Html.video
|
||||
[ Html.Attributes.width 960
|
||||
, Html.Attributes.height 650
|
||||
, Html.Attributes.controls True
|
||||
]
|
||||
<|
|
||||
List.map (Video.htmlSourceElem path) model.extensions
|
||||
|
||||
|
||||
downloadList : Types.Model -> Html.Html Msg.Msg
|
||||
downloadList model =
|
||||
let
|
||||
selected =
|
||||
download : Model -> Html.Html Msg.Msg
|
||||
download model =
|
||||
Video.downloadList
|
||||
{ selected =
|
||||
if not model.withDownload then
|
||||
Nothing
|
||||
|
||||
else
|
||||
model.selected
|
||||
in
|
||||
case selected of
|
||||
Nothing ->
|
||||
empty
|
||||
|
||||
Just path ->
|
||||
Html.div [ Html.Attributes.class "downloadList" ] <|
|
||||
List.map (\e -> Video.downloadLink path e) model.extensions
|
||||
, extensions = model.extensions
|
||||
, attrs = [ Html.Attributes.class "downloadList" ]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue