{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
module Network.Protocol.MusicBrainz.XML2.WebService (
getRecordingById
, getReleaseById
, searchReleasesByArtistAndRelease
) where
import Network.Protocol.MusicBrainz.Types
import Control.Applicative (liftA2, (<|>))
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Resource (MonadThrow)
import qualified Data.ByteString.Lazy as BL
import Data.Conduit (ConduitM, (.|), runConduitRes)
import Data.Conduit.Binary (sourceLbs)
import Data.List (intercalate)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Read as TR
import Data.Time.Format (parseTimeM)
import qualified Data.Vector as V
import Data.Void (Void)
import Data.XML.Types (Event)
import Network.HTTP.Base (urlEncode)
import Network.HTTP.Conduit (simpleHttp)
import Data.Time.Locale.Compat (defaultTimeLocale)
import Text.XML.Stream.Parse (parseBytes, def, content, tagNoAttr, tag', requireAttr, attr, force, many, AttrParser)
import Text.XML (Name(..))
musicBrainzWSLookup :: MonadIO m => Text -> Text -> [Text] -> m BL.ByteString
musicBrainzWSLookup :: forall (m :: * -> *).
MonadIO m =>
Text -> Text -> [Text] -> m ByteString
musicBrainzWSLookup Text
reqtype Text
param [Text]
incparams = do
let url :: [Char]
url = [Char]
"https://musicbrainz.org/ws/2/" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
reqtype [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
param [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Text] -> [Char]
incs [Text]
incparams
[Char] -> m ByteString
forall (m :: * -> *). MonadIO m => [Char] -> m ByteString
simpleHttp [Char]
url
where
incs :: [Text] -> [Char]
incs [] = [Char]
""
incs [Text]
xs = ([Char]
"?inc="[Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++) ([Char] -> [Char]) -> ([Text] -> [Char]) -> [Text] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"+" ([[Char]] -> [Char]) -> ([Text] -> [[Char]]) -> [Text] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> [Char]) -> [Text] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map Text -> [Char]
T.unpack ([Text] -> [Char]) -> [Text] -> [Char]
forall a b. (a -> b) -> a -> b
$ [Text]
xs
musicBrainzWSSearch :: MonadIO m => Text -> Text -> Maybe Int -> Maybe Int -> m BL.ByteString
musicBrainzWSSearch :: forall (m :: * -> *).
MonadIO m =>
Text -> Text -> Maybe Int -> Maybe Int -> m ByteString
musicBrainzWSSearch Text
reqtype Text
query Maybe Int
mlimit Maybe Int
moffset = do
let url :: [Char]
url = [Char]
"https://musicbrainz.org/ws/2/" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Text -> [Char]
T.unpack Text
reqtype [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"/?query=" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char] -> [Char]
urlEncode (Text -> [Char]
T.unpack Text
query) [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe Int -> [Char]
forall {a}. Show a => Maybe a -> [Char]
limit Maybe Int
mlimit [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Maybe Int -> [Char]
forall {a}. Show a => Maybe a -> [Char]
offset Maybe Int
moffset
[Char] -> m ByteString
forall (m :: * -> *). MonadIO m => [Char] -> m ByteString
simpleHttp [Char]
url
where
limit :: Maybe a -> [Char]
limit Maybe a
Nothing = [Char]
""
limit (Just a
l) = [Char]
"&limit=" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ a -> [Char]
forall a. Show a => a -> [Char]
show a
l
offset :: Maybe a -> [Char]
offset Maybe a
Nothing = [Char]
""
offset (Just a
o) = [Char]
"&offset=" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ a -> [Char]
forall a. Show a => a -> [Char]
show a
o
getRecordingById :: (MonadBaseControl IO m, MonadIO m, MonadThrow m, MonadUnliftIO m) => MBID -> m Recording
getRecordingById :: forall (m :: * -> *).
(MonadBaseControl IO m, MonadIO m, MonadThrow m,
MonadUnliftIO m) =>
MBID -> m Recording
getRecordingById MBID
mbid = do
lbs <- Text -> Text -> [Text] -> m ByteString
forall (m :: * -> *).
MonadIO m =>
Text -> Text -> [Text] -> m ByteString
musicBrainzWSLookup Text
"recording" (MBID -> Text
unMBID MBID
mbid) [Text
"artist-credits"]
rs <- runConduitRes $ sourceLbs lbs .| parseBytes def .| sinkRecordings
return $ head rs
getReleaseById :: (MonadBaseControl IO m, MonadIO m, MonadThrow m, MonadUnliftIO m) => MBID -> m Release
getReleaseById :: forall (m :: * -> *).
(MonadBaseControl IO m, MonadIO m, MonadThrow m,
MonadUnliftIO m) =>
MBID -> m Release
getReleaseById MBID
mbid = do
lbs <- Text -> Text -> [Text] -> m ByteString
forall (m :: * -> *).
MonadIO m =>
Text -> Text -> [Text] -> m ByteString
musicBrainzWSLookup Text
"release" (MBID -> Text
unMBID MBID
mbid) [Text
"recordings", Text
"artist-credits"]
rs <- runConduitRes $ sourceLbs lbs .| parseBytes def .| sinkReleases
return $ head rs
sinkRecordings :: MonadThrow m => ConduitM Event Void m [Recording]
sinkRecordings :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m [Recording]
sinkRecordings = [Char]
-> ConduitT Event Void m (Maybe [Recording])
-> ConduitT Event Void m [Recording]
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"metadata required" (NameMatcher Name
-> ConduitT Event Void m [Recording]
-> ConduitT Event Void m (Maybe [Recording])
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}metadata" (ConduitT Event Void m [Recording]
-> ConduitT Event Void m (Maybe [Recording]))
-> ConduitT Event Void m [Recording]
-> ConduitT Event Void m (Maybe [Recording])
forall a b. (a -> b) -> a -> b
$ ConduitT Event Void m (Maybe Recording)
-> ConduitT Event Void m [Recording]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ConduitT Event Void m (Maybe Recording)
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Recording)
parseRecording)
sinkReleases :: MonadThrow m => ConduitM Event Void m [Release]
sinkReleases :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m [Release]
sinkReleases = [Char]
-> ConduitT Event Void m (Maybe [Release])
-> ConduitT Event Void m [Release]
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"metadata required" (NameMatcher Name
-> ConduitT Event Void m [Release]
-> ConduitT Event Void m (Maybe [Release])
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}metadata" (ConduitT Event Void m [Release]
-> ConduitT Event Void m (Maybe [Release]))
-> ConduitT Event Void m [Release]
-> ConduitT Event Void m (Maybe [Release])
forall a b. (a -> b) -> a -> b
$ ConduitT Event Void m (Maybe Release)
-> ConduitT Event Void m [Release]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ((Maybe (Int, Release) -> Maybe Release)
-> ConduitT Event Void m (Maybe (Int, Release))
-> ConduitT Event Void m (Maybe Release)
forall a b.
(a -> b) -> ConduitT Event Void m a -> ConduitT Event Void m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (((Int, Release) -> Release)
-> Maybe (Int, Release) -> Maybe Release
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int, Release) -> Release
forall a b. (a, b) -> b
snd) ConduitT Event Void m (Maybe (Int, Release))
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe (Int, Release))
parseRelease))
sinkReleaseList :: MonadThrow m => ConduitM Event Void m [(Int, Release)]
sinkReleaseList :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m [(Int, Release)]
sinkReleaseList = [Char]
-> ConduitT Event Void m (Maybe [(Int, Release)])
-> ConduitT Event Void m [(Int, Release)]
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"metadata required" (NameMatcher Name
-> AttrParser (Maybe Text)
-> (Maybe Text -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)])
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}metadata" (Name -> AttrParser (Maybe Text)
attr Name
"created") ((Maybe Text -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)]))
-> (Maybe Text -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)])
forall a b. (a -> b) -> a -> b
$ \Maybe Text
_ ->
[Char]
-> ConduitT Event Void m (Maybe [(Int, Release)])
-> ConduitT Event Void m [(Int, Release)]
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"release-list required" (NameMatcher Name
-> AttrParser (Text, Text)
-> ((Text, Text) -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)])
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}release-list" ((Text -> Text -> (Text, Text))
-> AttrParser Text -> AttrParser Text -> AttrParser (Text, Text)
forall a b c.
(a -> b -> c) -> AttrParser a -> AttrParser b -> AttrParser c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) (Name -> AttrParser Text
requireAttr Name
"count") (Name -> AttrParser Text
requireAttr Name
"offset")) (((Text, Text) -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)]))
-> ((Text, Text) -> ConduitT Event Void m [(Int, Release)])
-> ConduitT Event Void m (Maybe [(Int, Release)])
forall a b. (a -> b) -> a -> b
$ \(Text, Text)
_ -> ConduitT Event Void m (Maybe (Int, Release))
-> ConduitT Event Void m [(Int, Release)]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ConduitT Event Void m (Maybe (Int, Release))
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe (Int, Release))
parseRelease))
parseRecording :: MonadThrow m => ConduitM Event Void m (Maybe Recording)
parseRecording :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Recording)
parseRecording = NameMatcher Name
-> AttrParser Text
-> (Text -> ConduitT Event Void m Recording)
-> ConduitT Event Void m (Maybe Recording)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}recording" (Name -> AttrParser Text
requireAttr Name
"id") ((Text -> ConduitT Event Void m Recording)
-> ConduitT Event Void m (Maybe Recording))
-> (Text -> ConduitT Event Void m Recording)
-> ConduitT Event Void m (Maybe Recording)
forall a b. (a -> b) -> a -> b
$ \Text
rid -> do
title <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}title" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
len <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}length" content
ncs <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}artist-credit" $ many parseArtistCredit
return Recording { _recordingId = MBID rid, _recordingTitle = title, _recordingLength = fmap forceReadDec len, _recordingArtistCredit = fromMaybe [] ncs }
parseArtistCredit :: MonadThrow m => ConduitM Event Void m (Maybe ArtistCredit)
parseArtistCredit :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe ArtistCredit)
parseArtistCredit = NameMatcher Name
-> AttrParser (Maybe Text)
-> (Maybe Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}name-credit" (AttrParser (Maybe Text)
buggyJoinPhrase) ((Maybe Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit))
-> (Maybe Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit)
forall a b. (a -> b) -> a -> b
$ \Maybe Text
mjp -> [Char]
-> ConduitT Event Void m (Maybe ArtistCredit)
-> ConduitT Event Void m ArtistCredit
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"artist required" (NameMatcher Name
-> AttrParser Text
-> (Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}artist" (Name -> AttrParser Text
requireAttr Name
"id") ((Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit))
-> (Text -> ConduitT Event Void m ArtistCredit)
-> ConduitT Event Void m (Maybe ArtistCredit)
forall a b. (a -> b) -> a -> b
$ \Text
aid -> do
name <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}name" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
sortName <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}sort-name" content
_ <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}disambiguation" content
let a = Artist { _artistId :: MBID
_artistId = Text -> MBID
MBID Text
aid, _artistName :: Maybe Text
_artistName = Maybe Text
name, _artistSortName :: Maybe Text
_artistSortName = Maybe Text
sortName, _artistDisambiguation :: Maybe Text
_artistDisambiguation = Maybe Text
forall a. Maybe a
Nothing }
return ArtistCredit { _artistCreditArtist = a, _artistCreditJoinPhrase = mjp, _artistCreditName = name }
)
buggyJoinPhrase :: AttrParser (Maybe Text)
buggyJoinPhrase :: AttrParser (Maybe Text)
buggyJoinPhrase = (Text -> Maybe Text) -> AttrParser Text -> AttrParser (Maybe Text)
forall a b. (a -> b) -> AttrParser a -> AttrParser b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Maybe Text
forall a. a -> Maybe a
Just (Name -> AttrParser Text
requireAttr Name
"{http://musicbrainz.org/ns/mmd-2.0#}joinphrase")
AttrParser (Maybe Text)
-> AttrParser (Maybe Text) -> AttrParser (Maybe Text)
forall a. AttrParser a -> AttrParser a -> AttrParser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Name -> AttrParser (Maybe Text)
attr Name
"{http://musicbrainz.org/ns/mmd-2.0#}joinphrase" { nameNamespace = Nothing }
forceReadDec :: Integral a => Text -> a
forceReadDec :: forall a. Integral a => Text -> a
forceReadDec = (\(Right (a
d, Text
_)) -> a
d) (Either [Char] (a, Text) -> a)
-> (Text -> Either [Char] (a, Text)) -> Text -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either [Char] (a, Text)
forall a. Integral a => Reader a
TR.decimal
parseRelease :: MonadThrow m => ConduitM Event Void m (Maybe (Int, Release))
parseRelease :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe (Int, Release))
parseRelease = NameMatcher Name
-> AttrParser (Text, Maybe Text)
-> ((Text, Maybe Text) -> ConduitT Event Void m (Int, Release))
-> ConduitT Event Void m (Maybe (Int, Release))
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}release" ((Text -> Maybe Text -> (Text, Maybe Text))
-> AttrParser Text
-> AttrParser (Maybe Text)
-> AttrParser (Text, Maybe Text)
forall a b c.
(a -> b -> c) -> AttrParser a -> AttrParser b -> AttrParser c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) (Name -> AttrParser Text
requireAttr Name
"id") (Name -> AttrParser (Maybe Text)
attr Name
"{http://musicbrainz.org/ns/ext#-2.0}score")) (((Text, Maybe Text) -> ConduitT Event Void m (Int, Release))
-> ConduitT Event Void m (Maybe (Int, Release)))
-> ((Text, Maybe Text) -> ConduitT Event Void m (Int, Release))
-> ConduitT Event Void m (Maybe (Int, Release))
forall a b. (a -> b) -> a -> b
$ \(Text
rid,Maybe Text
score) -> do
title <- [Char]
-> ConduitT Event Void m (Maybe Text) -> ConduitT Event Void m Text
forall (m :: * -> *) a.
MonadThrow m =>
[Char] -> m (Maybe a) -> m a
force [Char]
"title required" (NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}title" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content)
status <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}status" content
quality <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}quality" content
packaging <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}packaging" content
tr <- parseTextRepresentation
ncs <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}artist-credit" $ many parseArtistCredit
_ <- parseReleaseGroup
date <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}date" content
country <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}country" content
rel <- tag' "{http://musicbrainz.org/ns/mmd-2.0#}release-event-list" (requireAttr "count") $ \Text
_ -> ConduitT Event Void m (Maybe ReleaseEvent)
-> ConduitT Event Void m [ReleaseEvent]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ConduitT Event Void m (Maybe ReleaseEvent)
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe ReleaseEvent)
parseReleaseEvent
barcode <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}barcode" content
amazonASIN <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}asin" content
coverArtArchive <- parseCoverArtArchive
_ <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}label-info-list" $ parseLabelInfo
media <- tag' "{http://musicbrainz.org/ns/mmd-2.0#}medium-list" (requireAttr "count") $ \Text
_ -> (NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}track-count" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content ConduitT Event Void m (Maybe Text)
-> ConduitT Event Void m [Medium] -> ConduitT Event Void m [Medium]
forall a b.
ConduitT Event Void m a
-> ConduitT Event Void m b -> ConduitT Event Void m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ConduitT Event Void m (Maybe Medium)
-> ConduitT Event Void m [Medium]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ConduitT Event Void m (Maybe Medium)
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Medium)
parseMedium)
return (maybe 0 forceReadDec score, Release {
_releaseId = MBID rid
, _releaseTitle = title
, _releaseStatus = status
, _releaseQuality = quality
, _releasePackaging = packaging
, _releaseTextRepresentation = tr
, _releaseArtistCredit = fromMaybe [] ncs
, _releaseDate = parseTimeM True defaultTimeLocale "%Y-%m-%d" . T.unpack =<< date
, _releaseCountry = country
, _releaseEvents = fromMaybe [] rel
, _releaseBarcode = barcode
, _releaseASIN = amazonASIN
, _releaseCoverArtArchive = coverArtArchive
, _releaseMedia = V.fromList (fromMaybe [] media)
})
parseTextRepresentation :: MonadThrow m => ConduitM Event Void m (Maybe TextRepresentation)
parseTextRepresentation :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe TextRepresentation)
parseTextRepresentation = NameMatcher Name
-> ConduitT Event Void m TextRepresentation
-> ConduitT Event Void m (Maybe TextRepresentation)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}text-representation" (ConduitT Event Void m TextRepresentation
-> ConduitT Event Void m (Maybe TextRepresentation))
-> ConduitT Event Void m TextRepresentation
-> ConduitT Event Void m (Maybe TextRepresentation)
forall a b. (a -> b) -> a -> b
$ do
lang <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}language" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
script <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}script" content
return TextRepresentation {
_textRepLanguage = lang
, _textRepScript = script
}
parseMedium :: MonadThrow m => ConduitM Event Void m (Maybe Medium)
parseMedium :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Medium)
parseMedium = NameMatcher Name
-> ConduitT Event Void m Medium
-> ConduitT Event Void m (Maybe Medium)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}medium" (ConduitT Event Void m Medium
-> ConduitT Event Void m (Maybe Medium))
-> ConduitT Event Void m Medium
-> ConduitT Event Void m (Maybe Medium)
forall a b. (a -> b) -> a -> b
$ do
title <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}title" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
position <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}position" content
format <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}format" content
mmed <- tag' "{http://musicbrainz.org/ns/mmd-2.0#}track-list" (liftA2 (,) (requireAttr "count") (attr "offset")) $ \(Text
c,Maybe Text
o) -> do
tracks <- ConduitT Event Void m (Maybe Track)
-> ConduitT Event Void m [Track]
forall (m :: * -> *) o a.
Monad m =>
ConduitT Event o m (Maybe a) -> ConduitT Event o m [a]
many ConduitT Event Void m (Maybe Track)
forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Track)
parseTrack
return Medium {
_mediumTitle = title
, _mediumPosition = fmap forceReadDec position
, _mediumFormat = format
, _mediumTrackCount = forceReadDec c
, _mediumTrackOffset = fmap forceReadDec o
, _mediumTrackList = Just tracks
}
case mmed of
Just Medium
med -> Medium -> ConduitT Event Void m Medium
forall a. a -> ConduitT Event Void m a
forall (m :: * -> *) a. Monad m => a -> m a
return Medium
med
Maybe Medium
Nothing -> [Char] -> ConduitT Event Void m Medium
forall a. HasCallStack => [Char] -> a
error [Char]
"Missing track list"
parseTrack :: MonadThrow m => ConduitM Event Void m (Maybe Track)
parseTrack :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Track)
parseTrack = NameMatcher Name
-> AttrParser Text
-> (Text -> ConduitT Event Void m Track)
-> ConduitT Event Void m (Maybe Track)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}track" (Name -> AttrParser Text
requireAttr Name
"id") ((Text -> ConduitT Event Void m Track)
-> ConduitT Event Void m (Maybe Track))
-> (Text -> ConduitT Event Void m Track)
-> ConduitT Event Void m (Maybe Track)
forall a b. (a -> b) -> a -> b
$ \Text
i -> do
position <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}position" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
number <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}number" content
len <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}length" content
artistcredit <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}artist-credit" $ many parseArtistCredit
recording <- force "recording required" parseRecording
return Track {
_trackId = MBID i
, _trackArtistCredit = fromMaybe [] artistcredit
, _trackPosition = fmap forceReadDec position
, _trackNumber = number
, _trackLength = fmap forceReadDec len
, _trackRecording = recording
}
parseReleaseGroup :: MonadThrow m => ConduitM Event Void m (Maybe ReleaseGroup)
parseReleaseGroup :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe ReleaseGroup)
parseReleaseGroup = NameMatcher Name
-> AttrParser (Text, Text)
-> ((Text, Text) -> ConduitT Event Void m ReleaseGroup)
-> ConduitT Event Void m (Maybe ReleaseGroup)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}release-group" ((Text -> Text -> (Text, Text))
-> AttrParser Text -> AttrParser Text -> AttrParser (Text, Text)
forall a b c.
(a -> b -> c) -> AttrParser a -> AttrParser b -> AttrParser c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) (Name -> AttrParser Text
requireAttr Name
"type") (Name -> AttrParser Text
requireAttr Name
"id")) (((Text, Text) -> ConduitT Event Void m ReleaseGroup)
-> ConduitT Event Void m (Maybe ReleaseGroup))
-> ((Text, Text) -> ConduitT Event Void m ReleaseGroup)
-> ConduitT Event Void m (Maybe ReleaseGroup)
forall a b. (a -> b) -> a -> b
$ \(Text
t,Text
i) -> do
title <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}title" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
frd <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}first-release-date" content
pt <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}primary-type" content
ncs <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}artist-credit" $ many parseArtistCredit
return ReleaseGroup {
_releaseGroupId = MBID i
, _releaseGroupType = t
, _releaseGroupTitle = title
, _releaseGroupFirstReleaseDate = frd
, _releaseGroupPrimaryType = pt
, _releaseGroupArtistCredit = fromMaybe [] ncs
}
parseLabelInfo :: MonadThrow m => ConduitM Event Void m (Maybe LabelInfo)
parseLabelInfo :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe LabelInfo)
parseLabelInfo = NameMatcher Name
-> ConduitT Event Void m LabelInfo
-> ConduitT Event Void m (Maybe LabelInfo)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}label-info" (ConduitT Event Void m LabelInfo
-> ConduitT Event Void m (Maybe LabelInfo))
-> ConduitT Event Void m LabelInfo
-> ConduitT Event Void m (Maybe LabelInfo)
forall a b. (a -> b) -> a -> b
$ do
catno <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}catalog-number" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
label <- force "label required" parseLabel
return LabelInfo {
_labelInfoCatalogNumber = catno
, _labelInfoLabel = label
}
parseLabel :: MonadThrow m => ConduitM Event Void m (Maybe Label)
parseLabel :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Label)
parseLabel = NameMatcher Name
-> AttrParser Text
-> (Text -> ConduitT Event Void m Label)
-> ConduitT Event Void m (Maybe Label)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}label" (Name -> AttrParser Text
requireAttr Name
"id") ((Text -> ConduitT Event Void m Label)
-> ConduitT Event Void m (Maybe Label))
-> (Text -> ConduitT Event Void m Label)
-> ConduitT Event Void m (Maybe Label)
forall a b. (a -> b) -> a -> b
$ \Text
i -> do
name <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}name" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
sortname <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}sort-name" content
labelcode <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}label-code" content
return Label {
_labelId = MBID i
, _labelName = name
, _labelSortName = sortname
, _labelLabelCode = labelcode
}
parseReleaseEvent :: MonadThrow m => ConduitM Event Void m (Maybe ReleaseEvent)
parseReleaseEvent :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe ReleaseEvent)
parseReleaseEvent = NameMatcher Name
-> ConduitT Event Void m ReleaseEvent
-> ConduitT Event Void m (Maybe ReleaseEvent)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}release-event" (ConduitT Event Void m ReleaseEvent
-> ConduitT Event Void m (Maybe ReleaseEvent))
-> ConduitT Event Void m ReleaseEvent
-> ConduitT Event Void m (Maybe ReleaseEvent)
forall a b. (a -> b) -> a -> b
$ do
date <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}date" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
area <- parseArea
return ReleaseEvent {
_releaseEventDate = parseTimeM True defaultTimeLocale "%Y-%m-%d" . T.unpack =<< date
, _releaseEventArea = area
}
parseArea :: MonadThrow m => ConduitM Event Void m (Maybe Area)
parseArea :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe Area)
parseArea = NameMatcher Name
-> AttrParser Text
-> (Text -> ConduitT Event Void m Area)
-> ConduitT Event Void m (Maybe Area)
forall (m :: * -> *) a b o c.
MonadThrow m =>
NameMatcher a
-> AttrParser b
-> (b -> ConduitT Event o m c)
-> ConduitT Event o m (Maybe c)
tag' NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}area" (Name -> AttrParser Text
requireAttr Name
"id") ((Text -> ConduitT Event Void m Area)
-> ConduitT Event Void m (Maybe Area))
-> (Text -> ConduitT Event Void m Area)
-> ConduitT Event Void m (Maybe Area)
forall a b. (a -> b) -> a -> b
$ \Text
i -> do
name <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}name" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
sortname <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}sort-name" content
isocodes1 <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}iso-3166-1-code-list" $ many parseISO3166Code
isocodes2 <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}iso-3166-2-code-list" $ many parseISO3166Code
isocodes3 <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}iso-3166-3-code-list" $ many parseISO3166Code
return Area {
_areaId = MBID i
, _areaName = name
, _areaSortName = sortname
, _areaISO3166_1Codes = fromMaybe [] isocodes1
, _areaISO3166_2Codes = fromMaybe [] isocodes2
, _areaISO3166_3Codes = fromMaybe [] isocodes3
}
parseISO3166Code :: MonadThrow m => ConduitM Event Void m (Maybe ISO3166Code)
parseISO3166Code :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe ISO3166Code)
parseISO3166Code = NameMatcher Name
-> ConduitT Event Void m ISO3166Code
-> ConduitT Event Void m (Maybe ISO3166Code)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}iso-3166-1-code" (ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content ConduitT Event Void m Text
-> (Text -> ConduitT Event Void m ISO3166Code)
-> ConduitT Event Void m ISO3166Code
forall a b.
ConduitT Event Void m a
-> (a -> ConduitT Event Void m b) -> ConduitT Event Void m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (ISO3166Code -> ConduitT Event Void m ISO3166Code
forall a. a -> ConduitT Event Void m a
forall (m :: * -> *) a. Monad m => a -> m a
return (ISO3166Code -> ConduitT Event Void m ISO3166Code)
-> (Text -> ISO3166Code)
-> Text
-> ConduitT Event Void m ISO3166Code
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ISO3166Code
ISO3166Code))
parseCoverArtArchive :: MonadThrow m => ConduitM Event Void m (Maybe CoverArtArchive)
parseCoverArtArchive :: forall (m :: * -> *).
MonadThrow m =>
ConduitM Event Void m (Maybe CoverArtArchive)
parseCoverArtArchive = NameMatcher Name
-> ConduitT Event Void m CoverArtArchive
-> ConduitT Event Void m (Maybe CoverArtArchive)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}cover-art-archive" (ConduitT Event Void m CoverArtArchive
-> ConduitT Event Void m (Maybe CoverArtArchive))
-> ConduitT Event Void m CoverArtArchive
-> ConduitT Event Void m (Maybe CoverArtArchive)
forall a b. (a -> b) -> a -> b
$ do
artwork <- NameMatcher Name
-> ConduitT Event Void m Text -> ConduitT Event Void m (Maybe Text)
forall (m :: * -> *) a o b.
MonadThrow m =>
NameMatcher a
-> ConduitT Event o m b -> ConduitT Event o m (Maybe b)
tagNoAttr NameMatcher Name
"{http://musicbrainz.org/ns/mmd-2.0#}artwork" ConduitT Event Void m Text
forall (m :: * -> *) o. MonadThrow m => ConduitT Event o m Text
content
count <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}count" content
front <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}front" content
back <- tagNoAttr "{http://musicbrainz.org/ns/mmd-2.0#}back" content
return CoverArtArchive {
_coverArtArchiveArtwork = if artwork == Just "true" then Just True else Just False
, _coverArtArchiveCount = fmap forceReadDec count
, _coverArtArchiveFront = if front == Just "true" then Just True else Just False
, _coverArtArchiveBack = if back == Just "true" then Just True else Just False
}
searchReleasesByArtistAndRelease :: (MonadIO m, MonadBaseControl IO m, MonadThrow m, MonadUnliftIO m) => Text -> Text -> Maybe Int -> Maybe Int -> m [(Int, Release)]
searchReleasesByArtistAndRelease :: forall (m :: * -> *).
(MonadIO m, MonadBaseControl IO m, MonadThrow m,
MonadUnliftIO m) =>
Text -> Text -> Maybe Int -> Maybe Int -> m [(Int, Release)]
searchReleasesByArtistAndRelease Text
artist Text
release Maybe Int
mlimit Maybe Int
moffset = do
lbs <- Text -> Text -> Maybe Int -> Maybe Int -> m ByteString
forall (m :: * -> *).
MonadIO m =>
Text -> Text -> Maybe Int -> Maybe Int -> m ByteString
musicBrainzWSSearch Text
"release" ([Text] -> Text
T.concat [Text
"artist:\"", Text
artist, Text
"\" AND release:\"", Text
release, Text
"\""]) Maybe Int
mlimit Maybe Int
moffset
rs <- runConduitRes $ sourceLbs lbs .| parseBytes def .| sinkReleaseList
return rs