initial commit

This commit is contained in:
千住柱間 2025-10-15 00:21:25 -04:00
commit 36ac5731a7
Signed by: hashirama
GPG key ID: 53E62470A86BC185
7 changed files with 164 additions and 0 deletions

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
# .gitignore
*~
*#
.#*
*rej
*orig
pinentry*
*.hi
*.o
*.a
dist-newstyle

5
CHANGELOG.md Normal file
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
module Lastfm.Crypto where
test2 :: IO ()
test2 = do
putStrLn "hello 2"