{- | IsoHunt API; see . Sample use: > resp <- search (simpleQuery "ubuntu") The following terms and conditions apply to the IsoHunt API, as stated in the above link: In using our search API, you are free to do with it as you wish on condition that if your app is available publicly to users, you must link to torrent details pages on isoHunt.com, whether you link to the .torrent files or not. We reserve the right to ban you from using our API if you don't follow this simple rule. Refer to Louish's iPhone app for a good example of including links to our torrent details pages. Our torrent details pages have URLs like this: While we don't require developer tokens or place hard limits on api calls usage, excessive calls will also result in bans. If you think your app will consistently sustain multiple calls per second to our api, email admin at this site's domain first. You are free to promote your app using our API, by replying under this post () or post under this forum (). If your app is really good, we'll likely want to spotlight it on isoHunt's frontpage. Multiple posts to promote your app on our forum or comments is not allowed however, and will be treated as spam. -} module IsoHunt.Search( -- * The main function search, -- * Query Query(..), simpleQuery, Sort(..), Order(..), -- * Response -- $qualifiedimports Response, Item, Image, -- * Exceptions MalformedJSON(..), MalformedResponse(..), ) where import Data.ByteString.Lazy(ByteString) import Control.Monad import Control.Applicative import Data.Text(Text) import qualified Data.Text import qualified Data.Vector as V import GHC.Generics import Data.Aeson import Data.Aeson.TH import Data.Typeable import Data.Default import Text.URI import Control.Exception import Network.HTTP.Conduit(simpleHttp) import IsoHunt.Response import IsoHunt.Item import IsoHunt.Image ----------------------------- Queries -------------------------- data Sort = Composite -- ^ Overall factors such as age, query relevance -- seed/leechers counts and vots | Seeds -- ^ Seeds + leechers | Age | Size deriving(Eq, Ord, Show, Typeable) data Order = Descending | Ascending deriving(Eq, Ord, Show, Typeable) -- | See also 'simpleQuery' and 'def' for constructing queries data Query = Query { searchTerm :: !String, start :: !Int, -- ^ start+rows <= 1000 rows :: !Int, -- ^ <= 100 sort :: !Sort, order :: !Order } deriving(Eq, Ord, Show, Typeable) instance Default Query where def = Query "" 1 100 Composite Descending -- | A default query for the given search term simpleQuery :: String -> Query simpleQuery s = def{searchTerm = s} {- $qualifiedimports The 'Response', 'Item', 'Image' data types all share some field names, such as 'title'. To resolve ambiguity in these fields, use qualified imports as follows: > import qualified IsoHunt.Response as Response > import qualified IsoHunt.Item as Item > import qualified IsoHunt.Image as Image > > ... Response.title r ... Item.title i ... Image.title im ... The fields in these datatypes are mostly undocumented; see for an example response. -} -- | The response was invalid JSON. The unparsed contents are included. data MalformedJSON = MalformedJSON !ByteString deriving(Show, Typeable) instance Exception MalformedJSON -- | The response was valid JSON, but not of the expected -- format. Error message and the JSON value are included. data MalformedResponse = MalformedResponse !String !Value deriving(Show, Typeable) instance Exception MalformedResponse {- | Search IsoHunt with the given query. Throws 'MalformedJSON' or 'MalformedResponse' if the result is of an expected format. -} search :: Query -> IO Response search q = do resp <- simpleHttp (searchUrl q) case decode resp of Nothing -> throw (MalformedJSON resp) Just json -> case fromJSON json of Success v -> return v Error msg -> throw (MalformedResponse msg json) searchUrl :: Query -> String searchUrl q = "http://isohunt.com/js/json.php?" ++ pairsToQuery (concat [ [("ihq", searchTerm q), ("start", show $ start q), ("rows", show $ rows q), ("order", showOrder $ order q) ], if sort q == Composite then [] else [("sort", showSort $ sort q)] ]) where showOrder Ascending = "asc" showOrder Descending = "desc" showSort Seeds = "seeds" showSort Age = "age" showSort Size = "size"