mpd-current-json 1.2.0.0 → 1.3.0.0
raw patch · 5 files changed
+105/−30 lines, 5 filesdep ~mpd-current-json
Dependency ranges changed: mpd-current-json
Files
- CHANGELOG.md +10/−0
- README.org +18/−8
- lib/Network/MPD/Parse.hs +8/−0
- mpd-current-json.cabal +5/−3
- src/Main.hs +64/−19
CHANGELOG.md view
@@ -1,7 +1,17 @@+# v1.3+- Add `filename` key.+- Add `playlist` key and move existing keys to it.+- Customize ordering of displayed output JSON.+- Add cabal tested-with GHC versions+ # v1.2.0.0 - Move literate Org Mode code to LITERATE.org file - Move functions from executable source Main.hs to their own library - Bump dependency versions for `aeson` and `bytestring`+- Changed status.state from "play" to "playing" and "pause" to+ "paused".+ The reason why it was "play" and "pause" before was because+ that was the socket answer string. # v1.1.0.2 [comment]: # (2023-10-23)
README.org view
@@ -3,21 +3,31 @@ #+OPTIONS: toc:1 * Installation-: cabal install mpd-current-json-or+Packages are available:+- [[https://aur.archlinux.org/packages/mpd-current-json][in the AUR]]+- in Hackage:+ : cabal install mpd-current-json++or compile it from source: #+begin_example git clone https://codeberg.org/useless-utils/mpd-current-json cd mpd-current-json #+end_example-and to install the executable to =./dist=, in the current directory:+to install the executable to =./dist=, in the current directory: : cabal install --install-method=copy --overwrite-policy=always --installdir=dist or to install to =${CABAL_DIR}/bin= remove the =--installdir=dist= argument. =CABAL_DIR= defaults to =~/.local/share/cabal=. * Usage-get values-: mpd-current-json | jaq .tags.album-: mpd-current-json | jaq .status.elapsed_percent+- Get values+ : mpd-current-json | jaq .tags.album+ : mpd-current-json | jaq .status.elapsed_percent -provide host and port with-: mpd-current-json -h 'localhost' -p 4321+- Provide host and port with+ : mpd-current-json -h 'localhost' -p 4321++- Provide password+ : mpd-current-json --password 'abc123'+ note that the password will be sent as plain text.++Run =mpd-current-json --help= for all the available options.
lib/Network/MPD/Parse.hs view
@@ -2,6 +2,7 @@ ( getStatusItem , getTag , processSong+ , maybePath , headMay , valueToStringMay , (.=?)@@ -45,6 +46,13 @@ processSong tag (Just song) = do let tagVal = MPD.sgGetTag tag song valueToStringMay =<< (headMay =<< tagVal)++maybePath :: Either a (Maybe Song) -> Maybe String+maybePath cs =+ case cs of+ Left _ -> Nothing+ Right Nothing -> Nothing+ Right (Just song) -> Just $ MPD.toString $ MPD.sgFilePath song {- | Safely get the head of a list. Same as [Safe.headMay](Safe#headMay). -}
mpd-current-json.cabal view
@@ -7,7 +7,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 1.2.0.0+version: 1.3.0.0 synopsis: Print current MPD song and status as JSON -- A longer description of the package.@@ -24,13 +24,15 @@ -- A copyright notice. -- copyright: category: Network-extra-source-files: CHANGELOG.md+extra-doc-files: CHANGELOG.md README.org source-repository head type: git location: https://codeberg.org/useless-utils/mpd-current-json +-- tested-with: GHC == 9.4.8+ library -- exposed: False exposed-modules: Network.MPD.Parse@@ -58,7 +60,7 @@ , aeson , bytestring >=0.11 && <0.13 , aeson-pretty == 0.8.*- , mpd-current-json == 1.2.*+ , mpd-current-json == 1.3.* -- Directories containing source files. hs-source-dirs: src
src/Main.hs view
@@ -6,16 +6,21 @@ import Network.MPD ( Metadata(..), PlaybackState(Stopped, Playing, Paused) ) import Data.Maybe ( catMaybes )-import Data.Aeson ( object, KeyValue(..) )-import Data.Aeson.Encode.Pretty ( encodePretty )+import Data.Aeson ( object, KeyValue((.=)), Value )+import Data.Aeson.Encode.Pretty+ ( defConfig, encodePretty', keyOrder, Config(confCompare) ) import qualified Data.ByteString.Lazy.Char8 as C import Text.Printf ( printf ) import Options ( optsParserInfo, execParser, Opts(optPass, optHost, optPort) ) import Network.MPD.Parse ( getStatusItem- , getTag- , (.=?) )+ , getTag+ , maybePath+ , (.=?) )++import Text.Read (readMaybe)+import Data.Aeson.Types (Pair) {- | Where the program connects to MPD and uses the helper functions to extract values, organize them into a list of key/value pairs, make them a 'Data.Aeson.Value' using 'Data.Aeson.object', then encode it to@@ -69,20 +74,20 @@ elapsed = case time of Just t -> case t of Just (e, _) -> Just e- _ -> Nothing+ _noTag -> Nothing Nothing -> Nothing duration = case time of Just t -> case t of Just (_, d) -> Just d- _ -> Nothing+ _noTag -> Nothing Nothing -> Nothing elapsedPercent :: Maybe Double elapsedPercent = case time of Just t -> case t of- Just t1 -> Just (read $ printf "%.2f" (uncurry (/) t1 * 100))- Nothing -> Just 0+ Just t1 -> readMaybe $ printf "%.2f" (uncurry (/) t1 * 100)+ Nothing -> Just 0 Nothing -> Nothing repeatSt = getStatusItem st MPD.stRepeat@@ -95,8 +100,10 @@ audioFormat = getStatusItem st MPD.stAudio errorSt = getStatusItem st MPD.stError + let filename = maybePath cs+ -- sgTags- let jTags = object . catMaybes $+ let jTags = objectJson [ "artist" .=? artist , "artist_sort" .=? artistSort , "album" .=? album@@ -126,23 +133,61 @@ ] -- status- let jStatus = object . catMaybes $+ let jStatus = objectJson [ "state" .=? state , "repeat" .=? repeatSt- , "elapsed" .=? elapsed- , "duration" .=? duration- , "elapsed_percent" .=? elapsedPercent , "random" .=? randomSt , "single" .=? singleSt , "consume" .=? consumeSt- , "song_position" .=? pos- , "playlist_length" .=? playlistLength- , "bitrate" .=? bitrate+ , "duration" .=? duration+ , "elapsed" .=? elapsed+ , "elapsed_percent" .=? elapsedPercent , "audio_format" .=? audioFormat+ , "bitrate" .=? bitrate , "error" .=? errorSt ] - let jObject = object [ "tags" .= jTags- , "status" .= jStatus ]+ -- let jFilename = objectJson [ "file" .=? filename ] - C.putStrLn $ encodePretty jObject+ let jPlaylist = objectJson+ [ "position" .=? pos+ , "length" .=? playlistLength+ ]++ let jObject = object [ "filename" .= filename+ , "playlist" .= jPlaylist+ , "status" .= jStatus+ , "tags" .= jTags+ ]++ C.putStrLn $ encodePretty' customEncodeConf jObject++customEncodeConf :: Config+customEncodeConf = defConfig+ { confCompare = keyOrder [ "title", "name"+ , "artist", "album_artist", "artist_sort", "album_artist_sort"+ , "album", "album_sort"+ , "track", "disc"+ , "date", "original_date"+ , "genre", "composer", "performer", "conductor"+ , "work", "grouping", "label"+ , "comment"+ , "musicbrainz_artistid"+ , "musicbrainz_albumid"+ , "musicbrainz_albumartistid"+ , "musicbrainz_trackid"+ , "musicbrainz_releasetrackid"+ , "musicbrainz_workid"+ -- status+ , "state", "repeat", "random", "single", "consume"+ , "duration", "elapsed", "elapsed_percent"+ , "audio_format", "bitrate"+ , "error"+ ]+ }++-- | Helper function for creating an JSON 'Data.Aeson.object' where+-- 'Data.Maybe.catMaybes' won't include items from the '[Maybe Pair]'+-- list that return 'Nothing'.+objectJson :: [Maybe Pair] -> Value+objectJson = object . catMaybes