1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}
-- https://www.schoolofhaskell.com/school/starting-with-haskell/libraries-and-frameworks/text-manipulation/json
module GMPDP where
import Data.Aeson
import Data.Text
import Control.Applicative
import qualified Data.ByteString.Lazy as B
import Network.HTTP.Conduit (simpleHttp)
import GHC.Generics
import Xmobar
import Control.Monad (when, guard)
import Control.Concurrent.STM
import Control.Exception (SomeException (..), handle, evaluate)
import System.INotify (Event(..), EventVariety(..), initINotify, addWatch)
import qualified Data.ByteString.Char8 as BS (ByteString, pack)
-- | Type of each JSON entry in record syntax.
data Song =
Song { title :: !String
, artist :: !String
, album :: !String
} deriving (Eq,Show,Generic)
data PlayTime =
PlayTime { current :: !Int
, total :: !Int
} deriving (Eq,Show,Generic)
data GMPDPStatus =
GMPDPStatus { song :: Song
, time :: PlayTime
} deriving (Eq,Show,Generic)
-- Instances to convert our type to/from JSON.
instance FromJSON Song
instance FromJSON PlayTime
instance FromJSON GMPDPStatus
-- | Location of the local copy, in case you have it,
-- of the JSON file.
jsonFile :: FilePath
jsonFile = "/home/jao/.config/Google Play Music Desktop Player/json_store/playback.json"
getJSON :: IO B.ByteString
getJSON = B.readFile jsonFile
getGMPDPStatus :: IO (Maybe GMPDPStatus)
getGMPDPStatus = do
s <- (eitherDecode <$> getJSON) :: IO (Either String GMPDPStatus)
case s of
Left _ -> return Nothing
Right r -> return $ Just r
newtype GMPDP = GMPDP String deriving (Show,Read,Generic)
handleNotification :: TVar (Maybe GMPDPStatus) -> Event -> IO ()
handleNotification v _ =
getGMPDPStatus >>= \s -> atomically $ writeTVar v s
formatStatus Nothing = ""
formatStatus (Just s) =
fmtt (current $ time s) ++ "/" ++ fmtt (total $ time s) ++
" " ++ title (song s) ++ " <fc=sienna4>" ++ album (song s) ++ "</fc>" ++
" " ++ artist (song s)
where fmtt ms = let s = ms `div` 1000
sr x = if x < 10 then "0" ++ show x else show x
in sr (s `div` 60) ++ ":" ++ sr (s `mod` 60)
changeLoop :: Eq a => STM a -> (a -> IO ()) -> IO ()
changeLoop s f = atomically s >>= go
where
go old = do
f old
go =<< atomically (do
new <- s
guard (new /= old)
return new)
instance Exec GMPDP where
alias (GMPDP a) = a
start (GMPDP _) cb = do
i <- initINotify
s <- getGMPDPStatus
v <- newTVarIO s
addWatch i [CloseWrite] (BS.pack jsonFile) (handleNotification v)
changeLoop (readTVar v) $ \s -> cb (formatStatus s)
|