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.tmp.js
|
||||||
js/main.min.js
|
js/main.min.js
|
||||||
deploy.sh
|
deploy.sh
|
||||||
|
index.json
|
||||||
|
|
|
@ -37,6 +37,12 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (app.ports !== undefined && app.ports.eraseVideo !== undefined) {
|
||||||
|
app.ports.eraseVideo.subscribe(function() {
|
||||||
|
lastId = undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</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 Browser.Navigation as Nav
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
import Element
|
import Element
|
||||||
|
import Http
|
||||||
import Ports
|
import Ports
|
||||||
import Task
|
|
||||||
import Time
|
import Time
|
||||||
import Twitch
|
import Twitch
|
||||||
import Url
|
import Url
|
||||||
|
@ -27,19 +27,16 @@ type alias Model =
|
||||||
|
|
||||||
type Page
|
type Page
|
||||||
= Home
|
= Home
|
||||||
| Loading
|
| Playlist Twitch.Playlist
|
||||||
| Playlist Twitch.PlaylistWithVideos
|
| Video Twitch.Playlist Twitch.Video
|
||||||
| Video Twitch.PlaylistWithVideos Twitch.Video
|
|
||||||
|
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= Noop
|
= Noop
|
||||||
| PlaylistsReceived ( List Twitch.Playlist, Time.Zone )
|
| PlaylistsReceived ( List Twitch.Playlist, Time.Zone )
|
||||||
| HomeClicked
|
| HomeClicked
|
||||||
| PlaylistClicked String
|
| PlaylistClicked Twitch.Playlist
|
||||||
| PlaylistReceived Twitch.PlaylistWithVideos
|
| VideoClicked Twitch.Playlist Twitch.Video
|
||||||
| PlaylistReceivedVideo String (Maybe Int) Twitch.PlaylistWithVideos
|
|
||||||
| VideoClicked Twitch.PlaylistWithVideos Twitch.Video
|
|
||||||
| UrlReceived Url.Url
|
| UrlReceived Url.Url
|
||||||
| SizeReceived Int Int
|
| SizeReceived Int Int
|
||||||
|
|
||||||
|
@ -47,10 +44,20 @@ type Msg
|
||||||
init : { width : Int, height : Int } -> Url.Url -> Nav.Key -> ( FullModel, Cmd Msg )
|
init : { width : Int, height : Int } -> Url.Url -> Nav.Key -> ( FullModel, Cmd Msg )
|
||||||
init { width, height } url key =
|
init { width, height } url key =
|
||||||
( Unloaded (Element.classifyDevice { width = width, height = 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 : FullModel -> Sub Msg
|
||||||
subscriptions _ =
|
subscriptions _ =
|
||||||
Events.onResize (\w h -> SizeReceived w h)
|
Events.onResize (\w h -> SizeReceived w h)
|
||||||
|
@ -79,12 +86,12 @@ update msg model =
|
||||||
|
|
||||||
( PlaylistClicked playlist, Loaded m ) ->
|
( PlaylistClicked playlist, Loaded m ) ->
|
||||||
( model
|
( model
|
||||||
, Nav.pushUrl m.key ("#" ++ playlist)
|
, Nav.pushUrl m.key ("#" ++ playlist.url)
|
||||||
)
|
)
|
||||||
|
|
||||||
( VideoClicked playlist video, Loaded m ) ->
|
( VideoClicked playlist video, Loaded m ) ->
|
||||||
( model
|
( model
|
||||||
, Nav.pushUrl m.key ("#" ++ playlist.url ++ Twitch.videoName video)
|
, Nav.pushUrl m.key ("#" ++ playlist.url ++ video.url)
|
||||||
)
|
)
|
||||||
|
|
||||||
( UrlReceived url, Loaded m ) ->
|
( UrlReceived url, Loaded m ) ->
|
||||||
|
@ -95,14 +102,10 @@ update msg model =
|
||||||
( split, args ) =
|
( split, args ) =
|
||||||
case splits of
|
case splits of
|
||||||
h1 :: h2 :: _ ->
|
h1 :: h2 :: _ ->
|
||||||
( String.split "/" h1 |> List.filter (not << String.isEmpty)
|
( String.split "/" h1, parseQueryString h2 )
|
||||||
, parseQueryString h2
|
|
||||||
)
|
|
||||||
|
|
||||||
h1 :: _ ->
|
h1 :: _ ->
|
||||||
( String.split "/" h1 |> List.filter (not << String.isEmpty)
|
( String.split "/" h1, Dict.empty )
|
||||||
, Dict.empty
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( [], Dict.empty )
|
( [], Dict.empty )
|
||||||
|
@ -130,72 +133,38 @@ update msg model =
|
||||||
( Nothing, Nothing )
|
( Nothing, Nothing )
|
||||||
|
|
||||||
playlist =
|
playlist =
|
||||||
case m.page of
|
List.head (List.filter (\x -> Just x.url == playlistName) m.playlists)
|
||||||
Playlist p ->
|
|
||||||
Just p
|
|
||||||
|
|
||||||
Video p _ ->
|
video =
|
||||||
Just p
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
realVideo =
|
|
||||||
case playlist of
|
case playlist of
|
||||||
Just p ->
|
Just p ->
|
||||||
case List.head (List.filter (\x -> Just (Twitch.videoName x) == videoName) p.videos) of
|
List.head (List.filter (\x -> Just x.url == videoName) p.videos)
|
||||||
Just v ->
|
|
||||||
Just v
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
( page, cmd ) =
|
( page, cmd ) =
|
||||||
case ( ( playlist, realVideo ), ( playlistName, videoName ) ) of
|
case ( playlist, video ) of
|
||||||
( ( Just p, _ ), ( Just _, Nothing ) ) ->
|
( Just p, Just v ) ->
|
||||||
( Playlist p
|
( Video p v
|
||||||
, Cmd.none
|
, Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time )
|
||||||
)
|
)
|
||||||
|
|
||||||
( ( Just p, Just video ), ( Just _, Just _ ) ) ->
|
( Just p, Nothing ) ->
|
||||||
( Video p video
|
( Playlist p, Cmd.none )
|
||||||
, 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)
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( Home, Cmd.none )
|
( Home, Cmd.none )
|
||||||
in
|
|
||||||
( Loaded { m | page = page }, cmd )
|
|
||||||
|
|
||||||
( PlaylistReceived p, Loaded m ) ->
|
extraCmd =
|
||||||
( Loaded { m | page = Playlist p }, Cmd.none )
|
case page of
|
||||||
|
Video _ _ ->
|
||||||
( PlaylistReceivedVideo video time playlist, Loaded m ) ->
|
Cmd.none
|
||||||
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 )
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( Loaded { m | page = Home }, Cmd.none )
|
Ports.eraseVideo ()
|
||||||
|
in
|
||||||
|
( Loaded { m | page = page }, Cmd.batch [ cmd, extraCmd ] )
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none )
|
( 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 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
|
module Twitch exposing
|
||||||
( Playlist
|
( Playlist
|
||||||
, PlaylistWithVideos
|
|
||||||
, Video
|
, Video
|
||||||
, fetchPlaylistWithVideos
|
, decodePlaylists
|
||||||
, fetchPlaylists
|
, fetchPlaylists
|
||||||
, playlistMiniatureUrl
|
, playlistMiniatureUrl
|
||||||
, videoId
|
, videoId
|
||||||
|
@ -10,28 +9,12 @@ module Twitch exposing
|
||||||
, videoName
|
, videoName
|
||||||
)
|
)
|
||||||
|
|
||||||
import Html.Parser
|
|
||||||
import Http
|
import Http
|
||||||
import Iso8601
|
import Iso8601
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Task exposing (Task)
|
|
||||||
import Time
|
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 =
|
type alias Video =
|
||||||
{ name : String
|
{ name : String
|
||||||
, url : 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
|
||||||
videoName video =
|
videoName video =
|
||||||
String.join "/" (List.drop 3 (String.split "/" video.url))
|
video.url
|
||||||
|
|
||||||
|
|
||||||
videoId : Video -> String
|
videoId : Video -> String
|
||||||
|
@ -50,222 +70,16 @@ videoId video =
|
||||||
String.dropLeft 1 video.url |> String.replace "/" "-"
|
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 -> String
|
||||||
playlistMiniatureUrl playlist =
|
playlistMiniatureUrl playlist =
|
||||||
case List.head (List.reverse playlist.videos) of
|
case List.head (List.reverse playlist.videos) of
|
||||||
Just v ->
|
Just v ->
|
||||||
"videos/" ++ playlist.url ++ v ++ "miniature-050.png"
|
"videos/" ++ playlist.url ++ v.url ++ "miniature-050.png"
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
""
|
""
|
||||||
|
|
||||||
|
|
||||||
videoMiniatureUrl : Video -> String
|
videoMiniatureUrl : Playlist -> Video -> String
|
||||||
videoMiniatureUrl video =
|
videoMiniatureUrl playlist video =
|
||||||
video.url ++ "miniature-050.png"
|
"videos/" ++ playlist.url ++ 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))
|
|
||||||
|
|
|
@ -55,9 +55,6 @@ title model =
|
||||||
Core.Home ->
|
Core.Home ->
|
||||||
Consts.url
|
Consts.url
|
||||||
|
|
||||||
Core.Loading ->
|
|
||||||
Consts.url
|
|
||||||
|
|
||||||
Core.Playlist p ->
|
Core.Playlist p ->
|
||||||
Consts.url ++ " - " ++ p.name
|
Consts.url ++ " - " ++ p.name
|
||||||
|
|
||||||
|
@ -71,9 +68,6 @@ viewContent model =
|
||||||
Core.Home ->
|
Core.Home ->
|
||||||
playlistsView model.device model.playlists
|
playlistsView model.device model.playlists
|
||||||
|
|
||||||
Core.Loading ->
|
|
||||||
Element.el [ Element.padding 10, Element.centerX ] spinner
|
|
||||||
|
|
||||||
Core.Playlist playlist ->
|
Core.Playlist playlist ->
|
||||||
videoMiniaturesView model.device model.zone playlist
|
videoMiniaturesView model.device model.zone playlist
|
||||||
|
|
||||||
|
@ -177,13 +171,13 @@ playlistView playlist =
|
||||||
button =
|
button =
|
||||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
Input.button [ Element.width Element.fill, Element.alignTop ]
|
||||||
{ label = display
|
{ label = display
|
||||||
, onPress = Just (Core.PlaylistClicked playlist.url)
|
, onPress = Just (Core.PlaylistClicked playlist)
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
button
|
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 =
|
videoMiniaturesView device zone playlist =
|
||||||
let
|
let
|
||||||
empty =
|
empty =
|
||||||
|
@ -206,8 +200,8 @@ videoMiniaturesView device zone playlist =
|
||||||
final
|
final
|
||||||
|
|
||||||
|
|
||||||
videoMiniature : Twitch.Video -> Element Core.Msg
|
videoMiniature : Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||||
videoMiniature video =
|
videoMiniature playlist video =
|
||||||
let
|
let
|
||||||
inFront =
|
inFront =
|
||||||
Element.text label
|
Element.text label
|
||||||
|
@ -229,7 +223,7 @@ videoMiniature video =
|
||||||
, Element.height Element.fill
|
, Element.height Element.fill
|
||||||
, Element.inFront inFront
|
, Element.inFront inFront
|
||||||
]
|
]
|
||||||
{ description = "", src = Twitch.videoMiniatureUrl video }
|
{ description = "", src = Twitch.videoMiniatureUrl playlist video }
|
||||||
|
|
||||||
label =
|
label =
|
||||||
formatTime video.duration
|
formatTime video.duration
|
||||||
|
@ -237,12 +231,12 @@ videoMiniature video =
|
||||||
image
|
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 =
|
videoMiniatureView zone playlist video =
|
||||||
let
|
let
|
||||||
display =
|
display =
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ videoMiniature video
|
[ videoMiniature playlist video
|
||||||
, videoDescription zone video
|
, videoDescription zone video
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -255,7 +249,7 @@ videoMiniatureView zone playlist video =
|
||||||
button
|
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 =
|
videoInList zone playlist activeVideo video =
|
||||||
let
|
let
|
||||||
( msg, attr ) =
|
( msg, attr ) =
|
||||||
|
@ -274,7 +268,7 @@ videoInList zone playlist activeVideo video =
|
||||||
label =
|
label =
|
||||||
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ Element.el [ Element.width (Element.fillPortion 2) ]
|
[ 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 ]
|
, Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ]
|
||||||
(videoDescription zone video)
|
(videoDescription zone video)
|
||||||
]
|
]
|
||||||
|
@ -282,7 +276,7 @@ videoInList zone playlist activeVideo video =
|
||||||
Input.button attr { label = label, onPress = msg }
|
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 =
|
videoView device zone playlist video =
|
||||||
let
|
let
|
||||||
( builder, contentPadding ) =
|
( builder, contentPadding ) =
|
||||||
|
|
Loading…
Reference in New Issue