{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module VoicebaseClient
(
transcribe,
transcribeFile,
transcribeBytes,
transcribeParse,
BearerToken,
Error(..),
LazyByteFile(..),
Configuration(..),
Channels(..),
Speaker(..),
Language(..)
) where
import Control.Applicative
import Control.Lens hiding ((.=))
import Control.Monad
import Data.Bifunctor
import Data.Maybe
import Data.Monoid
import Data.Text.Encoding as TE
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Lazy.Char8 as C8LBS
import Data.Text (Text, unpack)
import Data.Aeson (FromJSON (parseJSON), ToJSON (..),
Value, eitherDecode, encode,
object, (.=))
import Data.Aeson.Types (parseEither)
import Network.HTTP.Client (defaultManagerSettings,
managerResponseTimeout,
responseTimeoutMicro)
import Network.HTTP.Client.OpenSSL
import Network.Mime (MimeType)
import Network.Wreq
import qualified Network.Wreq.Session as Sess
import OpenSSL.Session (context)
import GHC.Generics
import qualified Json.ProgressTypes as Progress
import qualified Json.SubmitMediaTypes as Submit
import qualified Json.TranscriptTypes as Transcript
import System.IO (fixIO)
type BearerToken = String
data Configuration = Configuration {
channels :: Maybe Channels
, language :: Language
} deriving Generic
data Channels = Channels {
left :: Speaker
, right :: Speaker
} deriving Generic
data Speaker = Speaker {
speaker :: Text
} deriving Generic
instance ToJSON Channels
instance ToJSON Speaker
instance ToJSON Configuration where
toJSON Configuration{..} = let
ingest c = [
"ingest" .= object [
"channels" .= c
]
]
in object [
"configuration" .= (object . concat) [
["language" .= language]
, maybe [] ingest channels
]
]
data Language =
Dutch | EnglishUS | EnglishUK | EnglishAus | French | German | Italian | Portuguese | SpanishLatinAmerican | SpanishSpain
instance ToJSON Language where
toJSON Dutch = "nl-NL"
toJSON EnglishUS = "en-US"
toJSON EnglishUK = "en-UK"
toJSON EnglishAus = "en-AU"
toJSON French = "fr-FR"
toJSON German = "de-DE"
toJSON Italian = "it-IT"
toJSON Portuguese = "pt-BR"
toJSON SpanishLatinAmerican = "es-LA"
toJSON SpanishSpain = "es-ES"
data Error = UnkownResponse | ParseError String
deriving(Show)
url :: String
url = "https://apis.voicebase.com/v2-beta/media"
timeout = responseTimeoutMicro 10000
makeOpts :: BearerToken -> Options
makeOpts token = withAuthorization token defaults
withAuthorization :: BearerToken -> Options -> Options
withAuthorization token options = options & manager .~ Left (opensslManagerSettings context)
& manager .~ Left (defaultManagerSettings { managerResponseTimeout = timeout} )
& header "Authorization" .~ [(BS.pack ("Bearer " ++ token))]
transcribeParse :: Configuration -> BearerToken -> FilePath -> IO(Either Error Transcript.Transcript)
transcribeParse config token filepath = fmap (join . second (first ParseError . parseEither parseJSON)) (transcribeFile config token filepath)
inputName = "media"
transcribeFile :: Configuration -> BearerToken -> FilePath -> IO(Either Error Value)
transcribeFile config bearerToken filePath = transcribe bearerToken defaults
[
partFileSource inputName filePath,
partLBS "configuration" $ encode $ config
]
data LazyByteFile = LazyByteFile {
content :: LBS.ByteString,
mimetype :: MimeType
}
transcribeBytes :: Configuration -> BearerToken -> LazyByteFile -> IO(Either Error Value)
transcribeBytes config bearerToken (LazyByteFile content mimetype) =
transcribe bearerToken defaults
[partLBS inputName content &
partContentType ?~ mimetype &
partFileName ?~ "",
partLBS "configuration" $ encode $ config
]
transcribe :: BearerToken -> Options -> [Part] -> IO(Either Error Value)
transcribe token options parts = do
session <- Sess.newSession
response <- Sess.postWith (withAuthorization token options) session url parts
case Submit.parse (response ^. responseBody) of
Left error -> return $ Left $ ParseError error
Right parsed -> do
waitResult <- pollStatus token session (Submit.topLevelMediaId parsed)
case waitResult of
Right _ -> first ParseError . eitherDecode <$> (requestTranscript token session (Submit.topLevelMediaId parsed))
Left error -> return $ Left $ error
pollStatus :: BearerToken -> Sess.Session -> Text -> IO(Either Error ())
pollStatus token session mediaId = pollStatus' token session mediaId "pending"
pollStatus' :: BearerToken -> Sess.Session -> Text -> Text -> IO (Either Error ())
pollStatus' token session mediaId "pending" = do
response <- Sess.getWith (makeOpts token) session (url <> "/" <> (unpack mediaId) <> "/progress")
case Progress.parse (response ^. responseBody) of
Left error -> return $ Left $ ParseError error
Right parsed -> pollStatus' token session mediaId ((Progress.progressStatus . Progress.topLevelProgress) $ parsed)
pollStatus' token session mediaId "started" = pollStatus' token session mediaId "pending"
pollStatus' token session mediaId "completed" = return $ Right ()
pollStatus' _ _ _ _ = return $ Left UnkownResponse
requestTranscript :: BearerToken -> Sess.Session -> Text -> IO (C8LBS.ByteString)
requestTranscript token session mediaId = do
response <- Sess.getWith (makeOpts token) session (url <> "/" <> (unpack mediaId))
return $ response ^. responseBody