packages feed

playlists 0.4.1.0 → 0.5.0.0

raw patch · 9 files changed

+97/−48 lines, 9 filesdep ~optparse-applicativePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: optparse-applicative

API changes (from Hackage documentation)

+ Text.Playlist: [trackDuration] :: Track -> Maybe Float
- Text.Playlist: Track :: Text -> Maybe Text -> Track
+ Text.Playlist: Track :: Text -> Maybe Text -> Maybe Float -> Track

Files

CHANGES.md view
@@ -1,3 +1,9 @@+# Version 0.5.0.0 (December 1, 2017)++  * Add support for track length (thanks to Catherine Galkina)++  * Widen dependency versions to latest available.+ # Version 0.4.1.0 (February 5, 2017)    * Remove redundant constraints as reported by GHC 8.X.
playlists.cabal view
@@ -1,5 +1,5 @@ name:          playlists-version:       0.4.1.0+version:       0.5.0.0 synopsis:      Library and executable for working with playlist files. homepage:      https://github.com/pjones/playlists license:       BSD3@@ -10,7 +10,7 @@ category:      Text build-type:    Simple cabal-version: >= 1.18-tested-with:   GHC==7.10.3, GHC==8.0.1+tested-with:   GHC==7.10.3, GHC==8.0.2 description:   Playlists is a library for working with media playlist files.  The   original motivation for the library was extracting URLs for@@ -104,7 +104,7 @@    build-depends: base                , bytestring-               , optparse-applicative >= 0.10.0 && < 0.14.0+               , optparse-applicative >= 0.10 && < 0.15                , playlists                , text 
src/Text/Playlist/M3U/Reader.hs view
@@ -16,11 +16,12 @@  -------------------------------------------------------------------------------- import Control.Applicative-import Control.Monad (msum, void)+import Control.Monad (void) import Data.Attoparsec.ByteString+import Data.Attoparsec.ByteString.Char8 (signed, double)+import Data.Maybe (catMaybes) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8)-import Data.Word8 (isDigit) import Text.Playlist.Internal.Attoparsec import Text.Playlist.Types @@ -29,21 +30,25 @@ parsePlaylist :: Parser Playlist parsePlaylist = do   ts <- many1 parseTrack-  void (many' commentOrTitle) -- Trailing comments.+  void (many' commentOrDirective) -- 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+  -- Get the length and title closest to the URL or Nothing.+  (title, len) <- maybeTitleAndLength . reverse <$> (many' commentOrDirective)   url   <- parseURL--  return Track { trackURL   = url-               , trackTitle = title+  return Track { trackURL      = url+               , trackTitle    = title+               , trackDuration = len                }-+    where+      maybeTitleAndLength lst =+        case catMaybes lst of+          []    -> (Nothing, Nothing)+          x : _ -> x -------------------------------------------------------------------------------- -- | 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.@@ -54,21 +59,21 @@ -- | 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)+-- always skips over the entire comment.  If the comment represents an+-- EXTINF directive then that information will be returned in a @Just@.+-- If it's just a regular comment then @Nothing@ is returned.+commentOrDirective :: Parser (Maybe (Maybe Text, Maybe Float))+commentOrDirective = do+  skipSpace+  skip (== 35) -- Comment character "#"+  isDirective <- (string "EXTINF:" >> return True) <|> return False+  if isDirective then directive <|> comment else comment+    where+      comment   = skipLine >> return Nothing+      directive = do+        mlen <- (Just . realToFrac <$> signed double) <|> return Nothing -- Parse length.+        skip (== 44)                                                     -- Skip comma.+        mtext <- (Just . decodeUtf8 <$> takeWhile1 (not . isEOL)) <|> (return Nothing)+        skipLine+        return (Just (mtext, mlen))+
src/Text/Playlist/M3U/Writer.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}  {- @@ -29,14 +30,27 @@ -------------------------------------------------------------------------------- writeTrack :: Track -> Builder writeTrack x =-  writeTitle (trackTitle x)              <>+  writeTitleAndLength x <>   B.byteString (encodeUtf8 $ trackURL x) <>   B.charUtf8 '\n'  ---------------------------------------------------------------------------------writeTitle :: Maybe Text -> Builder-writeTitle Nothing  = mempty-writeTitle (Just x) =-  B.byteString "#EXTINF:-1,"  <>-  B.byteString (encodeUtf8 x) <>+writeTitleAndLength :: Track -> Builder+writeTitleAndLength (Track _ Nothing Nothing) = mempty+writeTitleAndLength Track{..} =+  B.byteString "#EXTINF:"   <>+  writeLength trackDuration <>+  B.byteString ","          <>+  writeTitle trackTitle     <>   B.charUtf8 '\n'++--------------------------------------------------------------------------------+writeLength :: Maybe Float -> Builder+writeLength Nothing = mempty+writeLength (Just l) = B.stringUtf8 (show l)++--------------------------------------------------------------------------------+writeTitle :: Maybe Text -> Builder+writeTitle Nothing = mempty+writeTitle (Just x) = B.byteString (encodeUtf8 x)+
src/Text/Playlist/PLS/Reader.hs view
@@ -18,6 +18,7 @@ import Control.Applicative import Control.Monad (void) import Data.Attoparsec.ByteString+import Data.Attoparsec.ByteString.Char8 (signed, double) import Data.ByteString (ByteString) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8)@@ -50,11 +51,13 @@   (n, url) <- parseFileN   title    <- (Just <$> parseTitle n) <|> return Nothing -  -- Skip optional length field, we don't use it.-  (skipSpace >> string "Length" >> skipLine) <|> return ()+  -- Parse track length.+  mlen <- (Just . realToFrac <$> (skipSpace >> string "Length" >> skipWhile isDigit >> string "=" >> signed double))+      <|> return Nothing -  return Track { trackURL   = url-               , trackTitle = title+  return Track { trackURL      = url+               , trackTitle    = title+               , trackDuration = mlen                }  --------------------------------------------------------------------------------
src/Text/Playlist/PLS/Writer.hs view
@@ -35,7 +35,7 @@  -------------------------------------------------------------------------------- writeTrack :: (Track, Int) -> Builder-writeTrack x = writeFileN x <> writeTitle x+writeTrack x = writeFileN x <> writeTitle x <> writeLength x  -------------------------------------------------------------------------------- writeFileN :: (Track, Int) -> Builder@@ -56,3 +56,15 @@                B.charUtf8 '='              <>                B.byteString (encodeUtf8 y) <>                B.charUtf8 '\n'++--------------------------------------------------------------------------------+writeLength :: (Track, Int) -> Builder+writeLength (x, n) =+  case trackDuration x of+    Nothing -> mempty+    Just l  -> B.byteString "Length" <>+               B.stringUtf8 (show n) <>+               B.charUtf8 '='        <>+               B.stringUtf8 (show l) <>+               B.charUtf8 '\n'+
src/Text/Playlist/Types.hs view
@@ -22,8 +22,9 @@ -------------------------------------------------------------------------------- -- | A single music file or streaming URL. data Track = Track-  { trackURL   :: Text       -- ^ URL for a file or streaming resource.-  , trackTitle :: Maybe Text -- ^ Optional title.+  { trackURL      :: Text        -- ^ URL for a file or streaming resource.+  , trackTitle    :: Maybe Text  -- ^ Optional title.+  , trackDuration :: Maybe Float -- ^ Optional duration in seconds.   } deriving (Show, Eq)  --------------------------------------------------------------------------------
test/Examples.hs view
@@ -29,14 +29,17 @@ secretAgent =   [ Track { trackURL   = "http://mp2.somafm.com:9016"           , trackTitle = Just "SomaFM: Secret Agent (#1 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!"+          , trackDuration = Just (-1)           }    , Track { trackURL   = "http://mp3.somafm.com:443"           , trackTitle = Just "SomaFM: Secret Agent (#2 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!"+          , trackDuration = Just (-1)           }    , Track { trackURL   = "http://ice.somafm.com/secretagent"           , trackTitle = Just "SomaFM: Secret Agent (Firewall-friendly 128k mp3) The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!"+          , trackDuration = Just (-1)           }   ] @@ -45,10 +48,12 @@ pigRadio =   [ Track { trackURL   = "http://s6.mediastreaming.it:8080"           , trackTitle = Just "Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7"+          , trackDuration = Just 0           }    , Track { trackURL   = "http://s1.viastreaming.net:7480"           , trackTitle = Just "Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7"+          , trackDuration = Just (-1)           }   ] @@ -57,6 +62,7 @@ utf8Radio =   [ Track { trackURL   = "http://fake.com"           , trackTitle = Just "Alle otto i bambini erano già in costume da bagno"+          , trackDuration = Nothing           }   ] @@ -65,17 +71,19 @@ hitParty =   [ Track { trackURL   = "http://firewall.hitparty.net:443"           , trackTitle = Just "(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits"+          , trackDuration = Just (-1)           }   , Track { trackURL   = "http://icecast.pulsradio.com:80/hitpartyHD.mp3.m3u"           , trackTitle = Just "(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits"+          , trackDuration = Just (-1)           }   ]  -------------------------------------------------------------------------------- aaFile :: Playlist aaFile =-  [ Track { trackURL = "http://foo.com", trackTitle = Nothing}-  , Track { trackURL = "http://bar.com", trackTitle = Nothing}+  [ Track { trackURL = "http://foo.com", trackTitle = Nothing, trackDuration = Nothing}+  , Track { trackURL = "http://bar.com", trackTitle = Nothing, trackDuration = Nothing}   ]  --------------------------------------------------------------------------------
test/ResolveSpec.hs view
@@ -42,7 +42,7 @@  -------------------------------------------------------------------------------- resolved :: Playlist-resolved = [ Track "http://foo.com"             Nothing-           , Track "http://bar.com"             Nothing-           , Track "http://foo.com/aa.baz.live" Nothing+resolved = [ Track "http://foo.com"             Nothing Nothing+           , Track "http://bar.com"             Nothing Nothing+           , Track "http://foo.com/aa.baz.live" Nothing Nothing            ]