freesound 0.1.0 → 0.2.0
raw patch · 26 files changed
+1290/−711 lines, 26 filesdep +aesondep +blaze-builderdep +data-defaultdep −curldep −data-accessordep −data-accessor-templatedep ~basedep ~bytestringdep ~mtlPVP ok
version bump matches the API change (PVP)
Dependencies added: aeson, blaze-builder, data-default, filepath, freesound, hspec, http-types, lens, network, network-uri, old-locale, text, time, transformers, wreq
Dependencies removed: curl, data-accessor, data-accessor-template, directory, xml
Dependency ranges changed: base, bytestring, mtl
API changes (from Hackage documentation)
- Sound.Freesound: CurlError :: CurlCode -> Error
- Sound.Freesound: Dissimilar :: Similarity
- Sound.Freesound: Error :: String -> Error
- Sound.Freesound: LoginError :: Error
- Sound.Freesound: Sample :: Int -> Sample
- Sound.Freesound: Similar :: Similarity
- Sound.Freesound: UnknownError :: Error
- Sound.Freesound: XMLError :: Error
- Sound.Freesound: data Error
- Sound.Freesound: data Similarity
- Sound.Freesound: download :: (CurlBuffer b) => Sample -> Freesound b
- Sound.Freesound: errorString :: Error -> String
- Sound.Freesound: instance Eq Similarity
- Sound.Freesound: instance Error Error
- Sound.Freesound: instance Functor Freesound
- Sound.Freesound: instance Monad Freesound
- Sound.Freesound: instance MonadError Error Freesound
- Sound.Freesound: instance MonadIO Freesound
- Sound.Freesound: instance MonadReader Handle Freesound
- Sound.Freesound: instance Show Error
- Sound.Freesound: instance Show Similarity
- Sound.Freesound: newtype Sample
- Sound.Freesound: properties :: Sample -> Freesound Properties
- Sound.Freesound: propertiesXML :: Sample -> Freesound Element
- Sound.Freesound: sampleId :: Sample -> Int
- Sound.Freesound: searchSimilar :: Similarity -> Sample -> Freesound [Sample]
- Sound.Freesound: withFreesound :: String -> String -> Freesound a -> IO (Either Error a)
- Sound.Freesound.Properties: Description :: User -> String -> Description
- Sound.Freesound.Properties: Properties :: Int -> User -> String -> String -> Statistics -> URLString -> URLString -> URLString -> String -> Int -> Int -> Int -> Int -> Double -> Int -> [Description] -> [Tag] -> Properties
- Sound.Freesound.Properties: Statistics :: Int -> (Int, Int) -> Statistics
- Sound.Freesound.Properties: User :: Int -> String -> User
- Sound.Freesound.Properties: bitDepth :: Properties -> Int
- Sound.Freesound.Properties: bitRate :: Properties -> Int
- Sound.Freesound.Properties: channels :: Properties -> Int
- Sound.Freesound.Properties: colors :: Properties -> URLString
- Sound.Freesound.Properties: data Description
- Sound.Freesound.Properties: data Properties
- Sound.Freesound.Properties: data Statistics
- Sound.Freesound.Properties: data User
- Sound.Freesound.Properties: date :: Properties -> String
- Sound.Freesound.Properties: descriptions :: Properties -> [Description]
- Sound.Freesound.Properties: downloads :: Statistics -> Int
- Sound.Freesound.Properties: duration :: Properties -> Double
- Sound.Freesound.Properties: extension :: Properties -> String
- Sound.Freesound.Properties: fileSize :: Properties -> Int
- Sound.Freesound.Properties: fromXML :: Element -> Maybe Properties
- Sound.Freesound.Properties: image :: Properties -> URLString
- Sound.Freesound.Properties: instance Eq Description
- Sound.Freesound.Properties: instance Eq Properties
- Sound.Freesound.Properties: instance Eq Statistics
- Sound.Freesound.Properties: instance Eq User
- Sound.Freesound.Properties: instance Show Description
- Sound.Freesound.Properties: instance Show Properties
- Sound.Freesound.Properties: instance Show Statistics
- Sound.Freesound.Properties: instance Show User
- Sound.Freesound.Properties: listFromXML :: Element -> [Properties]
- Sound.Freesound.Properties: originalFileName :: Properties -> String
- Sound.Freesound.Properties: preview :: Properties -> URLString
- Sound.Freesound.Properties: rating :: Statistics -> (Int, Int)
- Sound.Freesound.Properties: sampleId :: Properties -> Int
- Sound.Freesound.Properties: sampleRate :: Properties -> Int
- Sound.Freesound.Properties: statistics :: Properties -> Statistics
- Sound.Freesound.Properties: tags :: Properties -> [Tag]
- Sound.Freesound.Properties: type Tag = String
- Sound.Freesound.Properties: user :: Properties -> User
- Sound.Freesound.Properties: userId :: User -> Int
- Sound.Freesound.Properties: userName :: User -> String
- Sound.Freesound.Query: Query :: String -> Maybe Double -> Maybe Double -> Maybe Int -> Maybe Int -> Maybe Int -> Query
- Sound.Freesound.Query: bitDepth :: Query -> Maybe Int
- Sound.Freesound.Query: bitRate :: Query -> Maybe Int
- Sound.Freesound.Query: data Query
- Sound.Freesound.Query: maxDur :: Query -> Maybe Double
- Sound.Freesound.Query: minDur :: Query -> Maybe Double
- Sound.Freesound.Query: sampleRate :: Query -> Maybe Int
- Sound.Freesound.Query: string :: Query -> String
- Sound.Freesound.Query: stringQuery :: String -> Query
- Sound.Freesound.Query: toPostFields :: Query -> [String]
- Sound.Freesound.Sample: Sample :: Int -> Sample
- Sound.Freesound.Sample: fromXML :: Element -> Maybe Sample
- Sound.Freesound.Sample: instance Eq Sample
- Sound.Freesound.Sample: instance Ord Sample
- Sound.Freesound.Sample: instance Read Sample
- Sound.Freesound.Sample: instance Show Sample
- Sound.Freesound.Sample: listFromXML :: Element -> [Sample]
- Sound.Freesound.Sample: newtype Sample
- Sound.Freesound.Sample: sampleId :: Sample -> Int
+ Sound.Freesound: apiKeyFromString :: String -> APIKey
+ Sound.Freesound: appendQuery :: QueryLike a => a -> Resource r -> Resource r
+ Sound.Freesound: data APIKey
+ Sound.Freesound: data Resource a
+ Sound.Freesound: data URI
+ 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: getUserByName :: Text -> Freesound Detail
+ Sound.Freesound: resourceURI :: [Text] -> Query -> Resource a
+ Sound.Freesound: runFreesound :: APIKey -> Freesound a -> IO a
+ 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: data List a
+ Sound.Freesound.List: elems :: List a -> [a]
+ Sound.Freesound.List: elemsFieldName :: Elem a => a -> Text
+ Sound.Freesound.List: getNext :: (Elem a, 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: instance (Sound.Freesound.List.Elem a, Data.Aeson.Types.Class.FromJSON a) => Data.Aeson.Types.Class.FromJSON (Sound.Freesound.List.List a)
+ Sound.Freesound.List: instance GHC.Classes.Eq a => GHC.Classes.Eq (Sound.Freesound.List.List a)
+ Sound.Freesound.List: instance GHC.Show.Show a => GHC.Show.Show (Sound.Freesound.List.List a)
+ Sound.Freesound.List: next :: List a -> Maybe (Resource (List a))
+ Sound.Freesound.List: numElems :: List a -> Int
+ Sound.Freesound.List: previous :: List a -> Maybe (Resource (List a))
+ Sound.Freesound.Pack: class Pack a
+ Sound.Freesound.Pack: created :: Pack a => a -> Text
+ 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: name :: Pack a => a -> Text
+ Sound.Freesound.Pack: numDownloads :: Pack a => a -> Integer
+ Sound.Freesound.Pack: ref :: Pack a => a -> Resource ()
+ Sound.Freesound.Pack: sounds :: Pack a => a -> Resource (List Summary)
+ Sound.Freesound.Pack: type Packs = List Summary
+ Sound.Freesound.Pack: url :: Pack a => a -> URI
+ Sound.Freesound.Pack: username :: Detail -> Text
+ Sound.Freesound.Search: Ascending :: SortDirection
+ Sound.Freesound.Search: Created :: SortMethod
+ Sound.Freesound.Search: Descending :: SortDirection
+ Sound.Freesound.Search: Downloads :: SortMethod
+ Sound.Freesound.Search: Duration :: SortMethod
+ Sound.Freesound.Search: Pagination :: Int -> Int -> Pagination
+ Sound.Freesound.Search: Rating :: SortMethod
+ Sound.Freesound.Search: [page] :: Pagination -> Int
+ Sound.Freesound.Search: [resultsPerPage] :: Pagination -> Int
+ Sound.Freesound.Search: data Pagination
+ Sound.Freesound.Search: data SortDirection
+ Sound.Freesound.Search: data SortMethod
+ Sound.Freesound.Search: data Sorting
+ Sound.Freesound.Search: instance Data.Default.Class.Default Sound.Freesound.Search.Pagination
+ Sound.Freesound.Search: instance Data.Default.Class.Default Sound.Freesound.Search.Sorting
+ Sound.Freesound.Search: instance GHC.Classes.Eq Sound.Freesound.Search.Pagination
+ Sound.Freesound.Search: instance GHC.Classes.Eq Sound.Freesound.Search.SortDirection
+ Sound.Freesound.Search: instance GHC.Classes.Eq Sound.Freesound.Search.SortMethod
+ Sound.Freesound.Search: instance GHC.Classes.Eq Sound.Freesound.Search.Sorting
+ Sound.Freesound.Search: instance GHC.Show.Show Sound.Freesound.Search.Pagination
+ Sound.Freesound.Search: instance GHC.Show.Show Sound.Freesound.Search.SortDirection
+ Sound.Freesound.Search: instance GHC.Show.Show Sound.Freesound.Search.SortMethod
+ Sound.Freesound.Search: instance GHC.Show.Show Sound.Freesound.Search.Sorting
+ Sound.Freesound.Search: instance Network.HTTP.Types.QueryLike.QueryLike Sound.Freesound.Search.Pagination
+ Sound.Freesound.Search: instance Network.HTTP.Types.QueryLike.QueryValueLike Sound.Freesound.Search.Sorting
+ Sound.Freesound.Search: sortedBy :: SortMethod -> SortDirection -> Sorting
+ Sound.Freesound.Search: unsorted :: Sorting
+ 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: AIFF :: FileType
+ Sound.Freesound.Sound: FLAC :: FileType
+ Sound.Freesound.Sound: MP3 :: FileType
+ Sound.Freesound.Sound: OGG :: FileType
+ Sound.Freesound.Sound: WAV :: FileType
+ Sound.Freesound.Sound: analysisFrames :: Detail -> Resource ()
+ Sound.Freesound.Sound: analysisStats :: Detail -> Resource ()
+ Sound.Freesound.Sound: avgRating :: Detail -> Double
+ Sound.Freesound.Sound: bitdepth :: Detail -> Int
+ Sound.Freesound.Sound: bitrate :: Detail -> Int
+ Sound.Freesound.Sound: bookmark :: Detail -> URI
+ Sound.Freesound.Sound: channels :: Detail -> Int
+ Sound.Freesound.Sound: class Sound a
+ Sound.Freesound.Sound: comment :: Detail -> URI
+ Sound.Freesound.Sound: comments :: Detail -> Resource ()
+ Sound.Freesound.Sound: created :: Detail -> Text
+ Sound.Freesound.Sound: data Detail
+ Sound.Freesound.Sound: data FileType
+ Sound.Freesound.Sound: data SoundId
+ Sound.Freesound.Sound: data Summary
+ Sound.Freesound.Sound: description :: Detail -> Text
+ Sound.Freesound.Sound: download :: Detail -> URI
+ Sound.Freesound.Sound: duration :: Detail -> Double
+ Sound.Freesound.Sound: fileType :: Detail -> FileType
+ Sound.Freesound.Sound: filesize :: Detail -> Integer
+ Sound.Freesound.Sound: geotag :: Detail -> Maybe GeoTag
+ Sound.Freesound.Sound: getSimilar :: Pagination -> Detail -> Freesound Sounds
+ Sound.Freesound.Sound: getSimilar_ :: Detail -> Freesound Sounds
+ Sound.Freesound.Sound: id :: Sound a => a -> SoundId
+ Sound.Freesound.Sound: images :: Detail -> Images
+ Sound.Freesound.Sound: license :: Sound a => a -> License
+ Sound.Freesound.Sound: name :: Sound a => a -> Text
+ Sound.Freesound.Sound: numComments :: Detail -> Integer
+ Sound.Freesound.Sound: numDownloads :: Detail -> Integer
+ Sound.Freesound.Sound: numRatings :: Detail -> Integer
+ Sound.Freesound.Sound: pack :: Detail -> Maybe (Resource ())
+ Sound.Freesound.Sound: previews :: Detail -> Previews
+ Sound.Freesound.Sound: rate :: Detail -> URI
+ Sound.Freesound.Sound: samplerate :: Detail -> Int
+ Sound.Freesound.Sound: search :: Pagination -> Sorting -> Filters -> Query -> Freesound Sounds
+ Sound.Freesound.Sound: search_ :: Query -> Freesound Sounds
+ Sound.Freesound.Sound: similarSounds :: Detail -> Resource (List Summary)
+ Sound.Freesound.Sound: sound_license :: Detail -> License
+ Sound.Freesound.Sound: sound_username :: Detail -> Text
+ Sound.Freesound.Sound: tags :: Sound a => a -> [Text]
+ Sound.Freesound.Sound: type Sounds = List Summary
+ Sound.Freesound.Sound: url :: Detail -> URI
+ Sound.Freesound.Sound: username :: Sound a => a -> Text
+ 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: getUserByName :: Text -> 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: search :: Query -> Freesound [Sample]
+ Sound.Freesound: search :: Pagination -> Sorting -> Filters -> Query -> Freesound Sounds
Files
- CHANGELOG.md +3/−0
- Sound/Freesound.hs +0/−174
- Sound/Freesound/Properties.hs +0/−147
- Sound/Freesound/Query.hs +0/−31
- Sound/Freesound/Sample.hs +0/−20
- Sound/Freesound/URL.hs +0/−21
- Sound/Freesound/Util.hs +0/−42
- freesound.cabal +60/−26
- freesound/Main.hs +0/−250
- src/Sound/Freesound.hs +38/−0
- src/Sound/Freesound/API.hs +128/−0
- src/Sound/Freesound/Bookmark.hs +50/−0
- src/Sound/Freesound/Bookmark/Internal.hs +24/−0
- src/Sound/Freesound/List.hs +45/−0
- src/Sound/Freesound/Pack.hs +24/−0
- src/Sound/Freesound/Pack/Type.hs +78/−0
- src/Sound/Freesound/Search.hs +59/−0
- src/Sound/Freesound/Search/Filter.hs +215/−0
- src/Sound/Freesound/Search/Numerical.hs +65/−0
- src/Sound/Freesound/Search/Query.hs +36/−0
- src/Sound/Freesound/Sound.hs +85/−0
- src/Sound/Freesound/Sound/Type.hs +227/−0
- src/Sound/Freesound/User.hs +58/−0
- src/Sound/Freesound/User/Type.hs +85/−0
- test/Sound/Freesound/Test.hs +9/−0
- test/Spec.hs +1/−0
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+## 0.2.0++Beginning of transition to Freesound API v2: Working resources are textual search and `Sound` resources.
− Sound/Freesound.hs
@@ -1,174 +0,0 @@--- | This module provides access to the Freesound Project, a database of--- Creative Commons licensed sounds.------ * <http://www.freesound.org/>------ * <http://www.creativecommons.org/>-module Sound.Freesound (- -- * The Freesound monad- Freesound,- withFreesound,- -- * Error handling- Error(..), errorString,- -- * Sample handles- Sample(..),- -- * API methods- search,- Similarity(..), searchSimilar,- propertiesXML, properties,- download-) where--import qualified Data.ByteString.Lazy as BS-import Data.List (find, intercalate, stripPrefix)-import Data.Maybe (listToMaybe, mapMaybe)-import Network.Curl-import qualified Network.Curl.Easy as Curl-import Control.Monad.Error (ErrorT(..), MonadError, MonadIO, liftIO, noMsg, strMsg, throwError)-import Control.Monad.Reader (MonadReader, ReaderT(..), ask)-import qualified Control.Monad.Error as Error-import Numeric (readDec)-import qualified Text.XML.Light as XML--import Sound.Freesound.Properties (Properties)-import qualified Sound.Freesound.Properties as Properties-import Sound.Freesound.Query (Query)-import qualified Sound.Freesound.Query as Query-import Sound.Freesound.Sample (Sample(..))-import qualified Sound.Freesound.Sample as Sample-import qualified Sound.Freesound.URL as URL-import Sound.Freesound.Util (findString, readMaybe)--mkURL :: String -> URLString-mkURL page = baseURL ++ "/" ++ page---- The various API URLs.-baseURL = "http://www.freesound.org"-loginURL = mkURL "forum/login.php"-loginRedirect = "../index.php"-searchURL = mkURL "searchTextXML.php"-searchSimilarURL = mkURL "searchSimilarXML.php"-xmlURL = mkURL "samplesViewSingleXML.php"-audioURL = mkURL "samplesDownload.php"---- | Curl options used by default in all the interaction with the database.-defaultCurlOptions :: [CurlOption]-defaultCurlOptions = [- -- Some servers require this to be set- CurlUserAgent "libcurl-agent/1.0",- -- Enable cookie handling; cookies are passed around in the session- -- handle and not saved to disk- CurlCookieFile ""- ]---- | Error type.-data Error =- Error String- | CurlError CurlCode- | LoginError- | XMLError- | UnknownError- deriving (Show)--instance Error.Error Error where- noMsg = UnknownError- strMsg s = Error s---- | Convert an 'Error' into a 'String'.-errorString :: Error -> String-errorString (Error s) = s-errorString (CurlError c) = maybe s id (stripPrefix "Curl" (show c))- where s = show c-errorString e = show e---- | Curl handle.-type Handle = Curl-type Response b = CurlResponse_ [(String, String)] b---- | The 'Freesound' monad.---- Adds an environment (the 'Curl' handle) and error handling to the 'IO'--- monad. Actions in the 'Freesound' monad can only be executed by--- 'withFreesound', which handles all the initialization and cleanup details.-newtype Freesound a = Freesound { runFreesound :: ReaderT Handle (ErrorT Error IO) a }- deriving (Functor, Monad, MonadError Error, MonadIO, MonadReader Handle)---- | Make a request using 'Handle' and converting propagating failure codes--- to the ErrorT monad.-request :: CurlBuffer b => URLString -> [CurlOption] -> Freesound (Response b)-request url options = do- curl <- ask- resp <- liftIO $ do_curl_ curl url (defaultCurlOptions ++ options)- case respCurlCode resp of- CurlOK -> return resp- code -> throwError (CurlError code)---- | Make a request and parse the returned XML data.-requestXML :: URLString -> [CurlOption] -> Freesound XML.Element-requestXML url options = do- resp <- request url options :: Freesound (Response BS.ByteString)- case XML.parseXMLDoc (respBody resp) of- Nothing -> throwError XMLError- Just xml -> return xml---- | Log into freesound.-login :: String -> String -> Freesound ()-login user password = do- resp <- request loginURL opts- -- Check for login success (duh!)- -- TODO: Figure out a better way- case findString "logged" (respBody resp) of- Nothing -> throwError LoginError- _ -> return ()- where- post = URL.postFields [- ("username", user),- ("password", password),- ("login", "login"),- ("redirect", loginRedirect) ]- opts = [ CurlPostFields post,- -- CurlCookieJar cookieFile,- CurlFollowLocation True ]---- | Log into Freesound with and perform an action in the 'Freesound' monad.-withFreesound :: String -> String -> Freesound a -> IO (Either Error a)-withFreesound user password f =- withCurlDo $ liftIO Curl.initialize >>= runErrorT . runReaderT action- where action = runFreesound (login user password >> f)---- | Search the Freesound database.-search :: Query -> Freesound [Sample]-search query = Sample.listFromXML `fmap` requestXML searchURL opts- where opts = [ CurlPostFields (Query.toPostFields query) ]---- | Similarity type used by 'searchSimilar'.-data Similarity = Similar | Dissimilar deriving (Eq, Show)---- | Search samples similar (or dissimilar) to a 'Sample'.-searchSimilar :: Similarity -> Sample -> Freesound [Sample]-searchSimilar similarity sample = Sample.listFromXML `fmap` requestXML url []- where- url = URL.addParams params searchSimilarURL- params = [ ("id", show (sampleId sample)),- ("inverse", case similarity of- Similar -> "false"- Dissimilar -> "true") ]---- | Get the properties of a 'Sample' as an XML document.-propertiesXML :: Sample -> Freesound XML.Element-propertiesXML sample = requestXML url []- where url = URL.addParams [("id", show (sampleId sample))] xmlURL---- | Get the properties of a 'Sample'.-properties :: Sample -> Freesound Properties-properties sample = do- xml <- propertiesXML sample- let props = Properties.listFromXML xml- case find ((== sampleId sample) . Properties.sampleId) props of- Just p -> return p- Nothing -> throwError $ Error ("Properties for sample " ++ (show $ sampleId sample) ++ " not found")---- | Download a 'Sample' as an instance of 'CurlBuffer'.-download :: CurlBuffer b => Sample -> Freesound b-download sample = respBody `fmap` request url []- where url = URL.addParams [("id", show (sampleId sample))] audioURL
− Sound/Freesound/Properties.hs
@@ -1,147 +0,0 @@-module Sound.Freesound.Properties (- User(..),- Description(..),- Statistics(..),- Tag,- Properties(..),- fromXML, listFromXML-) where--import Data.Maybe (mapMaybe)-import Network.Curl (URLString)-import Sound.Freesound.Util-import Text.XML.Light (unqual)-import qualified Text.XML.Light as XML---- | User of the Freesound database.-data User = User {- userId :: Int,- userName :: String-} deriving (Eq, Show)---- | Description of a 'Sample', containing user and text.-data Description = Description User String deriving (Eq, Show)---- | Tag (a single word) for describing a 'Sound'-type Tag = String---- | Statistics of a 'Sample' in the database.-data Statistics = Statistics {- downloads :: Int,- rating :: (Int, Int)-} deriving (Eq, Show)---- | Properties of a 'Sample' in the database.-data Properties = Properties {- sampleId :: Int,- - user :: User,- date :: String,- originalFileName :: String,- statistics :: Statistics,- - image :: URLString,- preview :: URLString,- colors :: URLString,-- -- descriptors ??- -- parent ??- -- geotag ??- - extension :: String,- sampleRate :: Int,- bitRate :: Int,- bitDepth :: Int,- channels :: Int,- duration :: Double,- fileSize :: Int,- - descriptions :: [Description],- tags :: [Tag]- - -- comments ??-} deriving (Eq, Show)---- | Read a 'User' value from an 'XML.Element' in the 'Maybe' monad.-userFromXML :: XML.Element -> Maybe User-userFromXML e = do- _userId <- XML.findAttr (unqual "id") e >>= readMaybe- _userName <- XML.findChild (unqual "name") e >>= return . XML.strContent- return (User _userId _userName)---- | Read a rating from an 'XML.Element' in the 'Maybe' monad.-ratingFromXML :: XML.Element -> Maybe (Int, Int)-ratingFromXML e = do- count <- findAttr "count" e >>= readMaybe- value <- strContent e >>= readMaybe- return (count, value)- --- | Read a 'Statistics' value from an 'XML.Element' in the 'Maybe' monad.-statisticsFromXML :: XML.Element -> Maybe Statistics-statisticsFromXML e = do- _downloads <- findChild "downloads" e >>= strContent >>= readMaybe- _rating <- findChild "rating" e >>= ratingFromXML- return (Statistics _downloads _rating)---- | Read a 'Description' value from an 'XML.Element' in the 'Maybe' monad.-descriptionFromXML :: XML.Element -> Maybe Description-descriptionFromXML e = do- _user <- userFromXML e- _text <- findChild "text" e >>= strContent- return (Description _user _text)---- | Read a list of 'Description's from an 'XML.Element' in the 'Maybe' monad.-descriptionsFromXML :: XML.Element -> Maybe [Description]-descriptionsFromXML = return . mapMaybe descriptionFromXML . findChildren "description"---- | Read a list of 'Tag's from an 'XML.Element' in the 'Maybe' monad.-tagsFromXML :: XML.Element -> Maybe [Tag]-tagsFromXML = return . map XML.strContent . findChildren "tag"---- | Read a 'Properties' value from an 'XML.Element' in the 'Maybe' monad.-fromXML :: XML.Element -> Maybe Properties-fromXML e = do- _sampleId <- findAttr "id" e >>= readMaybe- _user <- findChild "user" e >>= userFromXML- _date <- findChild "date" e >>= strContent- _originalFileName <- findChild "originalFilename" e >>= strContent- _statistics <- findChild "statistics" e >>= statisticsFromXML- _image <- findChild "image" e >>= strContent- _preview <- findChild "preview" e >>= strContent- _colors <- findChild "colors" e >>= strContent- _extension <- findChild "extension" e >>= strContent- _sampleRate <- findChild "samplerate" e >>= strContent >>= readMaybe- _bitRate <- findChild "bitrate" e >>= strContent >>= readMaybe- _bitDepth <- findChild "bitdepth" e >>= strContent >>= readMaybe- _channels <- findChild "channels" e >>= strContent >>= readMaybe- _duration <- findChild "duration" e >>= strContent >>= readMaybe- _fileSize <- findChild "filesize" e >>= strContent >>= readMaybe- _descriptions <- findChild "descriptions" e >>= descriptionsFromXML- _tags <- findChild "tags" e >>= tagsFromXML- return $ Properties {- sampleId = _sampleId,-- user = _user,- date = _date,- originalFileName = _originalFileName,- statistics = _statistics,-- image = _image,- preview = _preview,- colors = _colors,-- extension = _extension,- sampleRate = _sampleRate,- bitRate = _bitRate,- bitDepth = _bitDepth,- channels = _channels,- duration = _duration,- fileSize = _fileSize,-- descriptions = _descriptions,- tags = _tags- }---- | Read a list of 'Properties' from an 'XML.Element'.-listFromXML :: XML.Element -> [Properties]-listFromXML = mapMaybe fromXML . XML.findChildren (unqual "sample")
− Sound/Freesound/Query.hs
@@ -1,31 +0,0 @@-module Sound.Freesound.Query (- Query(..),- stringQuery,- toPostFields-) where--import qualified Sound.Freesound.URL as URL---- | A 'Query' type for searching the database.-data Query = Query {- string :: String, -- ^ The query string- minDur :: Maybe Double, -- ^ Minimum duration in seconds- maxDur :: Maybe Double, -- ^ Maximum duration in seconds- bitRate :: Maybe Int, -- ^ Bit rate of the soundfile- bitDepth :: Maybe Int, -- ^ Bit depth of the soundfile- sampleRate :: Maybe Int -- ^ Sample rate of the soundfile-}---- | Construct a 'Query' to search the database for a 'String'.-stringQuery :: String -> Query-stringQuery s = Query s Nothing Nothing Nothing Nothing Nothing---- | Convert a query to POST request fields.-toPostFields :: Query -> [String]-toPostFields query = URL.postFields [ x | Just x <- [- Just ("search", string query),- fmap ((,) "durationMin" . show) (minDur query),- fmap ((,) "durationMax" . show) (maxDur query),- fmap ((,) "bitrate" . show) (bitRate query),- fmap ((,) "bitdepth" . show) (bitDepth query),- fmap ((,) "samplerate" . show) (sampleRate query) ] ]
− Sound/Freesound/Sample.hs
@@ -1,20 +0,0 @@-module Sound.Freesound.Sample (- Sample(..),- fromXML, listFromXML-) where--import Data.Maybe (mapMaybe)-import Sound.Freesound.Util-import qualified Text.XML.Light as XML---- | A handle to a sample in the database.-newtype Sample = Sample { sampleId :: Int }- deriving (Eq, Ord, Read, Show)---- | Read a 'Sample' from an 'XML.Element'.-fromXML :: XML.Element -> Maybe Sample-fromXML e = Sample `fmap` (findAttr "id" e >>= readMaybe)---- | Read a list of 'Sample's from an 'XML.Element'.-listFromXML :: XML.Element -> [Sample]-listFromXML = mapMaybe fromXML . findChildren "sample"
− Sound/Freesound/URL.hs
@@ -1,21 +0,0 @@-module Sound.Freesound.URL (- postFields, addParams-) where--import Data.List (intercalate)-import Network.Curl (URLString)---- | Construct a POST field from a key/value pair.-postField :: String -> String -> String-postField k v = k ++ "=" ++ v--postFields :: [(String, String)] -> [String]-postFields = map (uncurry postField)--addParams :: [(String, String)] -> URLString -> URLString-addParams [] u = u-addParams (p:ps) u = addpn ps (addp1 p u)- where- addp1 (k, v) u = u ++ "?" ++ (postField k v)- addpn [] u = u- addpn ((k, v):rest) u = u ++ "&" ++ postField k v ++ addpn rest u
− Sound/Freesound/Util.hs
@@ -1,42 +0,0 @@-module Sound.Freesound.Util (- readMaybe,- findAttr,- findChild,- findChildren,- strContent,- findString-) where--import Data.List (isPrefixOf)-import qualified Text.XML.Light as XML---- | Find substring.-findString :: String -> String -> Maybe String-findString "" xs = Just xs-findString a "" = Nothing-findString a xs- | isPrefixOf a xs = Just xs-findString a (_:xs) = findString a xs---- | Read a value from a 'String', returning 'Nothing' when the parse fails.-readMaybe :: (Read a) => String -> Maybe a-readMaybe s = case reads s of- [(a, "")] -> Just a- _ -> Nothing---- | Find an attribute of an 'XML.Element'.-findAttr :: String -> XML.Element -> Maybe String-findAttr name = XML.findAttr (XML.unqual name)---- | Find a child element of an 'XML.Element'.-findChild :: String -> XML.Element -> Maybe XML.Element-findChild name = XML.findChild (XML.unqual name)---- | Find all child elements of an 'XML.Element'.-findChildren :: String -> XML.Element -> [XML.Element]-findChildren name = XML.findChildren (XML.unqual name)---- | Return the string content of an 'XML.Element' in the 'Maybe' monad.-strContent :: XML.Element -> Maybe String-strContent = return . XML.strContent-
freesound.cabal view
@@ -1,5 +1,5 @@ name: freesound-version: 0.1.0+version: 0.2.0 synopsis: Access the Freesound Project database description: Access the Freesound Project database. The Freesound Project is a collaborative database of Creative Commons@@ -11,35 +11,69 @@ license: BSD3 license-file: LICENSE category: Sound, Web-copyright: Copyright (c) Stefan Kersten 2008-2010-author: Stefan Kersten-maintainer: Stefan Kersten-stability: provisional-homepage: http://code.haskell.org/~StefanKersten/code/freesound-tested-with: GHC == 6.10.1, GHC == 6.10.4, GHC == 6.12.1+copyright: Copyright (c) Stefan Kersten 2008-2016+author: Stefan Kersten <kaoskorobase@gmail.com>+maintainer: Stefan Kersten <kaoskorobase@gmail.com>+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 build-type: Simple-cabal-version: >= 1.2+cabal-version: >= 1.8 +extra-source-files:+ CHANGELOG.md++source-repository head+ type: git+ location: https://github.com/kaoskorobase/freesound+ library exposed-modules: Sound.Freesound- Sound.Freesound.Properties- Sound.Freesound.Query- Sound.Freesound.Sample- other-modules: Sound.Freesound.URL- Sound.Freesound.Util+ Sound.Freesound.Bookmark+ 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.Sound.Type+ Sound.Freesound.User.Type build-depends: base >= 3 && < 5- , bytestring >= 0.9 && < 1.0- , curl >= 1.3 && < 1.4- , data-accessor >= 0.1 && < 0.3- , data-accessor-template >= 0.1 && < 0.3- , directory >= 1- , mtl >= 1.1- , xml >= 1.3 && < 1.4+ , aeson >= 0.6+ , blaze-builder >= 0.3+ , bytestring >= 0.9+ , data-default >= 0.5+ , filepath >= 1.2+ , http-types >= 0.7+ , lens+ , mtl+ , network >= 2.4+ , network-uri+ , old-locale >= 1.0+ , text >= 0.11+ , time >= 1.2+ , transformers >= 0.3+ , wreq+ hs-source-dirs: src+ Ghc-Options: -Wall - extensions: GeneralizedNewtypeDeriving+-- executable freesound+-- hs-source-dirs: ., freesound+-- main-is: Main.hs -executable freesound- hs-source-dirs: ., freesound- main-is: Main.hs- extensions: GeneralizedNewtypeDeriving,- TemplateHaskell+test-suite spec+ type: exitcode-stdio-1.0+ ghc-options: -Wall+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules: Sound.Freesound.Test+ build-depends: base == 4.*+ , data-default+ , freesound+ , hspec
− freesound/Main.hs
@@ -1,250 +0,0 @@--- | Access the freesound database from the command line.------ /TODO/:--- * flesh out error handling-----import qualified Data.ByteString.Lazy as BS-import Control.Monad.Trans (liftIO)-import Sound.Freesound-import qualified Sound.Freesound.Properties as Props-import Sound.Freesound.Query-import Sound.Freesound.Util-import System.IO (hGetLine, hGetEcho, hSetEcho, stdin)-import Text.XML.Light as XML-import System.Console.GetOpt--import Data.Accessor.Template-import Data.Accessor--import System.Exit-import System.IO-import System.Environment (getArgs)--data Options = Options- { optHelp_ :: Bool- , optUser_ :: Maybe String- , optPassword_ :: Maybe String- , optSearch_ :: SearchOptions- , optSearchSimilar_ :: SearchSimilarOptions- , optProperties_ :: PropertiesOptions- , optDownload_ :: DownloadOptions- } deriving (Eq, Show)--data SearchOptions = SearchOptions- {- } deriving (Eq, Show)--data SearchSimilarOptions = SearchSimilarOptions- { optSimilarity_ :: Similarity- } deriving (Eq, Show)--data PropertiesOptions = PropertiesOptions- { optXML_ :: Bool- } deriving (Eq, Show)--data DownloadOptions = DownloadOptions- { optOutputFile_ :: Maybe FilePath- , optAddExtension_ :: Bool- , optUseOriginalFileName_ :: Bool- } deriving (Eq, Show)--$( deriveAccessors ''Options )--- $( deriveAccessors ''SearchOptions )-$( deriveAccessors ''SearchSimilarOptions )-$( deriveAccessors ''PropertiesOptions )-$( deriveAccessors ''DownloadOptions )--type Action = Freesound ()-type Result = Either String Action--data Command = Command {- cmdOptions :: [OptDescr (Options -> Options)],- cmdHelp :: String,- cmdAction :: Options -> [String] -> Result-}--defaultOptions :: Options-defaultOptions = Options- { optHelp_ = False- , optUser_ = Nothing- , optPassword_ = Nothing- , optSearch_ = SearchOptions { }- , optSearchSimilar_ = SearchSimilarOptions {- optSimilarity_ = Similar }- , optProperties_ = PropertiesOptions {- optXML_ = False }- , optDownload_ = DownloadOptions- { optOutputFile_ = Nothing- , optAddExtension_ = False- , optUseOriginalFileName_ = False }- }--globalOptions :: [OptDescr (Options -> Options)]-globalOptions =- [ Option ['h'] ["help"]- (NoArg (optHelp ^= True))- "Print this help message."- , Option ['u'] ["user"]- (ReqArg (\x -> optUser ^= Just x) "STRING")- "Freesound user name."- , Option ['p'] ["password"]- (ReqArg (\x -> optPassword ^= Just x) "STRING")- "Freesound password."- ]--searchOptions :: [OptDescr (Options -> Options)]-searchOptions = [ ]--searchSimilarOptions :: [OptDescr (Options -> Options)]-searchSimilarOptions =- [ Option [] ["dissimilar"]- (NoArg (optSearchSimilar ^: optSimilarity ^= Dissimilar))- "Dissimilar"- ]--propertiesOptions :: [OptDescr (Options -> Options)]-propertiesOptions =- [ Option [] ["xml"]- (NoArg (optProperties ^: optXML ^= True))- "List xml"- ]--downloadOptions :: [OptDescr (Options -> Options)]-downloadOptions =- [ Option ['o'] ["output-file"]- (ReqArg (\x -> optDownload ^: optOutputFile ^= Just x) "PATH")- "Output file name (overrides --use-original-filename)"- , Option ['e'] ["add-extension"]- (NoArg (optDownload ^: optAddExtension ^= True))- "Add original extension to output file"- , Option ['O'] ["use-original-filename"]- (NoArg (optDownload ^: optUseOriginalFileName ^= True))- "Use original file name for output"- ]---- Utilities--readSample :: [String] -> Either String Sample-readSample [] = Left "Missing sample ID"-readSample (x:_) = case readMaybe x of- Nothing -> Left "Invalid sample ID"- Just i -> Right (Sample i)--printSamples :: [Sample] -> Action-printSamples = liftIO . mapM_ (print . sampleId)---- Commands--do_search :: Options -> [String] -> Result-do_search options args = Right $ do- samples <- search (stringQuery $ unwords args)- printSamples samples--do_searchSimilar :: Options -> [String] -> Result-do_searchSimilar options args =- case readSample args of- Left e -> Left e- Right s -> Right $ searchSimilar (options^.optSearchSimilar^.optSimilarity) s >>= printSamples--do_properties :: Options -> [String] -> Result-do_properties options args =- case readSample args of- Left e -> Left e- Right s -> if options^.optProperties^.optXML- then Right $ propertiesXML s >>= liftIO . putStrLn . XML.ppElement- else Right $ properties s >>= liftIO . print--do_download :: Options -> [String] -> Result-do_download options args =- case readSample args of- Left e -> Left e- Right s -> Right $ do- path <- if options^.optDownload^.optUseOriginalFileName- then properties s >>= return . Just . Props.originalFileName- else case options^.optDownload^.optOutputFile of- Nothing -> return Nothing- Just p -> if options^.optDownload^.optAddExtension- then properties s >>= return . Just . (\props -> p ++ "." ++ Props.extension props)- else return (Just p)- download s >>= maybe (liftIO . BS.putStrLn) (\p -> liftIO . BS.writeFile p) path--commands :: [(String, Command)]-commands = [- ("search", Command searchOptions "freesound search [OPTION...] PREDICATES.." do_search),- ("similar", Command searchSimilarOptions "freesound similar [OPTION...] SAMPLE" do_searchSimilar),- ("properties", Command propertiesOptions "freesound properties [OPTION...] SAMPLE" do_properties),- ("download", Command downloadOptions "freesound download [OPTION...] SAMPLE" do_download)- ]--printHelp :: ExitCode -> String -> [OptDescr (Options -> Options)] -> IO a-printHelp code header options = do- hPutStr stderr (usageInfo header options)- exitWith code--parseOptions :: String -> [OptDescr (Options -> Options)] -> [String] -> IO (Options, [String])-parseOptions header options argv = - case getOpt Permute options argv of- (o, n, []) -> let o' = foldl (flip ($)) defaultOptions o in- if o'^.optHelp- then printHelp ExitSuccess header options- else return (o', n)- (_, _, es) -> ioError (userError (concat es ++ usageInfo header options))--globalHelp :: String-globalHelp = "Usage: freesound COMMAND [OPTION...] [ARG...]\- \\n\n\- \Try freesound COMMAND --help for individual command options.\- \\n\n\- \Commands:\n\n\- \ search\n\- \ similar\n\- \ properties\n\- \ downloads\n\n\- \Global options:\n"---- | Read username from stdin.-getUser :: IO String-getUser = putStrLn "User:" >> hGetLine stdin---- | Read password from stdin.-getPassword :: IO String-getPassword = do- putStrLn "Password:"- echo <- hGetEcho stdin- hSetEcho stdin False- password <- hGetLine stdin- hSetEcho stdin echo- return password---- | Read credentials that are still unknown from stdin.-getCredentials :: Maybe String -> Maybe String -> IO (String, String)-getCredentials user password = do- user' <- maybe getUser return user- password' <- maybe getPassword return password- return (user', password')---- | Perform a command with the given user name and password.-doCommand :: String -> String -> Action -> IO ()-doCommand user password action = do- res <- withFreesound user password action- case res of- Left e -> putStrLn ("ERROR: " ++ errorString e)- Right r -> return ()--main :: IO ()-main = do- argv <- getArgs- case argv of- [] -> printHelp (ExitFailure 1) globalHelp globalOptions- (cmdName:rest) ->- case lookup cmdName commands of- Nothing ->- printHelp (ExitFailure 1) globalHelp globalOptions- Just cmd -> do- (options, args) <- parseOptions (cmdHelp cmd) (globalOptions ++ cmdOptions cmd) rest- case cmdAction cmd options args of- Left e -> putStrLn ("ERROR: " ++ e)- Right a -> do- (user, password) <- getCredentials (options^.optUser) (options^.optPassword)- doCommand user password a
+ src/Sound/Freesound.hs view
@@ -0,0 +1,38 @@+-- | This module provides access to the Freesound Project, a database of+-- Creative Commons licensed sounds.+--+-- * <http://www.freesound.org/>+--+-- * <http://www.creativecommons.org/>+--+-- > 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++import Sound.Freesound.API (Freesound, runFreesound, APIKey, apiKeyFromString)+import Sound.Freesound.List+import Sound.Freesound.Search+import Sound.Freesound.Search.Query+import Sound.Freesound.User+import Sound.Freesound.Sound
+ src/Sound/Freesound/API.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE CPP, FlexibleInstances, GeneralizedNewtypeDeriving, OverloadedStrings #-}+module Sound.Freesound.API (+ APIKey+, apiKeyFromString+-- , HTTPRequest+, Freesound+, runFreesound+-- , request+, URI+, getURI+, Resource+, resourceURI+, appendQuery+, getResource+) 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 qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy.Char8 as BL+import Data.Text (Text)+import qualified Data.Text as T+import qualified Network.HTTP.Types as HTTP+import qualified Network.URI as URI+import qualified Network.Wreq as HTTP+import Network.Wreq.Session (Session)+import qualified Network.Wreq.Session as Session++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+import Data.Monoid+#endif++-- | API key required for each call to the Freesound server.+newtype APIKey = APIKey BS.ByteString deriving (Eq, Show)++-- | Construct an API key from a String.+apiKeyFromString :: String -> APIKey+apiKeyFromString = APIKey . BS.pack++-- | Reader monad environment.+data Env = Env {+ apiKey :: APIKey+ , session :: Session+ }++-- | Freesound API monad for communication with the Freesound server.+newtype Freesound a = Freesound (R.ReaderT Env IO a)+ deriving (Applicative, Functor, Monad, MonadIO, R.MonadReader Env)++-- instance MonadTrans Freesound where+-- lift = Freesound . lift++-- | Perform an API action and return the result.+runFreesound :: APIKey -> Freesound a -> IO a+runFreesound k (Freesound m) = Session.withAPISession (R.runReaderT m . Env k)++-- | Newtype wrapper of Network.URI.URI to avoid orphan instance.+newtype URI = URI URI.URI deriving (Eq, Show)++instance FromJSON URI where+ parseJSON (String v) = maybe mzero return (parseURI' (T.unpack v))+ parseJSON _ = mzero++-- | 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+ 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 ""+ r <- liftIO $ Session.getWith opts s u'+ return $ r ^. HTTP.responseBody++-- | Resource URI.+newtype Resource a = Resource URI deriving (Eq, FromJSON, Show)++-- | Append a query string to a resource URI.+appendQuery :: HTTP.QueryLike a => a -> Resource r -> Resource r+appendQuery q (Resource (URI u)) = Resource $ URI $ u { URI.uriQuery = q' }+ where q' = BS.unpack+ $ Builder.toByteString+ $ HTTP.renderQueryBuilder True+ $ 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++-- | The base URI of the Freesound API.+baseURI :: Builder.Builder+baseURI = Builder.fromString "http://www.freesound.org/apiv2"++-- | Construct an API uri from path components and a query.+resourceURI :: [Text] -> HTTP.Query -> Resource a+resourceURI path query = Resource u+ where Just u = parseURI'+ . BL.unpack+ . Builder.toLazyByteString+ . 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
@@ -0,0 +1,50 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Bookmark (+ Bookmark+, sound+, name+, Category+, category+, url+, sounds+, getSounds+) where++import Control.Monad (mzero)+import Data.Aeson+import Data.Text (Text)+import Sound.Freesound.API (Freesound, Resource, URI, getResource)+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]+} deriving (Eq, Show)++instance FromJSON Category where+ parseJSON (Object v) =+ Category+ <$> v .: "name"+ <*> v .: "url"+ <*> v .: "sounds"+ parseJSON _ = mzero++getSounds :: Category -> Freesound [Bookmark]+getSounds = getResource . sounds
+ src/Sound/Freesound/Bookmark/Internal.hs view
@@ -0,0 +1,24 @@+{-# 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/List.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE CPP, OverloadedStrings, ScopedTypeVariables #-}+module Sound.Freesound.List (+ List+, Elem(..)+, elems+, numElems+, previous+, next+, getPrevious+, getNext+) where++import Control.Monad (liftM, mzero)+import Data.Aeson+import Data.Text (Text)+import Sound.Freesound.API (Freesound, Resource, getResource)++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif++data List a = List {+ elems :: [a]+, numElems :: Int+, previous :: Maybe (Resource (List a))+, next :: Maybe (Resource (List a))+} deriving (Eq, Show)++class Elem a where+ elemsFieldName :: a -> Text++instance (Elem a, FromJSON a) => FromJSON (List a) where+ parseJSON (Object v) =+ List+ <$> v .: "results"+ <*> v .: "count"+ <*> v .:? "previous"+ <*> v .:? "next"+ parseJSON _ = mzero++getPrevious :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))+getPrevious = maybe (return Nothing) (liftM Just . getResource) . previous++getNext :: (Elem a, FromJSON a) => List a -> Freesound (Maybe (List a))+getNext = maybe (return Nothing) (liftM Just . getResource) . next
+ src/Sound/Freesound/Pack.hs view
@@ -0,0 +1,24 @@+module Sound.Freesound.Pack (+ Pack(..)+, Packs+, Summary+, Detail+, username+, getSounds+, getSounds_+) 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++getSounds_ :: (Pack a) => a -> Freesound Sounds+getSounds_ = getSounds def
+ src/Sound/Freesound/Pack/Type.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Pack.Type where++import Control.Monad (mzero)+import Data.Aeson (FromJSON(..), Value(..), (.:))+import Data.Text (Text)+import Sound.Freesound.API (Resource, URI)+import Sound.Freesound.List (List, Elem(..))+import qualified Sound.Freesound.Sound as Sound++#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++instance Elem Summary where+ elemsFieldName _ = "packs"++data Detail = Detail {+ summary :: Summary+, username :: Text -- ^ A JSON object with the user’s username, url, and ref.+} 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
+ src/Sound/Freesound/Search.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE OverloadedStrings #-}+module Sound.Freesound.Search (+ SortMethod(..)+ , SortDirection(..)+ , Sorting+ , unsorted+ , sortedBy+ , Pagination(..)+ , module Sound.Freesound.Search.Filter+ , module Sound.Freesound.Search.Numerical+ , module Sound.Freesound.Search.Query+) where++import qualified Data.ByteString.Char8 as BS+import Data.Default (Default(..))+import Network.HTTP.Types.QueryLike (QueryLike(..), QueryValueLike(..))+import Sound.Freesound.Search.Filter+import Sound.Freesound.Search.Numerical+import Sound.Freesound.Search.Query++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)++unsorted :: Sorting+unsorted = Unsorted++sortedBy :: SortMethod -> SortDirection -> Sorting+sortedBy = SortedBy++instance Default Sorting where+ def = unsorted++instance QueryValueLike Sorting where+ toQueryValue Unsorted = Nothing+ toQueryValue (SortedBy Duration Ascending) = Just $ BS.pack "duration_asc"+ toQueryValue (SortedBy Duration Descending) = Just $ BS.pack "duration_desc"+ toQueryValue (SortedBy Created Ascending) = Just $ BS.pack "created_asc"+ toQueryValue (SortedBy Created Descending) = Just $ BS.pack "created_desc"+ toQueryValue (SortedBy Downloads Ascending) = Just $ BS.pack "downloads_asc"+ toQueryValue (SortedBy Downloads Descending) = Just $ BS.pack "downloads_desc"+ toQueryValue (SortedBy Rating Ascending) = Just $ BS.pack "rating_asc"+ toQueryValue (SortedBy Rating Descending) = Just $ BS.pack "rating_desc"++data Pagination = Pagination {+ page :: Int+, resultsPerPage :: Int+} deriving (Eq, Show)++instance Default Pagination where+ def = Pagination 0 15++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+ then Nothing+ else Just (BS.pack "sounds_per_page", show (resultsPerPage a)) ]
+ src/Sound/Freesound/Search/Filter.hs view
@@ -0,0 +1,215 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Search.Filter (+ Filters+, id+, username+, created+, originalFilename+, description+, tag+, license+, isRemix+, wasRemixed+, pack+, isGeotagged+, fileType+, duration+, bitdepth+, bitrate+, samplerate+, filesize+, channels+, md5+, numDownloads+, avgRating+, numRatings+, comment+, comments+) where++import Data.Default (Default(..))+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BS+import Data.Maybe (mapMaybe)+import Data.Text (Text)+import Network.HTTP.Types.QueryLike (QueryValueLike(..))+import Prelude hiding (id)+import Sound.Freesound.Search.Numerical (Numerical)+import Sound.Freesound.Sound.Type (FileType, SoundId)++#if __GLASGOW_HASKELL__ < 710+import Data.Monoid (Monoid(..))+#endif++data License = Attribution | AttributionNoncommercial | CreativeCommons0 deriving (Eq, Show)++instance QueryValueLike License where+ toQueryValue Attribution = toQueryValue $ BS.pack "Attribution"+ toQueryValue AttributionNoncommercial = toQueryValue $ BS.pack "Attribution Noncommercial"+ toQueryValue CreativeCommons0 = toQueryValue $ BS.pack "Creative Commons 0"++-- | Newtype wrapper for avoiding orphan instance.+newtype F_Bool = F_Bool Bool deriving (Eq, Show)++instance QueryValueLike F_Bool where+ toQueryValue (F_Bool True) = toQueryValue $ BS.pack "true"+ toQueryValue (F_Bool False) = toQueryValue $ BS.pack "false"++data Filter =+ F_id (Numerical SoundId) -- integer, sound id on freesound+ | F_username Text -- string, not tokenized+ | F_created Text -- date+ | F_original_filename Text -- string, tokenized+ | F_description Text -- string, tokenized+ | F_tag Text+ | F_license License+ | F_is_remix F_Bool+ | F_was_remixed F_Bool+ | F_pack Text+ --pack_tokenized: string, tokenized+ | F_is_geotagged F_Bool+ | F_type FileType+ | F_duration (Numerical Double) -- duration of sound in seconds+ | F_bitdepth (Numerical Integer) -- WARNING is not to be trusted right now+ | F_bitrate (Numerical Integer) -- WARNING is not to be trusted right now+ | F_samplerate (Numerical Integer)+ | F_filesize (Numerical Integer) -- file size in bytes+ | F_channels (Numerical Integer) -- number of channels in sound, mostly 1 or 2, sometimes more+ | F_md5 Text -- 32-byte md5 hash of file+ | F_num_downloads (Numerical Integer) -- all zero right now (not imported data)+ | F_avg_rating (Numerical Double) -- average rating, from 0 to 5+ | F_num_ratings (Numerical Integer) -- number of ratings+ | F_comment Text -- tokenized+ | F_comments (Numerical Integer) -- number of comments+ deriving (Eq, Show)++mkF :: QueryValueLike a => ByteString -> a -> Maybe ByteString+mkF t = fmap (BS.append (BS.append t ":")) . toQueryValue++instance QueryValueLike Filter where+ toQueryValue filterSpec =+ case filterSpec of+ F_id x -> mkF "id" x+ F_username x -> mkF "username" x+ F_created x -> mkF "created" x+ F_original_filename x -> mkF "original_filename" x+ F_description x -> mkF "description" x+ F_tag x -> mkF "tag" x+ F_license x -> mkF "license" x+ F_is_remix x -> mkF "is_remix" x+ F_was_remixed x -> mkF "was_remixed" x+ F_pack x -> mkF "pack" x+ --F_pack_tokenized: string, tokenized+ F_is_geotagged x -> mkF "is_geotagged" x+ F_type x -> mkF "type" x+ F_duration x -> mkF "duration" x+ F_bitdepth x -> mkF "bitdepth" x+ F_bitrate x -> mkF "bitrate" x+ F_samplerate x -> mkF "samplerate" x+ F_filesize x -> mkF "filesize" x+ F_channels x -> mkF "channels" x+ F_md5 x -> mkF "md5" x+ F_num_downloads x -> mkF "num_downloads" x+ F_avg_rating x -> mkF "avg_rating" x+ F_num_ratings x -> mkF "num_ratings" x+ F_comment x -> mkF "comment" x+ F_comments x -> mkF "comments" x++newtype Filters = Filters [Filter] deriving (Show)++instance Monoid Filters where+ mempty = Filters []+ mappend (Filters a) (Filters b) = Filters (a++b)++instance Default Filters where+ def = mempty++instance QueryValueLike Filters where+ toQueryValue (Filters []) = Nothing+ toQueryValue (Filters fs) = Just $ BS.unwords $ mapMaybe toQueryValue fs++fromFilter :: Filter -> Filters+fromFilter = Filters . (:[])++-- | Sound id on Freesound.+id :: Numerical SoundId -> Filters+id = fromFilter . F_id++username :: Text -> Filters+username = fromFilter . F_username++created :: Text -> Filters+created = fromFilter . F_created++originalFilename :: Text -> Filters+originalFilename = fromFilter . F_original_filename++description :: Text -> Filters+description = fromFilter . F_description++tag :: Text -> Filters+tag = fromFilter . F_tag++license :: License -> Filters+license = fromFilter . F_license++isRemix :: Bool -> Filters+isRemix = fromFilter . F_is_remix . F_Bool++wasRemixed :: Bool -> Filters+wasRemixed = fromFilter . F_was_remixed . F_Bool++pack :: Text -> Filters+pack = fromFilter . F_pack++isGeotagged :: Bool -> Filters+isGeotagged = fromFilter . F_is_geotagged . F_Bool++fileType :: FileType -> Filters+fileType = fromFilter . F_type++-- | Duration of sound in seconds.+duration :: Numerical Double -> Filters+duration = fromFilter . F_duration++-- | WARNING is not to be trusted right now.+bitdepth :: Numerical Integer -> Filters+bitdepth = fromFilter . F_bitdepth++-- | WARNING is not to be trusted right now.+bitrate :: Numerical Integer -> Filters+bitrate = fromFilter . F_bitrate++samplerate :: Numerical Integer -> Filters+samplerate = fromFilter . F_samplerate++-- | File size in bytes.+filesize :: Numerical Integer -> Filters+filesize = fromFilter . F_filesize++-- | Number of channels in sound, mostly 1 or 2, sometimes more.+channels :: Numerical Integer -> Filters+channels = fromFilter . F_channels++-- | 32-byte md5 hash of file.+md5 :: Text -> Filters+md5 = fromFilter . F_md5++-- | All zero right now (not imported data).+numDownloads :: Numerical Integer -> Filters+numDownloads = fromFilter . F_num_downloads++-- | Average rating, from 0 to 5.+avgRating :: Numerical Double -> Filters+avgRating = fromFilter . F_avg_rating++-- | Number of ratings.+numRatings :: Numerical Integer -> Filters+numRatings = fromFilter . F_num_ratings++comment :: Text -> Filters+comment = fromFilter . F_comment++-- | Number of comments.+comments :: Numerical Integer -> Filters+comments = fromFilter . F_comments
+ src/Sound/Freesound/Search/Numerical.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE CPP #-}+module Sound.Freesound.Search.Numerical (+ Numerical+, equals+, between+, lessThan+, greaterThan+) where++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BS+import qualified Data.Time.Clock as Time+import qualified Data.Time.Format as Time+import Network.HTTP.Types.QueryLike (QueryValueLike(..))+import Sound.Freesound.Sound.Type (SoundId, soundIdToInteger)++#if __GLASGOW_HASKELL__ < 710+import System.Locale (defaultTimeLocale)+#else+import Data.Time (defaultTimeLocale)+#endif++-- | Numerical constraint.+data Numerical a = Equals a | Between a a | GreaterThan a | LessThan a deriving (Eq, Show)++class NumericalQueryValue a where+ toNumericalQueryValue :: a -> ByteString++instance NumericalQueryValue Double where+ toNumericalQueryValue = BS.pack . show++instance NumericalQueryValue Integer where+ toNumericalQueryValue = BS.pack . show++instance NumericalQueryValue SoundId where+ toNumericalQueryValue = BS.pack . show . soundIdToInteger++instance NumericalQueryValue Time.UTCTime where+ toNumericalQueryValue = BS.pack . Time.formatTime defaultTimeLocale "%FT%H:%M:%S%QZ"++equals :: a -> Numerical a+equals = Equals++between :: Ord a => a -> a -> Numerical a+between a b+ | a <= b = Between a b+ | otherwise = Between b a++greaterThan :: a -> Numerical a+greaterThan = GreaterThan++lessThan :: a -> Numerical a+lessThan = LessThan++wrap :: ByteString -> ByteString -> ByteString+wrap a b = BS.unwords [ BS.pack "[", a, BS.pack "TO", b, BS.pack "]" ]++wildcard :: ByteString+wildcard = BS.pack "*"++instance (NumericalQueryValue a) => QueryValueLike (Numerical a) where+ toQueryValue (Equals a) = toQueryValue $ toNumericalQueryValue a+ toQueryValue (Between a b) = toQueryValue $ wrap (toNumericalQueryValue a) (toNumericalQueryValue b)+ toQueryValue (GreaterThan a) = toQueryValue $ wrap (toNumericalQueryValue a) wildcard+ toQueryValue (LessThan a) = toQueryValue $ wrap wildcard (toNumericalQueryValue a)
+ src/Sound/Freesound/Search/Query.hs view
@@ -0,0 +1,36 @@+module Sound.Freesound.Search.Query (+ Query+ , include+ , exclude+ , (&)+) where++import Data.Char (isSpace)+import Data.Text (Text)+import qualified Data.Text as Text+import Network.HTTP.Types.QueryLike (QueryValueLike(..))++newtype Query = Query [Term] deriving (Eq, Show)++instance QueryValueLike Query where+ toQueryValue (Query ts) = toQueryValue $ Text.unwords (map f ts)+ where+ quote t+ | Text.any isSpace t = Text.cons '"' (Text.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)++include :: Text -> Query+include string = Query [Include string]++exclude :: Text -> Query+exclude string = Query [Exclude string]++append :: Query -> Query -> Query+append (Query xs) (Query ys) = Query (xs ++ ys)++(&) :: Query -> Query -> Query+(&) = append
+ src/Sound/Freesound/Sound.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Sound (+ SoundId+ , FileType(..)+ , Sound(..)+ , Summary+ , Detail+ , Sounds+ , url+ , description+ , geotag+ , created+ , sound_license+ , fileType+ , channels+ , filesize+ , bitrate+ , bitdepth+ , duration+ , samplerate+ , sound_username+ , pack+ , download+ , bookmark+ , previews+ , images+ , numDownloads+ , avgRating+ , numRatings+ , rate+ , comments+ , numComments+ , comment+ , similarSounds+ -- , analysis+ , analysisStats+ , analysisFrames++ , search+ , search_+ , getSimilar+ , getSimilar_+) 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++getSimilar_ :: Detail -> Freesound Sounds+getSimilar_ = getSimilar def++-- getAnalysisStats+-- getAnalysisFrames
+ src/Sound/Freesound/Sound/Type.hs view
@@ -0,0 +1,227 @@+{-# LANGUAGE CPP, OverloadedStrings #-}+module Sound.Freesound.Sound.Type where++import Control.Monad (mzero)+import Data.Aeson (FromJSON(..), Value(..), (.:), (.:?))+import qualified Data.ByteString.Char8 as BS+import Data.Text (Text)+import Network.HTTP.Types.QueryLike (QueryValueLike(..))+import Prelude hiding (id)+import Sound.Freesound.API (Resource, URI)+import Sound.Freesound.List (List, Elem(..))+-- import qualified Sound.Freesound.User.Type as User++#if __GLASGOW_HASKELL__ < 710+import Control.Applicative+#endif++newtype SoundId = SoundId 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++--fromInt :: Int64 -> SoundId+--fromInt = SoundId++data FileType = WAV | AIFF | OGG | MP3 | FLAC+ deriving (Eq, Show)++instance QueryValueLike FileType where+ toQueryValue WAV = toQueryValue $ BS.pack "wav"+ toQueryValue AIFF = toQueryValue $ BS.pack "aif"+ toQueryValue OGG = toQueryValue $ BS.pack "ogg"+ toQueryValue MP3 = toQueryValue $ BS.pack "mp3"+ toQueryValue FLAC = toQueryValue $ BS.pack "flac"++instance FromJSON FileType where+ parseJSON (String v) =+ case v of+ "wav" -> return WAV+ "aiff" -> return AIFF+ "aif" -> return AIFF+ "ogg" -> return OGG+ "mp3" -> return MP3+ "flac" -> return FLAC+ _ -> mzero+ parseJSON _ = mzero++class Sound a where+ -- | The sound’s unique identifier.+ id :: a -> SoundId+ -- | The name user gave to the sound.+ name :: a -> Text+ -- | An array of tags the user gave to the sound.+ tags :: a -> [Text]+ -- | The username of the uploader of the sound.+ username :: a -> Text+ -- | The license under which the sound is available to you.+ license :: a -> License++data License =+ Attribution+ | AttributionNoncommercial+ | CreativeCommons0+ | License Text+ deriving (Eq, Show)++instance FromJSON License where+ parseJSON (String x) =+ case x of+ "Attribution" -> pure Attribution+ "Attribution Noncommercial" -> pure AttributionNoncommercial+ "Creative Commons 0" -> pure CreativeCommons0+ u -> pure $ License u+ parseJSON _ = fail "Invalid license"++data Previews = Previews {+ preview_hq_mp3 :: URI -- ^ The URI for retrieving a high quality (~128kbps) mp3 preview of the sound.+ , preview_lq_mp3 :: URI -- ^ The URI for retrieving a low quality (~64kbps) mp3 preview of the sound.+ , preview_hq_ogg :: URI -- ^ The URI for retrieving a high quality (~192kbps) ogg preview of the sound.+ , preview_lq_ogg :: URI -- ^ The URI for retrieving a low quality (~80kbps) ogg of the sound.+ } deriving (Eq, Show)++instance FromJSON Previews where+ parseJSON (Object v) =+ Previews+ <$> v .: "preview-hq-mp3"+ <*> v .: "preview-lq-mp3"+ <*> v .: "preview-hq-ogg"+ <*> v .: "preview-lq-ogg"+ parseJSON _ = fail "Couldn't parse Previews"++data Images = Images {+ waveform_m :: URI -- ^ A visualization of the sounds waveform, png file (medium).+ , waveform_l :: URI -- ^ A visualization of the sounds waveform, png file (large).+ , spectral_m :: URI -- ^ A visualization of the sounds spectrum over time, jpeg file (medium).+ , spectral_l :: URI -- ^ A visualization of the sounds spectrum over time, jpeg file (large).+ } deriving (Eq, Show)++instance FromJSON Images where+ parseJSON (Object v) =+ Images+ <$> v .: "waveform_m"+ <*> v .: "waveform_l"+ <*> v .: "spectral_m"+ <*> v .: "spectral_l"+ 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).+ -- , 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.+ } deriving (Eq, Show)++instance FromJSON Detail where+ parseJSON (Object v) =+ Detail+ <$> v .: "id"+ <*> v .: "url"+ <*> v .: "name"+ <*> v .: "tags"+ <*> v .: "description"+ <*> v .:? "geotag"+ <*> v .: "created"+ <*> v .: "license"+ <*> v .: "type"+ <*> v .: "channels"+ <*> v .: "filesize"+ <*> v .: "bitrate"+ <*> v .: "bitdepth"+ <*> v .: "duration"+ <*> v .: "samplerate"+ <*> v .: "username"+ <*> v .:? "pack"+ <*> v .: "download"+ <*> v .: "bookmark"+ <*> v .: "previews"+ <*> v .: "images"+ <*> v .: "num_downloads"+ <*> v .: "avg_rating"+ <*> v .: "num_ratings"+ <*> v .: "rate"+ <*> v .: "comments"+ <*> v .: "num_comments"+ <*> v .: "comment"+ <*> v .: "similar_sounds"+ <*> v .: "analysis_stats"+ <*> v .: "analysis_frames"+ parseJSON _ = fail "Couldn't parse Sound"++instance Sound Detail where+ id = sound_id+ name = sound_name+ tags = sound_tags+ username = sound_username+ license = sound_license++data Summary = Summary {+ summary_id :: SoundId+ , summary_name :: Text+ , summary_tags :: [Text]+ , summary_username :: Text+ , summary_license :: License+ } deriving (Eq, Show)++instance FromJSON Summary where+ parseJSON (Object v) =+ Summary+ <$> v .: "id"+ <*> v .: "name"+ <*> v .: "tags"+ <*> v .: "username"+ <*> v .: "license"+ parseJSON _ = fail "Couldn't parse Sound"++instance Sound Summary where+ id = summary_id+ name = summary_name+ tags = summary_tags+ username = summary_username+ license = summary_license++instance Elem Summary where+ elemsFieldName _ = "sounds"++-- | Coordinate+data GeoTag = GeoTag {+ latitude :: Double+, longitude :: Double+} deriving (Eq, Show)++instance FromJSON GeoTag where+ parseJSON (String _) = pure (GeoTag 0 0)+ parseJSON _ = fail "Couldn't parse GeoTag"
+ src/Sound/Freesound/User.hs view
@@ -0,0 +1,58 @@+module Sound.Freesound.User (+ User(..)+, Summary+, Detail+, sounds+, packs+, firstName+, lastName+, about+, homePage+, signature+, dateJoined+, bookmarkCategories+, getUser+, getUserByName+, getBookmarkCategories+, getSounds+, getSounds_+, getPacks+) where++import Control.Monad (liftM)+import Data.Default (def)+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++-- | Get information about a user by name.+getUserByName :: Text -> Freesound Detail+getUserByName t = getResource $ resourceURI [ T.pack "people", t ] []++-- | Retrieve a list of a user's bookmark categories.+getBookmarkCategories :: Detail -> Freesound [Bookmark.Category]+getBookmarkCategories = liftM Bookmark.categories . getResource . bookmarkCategories++-- | 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++-- | Retrieve a user's sounds.++getSounds_ :: Detail -> Freesound Sounds+getSounds_ = getSounds def++-- | Retrieve a user's packs.+getPacks :: Detail -> Freesound Packs+getPacks = getResource . packs
+ src/Sound/Freesound/User/Type.hs view
@@ -0,0 +1,85 @@+{-# 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/Test.hs view
@@ -0,0 +1,9 @@+module Sound.Freesound.Test (+ fs+) where++import Sound.Freesound+import System.Environment (getEnv)++fs :: Freesound a -> IO a+fs a = flip runFreesound a =<< apiKeyFromString `fmap` getEnv "FREESOUND_API_KEY"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}