Skip to content

Commit 93e9025

Browse files
feat: pull album info script
Signed-off-by: Tsung-Ju Lii <usefulalgorithm@gmail.com>
1 parent c086ea4 commit 93e9025

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist-newstyle
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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>"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cradle:
2+
cabal:
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
cabal-version: >= 2.0
2+
-- The cabal-version field refers to the version of the .cabal specification,
3+
-- and can be different from the cabal-install (the tool) version and the
4+
-- Cabal (the library) version you are using. As such, the Cabal (the library)
5+
-- version used must be equal or greater than the version stated in this field.
6+
-- Starting from the specification version 2.2, the cabal-version field must be
7+
-- the first thing in the cabal file.
8+
9+
-- Initial package description 'pull-album-info' generated by
10+
-- 'cabal init'. For further documentation, see:
11+
-- http://haskell.org/cabal/users-guide/
12+
--
13+
-- The name of the package.
14+
name: pull-album-info
15+
16+
-- The package version.
17+
-- See the Haskell package versioning policy (PVP) for standards
18+
-- guiding when and how versions should be incremented.
19+
-- https://pvp.haskell.org
20+
-- PVP summary: +-+------- breaking API changes
21+
-- | | +----- non-breaking API additions
22+
-- | | | +--- code changes with no API change
23+
version: 0.1.0.0
24+
25+
-- A short (one-line) description of the package.
26+
-- synopsis:
27+
28+
-- A longer description of the package.
29+
-- description:
30+
31+
-- The license under which the package is released.
32+
license: NONE
33+
34+
-- The package author(s).
35+
author: Tsung-Ju Lii
36+
37+
-- An email address to which users can send suggestions, bug reports, and patches.
38+
maintainer: usefulalgorithm@gmail.com
39+
40+
-- A copyright notice.
41+
-- copyright:
42+
build-type: Simple
43+
44+
-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
45+
extra-doc-files: CHANGELOG.md
46+
47+
-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
48+
-- extra-source-files:
49+
50+
common warnings
51+
ghc-options: -Wall
52+
53+
executable pull-album-info
54+
-- Import common warning flags.
55+
import: warnings
56+
57+
-- .hs or .lhs file containing the Main module.
58+
main-is: Main.hs
59+
60+
-- Modules included in this executable, other than Main.
61+
-- other-modules:
62+
63+
-- LANGUAGE extensions used by modules in this package.
64+
-- other-extensions:
65+
66+
-- Other library packages from which modules are imported.
67+
build-depends: base ^>=4.17.2.1,
68+
http-conduit,
69+
aeson,
70+
bytestring,
71+
lens-aeson,
72+
lens
73+
74+
-- Directories containing source files.
75+
hs-source-dirs: app
76+
77+
-- Base language which the package is written in.
78+
default-language: Haskell2010

0 commit comments

Comments
 (0)