netease-fm-1.2.2: lib/FM/NetEase/JSON.hs
{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
module FM.NetEase.JSON (
encodeJSON
, decodeFM
, decodeRecommend
, decodePlayList
, decodePlayLists
, decodeUrl
, decodeLyrics
, decodeUserId
) where
import Control.Monad (forM)
import qualified Data.Aeson as JSON
import Data.Aeson ((.:))
import Data.Aeson.Extra
import qualified Data.Aeson.Types as JSON
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Vector as V
import qualified Data.Text as T
import qualified FM.Song as Song
newtype FM = FM [Song.Song]
newtype Recommend = Recommend [Song.Song]
newtype PlayList = PlayList [Song.Song]
instance JSON.FromJSON FM where
parseJSON = onObject $ \v -> FM <$> (v .: "data" >>= parseSongList)
instance JSON.FromJSON Recommend where
parseJSON = onObject $ \v -> Recommend <$> (v .: "recommend" >>= parseSongList)
instance JSON.FromJSON PlayList where
parseJSON = onObject $ \v -> PlayList <$> (v .: "result" >>= parse)
where parse = onObject $ \v -> v .: "tracks" >>= parseSongList
parseSongList :: JSON.Value -> JSON.Parser [Song.Song]
parseSongList = onArray $ \v -> mapM parseSong (V.toList v)
parseSong :: JSON.Value -> JSON.Parser Song.Song
parseSong = onObject $ \v -> do
let url = Nothing
title <- v .: "name"
uid <- v .: "id"
artists <- parseArtists =<< (v .: "artists")
album <- parseAlbum =<< (v .: "album")
return Song.Song {..}
parseArtists :: JSON.Value -> JSON.Parser [String]
parseArtists = onArray $ \v -> forM (V.toList v) (onObject $ \v -> v .: "name")
parseAlbum :: JSON.Value -> JSON.Parser String
parseAlbum = onObject $ \v -> v .: "name"
decodeFM :: BS.ByteString -> Either String [Song.Song]
decodeFM bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (FM xs) = xs
decodeRecommend :: BS.ByteString -> Either String [Song.Song]
decodeRecommend bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (Recommend xs) = xs
decodePlayList :: BS.ByteString -> Either String [Song.Song]
decodePlayList bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (PlayList xs) = xs
newtype PlayLists = PlayLists [(Int, String)]
instance JSON.FromJSON PlayLists where
parseJSON = onObject $ \v -> PlayLists <$> (v .: "playlist" >>= parse)
where
parse = onArray $ \v -> mapM parsePlayLists (V.toList v)
where
parsePlayLists = onObject $ \v -> do
id <- v .: "id"
name <- v .: "name"
return (id, name)
decodePlayLists :: BS.ByteString -> Either String [(Int, String)]
decodePlayLists bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (PlayLists xs) = xs
newtype Url = Url [String]
instance JSON.FromJSON Url where
parseJSON = onObject $ \v -> v .: "data" >>= parse
where parse = onArray $ \v -> Url <$> mapM parse2 (V.toList v)
parse2 = onObject $ \v -> v .: "url"
decodeUrl :: BS.ByteString -> Either String [String]
decodeUrl bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (Url url) = url
newtype Lyrics = Lyrics [String]
instance JSON.FromJSON Lyrics where
parseJSON = onObject $ \v -> v .: "lrc" >>= parse
where parse = onObject $ \v -> Lyrics . lines . T.unpack <$> (v .: "lyric")
decodeLyrics :: BS.ByteString -> Either String [String]
decodeLyrics bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (Lyrics lyrics) = lyrics
newtype UserId = UserId Int
instance JSON.FromJSON UserId where
parseJSON = onObject $ \v -> v .: "account" >>= parse
where parse = onObject $ \v -> UserId <$> (v .: "id")
decodeUserId :: BS.ByteString -> Either String Int
decodeUserId bs = unwrap <$> JSON.eitherDecode (BL.fromStrict bs)
where unwrap (UserId id) = id