2020-11-06 11:43:28 +01:00
|
|
|
module Core exposing (Model, Msg(..), Page(..), init, subscriptions, update)
|
2020-10-03 18:44:16 +02:00
|
|
|
|
2020-10-08 16:18:22 +02:00
|
|
|
import Browser
|
2020-10-04 20:20:16 +02:00
|
|
|
import Browser.Events as Events
|
2020-10-04 16:02:54 +02:00
|
|
|
import Browser.Navigation as Nav
|
2020-10-04 21:31:16 +02:00
|
|
|
import Dict exposing (Dict)
|
2020-10-04 20:20:16 +02:00
|
|
|
import Element
|
2020-10-16 12:03:07 +02:00
|
|
|
import Hover exposing (Hover)
|
2020-10-05 11:26:11 +02:00
|
|
|
import Http
|
2020-10-04 16:37:01 +02:00
|
|
|
import Ports
|
2020-10-15 16:47:10 +02:00
|
|
|
import Task
|
2020-10-04 15:24:04 +02:00
|
|
|
import Time
|
2020-10-28 16:57:08 +01:00
|
|
|
import TimeZone
|
2020-10-03 18:44:16 +02:00
|
|
|
import Twitch
|
|
|
|
import Url
|
2021-06-21 11:30:31 +02:00
|
|
|
import Video
|
|
|
|
import Video.Events
|
2020-10-03 18:44:16 +02:00
|
|
|
|
|
|
|
|
2020-10-04 13:15:57 +02:00
|
|
|
type alias Model =
|
|
|
|
{ playlists : List Twitch.Playlist
|
2020-10-04 15:24:04 +02:00
|
|
|
, zone : Time.Zone
|
2020-10-04 13:15:57 +02:00
|
|
|
, page : Page
|
2020-10-04 16:02:54 +02:00
|
|
|
, key : Nav.Key
|
2020-10-04 20:20:16 +02:00
|
|
|
, device : Element.Device
|
2020-10-16 12:03:07 +02:00
|
|
|
, time : Time.Posix
|
2020-11-06 12:19:42 +01:00
|
|
|
, currentDate : Time.Posix
|
2020-11-06 11:43:28 +01:00
|
|
|
, url : Url.Url
|
2021-04-03 23:33:01 +02:00
|
|
|
, darkMode : Bool
|
|
|
|
, darkSetting : Maybe Bool
|
2020-10-04 13:15:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type Page
|
2020-10-16 12:03:07 +02:00
|
|
|
= Home (Maybe (Hover Twitch.Playlist))
|
|
|
|
| Playlist Twitch.Playlist (Maybe (Hover Twitch.Video))
|
2021-06-21 11:30:31 +02:00
|
|
|
| Video Twitch.Playlist Twitch.Video Video.Video (Maybe (Hover Twitch.Video))
|
2020-10-03 18:44:16 +02:00
|
|
|
|
|
|
|
|
|
|
|
type Msg
|
|
|
|
= Noop
|
2020-10-15 16:47:10 +02:00
|
|
|
| PlaylistsReceived (List Twitch.Playlist)
|
2020-10-04 14:24:16 +02:00
|
|
|
| HomeClicked
|
2020-10-05 11:26:11 +02:00
|
|
|
| PlaylistClicked Twitch.Playlist
|
|
|
|
| VideoClicked Twitch.Playlist Twitch.Video
|
2020-10-04 16:02:54 +02:00
|
|
|
| UrlReceived Url.Url
|
2020-10-08 16:18:22 +02:00
|
|
|
| UrlRequested Browser.UrlRequest
|
2020-10-04 20:20:16 +02:00
|
|
|
| SizeReceived Int Int
|
2020-10-28 16:57:08 +01:00
|
|
|
| TimeZoneReceivedResult (Result TimeZone.Error ( String, Time.Zone ))
|
2020-10-15 16:47:10 +02:00
|
|
|
| TimeZoneReceived Time.Zone
|
2020-10-16 12:03:07 +02:00
|
|
|
| HoverPlaylist Twitch.Playlist
|
|
|
|
| HoverVideo Twitch.Video
|
|
|
|
| Unhover
|
|
|
|
| TimeReceived Time.Posix
|
2020-11-06 12:19:42 +01:00
|
|
|
| CurrentDateReceived Time.Posix
|
2021-04-03 23:33:01 +02:00
|
|
|
| DarkMode Bool
|
|
|
|
| DarkModeClicked
|
2021-06-21 11:30:31 +02:00
|
|
|
| VideoMsg Video.Msg
|
2020-10-03 18:44:16 +02:00
|
|
|
|
|
|
|
|
2021-04-03 23:33:01 +02:00
|
|
|
init : { width : Int, height : Int, darkMode : Bool, darkSetting : Maybe Bool } -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
|
|
|
|
init { width, height, darkMode, darkSetting } url key =
|
2020-11-06 12:19:42 +01:00
|
|
|
( Model
|
|
|
|
[]
|
|
|
|
Time.utc
|
|
|
|
(Home Nothing)
|
|
|
|
key
|
|
|
|
(Element.classifyDevice { width = width, height = height })
|
|
|
|
(Time.millisToPosix 0)
|
|
|
|
(Time.millisToPosix 0)
|
|
|
|
url
|
2021-04-03 23:33:01 +02:00
|
|
|
darkMode
|
|
|
|
darkSetting
|
2020-10-15 16:47:10 +02:00
|
|
|
, Cmd.batch
|
2020-10-28 16:57:08 +01:00
|
|
|
[ Task.attempt TimeZoneReceivedResult TimeZone.getZone
|
2020-11-06 12:19:42 +01:00
|
|
|
, Task.perform CurrentDateReceived Time.now
|
2020-10-15 16:47:10 +02:00
|
|
|
, Twitch.fetchPlaylists resultToMsg
|
|
|
|
]
|
2020-10-03 18:44:16 +02:00
|
|
|
)
|
2020-10-04 13:15:57 +02:00
|
|
|
|
|
|
|
|
2020-10-05 11:26:11 +02:00
|
|
|
resultToMsg : Result Http.Error (List Twitch.Playlist) -> Msg
|
|
|
|
resultToMsg result =
|
|
|
|
case result of
|
|
|
|
Ok o ->
|
2020-10-15 16:47:10 +02:00
|
|
|
PlaylistsReceived o
|
2020-10-05 11:26:11 +02:00
|
|
|
|
2020-10-05 11:33:49 +02:00
|
|
|
Err _ ->
|
2020-10-05 11:26:11 +02:00
|
|
|
Noop
|
|
|
|
|
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
subscriptions : Model -> Sub Msg
|
2021-06-21 11:30:31 +02:00
|
|
|
subscriptions model =
|
2020-10-16 12:03:07 +02:00
|
|
|
Sub.batch
|
|
|
|
[ Events.onResize (\w h -> SizeReceived w h)
|
|
|
|
, Time.every 200 TimeReceived
|
2021-04-03 23:33:01 +02:00
|
|
|
, Ports.darkMode DarkMode
|
2021-06-21 11:30:31 +02:00
|
|
|
, Sub.map VideoMsg
|
|
|
|
(case model.page of
|
|
|
|
Video _ _ v _ ->
|
|
|
|
Video.Events.subs v
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
Sub.none
|
|
|
|
)
|
2020-10-16 12:03:07 +02:00
|
|
|
]
|
2020-10-04 20:20:16 +02:00
|
|
|
|
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
2020-10-04 13:15:57 +02:00
|
|
|
update msg model =
|
2020-11-06 12:19:42 +01:00
|
|
|
case msg of
|
|
|
|
Noop ->
|
2020-10-04 13:15:57 +02:00
|
|
|
( model, Cmd.none )
|
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
TimeZoneReceived z ->
|
|
|
|
( { model | zone = z }, Cmd.none )
|
2020-10-28 16:57:08 +01:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
TimeZoneReceivedResult (Ok ( _, zone )) ->
|
|
|
|
( { model | zone = zone }, Cmd.none )
|
2020-10-28 16:57:08 +01:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
TimeZoneReceivedResult (Err _) ->
|
2020-10-28 16:57:08 +01:00
|
|
|
( model, Task.perform TimeZoneReceived Time.here )
|
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
TimeReceived p ->
|
|
|
|
( { model | time = p }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
CurrentDateReceived d ->
|
|
|
|
( { model | currentDate = d }, Cmd.none )
|
|
|
|
|
|
|
|
HoverPlaylist hover ->
|
|
|
|
case model.page of
|
2020-10-16 12:03:07 +02:00
|
|
|
Home Nothing ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( { model | page = Home (Just (Hover.hover hover model.time)) }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
|
|
|
_ ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( model, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
HoverVideo hover ->
|
|
|
|
case model.page of
|
2020-10-16 12:03:07 +02:00
|
|
|
Playlist p Nothing ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( { model | page = Playlist p (Just (Hover.hover hover model.time)) }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2021-06-21 11:30:31 +02:00
|
|
|
Video p v x Nothing ->
|
2021-06-22 09:21:37 +02:00
|
|
|
( { model | page = Video p v x (Just (Hover.hover hover model.time)) }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
|
|
|
_ ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( model, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
Unhover ->
|
|
|
|
case model.page of
|
2020-10-16 12:03:07 +02:00
|
|
|
Home _ ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( { model | page = Home Nothing }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
|
|
|
Playlist p _ ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( { model | page = Playlist p Nothing }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2021-06-21 11:30:31 +02:00
|
|
|
Video p v x _ ->
|
|
|
|
( { model | page = Video p v x Nothing }, Cmd.none )
|
2020-10-16 12:03:07 +02:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
SizeReceived w h ->
|
|
|
|
( { model | device = Element.classifyDevice { width = w, height = h } }
|
2020-10-04 20:20:16 +02:00
|
|
|
, Cmd.none
|
|
|
|
)
|
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
PlaylistsReceived playlists ->
|
2020-10-04 16:02:54 +02:00
|
|
|
update
|
2020-11-06 12:19:42 +01:00
|
|
|
(UrlReceived model.url)
|
|
|
|
{ model | playlists = playlists, page = Home Nothing }
|
2020-11-06 11:43:28 +01:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
HomeClicked ->
|
2020-10-04 17:14:00 +02:00
|
|
|
( model
|
2020-11-06 12:19:42 +01:00
|
|
|
, Nav.pushUrl model.key "#"
|
2020-10-04 16:02:54 +02:00
|
|
|
)
|
2020-10-04 14:24:16 +02:00
|
|
|
|
2021-04-03 23:33:01 +02:00
|
|
|
DarkModeClicked ->
|
|
|
|
let
|
|
|
|
next =
|
|
|
|
nextDarkSetting model.darkSetting
|
|
|
|
in
|
|
|
|
( { model | darkSetting = next }, Ports.setDarkMode next )
|
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
PlaylistClicked playlist ->
|
2020-10-04 17:14:00 +02:00
|
|
|
( model
|
2020-11-06 12:19:42 +01:00
|
|
|
, Nav.pushUrl model.key ("#" ++ playlist.url)
|
2020-10-04 16:02:54 +02:00
|
|
|
)
|
2020-10-04 14:24:16 +02:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
VideoClicked playlist video ->
|
2020-10-04 17:14:00 +02:00
|
|
|
( model
|
2020-11-06 12:19:42 +01:00
|
|
|
, Nav.pushUrl model.key ("#" ++ playlist.url ++ video.url)
|
2020-10-04 16:02:54 +02:00
|
|
|
)
|
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
UrlReceived url ->
|
|
|
|
if List.isEmpty model.playlists then
|
|
|
|
( { model | url = url }, Cmd.none )
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
else
|
|
|
|
let
|
|
|
|
splits =
|
|
|
|
String.split "?" (Maybe.withDefault "" url.fragment)
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
( split, args ) =
|
|
|
|
case splits of
|
|
|
|
h1 :: h2 :: _ ->
|
|
|
|
( String.split "/" h1, parseQueryString h2 )
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
h1 :: _ ->
|
|
|
|
( String.split "/" h1, Dict.empty )
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
_ ->
|
|
|
|
( [], Dict.empty )
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
time =
|
|
|
|
case Dict.get "t" args of
|
|
|
|
Just "0" ->
|
|
|
|
Nothing
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
Just t ->
|
|
|
|
Just t
|
2020-10-04 21:31:16 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
_ ->
|
|
|
|
Nothing
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
( playlistName, videoName ) =
|
|
|
|
case split of
|
|
|
|
p :: v :: _ ->
|
|
|
|
( Just (p ++ "/"), Just (v ++ "/") )
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
p :: _ ->
|
|
|
|
( Just (p ++ "/"), Nothing )
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
_ ->
|
|
|
|
( Nothing, Nothing )
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
playlist =
|
2020-11-06 12:19:42 +01:00
|
|
|
List.head (List.filter (\x -> Just x.url == playlistName) model.playlists)
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
video =
|
|
|
|
case playlist of
|
|
|
|
Just p ->
|
|
|
|
List.head (List.filter (\x -> Just x.url == videoName) p.videos)
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
_ ->
|
|
|
|
Nothing
|
2020-10-04 23:03:38 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
( page, cmd ) =
|
|
|
|
case ( playlist, video ) of
|
|
|
|
( Just p, Just v ) ->
|
2021-06-21 11:30:31 +02:00
|
|
|
let
|
|
|
|
( el, videoCommand ) =
|
2021-12-09 17:34:53 +01:00
|
|
|
Video.fromConfig
|
|
|
|
{ url = "/videos/" ++ p.url ++ v.url ++ "/manifest.m3u8"
|
|
|
|
, id = "video"
|
|
|
|
, autoplay = True
|
|
|
|
, enableMiniatures = True
|
|
|
|
, startTime = Nothing
|
|
|
|
}
|
2021-06-21 11:30:31 +02:00
|
|
|
in
|
|
|
|
( Video p v el Nothing, Cmd.map VideoMsg videoCommand )
|
2020-10-04 16:02:54 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
( Just p, Nothing ) ->
|
|
|
|
( Playlist p Nothing, Cmd.none )
|
2020-10-04 23:03:38 +02:00
|
|
|
|
2020-11-06 11:43:28 +01:00
|
|
|
_ ->
|
|
|
|
( Home Nothing, Cmd.none )
|
|
|
|
in
|
2021-06-21 11:30:31 +02:00
|
|
|
( { model | page = page }, cmd )
|
2020-11-06 11:43:28 +01:00
|
|
|
|
2020-11-06 12:19:42 +01:00
|
|
|
UrlRequested u ->
|
2020-10-08 16:18:22 +02:00
|
|
|
case u of
|
|
|
|
Browser.Internal url ->
|
2020-11-06 12:19:42 +01:00
|
|
|
( model, Nav.pushUrl model.key (Url.toString url) )
|
2020-10-08 16:18:22 +02:00
|
|
|
|
|
|
|
Browser.External s ->
|
|
|
|
( model, Nav.load s )
|
|
|
|
|
2021-04-03 23:33:01 +02:00
|
|
|
DarkMode dark ->
|
|
|
|
( { model | darkMode = dark }, Cmd.none )
|
|
|
|
|
2021-06-21 11:30:31 +02:00
|
|
|
VideoMsg vMsg ->
|
|
|
|
let
|
|
|
|
( newPage, newCommand ) =
|
|
|
|
case model.page of
|
|
|
|
Video a b v c ->
|
|
|
|
let
|
|
|
|
( newVideo, cmd ) =
|
|
|
|
Video.update vMsg v
|
|
|
|
in
|
|
|
|
( Video a b newVideo c, cmd )
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
( model.page, Cmd.none )
|
|
|
|
in
|
|
|
|
( { model | page = newPage }, Cmd.map VideoMsg newCommand )
|
|
|
|
|
2020-10-04 21:31:16 +02:00
|
|
|
|
|
|
|
splitter : String -> Maybe ( String, String )
|
|
|
|
splitter input =
|
|
|
|
case String.split "=" input of
|
|
|
|
h :: t ->
|
|
|
|
Just ( h, String.join "=" t )
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
Nothing
|
|
|
|
|
|
|
|
|
|
|
|
parseQueryString : String -> Dict String String
|
|
|
|
parseQueryString input =
|
|
|
|
Dict.fromList (List.filterMap splitter (String.split "&" input))
|
2021-04-03 23:33:01 +02:00
|
|
|
|
|
|
|
|
|
|
|
nextDarkSetting : Maybe Bool -> Maybe Bool
|
|
|
|
nextDarkSetting current =
|
|
|
|
case current of
|
|
|
|
Nothing ->
|
|
|
|
Just True
|
|
|
|
|
|
|
|
Just True ->
|
|
|
|
Just False
|
|
|
|
|
|
|
|
Just False ->
|
|
|
|
Nothing
|