{- This file is part of the Haskell package playlists. It is subject to the license terms in the LICENSE file found in the top-level directory of this distribution and at git://pmade.com/playlists/LICENSE. No part of playlists package, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the LICENSE file. -} -------------------------------------------------------------------------------- module Text.Playlist.M3U.Reader (parsePlaylist) where -------------------------------------------------------------------------------- import Control.Applicative import Control.Monad (msum, void) import Data.Attoparsec.ByteString import Data.Text (Text) import Data.Text.Encoding (decodeUtf8) import Data.Word8 (isDigit) import Text.Playlist.Internal.Attoparsec import Text.Playlist.Types -------------------------------------------------------------------------------- -- | Parser for a complete M3U playlist. parsePlaylist :: Parser Playlist parsePlaylist = do ts <- many1 parseTrack void (many' commentOrTitle) -- Trailing comments. return ts -------------------------------------------------------------------------------- -- | Parser for a single track in a M3U file. parseTrack :: Parser Track parseTrack = do -- Get the title closest to the URL or Nothing. title <- msum . reverse <$> many' commentOrTitle url <- parseURL return Track { trackURL = url , trackTitle = title } -------------------------------------------------------------------------------- -- | Parser for URL or file name in a M3U file. The URL is the entire -- line so this parser extracts the entire line and decodes it. parseURL :: Parser Text parseURL = decodeUtf8 <$> takeWhile1 (not . isEOL) <* skipSpace -------------------------------------------------------------------------------- -- | Comment parser with a twist. In the extended M3U format metadata -- for a track can be placed in a comment that appears just before the -- URL. This parser succeeds if the current line is a comment, and -- always skips over the entire comment. If the comment represents a -- track title then that information will be returned in a @Just@. If -- it's just a regular comment then @Nothing@ is returned. commentOrTitle :: Parser (Maybe Text) commentOrTitle = do skipSpace skip (== 35) -- Comment character "#" istitle <- (string "EXTINF:" >> return True) <|> return False if istitle then title <|> comment else comment where comment = skipLine >> return Nothing title = do skip (== 45) <|> return () -- Skip optional negative sign. skipWhile isDigit -- Skip length. skip (== 44) -- Skip comma. skipSpace text <- decodeUtf8 <$> takeWhile1 (not . isEOL) skipSpace return $! Just text