PageRenderTime 81ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Playlist/Format.hs

https://github.com/upwawet/vision
Haskell | 194 lines | 139 code | 36 blank | 19 comment | 0 complexity | 2233ea11435760ac254e16b69bf95cf5 MD5 | raw file
  1. -- -*-haskell-*-
  2. -- Vision (for the Voice): an XMMS2 client.
  3. --
  4. -- Author: Oleg Belozeorov
  5. -- Created: 22 Feb. 2010
  6. --
  7. -- Copyright (C) 2009-2011 Oleg Belozeorov
  8. --
  9. -- This program is free software; you can redistribute it and/or
  10. -- modify it under the terms of the GNU General Public License as
  11. -- published by the Free Software Foundation; either version 3 of
  12. -- the License, or (at your option) any later version.
  13. --
  14. -- This program is distributed in the hope that it will be useful,
  15. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. -- General Public License for more details.
  18. --
  19. {-# LANGUAGE TupleSections, DeriveDataTypeable, Rank2Types #-}
  20. module Playlist.Format
  21. ( initFormat
  22. , withFormat
  23. , makeTrackInfo
  24. , getFormatDefs
  25. , putFormatDefs
  26. , TrackInfo (..)
  27. , trackInfoAttrs
  28. , trackInfoText
  29. , trackInfoDuration
  30. , formatsGeneration
  31. ) where
  32. import Prelude hiding (lookup, catch)
  33. import Control.Exception
  34. import Control.Concurrent
  35. import Control.Concurrent.STM
  36. import Control.Concurrent.STM.TWatch
  37. import Control.Applicative
  38. import Control.Monad
  39. import Data.IORef
  40. import Data.Maybe
  41. import Data.Either
  42. import Data.Char (toLower)
  43. import Data.Env
  44. import Data.Typeable
  45. import Graphics.UI.Gtk hiding (add)
  46. import Medialib
  47. import Properties
  48. import Config
  49. import Utils
  50. import Registry
  51. import Playlist.Format.Format
  52. import Playlist.Format.Parser
  53. data TrackInfo
  54. = TrackInfo { tAttrs :: [AttrOp CellRendererText]
  55. , tText :: String
  56. , tDuration :: String }
  57. trackInfoAttrs Nothing = [ cellTextMarkup := Just "" ]
  58. trackInfoAttrs (Just i) = tAttrs i
  59. trackInfoText Nothing = ""
  60. trackInfoText (Just i) = tText i
  61. trackInfoDuration Nothing = ""
  62. trackInfoDuration (Just i) = tDuration i
  63. data Ix = Ix deriving (Typeable)
  64. data Format
  65. = Format { _makeInfoRef :: IORef (MediaInfo -> IO ([AttrOp CellRendererText], String))
  66. , _formatDefsRef :: IORef [String]
  67. , _lookupDuration :: MediaInfo -> String
  68. , _lookupURL :: MediaInfo -> String
  69. , _generation :: TVar Integer
  70. }
  71. deriving (Typeable)
  72. getMakeInfo = readIORef (_makeInfoRef ?_Playlist_Format)
  73. putMakeInfo = writeIORef (_makeInfoRef ?_Playlist_Format)
  74. getFormatDefs = readIORef (_formatDefsRef ?_Playlist_Format)
  75. putFormatDefs' = writeIORef (_formatDefsRef ?_Playlist_Format)
  76. putFormatDefs defs = do
  77. putFormatDefs' defs
  78. saveFormatDefs
  79. updateFormats True
  80. lookupDuration = _lookupDuration ?_Playlist_Format
  81. lookupURL = _lookupURL ?_Playlist_Format
  82. formatsGeneration = _generation ?_Playlist_Format
  83. initFormat = do
  84. format <- mkFormat
  85. addEnv Ix format
  86. let ?_Playlist_Format = format
  87. loadFormatDefs
  88. prW <- atomically $ newEmptyTWatch propertiesGeneration
  89. forkIO $ forever $ do
  90. void $ atomically $ watch prW
  91. postGUISync $ updateFormats True
  92. return ()
  93. newtype Wrap a = Wrap { unWrap :: (?_Playlist_Format :: Format) => a }
  94. withFormat = withFormat' . Wrap
  95. withFormat' w = do
  96. Just (Env format) <- getEnv (Extract :: Extract Ix Format)
  97. let ?_Playlist_Format = format in unWrap w
  98. mkFormat = do
  99. formatDefsRef <- newIORef []
  100. makeInfoRef <- newIORef $ const $ return ([], "")
  101. duration <- fromJust <$> property "Duration"
  102. url <- fromJust <$> property "URL"
  103. generation <- newTVarIO 0
  104. return Format { _makeInfoRef = makeInfoRef
  105. , _formatDefsRef = formatDefsRef
  106. , _lookupDuration = maybe "" escapeMarkup . lookup duration
  107. , _lookupURL = maybe "" escapeMarkup . lookup url
  108. , _generation = generation
  109. }
  110. loadFormatDefs = do
  111. putFormatDefs' . map trim =<< config "playlist-formats.conf" builtinFormats
  112. updateFormats False
  113. where builtinFormats =
  114. [ "<b>{Movement}[: {Title}]</b>\n\
  115. \{Composer} {Work}[, {Catalog}]"
  116. , "<b>{Work}</b>[<b>, </b>{Catalog}]\n\
  117. \{Composer}"
  118. , "<b>{Movement}[: {Title}]</b>\n\
  119. \{Composer} {Work}[, {Catalog}][\n\
  120. \{Performer}][\n\
  121. \[{Conductor}, ]{Orchestra}][\n\
  122. \[{Chorus master}, ]{Chorus}]"
  123. , "<b>{Work}</b>[<b>, </b>{Catalog}]\n\
  124. \{Composer}[\n\
  125. \{Performer}][\n\
  126. \[{Conductor}, ]{Orchestra}][\n\
  127. \[{Chorus master}, ]{Chorus}]"
  128. , "[<b>{Title}</b>\n]\
  129. \{Channel}"
  130. , "[{Track} ]<b>{Title}</b>\n\
  131. \{Artist} {Album}" ]
  132. saveFormatDefs = do
  133. writeConfig "playlist-formats.conf" =<< getFormatDefs
  134. return ()
  135. getFormats = (rights . map parseFormat) <$> getFormatDefs
  136. updateFormats notify = do
  137. putMakeInfo =<< makeMakeInfo =<< getFormats
  138. when notify $ atomically $ do
  139. g <- readTVar formatsGeneration
  140. writeTVar formatsGeneration $ g + 1
  141. makeTrackInfo info = do
  142. makeInfo <- getMakeInfo
  143. (attrs, text) <- makeInfo info
  144. return TrackInfo { tAttrs = attrs
  145. , tText = text
  146. , tDuration = lookupDuration info }
  147. makeMakeInfo fs = do
  148. fs' <- rights <$> mapM cookFormat fs
  149. return $ \pm -> do
  150. let (text, ellipsize) =
  151. maybe (lookupURL pm, EllipsizeMiddle) (, EllipsizeEnd) $
  152. formatMediaInfo fs' pm
  153. search <- map toLower <$> plain text
  154. return ( [ cellTextMarkup := Just text
  155. , cellTextEllipsize := ellipsize ]
  156. , search )
  157. where plain text =
  158. (trd <$> parseMarkup text '\0') `catch` \(_ :: SomeException) -> return ""