module Twitch exposing (Playlist, Video, fetchPlaylists) import Html.Parser import Http import Json.Decode as Decode import Task exposing (Task) type alias Playlist = { name : String , videos : List Video } type alias Video = { name : String , url : String , duration : Float } 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 } fetchPlaylists : Task x (List Playlist) fetchPlaylists = fetchPlaylistPath |> Task.andThen fetchPlaylistsMapper fetchPlaylistPath : Task x (List String) fetchPlaylistPath = get { url = "/videos" , resolver = Http.stringResolver parsePlaylistPath } fetchPlaylist : String -> Task x Playlist fetchPlaylist name = get { url = "/videos/" ++ name ++ "/description.json" , resolver = Http.stringResolver parsePlaylistName } |> Task.andThen (\a -> Task.map (\b -> Playlist a b) (get { url = "/videos/" ++ name , resolver = Http.stringResolver parsePlaylistVideoPaths } |> Task.andThen (\c -> fetchVideos name c) ) ) fetchVideo : String -> String -> Task x Video fetchVideo playlist video = let url = "/videos/" ++ playlist ++ video in get { url = "/videos/" ++ playlist ++ "/" ++ video ++ "/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) 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 "" parsePlaylistVideoPaths : Http.Response String -> Result x (List String) parsePlaylistVideoPaths 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 (List.filter (String.endsWith "/") h) _ -> Ok [] _ -> Ok [] parsePlaylistPath : Http.Response String -> Result x (List String) parsePlaylistPath 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.field "duration" Decode.float)