packages feed

playlists 0.1.0.0 → 0.2.0.0

raw patch · 17 files changed

+435/−20 lines, 17 filesdep +doctestdep +filepathdep +hlintdep ~attoparsecdep ~basedep ~bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: doctest, filepath, hlint

Dependency ranges changed: attoparsec, base, bytestring, hspec, optparse-applicative, text, word8

API changes (from Hackage documentation)

+ Text.Playlist: appendExtension :: Format -> FilePath -> FilePath
+ Text.Playlist: fileNameToFormat :: FilePath -> Maybe Format

Files

LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2013, Peter Jones <pjones@devalot.com>+Copyright (c) 2013,2014 Peter Jones <pjones@devalot.com>  All rights reserved. 
+ README.md view
@@ -0,0 +1,42 @@+# Haskell Playlists Library and Tool++Playlists is a library for working with media playlist files.  The+original motivation for the library was extracting URLs for streaming+radio stations that use PLS and M3U playlist files.++The package also includes an executable that can dump the URLs from a+playlist file and convert between playlist file formats.++## Supported Formats++  * [PLS] []+  * [M3U and M3U8] [M3U]++## Future Plans++Some playlist files can be really big.  I plan on adding support for+incremental parsing and generating via [io-streams] [] at some point+in the future.++## Library Example++    import qualified Data.ByteString as BS+    import Text.Playlist++    readPlaylist :: Format -> IO Playlist+    readPlaylist fmt = do+      content <- BS.getContents+      case parsePlaylist fmt content of+        Left err -> fail $ "failed to parse playlist on stdin: " ++ err+        Right x  -> return x+++## Executable Example++    $ playlist urls --format PLS < somefile.pls++    $ playlist convert --from PLS --to M3U < somefile.pls++[pls]: http://en.wikipedia.org/wiki/PLS_(file_format)+[m3u]: http://en.wikipedia.org/wiki/M3U+[io-streams]: http://hackage.haskell.org/package/io-streams
playlists.cabal view
@@ -1,12 +1,12 @@ name:          playlists-version:       0.1.0.0+version:       0.2.0.0 synopsis:      Library and executable for working with playlist files. homepage:      https://github.com/pjones/playlists license:       BSD3 license-file:  LICENSE author:        Peter Jones <pjones@devalot.com> maintainer:    Peter Jones <pjones@devalot.com>-copyright:     Copyright (c) 2013 Peter Jones <pjones@devalot.com>+copyright:     Copyright (c) 2013,2014 Peter Jones <pjones@devalot.com> category:      Text build-type:    Simple cabal-version: >=1.8@@ -37,10 +37,18 @@   > $ playlist convert --from PLS --to M3U < somefile.pls  ----------------------------------------------------------------------------------- Work around a bug in Cabal < 1.16.0.3.-flag profiling-  description: Build with executable profiling enabled-  default: False+extra-source-files:+  README.md+  test/aa.m3u+  test/ab.m3u+  test/Examples.hs+  test/Helper.hs+  test/hp.m3u+  test/M3USpec.hs+  test/pig.pls+  test/PLSSpec.hs+  test/sa.pls+  test/utf8.pls  -------------------------------------------------------------------------------- source-repository head@@ -48,6 +56,23 @@   location: https://github.com/pjones/playlists.git  --------------------------------------------------------------------------------+flag maintainer+  description: Enable settings for the package maintainer.+  default: False++------------------------------------------------------------------------------+-- You can disable the hlint test suite with -f-test-hlint+flag test-hlint+  default: True+  manual: True++------------------------------------------------------------------------------+-- You can disable the doctest test suite with -f-test-doctest+flag test-doctest+  default: True+  manual: True++-------------------------------------------------------------------------------- library   exposed-modules:     Text.Playlist@@ -63,11 +88,16 @@   ghc-options: -Wall   ghc-prof-options: -fprof-auto-top   extensions: OverloadedStrings++  if flag(maintainer)+    ghc-options: -Werror+   build-depends: base       >= 4.6   && < 5-               , attoparsec >= 0.10  && < 0.11-               , bytestring >= 0.10  && < 0.11-               , text       >= 0.11  && < 0.12-               , word8      >= 0.0   && < 0.1+               , attoparsec >= 0.10  && < 1.0+               , bytestring >= 0.10  && < 1.0+               , text       >= 0.11  && < 1.2+               , word8      >= 0.0   && < 1.0+               , filepath   >= 1.3   && < 2.0  -------------------------------------------------------------------------------- executable playlist@@ -76,13 +106,12 @@   ghc-prof-options: -fprof-auto-top -fprof-cafs   extensions: OverloadedStrings -  -- Work around a bug in Cabal < 1.16.0.3-  -- if flag(profiling)-  --   ghc-options: -prof -fprof-auto-top -fprof-cafs+  if flag(maintainer)+    ghc-options: -Werror    build-depends: base                , bytestring-               , optparse-applicative >= 0.5 && < 0.6+               , optparse-applicative >= 0.5 && < 1.0                , playlists                , text @@ -95,5 +124,31 @@   main-is: Main.hs   build-depends: base                , bytestring-               , hspec >= 1.4.0 && < 1.5+               , hspec >= 1.4.0                , playlists++------------------------------------------------------------------------------+test-suite hlint+  type: exitcode-stdio-1.0+  main-is: hlint.hs+  ghc-options: -w+  hs-source-dirs: test++  if !flag(test-hlint)+    buildable: False+  else+    build-depends: base+                 , hlint >= 1.7++------------------------------------------------------------------------------+test-suite doctests+  type: exitcode-stdio-1.0+  ghc-options: -threaded+  main-is: doctest.hs+  hs-source-dirs: test++  if !flag(test-doctest)+    buildable: False+  else+    build-depends: base+                 , doctest >= 0.9.11
src/Text/Playlist.hs view
@@ -19,6 +19,8 @@          -- * Parsing and Generating        , parsePlaylist        , generatePlaylist+       , fileNameToFormat+       , appendExtension        ) where  --------------------------------------------------------------------------------@@ -26,6 +28,7 @@ import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Builder as BL+import System.FilePath (takeExtension) import qualified Text.Playlist.M3U.Reader as M3U import qualified Text.Playlist.M3U.Writer as M3U import qualified Text.Playlist.PLS.Reader as PLS@@ -52,3 +55,29 @@ generatePlaylist :: Format -> Playlist -> BL.ByteString generatePlaylist M3U = BL.toLazyByteString . M3U.writePlaylist generatePlaylist PLS = BL.toLazyByteString . PLS.writePlaylist++--------------------------------------------------------------------------------+-- | Try to figure out a file's format from it's file extension.+--+-- >>> fileNameToFormat "foo.m3u"+-- Just M3U+--+-- >>> fileNameToFormat "foo.txt"+-- Nothing+fileNameToFormat :: FilePath -> Maybe Format+fileNameToFormat ext = case takeExtension ext of+  ".m3u"  -> Just M3U+  ".m3u8" -> Just M3U+  ".pls"  -> Just PLS+  _       -> Nothing++--------------------------------------------------------------------------------+-- | Given a file name that does not have a file extension, return a+-- file name with the appropriate extension included based on the+-- given format.+--+-- >>> appendExtension M3U "foo"+-- "foo.m3u"+appendExtension :: Format -> FilePath -> FilePath+appendExtension M3U = (++ ".m3u")+appendExtension PLS = (++ ".pls")
src/Text/Playlist/PLS/Writer.hs view
@@ -25,10 +25,10 @@ -------------------------------------------------------------------------------- writePlaylist :: Playlist -> Builder writePlaylist x =-  B.byteString "[playlist]\n"            <>-  mconcat (map writeTrack $ zip x [1..]) <>-  B.byteString "NumberOfEntries="        <>-  B.stringUtf8 (show . length $ x)       <>+  B.byteString "[playlist]\n"                  <>+  mconcat (zipWith (curry writeTrack) x [1..]) <>+  B.byteString "NumberOfEntries="              <>+  B.stringUtf8 (show . length $ x)             <>   B.byteString "\nVersion=2\n"  --------------------------------------------------------------------------------
+ test/Examples.hs view
@@ -0,0 +1,81 @@+{-++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 Examples+       ( secretAgent+       , pigRadio+       , utf8Radio+       , hitParty+       , aaFile+       , abFile+       ) where++--------------------------------------------------------------------------------+import Text.Playlist++--------------------------------------------------------------------------------+secretAgent :: Playlist+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!"+          }++  , 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!"+          }++  , 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!"+          }+  ]++--------------------------------------------------------------------------------+pigRadio :: Playlist+pigRadio =+  [ Track { trackURL   = "http://s6.mediastreaming.it:8080"+          , trackTitle = Just "Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7"+          }++  , Track { trackURL   = "http://s1.viastreaming.net:7480"+          , trackTitle = Just "Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7"+          }+  ]++--------------------------------------------------------------------------------+utf8Radio :: Playlist+utf8Radio =+  [ Track { trackURL   = "http://fake.com"+          , trackTitle = Just "Alle otto i bambini erano già in costume da bagno"+          }+  ]++--------------------------------------------------------------------------------+hitParty :: Playlist+hitParty =+  [ Track { trackURL   = "http://firewall.hitparty.net:443"+          , trackTitle = Just "(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits"+          }+  , Track { trackURL   = "http://icecast.pulsradio.com:80/hitpartyHD.mp3.m3u"+          , trackTitle = Just "(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits"+          }+  ]++--------------------------------------------------------------------------------+aaFile :: Playlist+aaFile =+  [ Track { trackURL = "http://foo.com", trackTitle = Nothing}+  , Track { trackURL = "http://bar.com", trackTitle = Nothing}+  ]++--------------------------------------------------------------------------------+abFile :: Playlist+abFile = aaFile
+ test/Helper.hs view
@@ -0,0 +1,35 @@+{-++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 Helper (playlistFromFile, roundTrip) where++--------------------------------------------------------------------------------+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BL+import Data.Char (toLower)+import Text.Playlist++--------------------------------------------------------------------------------+playlistFromFile :: Format -> FilePath -> IO Playlist+playlistFromFile fmt file = do+    content <- BS.readFile file'+    case parsePlaylist fmt content of+      Left err   -> fail $ "failed to parse: " ++ file' ++ ": " ++ err+      Right plst -> return plst+  where file' = "test/" ++ file ++ "." ++ map toLower (show fmt)++--------------------------------------------------------------------------------+roundTrip :: Format -> Playlist -> IO Playlist+roundTrip fmt plst =+  case parsePlaylist fmt $ BL.toStrict $ generatePlaylist fmt plst of+    Left err    -> fail $ "failed to roundTrip playlist: " ++ err+    Right plst' -> return plst'
+ test/M3USpec.hs view
@@ -0,0 +1,38 @@+{-++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 M3USpec (spec) where++--------------------------------------------------------------------------------+import Examples (hitParty, aaFile, abFile)+import Helper (playlistFromFile, roundTrip)+import Test.Hspec+import Text.Playlist++--------------------------------------------------------------------------------+spec :: Spec+spec = do+  describe "Parsing" $ do+    it "HIT Party" $ playlistFromFile' "hp" `shouldReturn` hitParty+    it "File aa"   $ playlistFromFile' "aa" `shouldReturn` aaFile+    it "File ab"   $ playlistFromFile' "ab" `shouldReturn` abFile+  describe "Generating" $ do+    it "HIT Party" $ roundTrip' hitParty `shouldReturn` hitParty+    it "File aa"   $ roundTrip' aaFile   `shouldReturn` aaFile++--------------------------------------------------------------------------------+playlistFromFile' :: FilePath -> IO Playlist+playlistFromFile' = playlistFromFile M3U++--------------------------------------------------------------------------------+roundTrip' :: Playlist -> IO Playlist+roundTrip' = roundTrip M3U
+ test/PLSSpec.hs view
@@ -0,0 +1,39 @@+{-++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 PLSSpec (spec) where++--------------------------------------------------------------------------------+import Examples (secretAgent, pigRadio, utf8Radio)+import Helper (playlistFromFile, roundTrip)+import Test.Hspec+import Text.Playlist++--------------------------------------------------------------------------------+spec :: Spec+spec = do+  describe "Parsing" $ do+    it "Secret Agent" $ playlistFromFile' "sa"   `shouldReturn` secretAgent+    it "Pig Radio"    $ playlistFromFile' "pig"  `shouldReturn` pigRadio+    it "UTF8 Radio"   $ playlistFromFile' "utf8" `shouldReturn` utf8Radio+  describe "Generating" $ do+    it "Secret Agent" $ roundTrip' secretAgent `shouldReturn` secretAgent+    it "Pig Radio"    $ roundTrip' pigRadio    `shouldReturn` pigRadio+    it "UTF8 Radio"   $ roundTrip' utf8Radio   `shouldReturn` utf8Radio++--------------------------------------------------------------------------------+playlistFromFile' :: FilePath -> IO Playlist+playlistFromFile' = playlistFromFile PLS++--------------------------------------------------------------------------------+roundTrip' :: Playlist -> IO Playlist+roundTrip' = roundTrip PLS
+ test/aa.m3u view
@@ -0,0 +1,2 @@+http://foo.com+http://bar.com
+ test/ab.m3u view
@@ -0,0 +1,3 @@+http://foo.com+# This is a comment+http://bar.com
+ test/doctest.hs view
@@ -0,0 +1,36 @@+{-++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.++-}++--------------------------------------------------------------------------------+-- | Wrapper around doctest to make sure examples in the source code work.+module Main (main) where++--------------------------------------------------------------------------------+import Control.Applicative+import System.Environment+import Test.DocTest++--------------------------------------------------------------------------------+-- Totally stupid right now.  Will search the directory later.+files :: IO [String]+files = fmap ("playlist.hs" :) files' where+  files' = return ["src/Text/Playlist.hs"]++--------------------------------------------------------------------------------+-- | GHC flags needed by doctest.  Would be better to fetch these from+-- cabal directly though.+flags :: [String]+flags = ["-isrc", "-XOverloadedStrings"]++--------------------------------------------------------------------------------+-- | Check the docs.+main :: IO ()+main = doctest =<< (++) <$> getArgs <*> fmap (flags ++) files
+ test/hlint.hs view
@@ -0,0 +1,23 @@+-----------------------------------------------------------------------------+-- |+-- Module      :  Main (hlint)+-- Copyright   :  (C) 2013 Edward Kmett+-- License     :  BSD-style (see the file https://github.com/ekmett/succinct/blob/master/LICENSE)+-- Maintainer  :  Edward Kmett <ekmett@gmail.com>+-- Stability   :  provisional+-- Portability :  portable+--+-- This module runs HLint on the lens source tree.+-----------------------------------------------------------------------------+module Main where++import Control.Monad+import Language.Haskell.HLint+import System.Environment+import System.Exit++main :: IO ()+main = do+    args <- getArgs+    hints <- hlint $ ["src", "playlist.hs", "--cpp-define=HLINT"] ++ args+    unless (null hints) exitFailure
+ test/hp.m3u view
@@ -0,0 +1,6 @@+#EXTM3U
+#EXTINF:-1,(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits
+http://firewall.hitparty.net:443
+#EXTINF:-1,(#1 - Generique) HITPARTY HIT - Pas de pub Que du HIT - Only Hits
+http://icecast.pulsradio.com:80/hitpartyHD.mp3.m3u
+
+ test/pig.pls view
@@ -0,0 +1,11 @@+[playlist]
+File1=http://s6.mediastreaming.it:8080
+Title1=Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7
+Length1=0+File2=http://s1.viastreaming.net:7480
+Title2=Pig Radio - Devoted in Playing the Best Indie Pop/Rock & Electronic. 24/7
+Length2=-1
+
+
+NumberOfEntries=2
+Version=2
+ test/sa.pls view
@@ -0,0 +1,12 @@+[playlist]+numberofentries=3+File1=http://mp2.somafm.com:9016+Title1=SomaFM: Secret Agent (#1 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!+Length1=-1+File2=http://mp3.somafm.com:443+Title2=SomaFM: Secret Agent (#2 128k mp3): The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!+Length2=-1+File3=http://ice.somafm.com/secretagent+Title3=SomaFM: Secret Agent (Firewall-friendly 128k mp3) The soundtrack for your stylish, mysterious, dangerous life. For Spies and PIs too!+Length3=-1+Version=2
+ test/utf8.pls view
@@ -0,0 +1,3 @@+[playlist]+File1=http://fake.com+Title1=Alle otto i bambini erano già in costume da bagno