module Twitch exposing ( Playlist , Video , fetchPlaylists , playlistMiniatureUrl ) import Html.Parser import Http import Json.Decode as Decode import Task exposing (Task) type alias Playlist = { url : String , name : String , videos : List Video } type alias Video = { name : String , url : String , duration : Int } 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 playlist.videos of Just v -> v.url ++ "miniature-050.png" _ -> "" fetchPlaylists : Task x (List Playlist) fetchPlaylists = fetchPlaylistPath |> Task.andThen fetchPlaylistsMapper 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) ) 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 } _ -> Ok { name = "", url = url, duration = 0 } 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 " 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" 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.map2 (\x y -> Video x url y) (Decode.field "title" Decode.string) (Decode.map Basics.round (Decode.field "duration" Decode.float))