Version with global json
This commit is contained in:
parent
00b68959e5
commit
98c34600d1
|
@ -4,3 +4,4 @@ js/main.js
|
|||
js/main.tmp.js
|
||||
js/main.min.js
|
||||
deploy.sh
|
||||
index.json
|
||||
|
|
|
@ -37,6 +37,12 @@
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (app.ports !== undefined && app.ports.eraseVideo !== undefined) {
|
||||
app.ports.eraseVideo.subscribe(function() {
|
||||
lastId = undefined;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const VIDEO_DIR = "videos";
|
||||
const DESCRIPTION_FILE = "description.json";
|
||||
|
||||
let info = [];
|
||||
|
||||
for (let dir of fs.readdirSync(VIDEO_DIR)) {
|
||||
let description = JSON.parse(fs.readFileSync(path.join(VIDEO_DIR, dir, DESCRIPTION_FILE)));
|
||||
description.url = dir + "/";
|
||||
description.videos = [];
|
||||
|
||||
for (let subdir of fs.readdirSync(path.join(VIDEO_DIR, dir))) {
|
||||
if (subdir === DESCRIPTION_FILE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let subdescription = JSON.parse(fs.readFileSync(path.join(VIDEO_DIR, dir, subdir, DESCRIPTION_FILE)));
|
||||
subdescription.url = subdir + "/";
|
||||
|
||||
description.videos.push(subdescription);
|
||||
}
|
||||
|
||||
info.push(description);
|
||||
}
|
||||
|
||||
fs.writeFileSync('index.json', JSON.stringify(info));
|
||||
|
103
src/Core.elm
103
src/Core.elm
|
@ -4,8 +4,8 @@ import Browser.Events as Events
|
|||
import Browser.Navigation as Nav
|
||||
import Dict exposing (Dict)
|
||||
import Element
|
||||
import Http
|
||||
import Ports
|
||||
import Task
|
||||
import Time
|
||||
import Twitch
|
||||
import Url
|
||||
|
@ -27,19 +27,16 @@ type alias Model =
|
|||
|
||||
type Page
|
||||
= Home
|
||||
| Loading
|
||||
| Playlist Twitch.PlaylistWithVideos
|
||||
| Video Twitch.PlaylistWithVideos Twitch.Video
|
||||
| Playlist Twitch.Playlist
|
||||
| Video Twitch.Playlist Twitch.Video
|
||||
|
||||
|
||||
type Msg
|
||||
= Noop
|
||||
| PlaylistsReceived ( List Twitch.Playlist, Time.Zone )
|
||||
| HomeClicked
|
||||
| PlaylistClicked String
|
||||
| PlaylistReceived Twitch.PlaylistWithVideos
|
||||
| PlaylistReceivedVideo String (Maybe Int) Twitch.PlaylistWithVideos
|
||||
| VideoClicked Twitch.PlaylistWithVideos Twitch.Video
|
||||
| PlaylistClicked Twitch.Playlist
|
||||
| VideoClicked Twitch.Playlist Twitch.Video
|
||||
| UrlReceived Url.Url
|
||||
| SizeReceived Int Int
|
||||
|
||||
|
@ -47,10 +44,20 @@ type Msg
|
|||
init : { width : Int, height : Int } -> Url.Url -> Nav.Key -> ( FullModel, Cmd Msg )
|
||||
init { width, height } url key =
|
||||
( Unloaded (Element.classifyDevice { width = width, height = height }) url key
|
||||
, Task.perform PlaylistsReceived Twitch.fetchPlaylists
|
||||
, Twitch.fetchPlaylists resultToMsg
|
||||
)
|
||||
|
||||
|
||||
resultToMsg : Result Http.Error (List Twitch.Playlist) -> Msg
|
||||
resultToMsg result =
|
||||
case result of
|
||||
Ok o ->
|
||||
PlaylistsReceived ( o, Time.utc )
|
||||
|
||||
_ ->
|
||||
Noop
|
||||
|
||||
|
||||
subscriptions : FullModel -> Sub Msg
|
||||
subscriptions _ =
|
||||
Events.onResize (\w h -> SizeReceived w h)
|
||||
|
@ -79,12 +86,12 @@ update msg model =
|
|||
|
||||
( PlaylistClicked playlist, Loaded m ) ->
|
||||
( model
|
||||
, Nav.pushUrl m.key ("#" ++ playlist)
|
||||
, Nav.pushUrl m.key ("#" ++ playlist.url)
|
||||
)
|
||||
|
||||
( VideoClicked playlist video, Loaded m ) ->
|
||||
( model
|
||||
, Nav.pushUrl m.key ("#" ++ playlist.url ++ Twitch.videoName video)
|
||||
, Nav.pushUrl m.key ("#" ++ playlist.url ++ video.url)
|
||||
)
|
||||
|
||||
( UrlReceived url, Loaded m ) ->
|
||||
|
@ -95,14 +102,10 @@ update msg model =
|
|||
( split, args ) =
|
||||
case splits of
|
||||
h1 :: h2 :: _ ->
|
||||
( String.split "/" h1 |> List.filter (not << String.isEmpty)
|
||||
, parseQueryString h2
|
||||
)
|
||||
( String.split "/" h1, parseQueryString h2 )
|
||||
|
||||
h1 :: _ ->
|
||||
( String.split "/" h1 |> List.filter (not << String.isEmpty)
|
||||
, Dict.empty
|
||||
)
|
||||
( String.split "/" h1, Dict.empty )
|
||||
|
||||
_ ->
|
||||
( [], Dict.empty )
|
||||
|
@ -130,72 +133,38 @@ update msg model =
|
|||
( Nothing, Nothing )
|
||||
|
||||
playlist =
|
||||
case m.page of
|
||||
Playlist p ->
|
||||
Just p
|
||||
List.head (List.filter (\x -> Just x.url == playlistName) m.playlists)
|
||||
|
||||
Video p _ ->
|
||||
Just p
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
realVideo =
|
||||
video =
|
||||
case playlist of
|
||||
Just p ->
|
||||
case List.head (List.filter (\x -> Just (Twitch.videoName x) == videoName) p.videos) of
|
||||
Just v ->
|
||||
Just v
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
List.head (List.filter (\x -> Just x.url == videoName) p.videos)
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
( page, cmd ) =
|
||||
case ( ( playlist, realVideo ), ( playlistName, videoName ) ) of
|
||||
( ( Just p, _ ), ( Just _, Nothing ) ) ->
|
||||
( Playlist p
|
||||
, Cmd.none
|
||||
case ( playlist, video ) of
|
||||
( Just p, Just v ) ->
|
||||
( Video p v
|
||||
, Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time )
|
||||
)
|
||||
|
||||
( ( Just p, Just video ), ( Just _, Just _ ) ) ->
|
||||
( Video p video
|
||||
, Ports.registerVideo ( Twitch.videoId video, video.url, time )
|
||||
)
|
||||
|
||||
( ( _, _ ), ( Just name, Nothing ) ) ->
|
||||
( Loading
|
||||
, Task.perform
|
||||
PlaylistReceived
|
||||
(Twitch.fetchPlaylistWithVideos name)
|
||||
)
|
||||
|
||||
( ( _, _ ), ( Just name, Just video ) ) ->
|
||||
( Loading
|
||||
, Task.perform
|
||||
(PlaylistReceivedVideo video time)
|
||||
(Twitch.fetchPlaylistWithVideos name)
|
||||
)
|
||||
( Just p, Nothing ) ->
|
||||
( Playlist p, Cmd.none )
|
||||
|
||||
_ ->
|
||||
( Home, Cmd.none )
|
||||
in
|
||||
( Loaded { m | page = page }, cmd )
|
||||
|
||||
( PlaylistReceived p, Loaded m ) ->
|
||||
( Loaded { m | page = Playlist p }, Cmd.none )
|
||||
|
||||
( PlaylistReceivedVideo video time playlist, Loaded m ) ->
|
||||
case List.head (List.filter (\x -> Twitch.videoName x == video) playlist.videos) of
|
||||
Just v ->
|
||||
( Loaded { m | page = Video playlist v }
|
||||
, Ports.registerVideo ( Twitch.videoId v, v.url, time )
|
||||
)
|
||||
extraCmd =
|
||||
case page of
|
||||
Video _ _ ->
|
||||
Cmd.none
|
||||
|
||||
_ ->
|
||||
( Loaded { m | page = Home }, Cmd.none )
|
||||
Ports.eraseVideo ()
|
||||
in
|
||||
( Loaded { m | page = page }, Cmd.batch [ cmd, extraCmd ] )
|
||||
|
||||
_ ->
|
||||
( model, Cmd.none )
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
port module Ports exposing (registerVideo)
|
||||
port module Ports exposing (eraseVideo, registerVideo)
|
||||
|
||||
|
||||
port registerVideo : ( String, String, Maybe Int ) -> Cmd msg
|
||||
|
||||
|
||||
port eraseVideo : () -> Cmd msg
|
||||
|
|
272
src/Twitch.elm
272
src/Twitch.elm
|
@ -1,8 +1,7 @@
|
|||
module Twitch exposing
|
||||
( Playlist
|
||||
, PlaylistWithVideos
|
||||
, Video
|
||||
, fetchPlaylistWithVideos
|
||||
, decodePlaylists
|
||||
, fetchPlaylists
|
||||
, playlistMiniatureUrl
|
||||
, videoId
|
||||
|
@ -10,28 +9,12 @@ module Twitch exposing
|
|||
, videoName
|
||||
)
|
||||
|
||||
import Html.Parser
|
||||
import Http
|
||||
import Iso8601
|
||||
import Json.Decode as Decode
|
||||
import Task exposing (Task)
|
||||
import Time
|
||||
|
||||
|
||||
type alias Playlist =
|
||||
{ url : String
|
||||
, name : String
|
||||
, videos : List String
|
||||
}
|
||||
|
||||
|
||||
type alias PlaylistWithVideos =
|
||||
{ url : String
|
||||
, name : String
|
||||
, videos : List Video
|
||||
}
|
||||
|
||||
|
||||
type alias Video =
|
||||
{ name : String
|
||||
, url : String
|
||||
|
@ -40,9 +23,46 @@ type alias Video =
|
|||
}
|
||||
|
||||
|
||||
type alias Playlist =
|
||||
{ url : String
|
||||
, name : String
|
||||
, videos : List Video
|
||||
}
|
||||
|
||||
|
||||
decodeVideo : Decode.Decoder Video
|
||||
decodeVideo =
|
||||
Decode.map4 Video
|
||||
(Decode.field "title" Decode.string)
|
||||
(Decode.field "url" Decode.string)
|
||||
(Decode.field "duration" Decode.int)
|
||||
(Decode.maybe (Decode.field "date" Iso8601.decoder))
|
||||
|
||||
|
||||
decodePlaylist : Decode.Decoder Playlist
|
||||
decodePlaylist =
|
||||
Decode.map3 Playlist
|
||||
(Decode.field "url" Decode.string)
|
||||
(Decode.field "title" Decode.string)
|
||||
(Decode.field "videos" (Decode.map (List.sortBy .url >> List.reverse) (Decode.list decodeVideo)))
|
||||
|
||||
|
||||
decodePlaylists : Decode.Decoder (List Playlist)
|
||||
decodePlaylists =
|
||||
Decode.map (List.sortBy .url >> List.reverse) (Decode.list decodePlaylist)
|
||||
|
||||
|
||||
fetchPlaylists : (Result Http.Error (List Playlist) -> msg) -> Cmd msg
|
||||
fetchPlaylists resultToMsg =
|
||||
Http.get
|
||||
{ url = "/index.json"
|
||||
, expect = Http.expectJson resultToMsg decodePlaylists
|
||||
}
|
||||
|
||||
|
||||
videoName : Video -> String
|
||||
videoName video =
|
||||
String.join "/" (List.drop 3 (String.split "/" video.url))
|
||||
video.url
|
||||
|
||||
|
||||
videoId : Video -> String
|
||||
|
@ -50,222 +70,16 @@ videoId video =
|
|||
String.dropLeft 1 video.url |> String.replace "/" "-"
|
||||
|
||||
|
||||
get : { url : String, resolver : Http.Resolver x a } -> Task x a
|
||||
get { url, resolver } =
|
||||
Http.task
|
||||
{ body = Http.emptyBody
|
||||
, headers = []
|
||||
, method = "GET"
|
||||
, resolver = resolver
|
||||
, timeout = Nothing
|
||||
, url = url
|
||||
}
|
||||
|
||||
|
||||
playlistMiniatureUrl : Playlist -> String
|
||||
playlistMiniatureUrl playlist =
|
||||
case List.head (List.reverse playlist.videos) of
|
||||
Just v ->
|
||||
"videos/" ++ playlist.url ++ v ++ "miniature-050.png"
|
||||
"videos/" ++ playlist.url ++ v.url ++ "miniature-050.png"
|
||||
|
||||
_ ->
|
||||
""
|
||||
|
||||
|
||||
videoMiniatureUrl : Video -> String
|
||||
videoMiniatureUrl video =
|
||||
video.url ++ "miniature-050.png"
|
||||
|
||||
|
||||
sortPlaylistWithVideos : PlaylistWithVideos -> PlaylistWithVideos
|
||||
sortPlaylistWithVideos playlist =
|
||||
{ playlist | videos = List.sortBy .url playlist.videos |> List.reverse }
|
||||
|
||||
|
||||
sortPlaylists : List Playlist -> List Playlist
|
||||
sortPlaylists playlists =
|
||||
List.sortBy .url playlists |> List.reverse
|
||||
|
||||
|
||||
fetchPlaylists : Task x ( List Playlist, Time.Zone )
|
||||
fetchPlaylists =
|
||||
fetchPlaylistPath
|
||||
|> Task.andThen fetchPlaylistsMapper
|
||||
|> Task.map sortPlaylists
|
||||
|> Task.andThen fetchTimezone
|
||||
|
||||
|
||||
fetchTimezone : List Playlist -> Task x ( List Playlist, Time.Zone )
|
||||
fetchTimezone playlists =
|
||||
Task.map (\zone -> ( playlists, zone )) Time.here
|
||||
|
||||
|
||||
fetchPlaylistPath : Task x (List String)
|
||||
fetchPlaylistPath =
|
||||
get
|
||||
{ url = "/videos"
|
||||
, resolver = Http.stringResolver parseHrefs
|
||||
}
|
||||
|
||||
|
||||
fetchPlaylist : String -> Task x Playlist
|
||||
fetchPlaylist name =
|
||||
fetchPlaylistName name
|
||||
|> Task.andThen
|
||||
(\a ->
|
||||
fetchPlaylistVideoPaths name
|
||||
-- |> Task.andThen (\c -> fetchVideos name c)
|
||||
|> Task.map (Playlist name a)
|
||||
)
|
||||
|
||||
|
||||
fetchPlaylistWithVideos : String -> Task x PlaylistWithVideos
|
||||
fetchPlaylistWithVideos name =
|
||||
fetchPlaylistName name
|
||||
|> Task.andThen
|
||||
(\a ->
|
||||
fetchPlaylistVideoPaths name
|
||||
|> Task.andThen (\c -> fetchVideos name c)
|
||||
|> Task.map (PlaylistWithVideos name a)
|
||||
|> Task.map sortPlaylistWithVideos
|
||||
)
|
||||
|
||||
|
||||
fetchVideo : String -> String -> Task x Video
|
||||
fetchVideo playlist video =
|
||||
let
|
||||
url =
|
||||
"/videos/" ++ playlist ++ video
|
||||
in
|
||||
get
|
||||
{ url = url ++ "description.json"
|
||||
, resolver = Http.stringResolver (parseVideo url)
|
||||
}
|
||||
|
||||
|
||||
fetchVideos : String -> List String -> Task x (List Video)
|
||||
fetchVideos playlist videos =
|
||||
Task.sequence (List.map (fetchVideo playlist) videos)
|
||||
|
||||
|
||||
fetchPlaylistName : String -> Task x String
|
||||
fetchPlaylistName path =
|
||||
get
|
||||
{ url = "/videos/" ++ path ++ "description.json"
|
||||
, resolver = Http.stringResolver parsePlaylistName
|
||||
}
|
||||
|
||||
|
||||
fetchPlaylistVideoPaths : String -> Task x (List String)
|
||||
fetchPlaylistVideoPaths name =
|
||||
get
|
||||
{ url = "/videos/" ++ name
|
||||
, resolver = Http.stringResolver parseHrefs
|
||||
}
|
||||
|
||||
|
||||
parseVideo : String -> Http.Response String -> Result x Video
|
||||
parseVideo url result =
|
||||
case result of
|
||||
Http.GoodStatus_ _ content ->
|
||||
case Decode.decodeString (decodeVideo url) content of
|
||||
Ok v ->
|
||||
Ok v
|
||||
|
||||
_ ->
|
||||
Ok { name = "", url = url, duration = 0, date = Nothing }
|
||||
|
||||
_ ->
|
||||
Ok { name = "", url = url, duration = 0, date = Nothing }
|
||||
|
||||
|
||||
fetchPlaylistsMapper : List String -> Task x (List Playlist)
|
||||
fetchPlaylistsMapper names =
|
||||
Task.sequence (List.map fetchPlaylist names)
|
||||
|
||||
|
||||
parsePlaylistName : Http.Response String -> Result x String
|
||||
parsePlaylistName result =
|
||||
case result of
|
||||
Http.GoodStatus_ _ content ->
|
||||
case Decode.decodeString decodePlaylistName content of
|
||||
Ok p ->
|
||||
Ok p
|
||||
|
||||
_ ->
|
||||
Ok ""
|
||||
|
||||
_ ->
|
||||
Ok ""
|
||||
|
||||
|
||||
parseHrefs : Http.Response String -> Result x (List String)
|
||||
parseHrefs result =
|
||||
case result of
|
||||
Http.GoodStatus_ _ content ->
|
||||
let
|
||||
withoutDoctype =
|
||||
if String.startsWith "<!doctype" (String.toLower content) then
|
||||
String.lines content |> List.drop 1 |> String.join "\n"
|
||||
|
||||
else
|
||||
content
|
||||
|
||||
decoded =
|
||||
Html.Parser.run withoutDoctype
|
||||
|
||||
hrefs =
|
||||
Result.map findHrefs decoded
|
||||
in
|
||||
case hrefs of
|
||||
Ok h ->
|
||||
Ok h
|
||||
|
||||
_ ->
|
||||
Ok []
|
||||
|
||||
_ ->
|
||||
Ok []
|
||||
|
||||
|
||||
findHrefsAux : List String -> Html.Parser.Node -> List String
|
||||
findHrefsAux acc node =
|
||||
case node of
|
||||
Html.Parser.Element string (( key, value ) :: t) nodes ->
|
||||
let
|
||||
newAcc =
|
||||
if key == "href" && String.endsWith "/" value && value /= "../" then
|
||||
value :: acc
|
||||
|
||||
else
|
||||
acc
|
||||
in
|
||||
findHrefsAux newAcc (Html.Parser.Element string t nodes)
|
||||
|
||||
Html.Parser.Element string [] (h :: t) ->
|
||||
let
|
||||
attrs =
|
||||
findHrefsAux [] h
|
||||
in
|
||||
findHrefsAux (acc ++ attrs) (Html.Parser.Element string [] t)
|
||||
|
||||
_ ->
|
||||
acc
|
||||
|
||||
|
||||
findHrefs : List Html.Parser.Node -> List String
|
||||
findHrefs x =
|
||||
findHrefsAux [] (Html.Parser.Element "" [] x)
|
||||
|
||||
|
||||
decodePlaylistName : Decode.Decoder String
|
||||
decodePlaylistName =
|
||||
Decode.field "title" Decode.string
|
||||
|
||||
|
||||
decodeVideo : String -> Decode.Decoder Video
|
||||
decodeVideo url =
|
||||
Decode.map3 (\x y -> Video x url y)
|
||||
(Decode.field "title" Decode.string)
|
||||
(Decode.map Basics.round (Decode.field "duration" Decode.float))
|
||||
(Decode.maybe (Decode.field "date" Iso8601.decoder))
|
||||
videoMiniatureUrl : Playlist -> Video -> String
|
||||
videoMiniatureUrl playlist video =
|
||||
"videos/" ++ playlist.url ++ video.url ++ "miniature-050.png"
|
||||
|
|
|
@ -55,9 +55,6 @@ title model =
|
|||
Core.Home ->
|
||||
Consts.url
|
||||
|
||||
Core.Loading ->
|
||||
Consts.url
|
||||
|
||||
Core.Playlist p ->
|
||||
Consts.url ++ " - " ++ p.name
|
||||
|
||||
|
@ -71,9 +68,6 @@ viewContent model =
|
|||
Core.Home ->
|
||||
playlistsView model.device model.playlists
|
||||
|
||||
Core.Loading ->
|
||||
Element.el [ Element.padding 10, Element.centerX ] spinner
|
||||
|
||||
Core.Playlist playlist ->
|
||||
videoMiniaturesView model.device model.zone playlist
|
||||
|
||||
|
@ -177,13 +171,13 @@ playlistView playlist =
|
|||
button =
|
||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
||||
{ label = display
|
||||
, onPress = Just (Core.PlaylistClicked playlist.url)
|
||||
, onPress = Just (Core.PlaylistClicked playlist)
|
||||
}
|
||||
in
|
||||
button
|
||||
|
||||
|
||||
videoMiniaturesView : Element.Device -> Time.Zone -> Twitch.PlaylistWithVideos -> Element Core.Msg
|
||||
videoMiniaturesView : Element.Device -> Time.Zone -> Twitch.Playlist -> Element Core.Msg
|
||||
videoMiniaturesView device zone playlist =
|
||||
let
|
||||
empty =
|
||||
|
@ -206,8 +200,8 @@ videoMiniaturesView device zone playlist =
|
|||
final
|
||||
|
||||
|
||||
videoMiniature : Twitch.Video -> Element Core.Msg
|
||||
videoMiniature video =
|
||||
videoMiniature : Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||
videoMiniature playlist video =
|
||||
let
|
||||
inFront =
|
||||
Element.text label
|
||||
|
@ -229,7 +223,7 @@ videoMiniature video =
|
|||
, Element.height Element.fill
|
||||
, Element.inFront inFront
|
||||
]
|
||||
{ description = "", src = Twitch.videoMiniatureUrl video }
|
||||
{ description = "", src = Twitch.videoMiniatureUrl playlist video }
|
||||
|
||||
label =
|
||||
formatTime video.duration
|
||||
|
@ -237,12 +231,12 @@ videoMiniature video =
|
|||
image
|
||||
|
||||
|
||||
videoMiniatureView : Time.Zone -> Twitch.PlaylistWithVideos -> Twitch.Video -> Element Core.Msg
|
||||
videoMiniatureView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||
videoMiniatureView zone playlist video =
|
||||
let
|
||||
display =
|
||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||
[ videoMiniature video
|
||||
[ videoMiniature playlist video
|
||||
, videoDescription zone video
|
||||
]
|
||||
|
||||
|
@ -255,7 +249,7 @@ videoMiniatureView zone playlist video =
|
|||
button
|
||||
|
||||
|
||||
videoInList : Time.Zone -> Twitch.PlaylistWithVideos -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
||||
videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
||||
videoInList zone playlist activeVideo video =
|
||||
let
|
||||
( msg, attr ) =
|
||||
|
@ -274,7 +268,7 @@ videoInList zone playlist activeVideo video =
|
|||
label =
|
||||
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
||||
[ Element.el [ Element.width (Element.fillPortion 2) ]
|
||||
(videoMiniature video)
|
||||
(videoMiniature playlist video)
|
||||
, Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ]
|
||||
(videoDescription zone video)
|
||||
]
|
||||
|
@ -282,7 +276,7 @@ videoInList zone playlist activeVideo video =
|
|||
Input.button attr { label = label, onPress = msg }
|
||||
|
||||
|
||||
videoView : Element.Device -> Time.Zone -> Twitch.PlaylistWithVideos -> Twitch.Video -> Element Core.Msg
|
||||
videoView : Element.Device -> Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||
videoView device zone playlist video =
|
||||
let
|
||||
( builder, contentPadding ) =
|
||||
|
|
Loading…
Reference in New Issue