initial commit
This commit is contained in:
commit
36ac5731a7
7 changed files with 164 additions and 0 deletions
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# .gitignore
|
||||
|
||||
|
||||
*~
|
||||
*#
|
||||
.#*
|
||||
*rej
|
||||
*orig
|
||||
pinentry*
|
||||
*.hi
|
||||
*.o
|
||||
*.a
|
||||
dist-newstyle
|
||||
5
CHANGELOG.md
Normal file
5
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Revision history for lastfm-hs
|
||||
|
||||
## 0.1.0.0 -- YYYY-mm-dd
|
||||
|
||||
* First version. Released on an unsuspecting world.
|
||||
12
README.md
Normal file
12
README.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
## lastfm-hs
|
||||
|
||||
keep track of the things you've listened to!
|
||||
|
||||
TODO: image processing
|
||||
|
||||
|
||||
### build:
|
||||
```shell
|
||||
$ cabal run
|
||||
```
|
||||
|
||||
16
app/Main.hs
Normal file
16
app/Main.hs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
import Lastfm.Core
|
||||
import Control.Monad (forM_)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
let username = "your user"
|
||||
apiKey = "your key"
|
||||
putStrLn $ "Fetching artists for " ++ username ++ "..."
|
||||
result <- fetchArtists apiKey username
|
||||
case result of
|
||||
Left err -> putStrLn $ "Error: " ++ err
|
||||
Right root -> do
|
||||
forM_ (artist (artists root)) $ \a -> do
|
||||
putStrLn $ name a ++ " (" ++ show (playcount a) ++ " plays)"
|
||||
50
lastfm-hs.cabal
Normal file
50
lastfm-hs.cabal
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
cabal-version: 2.4
|
||||
-- Cabal file for lastfm-hs CLI
|
||||
|
||||
name: lastfm-hs
|
||||
version: 0.1.0.0
|
||||
synopsis: Last.fm CLI tool with encrypted API key storage
|
||||
description: A Haskell CLI application that stores Last.fm API key securely and fetches user library.
|
||||
category: Web
|
||||
author: Your Name
|
||||
maintainer: your.email@example.com
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
build-type: Simple
|
||||
|
||||
|
||||
|
||||
library
|
||||
|
||||
exposed-modules:
|
||||
Lastfm.Crypto
|
||||
Lastfm.Core
|
||||
|
||||
build-depends:
|
||||
base >=4.9 && <5,
|
||||
bytestring >=0.10,
|
||||
directory >=1.3,
|
||||
filepath >=1.3,
|
||||
cryptonite ,
|
||||
Rasterific == 0.7.5.4,
|
||||
JuicyPixels == 3.3.9,
|
||||
http-conduit,
|
||||
aeson >= 2.2.3.0,
|
||||
text >=1.2,
|
||||
process >=1.6,
|
||||
containers
|
||||
|
||||
hs-source-dirs: src
|
||||
default-language: GHC2021
|
||||
|
||||
executable lastfm-hs
|
||||
main-is: Main.hs
|
||||
hs-source-dirs: app
|
||||
ghc-options: -Wall -threaded
|
||||
default-language: Haskell2010
|
||||
build-depends:
|
||||
base ^>=4.16.4.0,
|
||||
lastfm-hs
|
||||
|
||||
|
||||
-- Optional: extra-libraries or extra-ghc-options can be added if needed
|
||||
63
src/Lastfm/Core.hs
Normal file
63
src/Lastfm/Core.hs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{-# LANGUAGE DeriveGeneric, OverloadedStrings #-}
|
||||
|
||||
module Lastfm.Core
|
||||
( Image(..)
|
||||
, Artist(..)
|
||||
, Artists(..)
|
||||
, Root(..)
|
||||
, fetchArtists
|
||||
) where
|
||||
|
||||
|
||||
import Data.Aeson
|
||||
import GHC.Generics
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import Network.HTTP.Simple
|
||||
import qualified Data.Map as M
|
||||
import Control.Monad (forM_)
|
||||
|
||||
-- Image
|
||||
data Image = Image
|
||||
{ size :: String
|
||||
, text :: String
|
||||
} deriving (Show, Generic)
|
||||
|
||||
instance FromJSON Image where
|
||||
parseJSON = withObject "Image" $ \v ->
|
||||
Image <$> v .: "size" <*> v .: "#text"
|
||||
|
||||
-- Artist
|
||||
data Artist = Artist
|
||||
{ name :: String
|
||||
, playcount :: Int
|
||||
, url :: String
|
||||
, image :: [Image]
|
||||
} deriving (Show, Generic)
|
||||
|
||||
instance FromJSON Artist where
|
||||
parseJSON = withObject "Artist" $ \v ->
|
||||
Artist <$> v .: "name"
|
||||
<*> (read <$> v .: "playcount")
|
||||
<*> v .: "url"
|
||||
<*> v .: "image"
|
||||
|
||||
newtype Artists = Artists { artist :: [Artist] }
|
||||
deriving (Show, Generic)
|
||||
|
||||
instance FromJSON Artists
|
||||
|
||||
newtype Root = Root { artists :: Artists }
|
||||
deriving (Show, Generic)
|
||||
|
||||
instance FromJSON Root
|
||||
|
||||
-- | Fetch artists from Last.fm API
|
||||
fetchArtists :: String -> String -> IO (Either String Root)
|
||||
fetchArtists apiKey username = do
|
||||
let url = "https://ws.audioscrobbler.com/2.0/?method=library.getartists"
|
||||
++ "&user=" ++ username
|
||||
++ "&api_key=" ++ apiKey
|
||||
++ "&format=json"
|
||||
response <- httpLBS (parseRequest_ url)
|
||||
let body = getResponseBody response
|
||||
return $ eitherDecode body
|
||||
5
src/Lastfm/Crypto.hs
Normal file
5
src/Lastfm/Crypto.hs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module Lastfm.Crypto where
|
||||
|
||||
test2 :: IO ()
|
||||
test2 = do
|
||||
putStrLn "hello 2"
|
||||
Loading…
Reference in a new issue