packages feed

freesound 0.2.0 → 0.3.0

raw patch · 20 files changed

+546/−456 lines, 20 filesdep +containersdep +hspec-corePVP ok

version bump matches the API change (PVP)

Dependencies added: containers, hspec-core

API changes (from Hackage documentation)

- Sound.Freesound: data Resource a
- Sound.Freesound: getBookmarkCategories :: Detail -> Freesound [Category]
- Sound.Freesound: getPacks :: Detail -> Freesound Packs
- Sound.Freesound: getResource :: (FromJSON a) => Resource a -> Freesound a
- Sound.Freesound: getSimilar :: Pagination -> Detail -> Freesound Sounds
- Sound.Freesound: getSimilar_ :: Detail -> Freesound Sounds
- Sound.Freesound: getSounds :: Pagination -> Detail -> Freesound Sounds
- Sound.Freesound: getSounds_ :: Detail -> Freesound Sounds
- Sound.Freesound: getURI :: URI -> Freesound ByteString
- Sound.Freesound: getUser :: (User a) => a -> Freesound Detail
- Sound.Freesound: search :: Pagination -> Sorting -> Filters -> Query -> Freesound Sounds
- Sound.Freesound: search_ :: Query -> Freesound Sounds
- Sound.Freesound.Bookmark: category :: Category -> Text
- Sound.Freesound.Bookmark: data Bookmark
- Sound.Freesound.Bookmark: data Category
- Sound.Freesound.Bookmark: getSounds :: Category -> Freesound [Bookmark]
- Sound.Freesound.Bookmark: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.Bookmark.Bookmark
- Sound.Freesound.Bookmark: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.Bookmark.Category
- Sound.Freesound.Bookmark: instance GHC.Classes.Eq Sound.Freesound.Bookmark.Bookmark
- Sound.Freesound.Bookmark: instance GHC.Classes.Eq Sound.Freesound.Bookmark.Category
- Sound.Freesound.Bookmark: instance GHC.Show.Show Sound.Freesound.Bookmark.Bookmark
- Sound.Freesound.Bookmark: instance GHC.Show.Show Sound.Freesound.Bookmark.Category
- Sound.Freesound.Bookmark: name :: Bookmark -> Text
- Sound.Freesound.Bookmark: sound :: Bookmark -> Summary
- Sound.Freesound.Bookmark: sounds :: Category -> Resource [Bookmark]
- Sound.Freesound.Bookmark: url :: Category -> URI
- Sound.Freesound.List: class Elem a
- Sound.Freesound.List: elemsFieldName :: Elem a => a -> Text
- Sound.Freesound.List: instance (Sound.Freesound.List.Elem a, Data.Aeson.Types.Class.FromJSON a) => Data.Aeson.Types.Class.FromJSON (Sound.Freesound.List.List a)
- Sound.Freesound.Pack: class Pack a
- Sound.Freesound.Pack: data Detail
- Sound.Freesound.Pack: data Summary
- Sound.Freesound.Pack: getSounds :: (Pack a) => Pagination -> a -> Freesound Sounds
- Sound.Freesound.Pack: getSounds_ :: (Pack a) => a -> Freesound Sounds
- Sound.Freesound.Pack: ref :: Pack a => a -> Resource ()
- Sound.Freesound.Pack: type Packs = List Summary
- Sound.Freesound.Search: [resultsPerPage] :: Pagination -> Int
- Sound.Freesound.Search.Filter: avgRating :: Numerical Double -> Filters
- Sound.Freesound.Search.Filter: bitdepth :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: bitrate :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: channels :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: comment :: Text -> Filters
- Sound.Freesound.Search.Filter: comments :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: created :: Text -> Filters
- Sound.Freesound.Search.Filter: data Filters
- Sound.Freesound.Search.Filter: description :: Text -> Filters
- Sound.Freesound.Search.Filter: duration :: Numerical Double -> Filters
- Sound.Freesound.Search.Filter: fileType :: FileType -> Filters
- Sound.Freesound.Search.Filter: filesize :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: id :: Numerical SoundId -> Filters
- Sound.Freesound.Search.Filter: instance Data.Default.Class.Default Sound.Freesound.Search.Filter.Filters
- Sound.Freesound.Search.Filter: instance GHC.Base.Monoid Sound.Freesound.Search.Filter.Filters
- Sound.Freesound.Search.Filter: instance GHC.Classes.Eq Sound.Freesound.Search.Filter.F_Bool
- Sound.Freesound.Search.Filter: instance GHC.Classes.Eq Sound.Freesound.Search.Filter.Filter
- Sound.Freesound.Search.Filter: instance GHC.Classes.Eq Sound.Freesound.Search.Filter.License
- Sound.Freesound.Search.Filter: instance GHC.Show.Show Sound.Freesound.Search.Filter.F_Bool
- Sound.Freesound.Search.Filter: instance GHC.Show.Show Sound.Freesound.Search.Filter.Filter
- Sound.Freesound.Search.Filter: instance GHC.Show.Show Sound.Freesound.Search.Filter.Filters
- Sound.Freesound.Search.Filter: instance GHC.Show.Show Sound.Freesound.Search.Filter.License
- Sound.Freesound.Search.Filter: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Filter.F_Bool
- Sound.Freesound.Search.Filter: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Filter.Filter
- Sound.Freesound.Search.Filter: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Filter.Filters
- Sound.Freesound.Search.Filter: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Filter.License
- Sound.Freesound.Search.Filter: isGeotagged :: Bool -> Filters
- Sound.Freesound.Search.Filter: isRemix :: Bool -> Filters
- Sound.Freesound.Search.Filter: license :: License -> Filters
- Sound.Freesound.Search.Filter: md5 :: Text -> Filters
- Sound.Freesound.Search.Filter: numDownloads :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: numRatings :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: originalFilename :: Text -> Filters
- Sound.Freesound.Search.Filter: pack :: Text -> Filters
- Sound.Freesound.Search.Filter: samplerate :: Numerical Integer -> Filters
- Sound.Freesound.Search.Filter: tag :: Text -> Filters
- Sound.Freesound.Search.Filter: username :: Text -> Filters
- Sound.Freesound.Search.Filter: wasRemixed :: Bool -> Filters
- Sound.Freesound.Search.Numerical: between :: Ord a => a -> a -> Numerical a
- Sound.Freesound.Search.Numerical: data Numerical a
- Sound.Freesound.Search.Numerical: equals :: a -> Numerical a
- Sound.Freesound.Search.Numerical: greaterThan :: a -> Numerical a
- Sound.Freesound.Search.Numerical: instance GHC.Classes.Eq a => GHC.Classes.Eq (Sound.Freesound.Search.Numerical.Numerical a)
- Sound.Freesound.Search.Numerical: instance GHC.Show.Show a => GHC.Show.Show (Sound.Freesound.Search.Numerical.Numerical a)
- Sound.Freesound.Search.Numerical: instance Sound.Freesound.Search.Numerical.NumericalQueryValue Data.Time.Clock.UTC.UTCTime
- Sound.Freesound.Search.Numerical: instance Sound.Freesound.Search.Numerical.NumericalQueryValue GHC.Integer.Type.Integer
- Sound.Freesound.Search.Numerical: instance Sound.Freesound.Search.Numerical.NumericalQueryValue GHC.Types.Double
- Sound.Freesound.Search.Numerical: instance Sound.Freesound.Search.Numerical.NumericalQueryValue Sound.Freesound.Sound.Type.SoundId
- Sound.Freesound.Search.Numerical: instance Sound.Freesound.Search.Numerical.NumericalQueryValue a => Network.HTTP.Types.QueryLike.QueryValueLike (Sound.Freesound.Search.Numerical.Numerical a)
- Sound.Freesound.Search.Numerical: lessThan :: a -> Numerical a
- Sound.Freesound.Search.Query: (&) :: Query -> Query -> Query
- Sound.Freesound.Search.Query: data Query
- Sound.Freesound.Search.Query: exclude :: Text -> Query
- Sound.Freesound.Search.Query: include :: Text -> Query
- Sound.Freesound.Search.Query: instance GHC.Classes.Eq Sound.Freesound.Search.Query.Query
- Sound.Freesound.Search.Query: instance GHC.Classes.Eq Sound.Freesound.Search.Query.Term
- Sound.Freesound.Search.Query: instance GHC.Show.Show Sound.Freesound.Search.Query.Query
- Sound.Freesound.Search.Query: instance GHC.Show.Show Sound.Freesound.Search.Query.Term
- Sound.Freesound.Search.Query: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Query.Query
- Sound.Freesound.Sound: data SoundId
- Sound.Freesound.Sound: getSimilar :: Pagination -> Detail -> Freesound Sounds
- Sound.Freesound.Sound: getSimilar_ :: Detail -> Freesound Sounds
- Sound.Freesound.Sound: search :: Pagination -> Sorting -> Filters -> Query -> Freesound Sounds
- Sound.Freesound.Sound: search_ :: Query -> Freesound Sounds
- Sound.Freesound.Sound: type Sounds = List Summary
- Sound.Freesound.User: about :: Detail -> Maybe Text
- Sound.Freesound.User: bookmarkCategories :: Detail -> Resource Categories
- Sound.Freesound.User: class User a
- Sound.Freesound.User: data Detail
- Sound.Freesound.User: data Summary
- Sound.Freesound.User: dateJoined :: Detail -> Text
- Sound.Freesound.User: firstName :: Detail -> Maybe Text
- Sound.Freesound.User: getBookmarkCategories :: Detail -> Freesound [Category]
- Sound.Freesound.User: getPacks :: Detail -> Freesound Packs
- Sound.Freesound.User: getSounds :: Pagination -> Detail -> Freesound Sounds
- Sound.Freesound.User: getSounds_ :: Detail -> Freesound Sounds
- Sound.Freesound.User: getUser :: (User a) => a -> Freesound Detail
- Sound.Freesound.User: homePage :: Detail -> Maybe Text
- Sound.Freesound.User: lastName :: Detail -> Maybe Text
- Sound.Freesound.User: name :: User a => a -> Text
- Sound.Freesound.User: packs :: Detail -> Resource (List Summary)
- Sound.Freesound.User: ref :: User a => a -> Resource Detail
- Sound.Freesound.User: signature :: Detail -> Maybe Text
- Sound.Freesound.User: sounds :: Detail -> Resource (List Summary)
- Sound.Freesound.User: url :: User a => a -> URI
+ Sound.Freesound: Resource :: URI -> Resource a
+ Sound.Freesound: download :: URI -> Freesound ByteString
+ Sound.Freesound: downloadToFile :: FilePath -> URI -> Freesound ()
+ Sound.Freesound: get :: (FromJSON a) => Resource a -> Freesound a
+ Sound.Freesound: newtype Resource a
+ Sound.Freesound.Bookmark: BookmarkCategory :: URI -> Text -> Int -> Resource (List Summary) -> BookmarkCategory
+ Sound.Freesound.Bookmark: [name] :: BookmarkCategory -> Text
+ Sound.Freesound.Bookmark: [numSounds] :: BookmarkCategory -> Int
+ Sound.Freesound.Bookmark: [sounds] :: BookmarkCategory -> Resource (List Summary)
+ Sound.Freesound.Bookmark: [url] :: BookmarkCategory -> URI
+ Sound.Freesound.Bookmark: data BookmarkCategory
+ Sound.Freesound.Bookmark: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.Bookmark.BookmarkCategory
+ Sound.Freesound.Bookmark: instance GHC.Classes.Eq Sound.Freesound.Bookmark.BookmarkCategory
+ Sound.Freesound.Bookmark: instance GHC.Show.Show Sound.Freesound.Bookmark.BookmarkCategory
+ Sound.Freesound.Comment: Comment :: Text -> Text -> UTCTime -> Comment
+ Sound.Freesound.Comment: [comment] :: Comment -> Text
+ Sound.Freesound.Comment: [created] :: Comment -> UTCTime
+ Sound.Freesound.Comment: [username] :: Comment -> Text
+ Sound.Freesound.Comment: data Comment
+ Sound.Freesound.Comment: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.Comment.Comment
+ Sound.Freesound.Comment: instance GHC.Classes.Eq Sound.Freesound.Comment.Comment
+ Sound.Freesound.Comment: instance GHC.Show.Show Sound.Freesound.Comment.Comment
+ Sound.Freesound.List: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Sound.Freesound.List.List a)
+ Sound.Freesound.Pack: data Pack
+ Sound.Freesound.Pack: description :: Pack -> Text
+ Sound.Freesound.Pack: id :: Pack -> PackId
+ Sound.Freesound.Pack: numSounds :: Pack -> Int
+ Sound.Freesound.Search: (&) :: Query -> Query -> Query
+ Sound.Freesound.Search: [pageSize] :: Pagination -> Int
+ Sound.Freesound.Search: avgRating :: Numerical Double -> Filters
+ Sound.Freesound.Search: between :: Ord a => a -> a -> Numerical a
+ Sound.Freesound.Search: bitdepth :: Numerical Integer -> Filters
+ Sound.Freesound.Search: bitrate :: Numerical Integer -> Filters
+ Sound.Freesound.Search: channels :: Numerical Integer -> Filters
+ Sound.Freesound.Search: comment :: Text -> Filters
+ Sound.Freesound.Search: comments :: Numerical Integer -> Filters
+ Sound.Freesound.Search: created :: Text -> Filters
+ Sound.Freesound.Search: data Filters
+ Sound.Freesound.Search: data Numerical a
+ Sound.Freesound.Search: data Query
+ Sound.Freesound.Search: description :: Text -> Filters
+ Sound.Freesound.Search: duration :: Numerical Double -> Filters
+ Sound.Freesound.Search: equals :: a -> Numerical a
+ Sound.Freesound.Search: exclude :: Text -> Query
+ Sound.Freesound.Search: fileType :: FileType -> Filters
+ Sound.Freesound.Search: filesize :: Numerical Integer -> Filters
+ Sound.Freesound.Search: greaterThan :: a -> Numerical a
+ Sound.Freesound.Search: id :: Numerical SoundId -> Filters
+ Sound.Freesound.Search: include :: Text -> Query
+ Sound.Freesound.Search: isGeotagged :: Bool -> Filters
+ Sound.Freesound.Search: isRemix :: Bool -> Filters
+ Sound.Freesound.Search: lessThan :: a -> Numerical a
+ Sound.Freesound.Search: license :: License -> Filters
+ Sound.Freesound.Search: md5 :: Text -> Filters
+ Sound.Freesound.Search: numDownloads :: Numerical Integer -> Filters
+ Sound.Freesound.Search: numRatings :: Numerical Integer -> Filters
+ Sound.Freesound.Search: originalFilename :: Text -> Filters
+ Sound.Freesound.Search: pack :: Text -> Filters
+ Sound.Freesound.Search: samplerate :: Numerical Integer -> Filters
+ Sound.Freesound.Search: search :: Pagination -> Sorting -> Filters -> Query -> Freesound (List Summary)
+ Sound.Freesound.Search: search_ :: Query -> Freesound (List Summary)
+ Sound.Freesound.Search: tag :: Text -> Filters
+ Sound.Freesound.Search: username :: Text -> Filters
+ Sound.Freesound.Search: wasRemixed :: Bool -> Filters
+ Sound.Freesound.Sound: GeoTag :: Double -> Double -> GeoTag
+ Sound.Freesound.Sound: SoundId :: Integer -> SoundId
+ Sound.Freesound.Sound: [latitude] :: GeoTag -> Double
+ Sound.Freesound.Sound: [longitude] :: GeoTag -> Double
+ Sound.Freesound.Sound: [soundIdToInteger] :: SoundId -> Integer
+ Sound.Freesound.Sound: data GeoTag
+ Sound.Freesound.Sound: newtype SoundId
+ Sound.Freesound.Sound: soundById :: SoundId -> Freesound Detail
+ Sound.Freesound.Sound: soundDetail :: Summary -> Freesound Detail
+ Sound.Freesound.User: Avatar :: URI -> URI -> URI -> Avatar
+ Sound.Freesound.User: User :: URI -> Text -> Maybe Text -> Maybe URI -> Maybe Avatar -> UTCTime -> Int -> Resource (List Summary) -> Int -> Resource (List Pack) -> Int -> Int -> Resource (List BookmarkCategory) -> User
+ Sound.Freesound.User: [about] :: User -> Maybe Text
+ Sound.Freesound.User: [avatar] :: User -> Maybe Avatar
+ Sound.Freesound.User: [bookmarkCategories] :: User -> Resource (List BookmarkCategory)
+ Sound.Freesound.User: [dateJoined] :: User -> UTCTime
+ Sound.Freesound.User: [homepage] :: User -> Maybe URI
+ Sound.Freesound.User: [large] :: Avatar -> URI
+ Sound.Freesound.User: [medium] :: Avatar -> URI
+ Sound.Freesound.User: [numComments] :: User -> Int
+ Sound.Freesound.User: [numPacks] :: User -> Int
+ Sound.Freesound.User: [numPosts] :: User -> Int
+ Sound.Freesound.User: [numSounds] :: User -> Int
+ Sound.Freesound.User: [packs] :: User -> Resource (List Pack)
+ Sound.Freesound.User: [small] :: Avatar -> URI
+ Sound.Freesound.User: [sounds] :: User -> Resource (List Summary)
+ Sound.Freesound.User: [url] :: User -> URI
+ Sound.Freesound.User: [username] :: User -> Text
+ Sound.Freesound.User: data Avatar
+ Sound.Freesound.User: data User
+ Sound.Freesound.User: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.User.Avatar
+ Sound.Freesound.User: instance Data.Aeson.Types.Class.FromJSON Sound.Freesound.User.User
+ Sound.Freesound.User: instance GHC.Classes.Eq Sound.Freesound.User.Avatar
+ Sound.Freesound.User: instance GHC.Classes.Eq Sound.Freesound.User.User
+ Sound.Freesound.User: instance GHC.Show.Show Sound.Freesound.User.Avatar
+ Sound.Freesound.User: instance GHC.Show.Show Sound.Freesound.User.User
- Sound.Freesound: getUserByName :: Text -> Freesound Detail
+ Sound.Freesound: getUserByName :: Text -> Freesound User
- Sound.Freesound.List: getNext :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))
+ Sound.Freesound.List: getNext :: FromJSON a => List a -> Freesound (Maybe (List a))
- Sound.Freesound.List: getPrevious :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))
+ Sound.Freesound.List: getPrevious :: FromJSON a => List a -> Freesound (Maybe (List a))
- Sound.Freesound.Pack: created :: Pack a => a -> Text
+ Sound.Freesound.Pack: created :: Pack -> UTCTime
- Sound.Freesound.Pack: name :: Pack a => a -> Text
+ Sound.Freesound.Pack: name :: Pack -> Text
- Sound.Freesound.Pack: numDownloads :: Pack a => a -> Integer
+ Sound.Freesound.Pack: numDownloads :: Pack -> Int
- Sound.Freesound.Pack: sounds :: Pack a => a -> Resource (List Summary)
+ Sound.Freesound.Pack: sounds :: Pack -> Resource (List Summary)
- Sound.Freesound.Pack: url :: Pack a => a -> URI
+ Sound.Freesound.Pack: url :: Pack -> URI
- Sound.Freesound.Pack: username :: Detail -> Text
+ Sound.Freesound.Pack: username :: Pack -> Text
- Sound.Freesound.Sound: comments :: Detail -> Resource ()
+ Sound.Freesound.Sound: comments :: Detail -> Resource (List Comment)
- Sound.Freesound.Sound: created :: Detail -> Text
+ Sound.Freesound.Sound: created :: Detail -> UTCTime
- Sound.Freesound.Sound: pack :: Detail -> Maybe (Resource ())
+ Sound.Freesound.Sound: pack :: Detail -> Maybe (Resource Pack)
- Sound.Freesound.User: getUserByName :: Text -> Freesound Detail
+ Sound.Freesound.User: getUserByName :: Text -> Freesound User

Files

freesound.cabal view
@@ -1,5 +1,5 @@ name:               freesound-version:            0.2.0+version:            0.3.0 synopsis:           Access the Freesound Project database description:        Access the Freesound Project database. The Freesound                     Project is a collaborative database of Creative Commons@@ -17,7 +17,7 @@ stability:          experimental homepage:           https://github.com/kaoskorobase/freesound bug-reports:        https://github.com/kaoskorobase/freesound/issues-tested-with:        GHC == 6.10.1, GHC == 6.10.4, GHC == 6.12.1, GHC == 7.10.3+tested-with:        GHC == 7.8, GHC == 7.10 build-type:         Simple cabal-version:      >= 1.8 @@ -31,19 +31,19 @@ library   exposed-modules:  Sound.Freesound                     Sound.Freesound.Bookmark+                    Sound.Freesound.Comment                     Sound.Freesound.List                     Sound.Freesound.Pack                     Sound.Freesound.Search-                    Sound.Freesound.Search.Filter-                    Sound.Freesound.Search.Numerical-                    Sound.Freesound.Search.Query                     Sound.Freesound.Sound                     Sound.Freesound.User   other-modules:    Sound.Freesound.API-                    Sound.Freesound.Bookmark.Internal                     Sound.Freesound.Pack.Type+                    Sound.Freesound.Search.Filter+                    Sound.Freesound.Search.Numerical+                    Sound.Freesound.Search.Query                     Sound.Freesound.Sound.Type-                    Sound.Freesound.User.Type+                    Sound.Freesound.Time   build-depends:    base >= 3 && < 5                   , aeson >= 0.6                   , blaze-builder >= 0.3@@ -73,7 +73,14 @@   hs-source-dirs:   test   main-is:          Spec.hs   other-modules:    Sound.Freesound.Test+                    Sound.Freesound.SearchSpec+                    Sound.Freesound.SoundSpec+                    Sound.Freesound.UserSpec   build-depends:    base == 4.*+                  , aeson >= 0.6+                  , bytestring >= 0.9+                  , containers                   , data-default                   , freesound                   , hspec+                  , hspec-core
src/Sound/Freesound.hs view
@@ -7,32 +7,15 @@ -- -- > import qualified Network.HTTP.Conduit as HTTP -module Sound.Freesound-(-  -- * Searching-  module Sound.Freesound.Search-, module Sound.Freesound.Search.Query-, module Sound.Freesound.List-  -- * Users-, getUser-, getUserByName-, getBookmarkCategories-, getSounds-, getSounds_-, getPacks-  -- * Sounds-, search-, search_-, getSimilar-, getSimilar_-  -- * Freesound API monad-, module Sound.Freesound.API-)-where+module Sound.Freesound (+    -- * Freesound API monad+    module Sound.Freesound.API+    -- * Result lists+  , module Sound.Freesound.List+    -- * Users+  , getUserByName+) where -import Sound.Freesound.API (Freesound, runFreesound, APIKey, apiKeyFromString)+import Sound.Freesound.API (Freesound, runFreesound, APIKey, apiKeyFromString, get, download, downloadToFile) import Sound.Freesound.List-import Sound.Freesound.Search-import Sound.Freesound.Search.Query-import Sound.Freesound.User-import Sound.Freesound.Sound+import Sound.Freesound.User (getUserByName)
src/Sound/Freesound/API.hs view
@@ -7,20 +7,21 @@ , runFreesound -- , request , URI-, getURI-, Resource+, download+, downloadToFile+, Resource(..) , resourceURI , appendQuery-, getResource+, get ) where  import qualified Blaze.ByteString.Builder as Builder import qualified Blaze.ByteString.Builder.Char8 as Builder-import           Control.Monad (liftM, mzero) import           Control.Monad.IO.Class (MonadIO, liftIO) import qualified Control.Monad.Reader as R import           Control.Lens-import           Data.Aeson as J+import           Data.Aeson+import           Data.Aeson.Types (typeMismatch) import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Lazy.Char8 as BL import           Data.Text (Text)@@ -64,23 +65,31 @@ newtype URI = URI URI.URI deriving (Eq, Show)  instance FromJSON URI where-  parseJSON (String v) = maybe mzero return (parseURI' (T.unpack v))-  parseJSON _ = mzero+  parseJSON (String v) =+    maybe (fail "Couldn't parse URI")+          return+          (parseURI' (T.unpack v))+  parseJSON v = typeMismatch "URI" v  -- | Cover up for Freesound sloppiness. parseURI' :: String -> Maybe URI parseURI' = fmap URI . URI.parseURI . URI.escapeURIString URI.isAllowedInURI  -- | Download the data referred to by a URI.-getURI :: URI -> Freesound BL.ByteString-getURI (URI u) = do+download :: URI -> Freesound BL.ByteString+download (URI u) = do   s <- R.asks session   APIKey k <- R.asks apiKey   let opts = HTTP.defaults & HTTP.header "Authorization" .~ ["Token " `BS.append` k]       u' = URI.uriToString id u ""+  -- liftIO $ print u'   r <- liftIO $ Session.getWith opts s u'   return $ r ^. HTTP.responseBody +-- | Download the data referred to by a URI to a file.+downloadToFile :: FilePath -> URI -> Freesound ()+downloadToFile path uri = liftIO . BL.writeFile path =<< download uri+ -- | Resource URI. newtype Resource a = Resource URI deriving (Eq, FromJSON, Show) @@ -93,19 +102,12 @@            $ HTTP.parseQuery (BS.pack (URI.uriQuery u))               ++ HTTP.toQuery q --- | Create a query item for the API key.--- apiKeyQuery :: Freesound HTTP.Query--- apiKeyQuery = do---   k <- Freesound $ R.asks apiKey---   return $ [("api_key", Just k)]---- | Download the resource referred to by a URI.-getResource :: (FromJSON a) => Resource a -> Freesound a-getResource (Resource u) = do-  -- q <- apiKeyQuery-  -- let Resource u = appendQuery q r-  liftM (handle . J.eitherDecode) $ getURI u-  where handle = either (\e -> error $ "Internal error (getResource): JSON decoding failed: " ++ e) id+-- | Get the resource referred to by a URI.+get :: (FromJSON a) => Resource a -> Freesound a+get (Resource u) = do+  handle . eitherDecode <$> download u+  -- TODO: Proper error handling+  where handle = either (\e -> error $ "JSON decoding failed: " ++ e) id  -- | The base URI of the Freesound API. baseURI :: Builder.Builder@@ -120,9 +122,3 @@                . mappend baseURI                . HTTP.encodePath path                $ query---- | Perform an HTTP request given the method, a URI and an optional body and return the response body as a Conduit source.--- request :: Monad m => HTTP.Method -> URI -> Maybe (C.Source m BS.ByteString) -> FreesoundT m (C.ResumableSource m BS.ByteString)--- request method (URI uri) body = do---   f <- FreesoundT $ R.asks httpRequest---   lift $ f method uri body
src/Sound/Freesound/Bookmark.hs view
@@ -1,50 +1,30 @@ {-# LANGUAGE CPP, OverloadedStrings #-} module Sound.Freesound.Bookmark (-  Bookmark-, sound-, name-, Category-, category-, url-, sounds-, getSounds+  BookmarkCategory(..) ) where -import           Control.Monad (mzero) import           Data.Aeson import           Data.Text (Text)-import           Sound.Freesound.API (Freesound, Resource, URI, getResource)+import           Sound.Freesound.API (Resource, URI)+import           Sound.Freesound.List (List) import qualified Sound.Freesound.Sound.Type as Sound  #if __GLASGOW_HASKELL__ < 710 import           Control.Applicative #endif -data Bookmark = Bookmark {-  sound :: Sound.Summary-, name :: Text-} deriving (Eq, Show)--instance FromJSON Bookmark where-  parseJSON j@(Object v) =-    Bookmark-    <$> parseJSON j-    <*> v .: "bookmark_name"-  parseJSON _ = mzero--data Category = Category {-  category :: Text-, url :: URI-, sounds :: Resource [Bookmark]+data BookmarkCategory = BookmarkCategory {+    url :: URI+  , name :: Text+  , numSounds :: Int+  , sounds :: Resource (List Sound.Summary) } deriving (Eq, Show) -instance FromJSON Category where+instance FromJSON BookmarkCategory where   parseJSON (Object v) =-    Category-    <$> v .: "name"-    <*> v .: "url"-    <*> v .: "sounds"-  parseJSON _ = mzero--getSounds :: Category -> Freesound [Bookmark]-getSounds = getResource . sounds+    BookmarkCategory+      <$> v .: "url"+      <*> v .: "name"+      <*> v .: "num_sounds"+      <*> v .: "sounds"+  parseJSON _ = fail "Couldn't parse BookmarkCategory"
− src/Sound/Freesound/Bookmark/Internal.hs
@@ -1,24 +0,0 @@-{-# LANGUAGE CPP, OverloadedStrings #-}-module Sound.Freesound.Bookmark.Internal (-  Categories(..)-) where--import           Control.Monad (mzero)-import           Data.Aeson-import           Sound.Freesound.Bookmark--#if __GLASGOW_HASKELL__ < 710-import           Control.Applicative-#endif--data Categories = Categories {-  numCategories :: Int-, categories :: [Category]-} deriving (Eq, Show)--instance FromJSON Categories where-  parseJSON (Object v) =-    Categories-    <$> v .: "num_results"-    <*> v .: "categories"-  parseJSON _ = mzero
+ src/Sound/Freesound/Comment.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Comment (+  Comment(..)+) where++import           Data.Aeson (FromJSON(..), Value(..), (.:))+import           Data.Aeson.Types (typeMismatch)+import           Data.Text (Text)+import           Sound.Freesound.Time++#if __GLASGOW_HASKELL__ < 710+import           Control.Applicative+#endif++data Comment = Comment {+  username :: Text+  , comment :: Text+  , created :: UTCTime+  } deriving (Eq, Show)++instance FromJSON Comment where+  parseJSON (Object v) =+    Comment <$> v .: "username"+            <*> v .: "comment"+            <*> (toUTCTime <$> (v .: "created"))+  parseJSON v = typeMismatch "Comment" v
src/Sound/Freesound/List.hs view
@@ -1,7 +1,6 @@ {-# LANGUAGE CPP, OverloadedStrings, ScopedTypeVariables #-} module Sound.Freesound.List (   List-, Elem(..) , elems , numElems , previous@@ -12,8 +11,7 @@  import Control.Monad (liftM, mzero) import Data.Aeson-import Data.Text (Text)-import Sound.Freesound.API (Freesound, Resource, getResource)+import Sound.Freesound.API (Freesound, Resource, get)  #if __GLASGOW_HASKELL__ < 710 import           Control.Applicative@@ -26,10 +24,7 @@ , next     :: Maybe (Resource (List a)) } deriving (Eq, Show) -class Elem a where-  elemsFieldName :: a -> Text--instance (Elem a, FromJSON a) => FromJSON (List a) where+instance FromJSON a => FromJSON (List a) where   parseJSON (Object v) =     List       <$> v .: "results"@@ -38,8 +33,8 @@       <*> v .:? "next"   parseJSON _ = mzero -getPrevious :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))-getPrevious = maybe (return Nothing) (liftM Just . getResource) . previous+getPrevious :: FromJSON a => List a -> Freesound (Maybe (List a))+getPrevious = maybe (return Nothing) (liftM Just . get) . previous -getNext :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))-getNext = maybe (return Nothing) (liftM Just . getResource) . next+getNext :: FromJSON a => List a -> Freesound (Maybe (List a))+getNext = maybe (return Nothing) (liftM Just . get) . next
src/Sound/Freesound/Pack.hs view
@@ -1,24 +1,23 @@ module Sound.Freesound.Pack (-  Pack(..)-, Packs-, Summary-, Detail-, username-, getSounds-, getSounds_+    Pack+  , id+  , url+  , description+  , created+  , name+  , username+  , numSounds+  , sounds+  , numDownloads ) where -import Data.Default (def)-import Sound.Freesound.API (Freesound, appendQuery, getResource)-import Sound.Freesound.List (List)-import Sound.Freesound.Pack.Type-import Sound.Freesound.Search (Pagination)-import Sound.Freesound.Sound (Sounds)--type Packs = List Summary--getSounds :: (Pack a) => Pagination -> a -> Freesound Sounds-getSounds p = getResource . appendQuery p . sounds+import           Prelude hiding (id)+import           Sound.Freesound.API+import           Sound.Freesound.List (List)+import           Sound.Freesound.Pack.Type hiding (sounds)+import qualified Sound.Freesound.Pack.Type as Pack+import qualified Sound.Freesound.Sound as Sound -getSounds_ :: (Pack a) => a -> Freesound Sounds-getSounds_ = getSounds def+-- ^ The URI for a list of sounds in the pack.+sounds :: Pack -> Resource (List Sound.Summary)+sounds = Resource . Pack.sounds
src/Sound/Freesound/Pack/Type.hs view
@@ -1,78 +1,46 @@ {-# LANGUAGE CPP, OverloadedStrings #-}-module Sound.Freesound.Pack.Type where+module Sound.Freesound.Pack.Type (+  Pack(..)+) where -import           Control.Monad (mzero) import           Data.Aeson (FromJSON(..), Value(..), (.:))+import           Data.Aeson.Types (typeMismatch) import           Data.Text (Text)-import           Sound.Freesound.API (Resource, URI)-import           Sound.Freesound.List (List, Elem(..))-import qualified Sound.Freesound.Sound as Sound+import           Sound.Freesound.API (URI)+import           Sound.Freesound.Time  #if __GLASGOW_HASKELL__ < 710 import           Control.Applicative #endif -class Pack a where-  -- | The URI for this resource.-  ref :: a -> Resource ()-  -- | The URL for this pack’s page on the Freesound website.-  url :: a -> URI-  -- | The API URI for the pack’s sound collection.-  sounds :: a -> Resource (List Sound.Summary)-  -- | The pack’s name.-  name :: a -> Text-  -- | The date when the pack was created.-  created :: a -> Text-  -- | The number of times the pack was downloaded.-  numDownloads :: a -> Integer--data Summary = Summary {-  pack_ref :: Resource ()             -- ^ The URI for this resource.-, pack_url :: URI                   -- ^ The URL for this pack’s page on the Freesound website.-, pack_sounds :: Resource (List Sound.Summary)           -- ^ The API URI for the pack’s sound collection.-, pack_name :: Text                 -- ^ The pack’s name.-, pack_created :: Text              -- ^ The date when the pack was created.-, pack_num_downloads :: Integer     -- ^ The number of times the pack was downloaded.-} deriving (Eq, Show)--instance Pack Summary where-  ref = pack_ref-  url = pack_url-  sounds = pack_sounds-  name = pack_name-  created = pack_created-  numDownloads = pack_num_downloads--instance FromJSON Summary where-  parseJSON (Object o) =-    Summary-    <$> o .: "ref"-    <*> o .: "url"-    <*> o .: "sounds"-    <*> o .: "name"-    <*> o .: "created"-    <*> o .: "num_downloads"-  parseJSON _ = mzero+newtype PackId = PackId Integer deriving (Eq, Ord, Show) -instance Elem Summary where-  elemsFieldName _ = "packs"+instance FromJSON PackId where+  parseJSON (Number i) = return $ PackId (truncate i)+  parseJSON v = typeMismatch "PackId" v -data Detail = Detail {-  summary :: Summary-, username :: Text         -- ^ A JSON object with the user’s username, url, and ref.+data Pack = Pack {+    id :: PackId        -- ^ The unique identifier of this pack.+  , url :: URI          -- ^ The URI for this pack on the Freesound website.+  , description :: Text -- ^ The description the user gave to the pack (if any).+  , created :: UTCTime  -- ^ The date when the pack was created (e.g. “2014-04-16T20:07:11.145”).+  , name :: Text        -- ^ The name user gave to the pack.+  , username :: Text    -- ^ Username of the creator of the pack.+  , numSounds :: Int    -- ^ The number of sounds in the pack.+  , sounds :: URI       -- ^ The URI for a list of sounds in the pack. Plain URI in order to break module dependency cycle.+  , numDownloads :: Int -- ^ The number of times this pack has been downloaded. } deriving (Eq, Show) -instance Pack Detail where-  ref = ref . summary-  url = url . summary-  sounds = sounds . summary-  name = name . summary-  created = created . summary-  numDownloads = numDownloads . summary--instance FromJSON Detail where-  parseJSON v@(Object o) =-    Detail-    <$> parseJSON v-    <*> o .: "username"-  parseJSON _ = mzero+instance FromJSON Pack where+  parseJSON (Object v) =+    Pack+      <$> v .: "id"+      <*> v .: "url"+      <*> v .: "description"+      <*> (toUTCTime <$> (v .: "created"))+      <*> v .: "name"+      <*> v .: "username"+      <*> v .: "num_sounds"+      <*> v .: "sounds"+      <*> v .: "num_downloads"+  parseJSON v = typeMismatch "Pack" v
src/Sound/Freesound/Search.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP, OverloadedStrings #-} module Sound.Freesound.Search (     SortMethod(..)   , SortDirection(..)@@ -9,15 +9,27 @@   , module Sound.Freesound.Search.Filter   , module Sound.Freesound.Search.Numerical   , module Sound.Freesound.Search.Query+  , search+  , search_ ) where  import qualified Data.ByteString.Char8 as BS-import           Data.Default (Default(..))-import           Network.HTTP.Types.QueryLike (QueryLike(..), QueryValueLike(..))+import           Data.Default (Default) import           Sound.Freesound.Search.Filter import           Sound.Freesound.Search.Numerical import           Sound.Freesound.Search.Query+import qualified Data.ByteString as B+import           Data.Default (def)+import qualified Data.Text as T+import           Network.HTTP.Types.QueryLike (QueryLike(..), QueryValueLike(..))+import           Sound.Freesound.List (List)+import           Sound.Freesound.Sound.Type+import           Sound.Freesound.API (Freesound, get, resourceURI) +#if __GLASGOW_HASKELL__ < 710+import           Control.Applicative+#endif+ data SortMethod    = Duration | Created | Downloads | Rating deriving (Eq, Show) data SortDirection = Ascending | Descending deriving (Eq, Show) data Sorting       = Unsorted | SortedBy SortMethod SortDirection deriving (Eq, Show)@@ -44,7 +56,7 @@  data Pagination = Pagination {   page :: Int-, resultsPerPage :: Int+, pageSize :: Int } deriving (Eq, Show)  instance Default Pagination where@@ -53,7 +65,23 @@ instance QueryLike Pagination where   toQuery a = toQuery [ if page def == page a                         then Nothing-                        else Just (BS.pack "p", show (page a + 1))-                      , if resultsPerPage def == resultsPerPage a+                        else Just (BS.pack "page", show (page a + 1))+                      , if pageSize def == pageSize a                         then Nothing-                        else Just (BS.pack "sounds_per_page", show (resultsPerPage a)) ]+                        else Just (BS.pack "page_size", show (pageSize a)) ]++search :: Pagination -> Sorting -> Filters -> Query -> Freesound (List Summary)+search p s fs q =+    get+  $ resourceURI+    [ "search", "text" ]+    (toQuery p ++ toQuery [ pair "query" q+                          , pair "filter" fs+                          , pair "sort" s ])+                          -- TODO: group_by_pack (changes response type)+  where+    pair :: QueryValueLike a => T.Text -> a -> Maybe (T.Text, B.ByteString)+    pair k a = (,) <$> pure k <*> toQueryValue a++search_ :: Query -> Freesound (List Summary)+search_ = search def def def
src/Sound/Freesound/Search/Query.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} module Sound.Freesound.Search.Query (     Query   , include@@ -5,23 +6,40 @@   , (&) ) where -import       Data.Char (isSpace)-import        Data.Text (Text)-import qualified Data.Text as Text-import        Network.HTTP.Types.QueryLike (QueryValueLike(..))+import           Data.Char (isSpace)+import           Data.Default (Default(..))+import           Data.String (IsString(..))+import           Data.Text (Text)+import qualified Data.Text as T+import           Network.HTTP.Types.QueryLike (QueryValueLike(..)) +#if __GLASGOW_HASKELL__ < 710+import           Data.Monoid+#endif++data Term = Include Text | Exclude Text deriving (Eq, Show)+ newtype Query = Query [Term] deriving (Eq, Show) +instance Monoid Query where+  mempty = Query []+  mappend (Query xs) (Query ys) = Query (xs ++ ys)++instance Default Query where+  def = mempty++instance IsString Query where+  fromString [] = mempty+  fromString s = include (T.pack s)+ instance QueryValueLike Query where-  toQueryValue (Query ts) = toQueryValue $ Text.unwords (map f ts)+  toQueryValue (Query ts) = toQueryValue $ T.unwords (map f ts)     where       quote t-        | Text.any isSpace t = Text.cons '"' (Text.snoc t '"')+        | T.any isSpace t = T.cons '"' (T.snoc t '"')         | otherwise = t-      f (Include t) = Text.cons '+' (quote t)-      f (Exclude t) = Text.cons '-' (quote t)--data Term = Include Text | Exclude Text deriving (Eq, Show)+      f (Include t) = T.cons '+' (quote t)+      f (Exclude t) = T.cons '-' (quote t)  include :: Text -> Query include string = Query [Include string]@@ -29,8 +47,5 @@ exclude :: Text -> Query exclude string = Query [Exclude string] -append :: Query -> Query -> Query-append (Query xs) (Query ys) = Query (xs ++ ys)- (&) :: Query -> Query -> Query-(&) = append+(&) = mappend
src/Sound/Freesound/Sound.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE CPP, OverloadedStrings #-} module Sound.Freesound.Sound (-    SoundId+    SoundId(..)   , FileType(..)+  , GeoTag(..)   , Sound(..)   , Summary   , Detail-  , Sounds   , url   , description   , geotag@@ -35,51 +35,18 @@   -- , analysis   , analysisStats   , analysisFrames--  , search-  , search_-  , getSimilar-  , getSimilar_+  , soundById+  , soundDetail ) where -import           Data.Default (def) import qualified Data.Text as T-import           Network.HTTP.Types.QueryLike (toQuery, toQueryValue)-import           Sound.Freesound.List (List)-import           Sound.Freesound.Search (Filters, Pagination, Query, Sorting)-import           Sound.Freesound.Sound.Type-import           Sound.Freesound.API (Freesound, appendQuery, getResource, resourceURI)--#if __GLASGOW_HASKELL__ < 710-import           Control.Applicative-#endif--type Sounds = List Summary--search :: Pagination -> Sorting -> Filters -> Query -> Freesound Sounds-search p s fs q =-    getResource-  $ resourceURI-    [ "search", "text" ]-    (toQuery p ++ toQuery [ ("q"::T.Text, toQueryValue q)-                          , ("f", toQueryValue fs)-                          , ("s", toQueryValue s) ])--search_ :: Query -> Freesound Sounds-search_ = search def def def---- | Search for sounds in a certain coordinate region.--- geotagged :: ...---- | Content based search.--- contentSearch :: ...---- Missing: distance field in the response-getSimilar :: Pagination -> Detail -> Freesound Sounds-getSimilar p = getResource . appendQuery p . similarSounds+import           Sound.Freesound.API (Freesound, get, resourceURI)+import           Sound.Freesound.Sound.Type as Sound -getSimilar_ :: Detail -> Freesound Sounds-getSimilar_ = getSimilar def+-- | Get a sound by id.+soundById :: SoundId -> Freesound Detail+soundById (SoundId i) = get $ resourceURI ["sounds", T.pack (show i)] [] --- getAnalysisStats--- getAnalysisFrames+-- | Get detailed sound information from its summary.+soundDetail :: Summary -> Freesound Detail+soundDetail = soundById . Sound.id
src/Sound/Freesound/Sound/Type.hs view
@@ -2,25 +2,27 @@ module Sound.Freesound.Sound.Type where  import           Control.Monad (mzero)-import           Data.Aeson (FromJSON(..), Value(..), (.:), (.:?))+import           Data.Aeson (FromJSON(..), Value(..), (.:), (.:?), decodeStrict) import qualified Data.ByteString.Char8 as BS import           Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Text.Encoding as T import           Network.HTTP.Types.QueryLike (QueryValueLike(..)) import           Prelude hiding (id) import           Sound.Freesound.API (Resource, URI)-import           Sound.Freesound.List (List, Elem(..))+import           Sound.Freesound.Comment (Comment)+import           Sound.Freesound.List (List)+import           Sound.Freesound.Pack.Type (Pack)+import           Sound.Freesound.Time -- import qualified Sound.Freesound.User.Type as User  #if __GLASGOW_HASKELL__ < 710 import           Control.Applicative #endif -newtype SoundId = SoundId Integer+newtype SoundId = SoundId { soundIdToInteger :: Integer }                   deriving (Eq, Ord, Show) -soundIdToInteger :: SoundId -> Integer-soundIdToInteger (SoundId i) = i- instance FromJSON SoundId where   parseJSON (Number i) = return $ SoundId (truncate i)   parseJSON _ = mzero@@ -111,35 +113,35 @@   parseJSON _ = fail "Couldn't parse Images"  data Detail = Detail {-    sound_id            :: SoundId      -- ^ The sound’s unique identifier.-  , url                 :: URI          -- ^ The URI for this sound on the Freesound website.-  , sound_name          :: Text         -- ^ The name user gave to the sound.-  , sound_tags          :: [Text]       -- ^ An array of tags the user gave to the sound.-  , description         :: Text         -- ^ The description the user gave to the sound.-  , geotag              :: Maybe GeoTag -- ^ Latitude and longitude of the geotag (only for sounds that have been geotagged).-  , created             :: Text         -- ^ The date of when the sound was uploaded.-  , sound_license       :: License      -- ^ The license under which the sound is available to you.-  , fileType            :: FileType     -- ^ The type of sound (wav, aif, mp3, etc.).-  , channels            :: Int          -- ^ The number of channels.-  , filesize            :: Integer      -- ^ The size of the file in bytes.-  , bitrate             :: Int          -- ^ The bit rate of the sound.-  , bitdepth            :: Int          -- ^ The bit depth of the sound.-  , duration            :: Double       -- ^ The duration of the sound in seconds.-  , samplerate          :: Int          -- ^ The samplerate of the sound.-  , sound_username      :: Text         -- ^ The username of the uploader of the sound.-  , pack                :: Maybe (Resource ()) -- ^ If the sound is part of a pack, this URI points to that pack’s API resource.-  , download            :: URI          -- ^ The URI for retrieving the original sound.-  , bookmark            :: URI          -- ^ The URI for bookmarking the sound.-  , previews            :: Previews     -- ^ Dictionary containing the URIs for mp3 and ogg versions of the sound. The dictionary includes the fields preview-hq-mp3 and preview-lq-mp3 (for ~128kbps quality and ~64kbps quality mp3 respectively), and preview-hq-ogg and preview-lq-ogg (for ~192kbps quality and ~80kbps quality ogg respectively).-  , images              :: Images       -- ^ Dictionary including the URIs for spectrogram and waveform visualizations of the sound. The dinctionary includes the fields waveform_l and waveform_m (for large and medium waveform images respectively), and spectral_l and spectral_m (for large and medium spectrogram images respectively).-  , numDownloads        :: Integer      -- ^ The number of times the sound was downloaded.-  , avgRating           :: Double       -- ^ The average rating of the sound.-  , numRatings          :: Integer      -- ^ The number of times the sound was rated.-  , rate                :: URI          -- ^ The URI for rating the sound.-  , comments            :: Resource ()     -- ^ The URI of a paginated list of the comments of the sound.-  , numComments         :: Integer      -- ^ The number of comments.-  , comment             :: URI          -- ^ The URI to comment the sound.-  , similarSounds       :: Resource (List Summary)     -- ^ URI pointing to the similarity resource (to get a list of similar sounds).+    sound_id            :: SoundId                  -- ^ The sound’s unique identifier.+  , url                 :: URI                      -- ^ The URI for this sound on the Freesound website.+  , sound_name          :: Text                     -- ^ The name user gave to the sound.+  , sound_tags          :: [Text]                   -- ^ An array of tags the user gave to the sound.+  , description         :: Text                     -- ^ The description the user gave to the sound.+  , geotag              :: Maybe GeoTag             -- ^ Latitude and longitude of the geotag (only for sounds that have been geotagged).+  , created             :: UTCTime                  -- ^ The date of when the sound was uploaded.+  , sound_license       :: License                  -- ^ The license under which the sound is available to you.+  , fileType            :: FileType                 -- ^ The type of sound (wav, aif, mp3, etc.).+  , channels            :: Int                      -- ^ The number of channels.+  , filesize            :: Integer                  -- ^ The size of the file in bytes.+  , bitrate             :: Int                      -- ^ The bit rate of the sound.+  , bitdepth            :: Int                      -- ^ The bit depth of the sound.+  , duration            :: Double                   -- ^ The duration of the sound in seconds.+  , samplerate          :: Int                      -- ^ The samplerate of the sound.+  , sound_username      :: Text                     -- ^ The username of the uploader of the sound.+  , pack                :: Maybe (Resource Pack)    -- ^ If the sound is part of a pack, this URI points to that pack’s API resource.+  , download            :: URI                      -- ^ The URI for retrieving the original sound.+  , bookmark            :: URI                      -- ^ The URI for bookmarking the sound.+  , previews            :: Previews                 -- ^ Dictionary containing the URIs for mp3 and ogg versions of the sound. The dictionary includes the fields preview-hq-mp3 and preview-lq-mp3 (for ~128kbps quality and ~64kbps quality mp3 respectively), and preview-hq-ogg and preview-lq-ogg (for ~192kbps quality and ~80kbps quality ogg respectively).+  , images              :: Images                   -- ^ Dictionary including the URIs for spectrogram and waveform visualizations of the sound. The dinctionary includes the fields waveform_l and waveform_m (for large and medium waveform images respectively), and spectral_l and spectral_m (for large and medium spectrogram images respectively).+  , numDownloads        :: Integer                  -- ^ The number of times the sound was downloaded.+  , avgRating           :: Double                   -- ^ The average rating of the sound.+  , numRatings          :: Integer                  -- ^ The number of times the sound was rated.+  , rate                :: URI                      -- ^ The URI for rating the sound.+  , comments            :: Resource (List Comment)  -- ^ The URI of a paginated list of the comments of the sound.+  , numComments         :: Integer                  -- ^ The number of comments.+  , comment             :: URI                      -- ^ The URI to comment the sound.+  , similarSounds       :: Resource (List Summary)  -- ^ URI pointing to the similarity resource (to get a list of similar sounds).   -- , analysis            :: Maybe   , analysisStats       :: Resource ()     -- ^ URI pointing to the complete analysis results of the sound.   , analysisFrames      :: Resource ()    -- ^ The URI for retrieving a JSON file with analysis information for each frame of the sound.@@ -154,7 +156,7 @@       <*> v .: "tags"       <*> v .: "description"       <*> v .:? "geotag"-      <*> v .: "created"+      <*> (toUTCTime <$> (v .: "created"))       <*> v .: "license"       <*> v .: "type"       <*> v .: "channels"@@ -213,9 +215,6 @@   username = summary_username   license = summary_license -instance Elem Summary where-  elemsFieldName _ = "sounds"- -- | Coordinate data GeoTag = GeoTag {   latitude :: Double@@ -223,5 +222,9 @@ } deriving (Eq, Show)  instance FromJSON GeoTag where-  parseJSON (String _) = pure (GeoTag 0 0)+  parseJSON (String s) = do+    let coords = T.concat ["[", T.intercalate "," (T.splitOn " " s), "]"]+    case decodeStrict (T.encodeUtf8 coords) of+      Just [lat, lon] -> return $ GeoTag lat lon+      _ -> fail "Couldn't parse GeoTag"   parseJSON _ = fail "Couldn't parse GeoTag"
+ src/Sound/Freesound/Time.hs view
@@ -0,0 +1,15 @@+module Sound.Freesound.Time (+    Time+  , toUTCTime+  , UTCTime+) where++import           Data.Aeson+import qualified Data.Text as T+import           Data.Time (UTCTime)++newtype Time = Time { toUTCTime :: UTCTime } deriving (Eq, Show)++instance FromJSON Time where+  parseJSON (String s) = Time `fmap` parseJSON (String (T.snoc s 'Z'))+  parseJSON _ = fail "Couldn't parse time"
src/Sound/Freesound/User.hs view
@@ -1,58 +1,72 @@+{-# LANGUAGE CPP, OverloadedStrings #-} module Sound.Freesound.User (   User(..)-, Summary-, Detail-, sounds-, packs-, firstName-, lastName-, about-, homePage-, signature-, dateJoined-, bookmarkCategories-, getUser+, Avatar(..) , getUserByName-, getBookmarkCategories-, getSounds-, getSounds_-, getPacks ) where -import           Control.Monad (liftM)-import           Data.Default (def)+import           Control.Monad (join)+import           Data.Aeson (FromJSON(..), Value(..), (.:), (.:?)) import           Data.Text (Text)-import qualified Data.Text as T-import           Sound.Freesound.API (Freesound, appendQuery, getResource, resourceURI)-import qualified Sound.Freesound.Bookmark as Bookmark-import qualified Sound.Freesound.Bookmark.Internal as Bookmark-import           Sound.Freesound.Pack (Packs)-import           Sound.Freesound.Search (Pagination)-import           Sound.Freesound.Sound (Sounds)-import           Sound.Freesound.User.Type---- | Get detailed information about a user.-getUser :: (User a) => a -> Freesound Detail-getUser = getResource . ref+import           Sound.Freesound.API (Freesound, Resource, URI, get, resourceURI)+import           Sound.Freesound.Bookmark (BookmarkCategory)+import           Sound.Freesound.List (List)+import           Sound.Freesound.Pack (Pack)+import qualified Sound.Freesound.Sound as Sound+import           Sound.Freesound.Time --- | Get information about a user by name.-getUserByName :: Text -> Freesound Detail-getUserByName t = getResource $ resourceURI [ T.pack "people", t ] []+#if __GLASGOW_HASKELL__ < 710+import           Control.Applicative+#endif --- | Retrieve a list of a user's bookmark categories.-getBookmarkCategories :: Detail -> Freesound [Bookmark.Category]-getBookmarkCategories = liftM Bookmark.categories . getResource . bookmarkCategories+data Avatar = Avatar {+    small :: URI+  , medium :: URI+  , large :: URI+  } deriving (Eq, Show) --- | Retrieve a user's sounds.--- This is broken: the response doesn't contain the User.-getSounds :: Pagination -> Detail -> Freesound Sounds-getSounds p = getResource . appendQuery p . sounds+instance FromJSON Avatar where+  parseJSON (Object v) =+    Avatar <$> v .: "small"+           <*> v .: "medium"+           <*> v .: "large"+  parseJSON _ = fail "Couldn't parse Avatar" --- | Retrieve a user's sounds.+-- | User of the Freesound database.+data User = User {+    url :: URI                              -- ^ The URI for this users' profile on the Freesound website.+  , username :: Text                        -- ^ The username.+  , about :: Maybe Text                     -- ^ The 'about' text of users' profile (if indicated).+  , homepage :: Maybe URI                   -- ^ The URI of users' homepage outside Freesound (if indicated).+  , avatar :: Maybe Avatar                  -- ^ The user's avatar image (if indicated).+  , dateJoined :: UTCTime                   -- ^ The date when the user joined Freesound.+  , numSounds :: Int                        -- ^ The number of sounds uploaded by the user.+  , sounds :: Resource (List Sound.Summary) -- ^ The API URI for this user’s sound collection.+  , numPacks :: Int                         -- ^ The number of packs by the user.+  , packs :: Resource (List Pack)           -- ^ The API URI for this user’s pack collection.+  , numPosts :: Int                         -- ^ The number of forum posts by the user.+  , numComments :: Int                      -- ^ The number of comments that user made in other users' sounds.+  , bookmarkCategories :: Resource (List BookmarkCategory)  -- ^ The URI for a list of bookmark categories by the user.+} deriving (Eq, Show) -getSounds_ :: Detail -> Freesound Sounds-getSounds_ = getSounds def+instance FromJSON User where+  parseJSON (Object v) =+    User+    <$> v .: "url"+    <*> v .: "username"+    <*> (join <$> (v .:? "about"))+    <*> (join <$> (v .:? "homepage"))+    <*> (join <$> (v .:? "avatar"))+    <*> (toUTCTime <$> (v .: "date_joined"))+    <*> v .: "num_sounds"+    <*> v .: "sounds"+    <*> v .: "num_packs"+    <*> v .: "packs"+    <*> v .: "num_posts"+    <*> v .: "num_comments"+    <*> v .: "bookmark_categories"+  parseJSON _ = fail "Couldn't parse User" --- | Retrieve a user's packs.-getPacks :: Detail -> Freesound Packs-getPacks = getResource . packs+-- | Get information about a user by name.+getUserByName :: Text -> Freesound User+getUserByName u = get $ resourceURI [ "users", u ] []
− src/Sound/Freesound/User/Type.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE CPP, OverloadedStrings #-}-module Sound.Freesound.User.Type where--import           Control.Monad (mzero)-import           Data.Aeson (FromJSON(..), Value(..), (.:))-import           Data.Text (Text)-import qualified Data.Text as T-import           Sound.Freesound.API (Resource, URI)-import qualified Sound.Freesound.Bookmark.Internal as Bookmark-import           Sound.Freesound.List (List)-import qualified Sound.Freesound.Pack.Type as Pack-import qualified Sound.Freesound.Sound.Type as Sound--#if __GLASGOW_HASKELL__ < 710-import           Control.Applicative-#endif---- | User of the Freesound database.-class User a where-  -- | The user’s username.-  name :: a -> Text-  -- | The URI for this resource.-  ref :: a -> Resource Detail-  -- | The profile page for the user on the Freesound website.-  url :: a -> URI---- | User of the Freesound database.-data Summary = Summary {-  user_name :: Text     -- ^ The user’s username.-, user_ref  :: Resource Detail -- ^ The URI for this resource.-, user_url :: URI       -- ^ The profile page for the user on the Freesound website.-} deriving (Eq, Show)--instance User Summary where-  name = user_name-  ref  = user_ref-  url  = user_url--instance FromJSON Summary where-  parseJSON (Object v) =-    Summary-    <$> v .: "username"-    <*> v .: "ref"-    <*> v .: "url"-  parseJSON _ = mzero--data Detail = Detail {-  summary :: Summary                -- ^ Summary.-, sounds :: Resource (List Sound.Summary)                -- ^ The API URI for this user’s sound collection.-, packs :: Resource (List Pack.Summary)                -- ^ The API URI for this user’s pack collection.-, firstName :: Maybe Text           -- ^ The user’s first name, possibly empty.-, lastName :: Maybe Text            -- ^ The user’s last name, possibly empty.-, about :: Maybe Text               -- ^ A small text the user wrote about himself.--- FIXME: homePage :: Maybe Data-, homePage :: Maybe Text            -- ^ The user’s homepage, possibly empty.-, signature :: Maybe Text           -- ^ The user’s signature, possibly empty.-, dateJoined :: Text                -- ^ The date the user joined Freesound.-, bookmarkCategories :: Resource Bookmark.Categories-} deriving (Eq, Show)--instance User Detail where-  name = name . summary-  ref  = ref . summary-  url  = url . summary--textToMaybe :: Maybe Text -> Maybe Text-textToMaybe Nothing = Nothing-textToMaybe (Just s)-  | T.null s  = Nothing-  | otherwise = Just s--instance FromJSON Detail where-  parseJSON j@(Object v) =-    Detail-    <$> parseJSON j-    <*> v .: "sounds"-    <*> v .: "packs"-    <*> (textToMaybe <$> (v .: "first_name"))-    <*> (textToMaybe <$> (v .: "last_name"))-    <*> (textToMaybe <$> (v .: "about"))-    <*> (textToMaybe <$> (v .: "home_page"))-    <*> (textToMaybe <$> (v .: "signature"))-    <*> v .: "date_joined"-    <*> v .: "bookmark_categories"-  parseJSON _ = mzero
+ test/Sound/Freesound/SearchSpec.hs view
@@ -0,0 +1,23 @@+{-# LANGUAGE OverloadedStrings #-}+module Sound.Freesound.SearchSpec (spec) where++import           Data.Default (def)+import           Sound.Freesound+import           Sound.Freesound.Search+import qualified Sound.Freesound.Sound as Sound+import           Sound.Freesound.Test+import           Test.Hspec++spec :: Spec+spec = do+  describe "search" $ do+    it "searches" $ do+      let p s =    Sound.username s == "k0s"+                && Sound.name s == "Spiffy Spank"+      fs (anySatisfy p =<< search_ (include "spank")) `shouldReturn` True+    it "filters by user name" $ do+      let p s = Sound.username s == "k0s"+      fs (allSatisfy p =<< search def def (username "k0s") (include "spank")) `shouldReturn` True+    it "paginates" $ do+      l <- fs $ search (Pagination 0 1) def def ""+      length (elems l) `shouldBe` 1
+ test/Sound/Freesound/SoundSpec.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.SoundSpec (spec) where++import qualified Data.ByteString.Lazy as BL+import           Data.List (find)+import           Data.Maybe (fromJust)+import           Sound.Freesound+import qualified Sound.Freesound.Comment as Comment+import qualified Sound.Freesound.Pack as Pack+import           Sound.Freesound.Search+import qualified Sound.Freesound.Sound as Sound+import           Sound.Freesound.Test+import           Test.Hspec++#if __GLASGOW_HASKELL__ < 710+import           Control.Applicative+#endif++spiffySpankId :: Sound.SoundId+spiffySpankId = Sound.SoundId 167068++spiffySpank :: Freesound Sound.Detail+spiffySpank = Sound.soundById spiffySpankId++nokia_2600_warmup :: Freesound Sound.Detail+nokia_2600_warmup = Sound.soundById (Sound.SoundId 330864)++spec :: Spec+spec = do+  describe "Spiffy Spank" $ do+    it "can be retrieved by id" $ do+      s <- fs spiffySpank+      Sound.name s `shouldBe` "Spiffy Spank"+    it "has the correct creation date" $ do+      s <- fs spiffySpank+      Sound.created s `shouldBe` read "2012-10-10 15:24:33.328 UTC"+    it "has the correct file type" $ do+      s <- fs spiffySpank+      Sound.fileType s `shouldBe` Sound.WAV+    it "can be recovered from its summary" $ do+      Just info <- find (\s -> Sound.id s == spiffySpankId)+                    <$> (fs $ getAll =<< search_ (include "Spiffy Spank"))+      (s, s') <- fs $ (,) <$> Sound.soundDetail info <*> spiffySpank+      s `shouldBe` s'+    it "has no geotag" $ do+      Sound.geotag <$> fs spiffySpank `shouldReturn` Nothing+    it "is not part of a pack" $ do+      Sound.pack <$> fs spiffySpank `shouldReturn` Nothing+  describe "nokia_2600_warmup" $ do+    it "has the correct geotag" $ do+      Sound.geotag <$> fs nokia_2600_warmup+        `shouldReturn` Just (Sound.GeoTag 41.4151829276 2.16533660889)+    it "is part of pack \"Nokia 2600\"" $ do+      r <- Sound.pack <$> fs nokia_2600_warmup+      r `shouldNotBe` Nothing+      p <- fs . get . fromJust $ r+      Pack.name p `shouldBe` "Nokia 2600"+    unlessCI $ do+      it "can be downloaded" $ do+        b <- fs $ download . Sound.download =<< nokia_2600_warmup+        b' <- BL.readFile "test/data/330864__k0s__nokia-2600-warmup.wav"+        b `shouldBe` b'+  describe "Nokia_2600_Silence.wav" $ do+    it "should have a lovely comment" $ do+      s <- fs . Sound.soundById $ Sound.SoundId 231519+      l <- fs . get . Sound.comments $ s+      let c = Comment.Comment {+            Comment.username = "toiletrolltube"+          , Comment.comment = "I love this sort of stuff, very useful indeed thanks."+          , Comment.created = read "2014-03-29 03:15:55.948 UTC" }+      fs (anySatisfy (==c) l) `shouldReturn` True
test/Sound/Freesound/Test.hs view
@@ -1,9 +1,52 @@ module Sound.Freesound.Test (-  fs+    fs+  , anySatisfy+  , allSatisfy+  , getAll+  , unlessCI ) where +import Data.Aeson (FromJSON) import Sound.Freesound-import System.Environment (getEnv)+import qualified Sound.Freesound.List as L+import System.Environment (getEnv, lookupEnv)+import Test.Hspec.Core.Spec  fs :: Freesound a -> IO a fs a = flip runFreesound a =<< apiKeyFromString `fmap` getEnv "FREESOUND_API_KEY"++anySatisfy :: (FromJSON a) => (a -> Bool) -> List a -> Freesound Bool+anySatisfy p l = do+  if any p (elems l)+    then return True+    else do+      n <- getNext l+      case n of+        Nothing -> return False+        Just l' -> anySatisfy p l'++allSatisfy :: (FromJSON a) => (a -> Bool) -> List a -> Freesound Bool+allSatisfy p l = do+  if all p (elems l)+    then do+      n <- getNext l+      case n of+        Nothing -> return True+        Just l' -> allSatisfy p l'+    else return False++getAll :: (FromJSON a) => List a -> Freesound [a]+getAll l =+  case L.next l of+    Nothing -> return $ L.elems l+    Just n -> do+      xs <- getAll =<< get n+      return $ L.elems l ++ xs++-- | Emit spec only when _not_ running on the CI server.+unlessCI :: SpecWith () -> SpecWith ()+unlessCI action = do+  e <- runIO $ lookupEnv "CI"+  case e of+    Nothing -> action+    _ -> return ()
+ test/Sound/Freesound/UserSpec.hs view
@@ -0,0 +1,66 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.UserSpec (spec) where++import           Data.Default (def)+import           Data.List (find)+import           Data.Maybe (fromJust)+import qualified Data.Set as Set+import           Sound.Freesound+import qualified Sound.Freesound.Bookmark as Bookmark+import qualified Sound.Freesound.Pack as Pack+import           Sound.Freesound.Search+import qualified Sound.Freesound.Sound as Sound+import qualified Sound.Freesound.User as User+import           Sound.Freesound.Test+import           Test.Hspec++#if __GLASGOW_HASKELL__ < 710+import           Control.Applicative+#endif++k0s :: Freesound User.User+k0s = User.getUserByName "k0s"++spec :: Spec+spec = do+  describe "k0s" $ do+    it "can be retrieved by name" $ do+      u <- fs k0s+      User.username u `shouldBe` "k0s"+    it "has the correct date" $ do+      u <- fs k0s+      User.dateJoined u `shouldBe` read "2007-02-02 17:32:34 UTC"+    it "has the correct number of sounds" $ do+      u <- fs k0s+      sounds <- fs $ get $ User.sounds u+      numElems sounds `shouldBe` User.numSounds u+    it "has the correct set of sounds" $ do+      sounds <- fs $ getAll =<< get . User.sounds =<< k0s+      sounds' <- fs $ getAll =<< search def def (username "k0s") def+      Set.fromList (map Sound.id sounds) `shouldBe` Set.fromList (map Sound.id sounds')+    it "has a pack called \"Nokia 2600\" containing 2 sounds" $ do+      u <- fs k0s+      l <- fs . get . User.packs $ u+      numElems l `shouldSatisfy` (>=1)+      r <- fs $ find (\p -> Pack.name p == "Nokia 2600") <$> getAll l+      r `shouldNotBe` Nothing+      let p = fromJust r+      Pack.numSounds p `shouldBe` 2+      Pack.username p `shouldBe` User.username u+      sounds <- fs . get . Pack.sounds $ p+      numElems sounds `shouldBe` 2+      length (elems sounds) `shouldBe` 2+    it "has haskell-freesound bookmark category" $ do+      l <- fs $ get . User.bookmarkCategories =<< k0s+      -- More than one category+      numElems l `shouldSatisfy` (>=1)+      -- Test bookmark category+      b <- fs $ find (\b -> Bookmark.name b == "haskell-freesound-tests") <$> getAll l+      b `shouldNotBe` Nothing+      -- Number of sounds in category+      Bookmark.numSounds (fromJust b) `shouldBe` 1+      sounds <- fs . get . Bookmark.sounds . fromJust $ b+      numElems sounds `shouldBe` 1+      -- Correct sound in category+      elems sounds `shouldSatisfy` (not.null)+      Sound.id (head (elems sounds)) `shouldBe` Sound.SoundId 250719