mp-0.1.3: src/Mp/Player/Server.hs
{-
* Programmer: Piotr Borek
* E-mail: piotrborek@op.pl
* Copyright 2014 Piotr Borek
*
* Distributed under the terms of the GPL (GNU Public License)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-}
module Mp.Player.Server (
startServerBlocking
) where
import qualified Media.Streaming.GStreamer as Gst
import qualified Control.Monad.State as State
import Network.Socket
import System.FilePath.Posix
import System.Posix.Signals
import System.Random
import Control.Monad
import Control.Concurrent
import System.Directory
import Data.Binary
import Data.Maybe
import Mp.Configuration.Configuration
import Mp.Player.GstPlayer
import Mp.Utils.Utils
import Mp.Utils.Shuffle
import Mp.Utils.Exception
import Mp.Utils.Network
import Mp.Player.ServerState
makeServerSocket :: String -> IO Socket
makeServerSocket socName = do
sock <- socket AF_UNIX Stream defaultProtocol
bind sock $ SockAddrUnix socName
listen sock 1
return sock
saveServerState :: MVar ServerState -> IO ()
saveServerState serverState = do
cfgDir <- configDirFilePath
state <- readMVar serverState
encodeFile (cfgDir </> "server.state") state
startServerBlocking :: String -> IO ()
startServerBlocking socName = do
player <- initGstPlayer
serverState <- newMVar defaultServerState
readServerState player serverState
schedulePlayerInfo player serverState
_ <- forkIO $ do
sock <- makeServerSocket socName
_ <- installHandler sigINT (Catch $ close sock) Nothing
_ <- installHandler sigTERM (Catch $ close sock) Nothing
talkBlocking serverState player sock
close sock
_ <- try' $ removeFile socName
return ()
gstBusAddWatch player $ \_ message -> do
case Gst.messageType message of
Gst.MessageEOS -> playerPlay serverState player succ
_ -> return ()
return True
gstPlayerMainLoopBlocking player
where
readServerState player serverState = do
cfgDir <- configDirFilePath
let file = cfgDir </> "server.state"
exists <- doesFileExist file
when exists $ do
modifyMVar_ serverState $ \_ -> do
state <- readStateFromFile file :: IO ServerState
gstPlayerSetVolume player $ getVolume state
when (getStatus state == "Playing") $ do
let f = getPlaylist state !! (shuffleFunc state) (getPlaying state)
gstPlayerPlay player f
return state
schedulePlayerInfo player serverState =
gstPlayerTimeoutAdd 100 $ do
maybeInfo <- gstPlayerGetTimeInfo player
if (isJust maybeInfo) then do
let (pos, dur) = fromJust maybeInfo
modifyMVar_ serverState $ \st -> do
return st {
getCurrentPosition = pos,
getCurrentDuration = dur
}
else
modifyMVar_ serverState $ \st -> do
return st {
getCurrentPosition = 0,
getCurrentDuration = 0
}
return True
playerPlay :: MVar ServerState -> GstPlayer -> (Int -> Int) -> IO ()
playerPlay serverState player indexFunction = do
_ <- forkIO $ do
threadDelay 400000
st <- readMVar serverState
when (and [getCurrentDuration st == 0, getStatus st == "Playing"]) $ playerPlay serverState player succ
modifyMVar_ serverState $ \st -> do
let list = getPlaylist st
let index = indexFunction $ getPlaying st
if and [index >= 0, index < length list]
then
modifyPlayerPlay st player index
else
if isRepeatMode st then do
let newIndex = if index < 0 then
pred $ length list
else if index >= length list then
0
else
index
modifyPlayerPlay st player newIndex
else
modifyPlayerStop st player
modifyPlayerStop :: ServerState -> GstPlayer -> IO ServerState
modifyPlayerStop st player = do
gstPlayerStop player
return st {
getCurrentPosition = 0,
getCurrentDuration = 0,
getPlaying = -1,
getStatus = "Stopped"
}
modifyPlayerPlay :: ServerState -> GstPlayer -> Int -> IO ServerState
modifyPlayerPlay st player index = do
let list = getPlaylist st
let (f, d) = if isShuffleMode st then
State.runState shuffleFunction $ shuffleData st
else
(id, shuffleData st)
gstPlayerPlay player $ list !! (f index)
return st {
getPlaying = index,
getStatus = "Playing",
shuffleFunc = f,
shuffleData = d
}
talkBlocking :: MVar ServerState -> GstPlayer -> Socket -> IO ()
talkBlocking serverState player sock = do
_ <- try' processMessages
saveServerState serverState
gstPlayerStop player
gstPlayerQuit player
where
processMessages = do
(conn, _) <- accept sock
msg <- recv conn 4096
case msg of
"Add" -> talkAdd serverState conn
"Remove" -> talkRemove serverState player conn
"Clear" -> talkClear serverState player conn
"GetPlaylist" -> talkGetPlaylist serverState conn
"GetStatus" -> talkGetStatus serverState conn
"SetPlay" -> talkSetPlay serverState conn player
"GetPlay" -> talkGetPlay serverState conn
"Stop" -> talkStop serverState conn player
"Pause" -> talkPause serverState conn player
"Resume" -> talkResume serverState conn player
"Next" -> talkNext serverState conn player
"Prev" -> talkPrev serverState conn player
"SeekForward" -> talkSeekForward serverState conn player
"SeekBackward" -> talkSeekBackward serverState conn player
"VolUp" -> talkVolumeUp serverState conn player
"VolDown" -> talkVolumeDown serverState conn player
"VolGet" -> talkVolumeGet serverState conn
"GetFlags" -> talkGetFlags serverState conn
"ToggleRepeat" -> talkToggleRepeat serverState conn
"ToggleShuffle" -> talkToggleShuffle serverState player conn
"Quit" -> sendString conn "OK"
_ -> sendString conn "NOK"
close conn
when (msg /= "Quit") processMessages
talkGetPlaylist :: MVar ServerState -> Socket -> IO ()
talkGetPlaylist serverState conn = do
list <- readMVar serverState
forM_ (reverse $ getPlaylist list) $ \item -> do
sendString conn item
recvString_ conn
return ()
sendString conn "EndPlaylist"
talkGetStatus :: MVar ServerState -> Socket -> IO ()
talkGetStatus serverState conn = do
st <- readMVar serverState
let status = getStatus st
sendString conn status
recvString_ conn
let dur = getCurrentDuration st
sendString conn $ show dur
recvString_ conn
let pos = getCurrentPosition st
sendString conn $ show pos
talkSetPlay :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkSetPlay serverState conn player = do
sendString conn "OK"
index <- recvString conn
sendString conn "OK"
st <- readMVar serverState
if isShuffleMode st
then
playerPlay serverState player $ const 0
else
playerPlay serverState player $ const (read index :: Int)
talkGetPlay :: MVar ServerState -> Socket -> IO ()
talkGetPlay serverState conn = do
st <- readMVar serverState
if getStatus st == "Stopped" then
sendString conn "-1"
else
sendString conn $ show $ shuffleFunc st $ getPlaying st
talkAdd :: MVar ServerState -> Socket -> IO ()
talkAdd serverState conn = do
sendString conn "OK"
file <- recvString conn
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
let (_, d) = State.runState shuffleAdd $ shuffleData st
return st {
getPlaylist = getPlaylist st ++ [file],
shuffleData = d
}
talkRemove :: MVar ServerState -> GstPlayer -> Socket -> IO ()
talkRemove serverState player conn = do
sendString conn "OK"
sIndex <- recvString conn
sendString conn "OK"
st <- takeMVar serverState
let shuffleEnabled = isShuffleMode st
let playing = getPlaying st
let index = read sIndex :: Int
let (_, d) = State.runState shuffleRemove $ shuffleData st
putMVar serverState $ update index playing d st
when (or [shuffleEnabled, playing == index]) $ playerPlay serverState player id
where
update index playing d st
| index < playing =
st {
getPlaying = pred playing,
getPlaylist = removeAt index $ getPlaylist st,
shuffleData = d
}
| otherwise =
st {
getPlaylist = removeAt index $ getPlaylist st,
shuffleData = d
}
talkClear :: MVar ServerState -> GstPlayer -> Socket -> IO ()
talkClear serverState player conn = do
sendString conn "OK"
gstPlayerStop player
modifyMVar_ serverState (const $ return defaultServerState)
talkStop :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkStop serverState conn player = do
sendString conn "OK"
modifyMVar_ serverState $ flip modifyPlayerStop player
talkPause :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkPause serverState conn player = do
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
gstPlayerPause player
return st {
getStatus = "Paused"
}
talkResume :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkResume serverState conn player = do
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
gstPlayerResume player
return st {
getStatus = "Playing"
}
talkNext :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkNext serverState conn player = do
sendString conn "OK"
st <- takeMVar serverState
let status = getStatus st
let index = succ $ getPlaying st
let len = length $ getPlaylist st
let rpt = isRepeatMode st
putMVar serverState st
if rpt then
when (status == "Playing") $ playerPlay serverState player succ
else
when (and [status == "Playing", index < len]) $ playerPlay serverState player succ
talkPrev :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkPrev serverState conn player = do
sendString conn "OK"
st <- takeMVar serverState
let status = getStatus st
let index = pred $ getPlaying st
let rpt = isRepeatMode st
putMVar serverState st
if rpt then
when (status == "Playing") $ playerPlay serverState player pred
else
when (and [status == "Playing", index >= 0]) $ playerPlay serverState player pred
talkVolumeUp :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkVolumeUp serverState conn player = do
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
let vol0 = (getVolume st) + 0.04
let vol1 = if vol0 > 1.0 then 1.0 else vol0
gstPlayerSetVolume player vol1
return st {
getVolume = vol1
}
talkVolumeDown :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkVolumeDown serverState conn player = do
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
let vol0 = (getVolume st) - 0.04
let vol1 = if vol0 < 0.0 then 0.0 else vol0
gstPlayerSetVolume player vol1
return st {
getVolume = vol1
}
talkVolumeGet :: MVar ServerState -> Socket -> IO ()
talkVolumeGet serverState conn = do
st <- readMVar serverState
let vol = ceiling $ (getVolume st) * 100 :: Int
sendString conn $ show vol
talkSeekForward :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkSeekForward serverState conn player = do
sendString conn "OK"
st <- readMVar serverState
let pos0 = (getCurrentPosition st) + 15
let pos1 = if pos0 > getCurrentDuration st then getCurrentDuration st else pos0
gstPlayerSeek player pos1
talkSeekBackward :: MVar ServerState -> Socket -> GstPlayer -> IO ()
talkSeekBackward serverState conn player = do
sendString conn "OK"
st <- readMVar serverState
let pos0 = (getCurrentPosition st) - 15
let pos1 = if pos0 < 0 then 0 else pos0
gstPlayerSeek player pos1
talkGetFlags :: MVar ServerState -> Socket -> IO ()
talkGetFlags serverState conn = do
st <- readMVar serverState
let r = if isRepeatMode st then 'r' else '-'
let z = if isShuffleMode st then 'z' else '-'
sendString conn $ [r, z]
talkToggleRepeat :: MVar ServerState -> Socket -> IO ()
talkToggleRepeat serverState conn = do
sendString conn "OK"
modifyMVar_ serverState $ \st -> do
return st {
isRepeatMode = not $ isRepeatMode st
}
talkToggleShuffle :: MVar ServerState -> GstPlayer -> Socket -> IO ()
talkToggleShuffle serverState player conn = do
sendString conn "OK"
shuffleEnabled <- modifyMVar serverState $ \st -> do
if isShuffleMode st then do
newSt <- disableShuffle st
return (newSt, False)
else do
newSt <- enableShuffle st
return (newSt, getStatus st == "Playing")
when (shuffleEnabled) $ playerPlay serverState player id
disableShuffle :: ServerState -> IO ServerState
disableShuffle st =
return st {
getPlaying = shuffleFunc st $ getPlaying st,
isShuffleMode = False,
shuffleFunc = id
}
enableShuffle :: ServerState -> IO ServerState
enableShuffle st = do
r <- randomIO
let (f, d) = State.runState (shuffleFilled $ length $ getPlaylist st) $ shuffleInitial (mkStdGen r)
return st {
getPlaying = 0,
isShuffleMode = True,
randomInitializer = r,
shuffleFunc = f,
shuffleData = d
}