|
| 1 | +{-# LANGUAGE DeriveGeneric #-} |
| 2 | +{-# LANGUAGE OverloadedStrings #-} |
| 3 | + |
| 4 | +module Main where |
| 5 | + |
| 6 | +-- Standard library imports |
| 7 | +import System.Environment (getArgs, lookupEnv) |
| 8 | + |
| 9 | +-- Third-party library imports |
| 10 | +import Control.Lens ((^?)) |
| 11 | +import Data.Aeson (FromJSON (parseJSON), ToJSON, |
| 12 | + Value (Object), decodeStrict, encode, |
| 13 | + (.:)) |
| 14 | +import Data.Aeson.Lens (AsNumber (_Integer), key, nth) |
| 15 | +import Data.ByteString (ByteString) |
| 16 | +import qualified Data.ByteString.Char8 as BS |
| 17 | +import GHC.Generics (Generic) |
| 18 | +import Network.HTTP.Simple (Query, getResponseBody, httpBS, |
| 19 | + parseRequest_, setRequestHeader, |
| 20 | + setRequestQueryString) |
| 21 | + |
| 22 | +-- Data type definitions |
| 23 | +data MainRelease = MainRelease { |
| 24 | + released :: String, |
| 25 | + imageUrl :: String, |
| 26 | + labels :: [String], |
| 27 | + uri :: String |
| 28 | +} deriving (Show, Eq, Generic) |
| 29 | + |
| 30 | +instance ToJSON MainRelease |
| 31 | + |
| 32 | +instance FromJSON MainRelease where |
| 33 | + parseJSON (Object v) = do |
| 34 | + uri <- v .: "uri" |
| 35 | + released <- v .: "released" |
| 36 | + images <- v .: "images" |
| 37 | + imageUrl <- case images of |
| 38 | + (img:_) -> img .: "resource_url" |
| 39 | + [] -> fail "No images found" |
| 40 | + labels <- v .: "labels" >>= traverse (.: "name") |
| 41 | + return MainRelease { |
| 42 | + uri = uri, |
| 43 | + released = released, |
| 44 | + imageUrl = imageUrl, |
| 45 | + labels = labels |
| 46 | + } |
| 47 | + |
| 48 | +-- Helper functions |
| 49 | +runDiscogsQuery :: Query -> String -> IO ByteString |
| 50 | +runDiscogsQuery query url = do |
| 51 | + maybeKey <- lookupEnv "DISCOG_KEY" |
| 52 | + maybeSecret <- lookupEnv "DISCOG_SECRET" |
| 53 | + (key, secret) <- case (maybeKey, maybeSecret) of |
| 54 | + (Just k, Just s) -> return (k, s) |
| 55 | + _ -> error "Environment variables DISCOG_KEY and/or DISCOG_SECRET are not set" |
| 56 | + let request = |
| 57 | + setRequestQueryString query $ |
| 58 | + setRequestHeader "Authorization" [BS.pack $ "Discogs key=" ++ key ++ ", secret=" ++ secret] $ |
| 59 | + setRequestHeader "User-Agent" ["pull-album-info/1.0 (usefulalgorithm@gmail.com)"] $ |
| 60 | + parseRequest_ url |
| 61 | + getResponseBody <$> httpBS request |
| 62 | + |
| 63 | +getMasterReleaseId :: String -> String -> IO String |
| 64 | +getMasterReleaseId artistName albumName = do |
| 65 | + let url = "https://api.discogs.com/database/search" |
| 66 | + query = |
| 67 | + [ ("artist", Just $ BS.pack artistName), |
| 68 | + ("release_title", Just $ BS.pack albumName), |
| 69 | + ("type", Just "master") |
| 70 | + ] |
| 71 | + body <- BS.unpack <$> runDiscogsQuery query url |
| 72 | + case body ^? key "results" . nth 0 . key "master_id" . _Integer of |
| 73 | + Just masterId -> return $ show masterId |
| 74 | + Nothing -> fail "Failed to extract master_id from the response" |
| 75 | + |
| 76 | +getMainReleaseId :: String -> IO String |
| 77 | +getMainReleaseId masterId = do |
| 78 | + let url = "https://api.discogs.com/masters/" ++ masterId |
| 79 | + body <- BS.unpack <$> runDiscogsQuery [] url |
| 80 | + case body ^? key "main_release" . _Integer of |
| 81 | + Just mainId -> return $ show mainId |
| 82 | + Nothing -> fail "Failed to extract main_release from the response" |
| 83 | + |
| 84 | +getMainRelease :: String -> IO MainRelease |
| 85 | +getMainRelease releaseId = do |
| 86 | + let url = "https://api.discogs.com/releases/" ++ releaseId |
| 87 | + body <- runDiscogsQuery [] url |
| 88 | + case (decodeStrict body :: Maybe MainRelease) of |
| 89 | + Just release -> return release |
| 90 | + Nothing -> fail "Cannot decode main release" |
| 91 | + |
| 92 | +-- Main function |
| 93 | +main :: IO () |
| 94 | +main = do |
| 95 | + args <- getArgs |
| 96 | + case args of |
| 97 | + [artistName, albumName] -> do |
| 98 | + release <- getMasterReleaseId artistName albumName |
| 99 | + >>= getMainReleaseId |
| 100 | + >>= getMainRelease |
| 101 | + putStrLn $ BS.unpack $ BS.toStrict $ encode release |
| 102 | + _ -> putStrLn "Usage: pull_album_info <artist_name> <album_name>" |
0 commit comments