Commits

Sebastián Magrí committed d502398

Add documentation to the modules. In Sets, the JSON downloadable item can be null. Drop some general functions. Fix scResourceType return value on otherwise

Comments (0)

Files changed (10)

src/Network/SoundCloud.hs

-{-
-  Implementing the SoundCloud API
+{- |
+   Module:      Network.SoundCloud
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   The 'Network.SoundCloud' module provides functions and types to access
+   the soundcloud.com public API.
 -}
+
 module Network.SoundCloud (
   scGet,
   scFetch,
   scResolve,
   scResourceType,
-  scShowInfo
+  scResourceShowInfo,
+  scShowInfo,
   ) where
 
 import Network.SoundCloud.Util (scGet, scFetch, scResolve, scResourceType)
 
-import qualified Network.SoundCloud.App as App
-import qualified Network.SoundCloud.Group as Group
-import qualified Network.SoundCloud.Set as Set
-import qualified Network.SoundCloud.Track as Track
-import qualified Network.SoundCloud.User as User
+import qualified Network.SoundCloud.App         as App
+import qualified Network.SoundCloud.Group       as Group
+import qualified Network.SoundCloud.Set         as Set
+import qualified Network.SoundCloud.Track       as Track
+import qualified Network.SoundCloud.User        as User
 
-
+-- | Show information about the resource pointed by the given API URL
 scResourceShowInfo :: String -> IO ()
 scResourceShowInfo url | scResourceType url == "track"   = Track.showInfo url
                        | scResourceType url == "user"    = User.showInfo url
                        | scResourceType url == "app"     = App.showInfo url
 scResourceShowInfo _                                     = putStrLn "Unrecognized resource"
 
+-- | Show information about a resource given its public URL
 scShowInfo :: String -> IO ()
 scShowInfo url =
     do rUrl <- scResolve url
-       scResourceShowInfo rUrl
+       scResourceShowInfo rUrl

src/Network/SoundCloud/App.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.App
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Represents SoundCloud applications as found at http://soundcloud.com/apps
+-}
+
 module Network.SoundCloud.App where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 
 import Network.SoundCloud.Util (scGet, scResolve)
 
+-- | JSON representation of applications as described
+-- in http://developers.soundcloud.com/docs/api/apps
 data JSON = JSON { id                     :: Int
                  , permalink_url          :: String
                  , external_url           :: String
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a valid JSON string into an application
+-- JSON record
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
 
+
+-- | Get an application JSON record given a public app URL
+-- as in http://soundcloud.com/apps/app_name
 getJSON :: String -> IO (Maybe JSON)
 getJSON url =
     do tUrl <- scResolve url
          Nothing -> return Nothing
          Just d  -> return $ decodeJSON d
 
+-- | Show general information about an application in the
+-- standard output
 showInfo :: String -> IO ()
 showInfo url =
     do obj <- getJSON url

src/Network/SoundCloud/Comment.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.Comment
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Represents SoundCloud comments
+-}
+
 module Network.SoundCloud.Comment where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 
 import qualified Network.SoundCloud.MiniUser as User
 
+-- | Represents comment's JSON as a record
 data JSON = JSON { id                     :: Int
                  , created_at             :: String
                  , body                   :: String
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a comment's valid JSON string into
+-- a comment's JSON record
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
+
+-- | Show a summary about a comment
+showComment :: JSON -> String
+showComment c = concat ["\nAt ", created_at c, ", ", User.username $ user c, " said:\n", body c, "\n"]

src/Network/SoundCloud/Const.hs

+{- |
+   Module:      Network.SoundCloud.Const
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Constants and URLs of the SoundCloud API resources
+-}
+
 module Network.SoundCloud.Const where
 
+-- | HScD SoundCloud API client ID
 clientId :: String
 clientId = "934a79db328a60a0ea459ab9e45c1735"
 
+-- | Base URL of the SoundCloud API
 apiURL :: String
 apiURL = "http://api.soundcloud.com"
 
+-- | Base URL of the SoundCloud API (Secure)
 apiURLS :: String
 apiURLS = "https://api.soundcloud.com"
 
+-- | Base URL for the authenticated user resources
 apiMeURLS :: String
 apiMeURLS = apiURLS ++ "/me"
 
+-- | URL for the authentication interface
 authURLS :: String
 authURLS = "https://soundcloud.com/connect"
 
+-- | URL of the OAuth2 token handler
 tokenURLS :: String
 tokenURLS = apiURLS ++ "/oauth2/token"
 
+-- | Base URL for Tracks
 tracksURL :: String
 tracksURL = apiURL ++ "/tracks"
 
+-- | Base URL for Users
 usersURL :: String
 usersURL = apiURL ++ "/users"
 
+-- | Base URL for Sets/Playlists
 playlistsURL :: String
 playlistsURL = apiURL ++ "/playlists"
 
+-- | Base URL for Groups
 groupsURL :: String
 groupsURL = apiURL ++ "/groups"
 
+-- | Base URL for Comments
 commentsURL :: String
 commentsURL = apiURL ++ "/comments"
 
+-- | URL of the authenticated user connections
 meConnectionsURLS :: String
 meConnectionsURLS = apiMeURLS ++ "/connections"
 
+-- | URL of the authenticated user activities, AKA dashboard
 meActivitiesURLS :: String
 meActivitiesURLS = apiMeURLS ++ "/activities"
 
+-- | Base URL for Applications
 appsURLS :: String
 appsURLS = apiURL ++ "/apps"
 
+-- | URL to the resource resolver
 resolveURL :: String
 resolveURL = apiURL ++ "/resolve"

src/Network/SoundCloud/Group.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.Group
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Represents Groups as in http://soundcloud.com/groups
+-}
+
 module Network.SoundCloud.Group where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 import qualified Network.SoundCloud.MiniUser as User
 import Network.SoundCloud.Util (scGet, scResolve)
 
+-- | Represents group's JSON as a record
 data JSON = JSON { id                     :: Int
                  , created_at             :: String
                  , permalink_url          :: String
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a group's valid JSON string into a record
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
 
+-- | Get a group record given a public URL
+-- as in http://soundcloud.com/groups/group_name
 getJSON :: String -> IO (Maybe JSON)
 getJSON url =
     do tUrl <- scResolve url
          Nothing -> return Nothing
          Just d  -> return $ decodeJSON d
 
+-- | Show general information about a group in the
+-- standard output
 showInfo :: String -> IO ()
 showInfo url =
     do obj <- getJSON url

src/Network/SoundCloud/MiniUser.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.MiniUser
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Minimal representation of an user used when embedding users
+   information on other resources information
+-}
+
 module Network.SoundCloud.MiniUser where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 import qualified Data.ByteString.Lazy.Char8 as BSL
 import GHC.Generics (Generic)
 
+-- | Represents mini user JSON as a record
 data JSON = JSON { id               :: Int
                  , username         :: String
                  , uri              :: String
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a JSON record from a valid miniuser
+-- JSON string
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON

src/Network/SoundCloud/Set.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.Set
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Representations for track sets
+-}
+
 module Network.SoundCloud.Set where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 import qualified Network.SoundCloud.Track as Track
 import Network.SoundCloud.Util (scGet, scResolve)
 
-
+-- | Represents Set JSON as a record
 data JSON = JSON { id                     :: Int
                  , created_at             :: String
                  , user                   :: User.JSON
                  , release_month          :: Maybe Int
                  , release_year           :: Maybe Int
                  , streamable             :: Bool
-                 , downloadable           :: Bool
+                 , downloadable           :: Maybe Bool
                  , playlist_type          :: String
                  , tracks                 :: [Track.JSON]
                  } deriving (Show, Generic)
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a JSON record from a valid set
+-- JSON string
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
 
+-- | Get a set JSON record given it's public URL
+-- as in http://soundcloud.com/artist/set_title
 getJSON :: String -> IO (Maybe JSON)
 getJSON url =
     do tUrl <- scResolve url
          Nothing -> return Nothing
          Just d  -> return $ decodeJSON d
 
+-- | Get a string with a summary description of a track
 showTrack :: Track.JSON -> String
 showTrack t = concat ["\t", Track.title t, " by ", User.username $ Track.user t, " (", Track.permalink_url t,")\n"]
 
-showTracks :: [Track.JSON] -> String
-showTracks = concatMap showTrack
-
+-- | Show general information about a set in the
+-- standard output
 showInfo :: String -> IO ()
 showInfo url =
     do obj <- getJSON url
                   (genre o)
                   (description o)
                   (created_at o)
-                  (showTracks $ tracks o)
+                  (concatMap showTrack $ tracks o)

src/Network/SoundCloud/Track.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.Track
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Implements tracks and related types and functions
+-}
+
 module Network.SoundCloud.Track where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 import qualified Network.SoundCloud.MiniUser as User
 import qualified Network.SoundCloud.Comment as Comment
 
+-- | Represent a track's JSON as a record
 data JSON = JSON { id                     :: Int
                  , created_at             :: String
                  , user                   :: User.JSON
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Simple record to parse download_url from a track's JSON
 data DownloadJSON = DownloadJSON { download_url     :: String
                                  } deriving (Show, Generic)
 
 instance FromJSON DownloadJSON
 instance ToJSON   DownloadJSON
 
+-- | Decode a JSON record from a track valid JSON string
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
 
+-- | Get a JSON record given a track URL
+-- as in http://soundcloud.com/artist/track_title
 getJSON :: String -> IO (Maybe JSON)
 getJSON url =
     do tUrl <- scResolve url
          Nothing -> return Nothing
          Just d  -> return $ decodeJSON d
 
+-- | Decode a DownloadJSON record out of a track's JSON
 decodeDownloadJSON :: String -> Maybe DownloadJSON
 decodeDownloadJSON dat = decode (BSL.pack dat) :: Maybe DownloadJSON
 
+-- | Decode a comment JSON list given a track id
 decodeComments :: String -> Maybe [Comment.JSON]
 decodeComments dat = decode (BSL.pack dat) :: Maybe [Comment.JSON]
 
+-- | Given the track id, get its comments as a list of Comment.JSON
 getComments :: Int -> IO (Maybe [Comment.JSON])
 getComments trackId =
     do let url = tracksURL ++ "/" ++ show trackId ++ "/comments.json?client_id=" ++ clientId
          Nothing -> return Nothing
          Just d  -> return $ decodeComments d
 
-showComment :: Comment.JSON -> String
-showComment c = concat ["\nAt ", Comment.created_at c, ", ", User.username $ Comment.user c, " said:\n", Comment.body c, "\n"]
-
-showComments :: [Comment.JSON] -> String
-showComments [] = "No comments"
-showComments xs = concatMap showComment xs
-
+-- | Fetch a downloadable track
 fetch :: String -> String -> IO ()
 fetch trackUrl output =
     do tUrl <- scResolve trackUrl
                              scFetch dUrlStr filename
                       else putStrLn "Track is not downloadable"
 
-trackComments :: Int -> IO [Comment.JSON]
-trackComments trackId =
-    do obj <- getComments trackId
-       case obj of
-         Nothing -> return []
-         Just o  -> return o
-
+-- | Show general information about the track at the given URL
+-- in the standard output
 showInfo :: String -> IO ()
 showInfo trackUrl =
     do obj <- getJSON trackUrl
          Nothing        -> putStrLn "Unable to get track information."
          Just o         ->
              do let tmp = "%s\n%s - %s\n\t%s\nPlays: %d\nComments: %d\nDownloads: %d\nTags:%s\n\nComments:\n%s\n"
-                comments <- trackComments $ id o
+                comments <- getComments $ id o
+                let commentsList = fromJust comments
                 printf
                   tmp
                   (permalink_url o)
                   (comment_count o)
                   (download_count o)
                   (tag_list o)
-                  (showComments comments)
+                  (if not $ null commentsList then concatMap Comment.showComment commentsList else "No comments")

src/Network/SoundCloud/User.hs

 {-# LANGUAGE DeriveGeneric #-}
 
+{- |
+   Module:      Network.SoundCloud.User
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   Implements tracks and related types and functions
+-}
+
 module Network.SoundCloud.User where
 
 import Data.Aeson (FromJSON, ToJSON, decode)
 
 import Network.SoundCloud.Util (scGet, scResolve)
 
-
+-- | Record representation of a user's JSON
 data JSON = JSON { id                     :: Int
                  , uri                    :: String
                  , permalink_url          :: String
 instance FromJSON JSON
 instance ToJSON   JSON
 
+-- | Decode a JSON record out of a user's valid JSON string
 decodeJSON :: String -> Maybe JSON
 decodeJSON dat = decode (BSL.pack dat) :: Maybe JSON
 
+-- | Get a JSON record given an user URL
+-- as in http://soundcloud.com/artist
 getJSON :: String -> IO (Maybe JSON)
 getJSON url =
     do tUrl <- scResolve url
          Nothing -> return Nothing
          Just d  -> return $ decodeJSON d
 
+-- | Show a summary of an user information in the standard output
+-- given the user's URL
 showInfo :: String -> IO ()
 showInfo url =
     do obj <- getJSON url

src/Network/SoundCloud/Util.hs

+{- |
+   Module:      Network.SoundCloud.Util
+   Copyright:   (c) 2012 Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   License:     BSD3
+   Maintainer:  Sebastián Ramírez Magrí <sebasmagri@gmail.com>
+   Stability:   experimental
+
+   General functions used by other modules
+-}
+
 module Network.SoundCloud.Util where
 
 import Data.List
 
 import Network.SoundCloud.Const
 
+-- | Issue a GET request to an URL and returns
+-- the response body as a String or Nothing on failure.
+-- If 'followRedirections' is set to True, new requests
+-- will be made on 3XX response codes to the Location of
+-- the response.
 scGet :: String -> Bool -> IO (Maybe String)
 scGet url followRedirections =
     do res <- simpleHTTP $ getRequest url
                          else return $ Just uri
                _ -> return Nothing
 
+-- | Issue a GET request to 'dUrl' and save the response body
+-- to a file in the path indicated by the 'out' parameter
 scFetch :: String -> String -> IO ()
 scFetch dUrl out =
     do contents <- scGet dUrl True
                 hPutStr file c
                 hClose file
 
+-- | Given an arbitrary resource URL, returns the type of the
+-- resource.
+-- The response can be one of:
+--   "track"
+--   "user"
+--   "set"
+--   "group"
+--   "comment"
+--   "app"
+--   "nothing"
 scResourceType :: String -> String
 scResourceType url | tracksURL    `isPrefixOf` url      = "track"
                    | usersURL     `isPrefixOf` url      = "user"
                    | groupsURL    `isPrefixOf` url      = "group"
                    | commentsURL  `isPrefixOf` url      = "comment"
                    | appsURLS     `isPrefixOf` url      = "app"
-                   | otherwise                          = "app"
+                   | otherwise                          = "nothing"
 
 {-
 This function's request will always return a (3,_,_) status,
 so we can just return the redirection Location
 -}
+-- | Get the API url of a resource given its public URL.
+-- In example, for a public URL like:
+--     http://soundcloud.com/user/track
+-- It returns the API URL:
+--     http://api.soundcloud.com/tracks/<track_id>.json?client_id=<foo>
 scResolve :: String -> IO String
 scResolve url =
     do dat <- scGet resolveUrl False