idiii 0.0 → 0.1
raw patch · 13 files changed
+229/−175 lines, 13 filesdep +textdep −encodingnew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies added: text
Dependencies removed: encoding
API changes (from Hackage documentation)
- ID3.Parser.Frame: frameHeader :: FrameID -> TagParser FrameHeader
- ID3.Parser.Frame: parseFrame :: FrameID -> TagParser ID3Frame
- ID3.Parser.General: curEncoding :: St -> Integer
- ID3.Parser.General: encGet :: TagParser Integer
- ID3.Parser.General: encRead :: TagParser Integer
- ID3.Parser.General: encSet :: Integer -> TagParser ()
- ID3.Parser.General: encUpdate :: (Integer -> Integer) -> TagParser ()
- ID3.Parser.General: formValues :: [String] -> TagParser [(String, String)]
- ID3.Parser.General: parseSize_ :: Integer -> TagParser Size
- ID3.Parser.General: parseValue :: TagParser [Token]
- ID3.Parser.General: parseValues :: (Num n) => n -> TagParser [[Token]]
- ID3.Parser.General: terminator :: TagParser [Token]
- ID3.Parser.Tag: ID3Tag :: ID3Header -> Maybe ID3ExtHeader -> Map FrameID ID3Frame -> [FrameID] -> Integer -> ID3Tag
- ID3.Parser.Tag: data ID3Tag
- ID3.Parser.Tag: run :: TagParser a -> [Word8] -> (Either String a, [Token])
- ID3.Parser.Tag: tagExtHeader :: ID3Tag -> Maybe ID3ExtHeader
- ID3.Parser.Tag: tagFrames :: ID3Tag -> Map FrameID ID3Frame
- ID3.Parser.Tag: tagFramesOrder :: ID3Tag -> [FrameID]
- ID3.Parser.Tag: tagHeader :: ID3Tag -> ID3Header
- ID3.Parser.Tag: tagPadding :: ID3Tag -> Integer
+ ID3.Parser.General: flagsGet :: TagParser Flags
+ ID3.Parser.General: flagsSet :: Flags -> TagParser ()
+ ID3.Parser.General: headerFlags :: St -> Flags
+ ID3.Parser.General: id3TagVersion :: St -> TagVersion
+ ID3.Parser.General: parseEncoding :: TagParser CharEncoding
+ ID3.Parser.General: parseSize :: Integer -> Bool -> TagParser Size
+ ID3.Parser.General: parseUntilWord16Null :: TagParser [Token]
+ ID3.Parser.General: parseUntilWord8Null :: TagParser [Token]
+ ID3.Parser.General: tagVersionGet :: TagParser TagVersion
+ ID3.Parser.General: tagVersionSet :: TagVersion -> TagParser ()
+ ID3.Parser.General: type CharEncoding = Integer
+ ID3.Type.Flags: instance Show Flags
+ ID3.Type.FrameInfo: ownerId :: FrameInfo -> String
+ ID3.Type.FrameInfo: privateData :: FrameInfo -> [Word8]
+ ID3.Type.FrameInfo: tocData :: FrameInfo -> [Word8]
- ID3.Parser.Frame: anyFrame :: TagParser ID3Frame
+ ID3.Parser.Frame: anyFrame :: TagParser (Either String ID3Frame)
- ID3.Parser.Frame: anyFrameHeader :: TagParser FrameHeader
+ ID3.Parser.Frame: anyFrameHeader :: TagParser (Either String FrameHeader)
- ID3.Parser.General: State :: Integer -> Integer -> Integer -> St
+ ID3.Parser.General: State :: TagVersion -> Flags -> Integer -> Integer -> St
- ID3.Parser.General: count :: (Num n) => n -> TagParser a -> TagParser [a]
+ ID3.Parser.General: count :: Num n => n -> TagParser a -> TagParser [a]
- ID3.Parser.General: count' :: (Num n) => n -> TagParser a -> TagParser [a]
+ ID3.Parser.General: count' :: Num n => n -> TagParser a -> TagParser [a]
- ID3.Parser.General: countSepBy' :: (Num n) => n -> TagParser a -> TagParser sep -> TagParser [a]
+ ID3.Parser.General: countSepBy' :: Num n => n -> TagParser a -> TagParser sep -> TagParser [a]
- ID3.Parser.General: encPack :: Integer -> [Token] -> String
+ ID3.Parser.General: encPack :: CharEncoding -> [Token] -> String
- ID3.Parser.General: parseString :: TagParser String
+ ID3.Parser.General: parseString :: CharEncoding -> TagParser String
- ID3.Type.FrameInfo: MCDI :: FrameInfo
+ ID3.Type.FrameInfo: MCDI :: [Word8] -> FrameInfo
- ID3.Type.FrameInfo: PRIV :: FrameInfo
+ ID3.Type.FrameInfo: PRIV :: String -> [Word8] -> FrameInfo
- ID3.Type.Header: type TagVersion = [Word8]
+ ID3.Type.Header: type TagVersion = (Word8, Word8)
- ID3.Type.Unparse: size :: (HasSize a) => Accessor a Integer
+ ID3.Type.Unparse: size :: HasSize a => Accessor a Integer
- ID3.Type.Unparse: unparse :: (Parsed a) => a -> [Word8]
+ ID3.Type.Unparse: unparse :: Parsed a => a -> [Word8]
- ID3.Type.Unparse: updateSize :: (HasSize a) => a -> a
+ ID3.Type.Unparse: updateSize :: HasSize a => a -> a
Files
- idiii.cabal +6/−6
- read-idiii.hs +18/−0
- src/ID3/Parser/ExtHeader.hs +2/−3
- src/ID3/Parser/Frame.hs +24/−24
- src/ID3/Parser/General.hs +73/−70
- src/ID3/Parser/Header.hs +7/−5
- src/ID3/Parser/NativeFrames.hs +74/−34
- src/ID3/Parser/Tag.hs +3/−10
- src/ID3/ReadTag.hs +3/−4
- src/ID3/Type/Flags.hs +1/−1
- src/ID3/Type/Frame.hs +3/−3
- src/ID3/Type/FrameInfo.hs +5/−4
- src/ID3/Type/Header.hs +10/−11
idiii.cabal view
@@ -1,15 +1,16 @@ Name: idiii-Version: 0.0-Synopsis: ID3v2 tag editing-suite-Description: ID3v2 tag editing-suite+Version: 0.1+Synopsis: Reading and writing of ID3v2 tags+Description: Reading and writing of ID3v2 tags Category: Text, Sound License: BSD3 License-file: LICENSE Author: laughedelic-Maintainer: laughedelic@gmail.com-Build-Depends: base < 5, polyparse, haskell98, bytestring, encoding, data-accessor, utf8-string, containers, MissingH+Maintainer: christopher.t.wagner@gmail.com+Build-Depends: base < 5, haskell98, bytestring, polyparse, text, data-accessor, utf8-string, containers, MissingH Build-Type: Simple hs-source-dirs: src+extra-source-files: LICENSE read-idiii.hs Exposed-modules: ID3, ID3.Simple, @@ -34,4 +35,3 @@ ID3.ReadTag, ID3.WriteTag ghc-options: -Wall-
+ read-idiii.hs view
@@ -0,0 +1,18 @@+module Main where++import ID3+import qualified Data.Map as Map+import System (getArgs)++main = do+ args <- getArgs+ let f = head args+ maybeTag <- readTag f+ case maybeTag of+ Nothing -> putStrLn "File has no tag."+ Just tag -> do+ putStrLn $ "Here's the header: " ++ (show $ tagHeader tag)+ putStrLn $ "Here are the frames: "+ let lines = Map.elems $ Map.mapWithKey (\key val -> " " ++ (show key) ++ ": " ++ (show $ frInfo_ val))+ (tagFrames tag)+ mapM_ putStrLn lines
src/ID3/Parser/ExtHeader.hs view
@@ -14,12 +14,11 @@ ---- - -- | Parses Extended Header as 'ID3ExtHeader' structure parseExtHeader :: TagParser ID3ExtHeader parseExtHeader = do- s <- parseSize_ 4 `err` "ext header size" -- Extended header size 4 * %0xxxxxxx- word8 0x01 `err` "ext header pre-flags" -- Number of flag bytes $01+ s <- parseSize 4 True `err` "ext header size" -- Extended header size 4 * %0xxxxxxx+ word8 0x01 `err` "ext header pre-flags" -- Number of flag bytes $01 [b,c,d] <- parseFlags_ [2,3,4] `err` "ext header flags" -- Extended Flags %0bcd0000 bb <- if b then parseB else return False cc <- if c then parseC else return []
src/ID3/Parser/Frame.hs view
@@ -9,8 +9,10 @@ import ID3.Parser.NativeFrames import ID3.Type.Frame import ID3.Type.Flags+import ID3.Type.Header (unsynch) import Data.Accessor+import Data.Either (rights) import Data.Map (Map) import qualified Data.Map as Map @@ -20,39 +22,31 @@ parseFrames :: TagParser ([FrameID], Map FrameID ID3Frame) parseFrames = do- frames <- many1 anyFrame+ framesAndErrs <- many1 anyFrame+ let frames = rights framesAndErrs let idList = map (\f -> f^.frHeader^.frID) frames return ( idList , Map.fromList (zip idList frames) ) -- | Parses any Frame Header-anyFrameHeader :: TagParser FrameHeader+anyFrameHeader :: TagParser (Either String FrameHeader) anyFrameHeader = do i <- frameID `err` "frame id" s <- frameSize `err` "frame size"- f <- frameFlags `err` "frame flags"- return $ initFrameHeader [frID^=i, frSize^=s, frFlags^=f]+ if s > 500000+ then return $ Left $ "Frame of type " ++ (show i) ++ " is too big!"+ else do+ f <- frameFlags `err` "frame flags"+ return $ Right $ initFrameHeader [frID^=i, frSize^=s, frFlags^=f] -- | Parses any Frame-anyFrame :: TagParser ID3Frame+anyFrame :: TagParser (Either String ID3Frame) anyFrame = do- h <- anyFrameHeader `err` "frame header"- i <- frameInfo (h^.frID) `err` "frame info"- return $ initID3Frame [frHeader^=h, frInfo^=i]---- | Parses Frame Header with given id-frameHeader :: FrameID -> TagParser FrameHeader-frameHeader i = do- string i- s <- frameSize- f <- frameFlags- return $ initFrameHeader [frID^=i, frSize^=s, frFlags^=f]---- | Parses Frame with given id-parseFrame :: FrameID -> TagParser ID3Frame-parseFrame id = do- h <- frameHeader id- i <- frameInfo id- return $ initID3Frame [frHeader^=h, frInfo^=i]+ hdr <- anyFrameHeader `err` "frame header"+ case hdr of+ Left e -> return $ Left e+ Right h -> do+ i <- frameInfo (h^.frID) `err` "frame info"+ return $ Right $ initID3Frame [frHeader^=h, frInfo^=i] frameID :: TagParser FrameID frameID = do@@ -61,7 +55,13 @@ return $ C.unpack $ BS.pack name frameSize :: TagParser FrameSize-frameSize = parseSize_ 4+frameSize = do+ version <- tagVersionGet+ let (majorVersion, _) = version+ flags <- flagsGet+ let doDecoding = (majorVersion >= 4 || flags^.unsynch)+ size <- parseSize 4 doDecoding+ return size frameFlags = do s <- frameStatusFlags `err` "status flags"
src/ID3/Parser/General.hs view
@@ -2,19 +2,23 @@ module ID3.Parser.General where ---- IMPORTS-import Text.ParserCombinators.Poly.State+import Text.ParserCombinators.Poly.State (Parser, runParser, stGet, stUpdate, onFail, next,+ satisfy, adjustErr, exactly) import ID3.Parser.UnSync-import qualified Data.ByteString.Lazy as BS-import qualified Data.ByteString.Lazy.Char8 as C+import ID3.Type.Header (TagVersion, unsynch)+import ID3.Type.Flags (Flags(..))+--import qualified Data.ByteString.Lazy as BS+--import qualified Data.ByteString.Lazy.Char8 as C+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as C import Data.Word (Word8) import Bits import Data.Accessor-import Data.Encoding-import Data.Encoding.ISO88591-import Data.Encoding.UTF8-import Data.Encoding.UTF16+import qualified Data.Text as Text+import Data.Text.Encoding (decodeASCII, decodeUtf16LE, decodeUtf16BE, decodeUtf8) import Data.ByteString.Lazy.UTF8 (toString) import Codec.Binary.UTF8.String as Codec+import Control.Monad (when) ---- ---,--------------------------------@@ -22,26 +26,41 @@ type Token = Word8 -- | Parsers state-data St = State { tagPos :: Integer -- ^ current position in tag--- , headerFlags :: [Bool] -- ^+data St = State { id3TagVersion :: TagVersion+ , headerFlags :: Flags -- ^ flags from tag's header+ , tagPos :: Integer -- ^ current position in tag -- , frFlags :: [Bool]} -- ^ current frame flags , curSize :: Integer -- ^ current frame size- , curEncoding :: Integer -- ^ current frame encoding } instance Show St where- show st = show (tagPos st- ,curSize st- ,curEncoding st)-initState = State 0 10 0+ show st = show (tagPos st, curSize st)+initState = State (4, 0) (Flags []) 0 10 +type CharEncoding = Integer+ type TagParser = Parser St Token run :: TagParser a -> [Word8] -> (Either String a, [Token]) run p cont = (result, rest) where (result, _, rest) = runParser p initState cont + ---,--------------------------------+tagVersionGet :: TagParser TagVersion+tagVersionGet = stGet >>= return . id3TagVersion +tagVersionSet :: TagVersion -> TagParser ()+tagVersionSet v = stUpdate (\st -> st {id3TagVersion = v})++---,--------------------------------+flagsGet :: TagParser Flags+flagsGet = stGet >>= return . headerFlags++flagsSet :: Flags -> TagParser ()+flagsSet fs = stUpdate (\st -> st {headerFlags = fs})++---,--------------------------------+ -- pos = accessor (stGet >>= return . tagPos) (stUpdate $ \p st -> st {tagPos = p} ) -- | Returns 'tagPos' from 'St'.@@ -78,21 +97,6 @@ sizeInc = sizeUpdate (\x -> x+1) ---,----------------------------------- | Returns 'curEncoding' from 'St'.-encGet :: TagParser Integer-encGet = stGet >>= return . curEncoding--- | Updates 'curEncoding' with given function.-encUpdate :: (Integer -> Integer) -> TagParser ()-encUpdate f = stUpdate ( \st -> st {curEncoding = f (curEncoding st)} )--- | Sets 'curEncoding' with given value.-encSet :: Integer -> TagParser ()-encSet e = encUpdate (\_ -> e)--- | Parses one byte and sets 'curEncoding'.-encRead :: TagParser Integer-encRead = anyWord8 >>= encSet . toInteger >> encGet------,-------------------------------- -- | Wrapper for /reiterative/ parsers. -- Mnemonic: @if 'curSize' > 0 then@ continue @else@ stop ifSize :: TagParser [a] -> TagParser [a]@@ -197,46 +201,52 @@ xs <- count' (n-1) (sep >> p) return (x:xs) ----,----------------------------------- | 'terminator' parses values termination symbol, according to current encoding ('curEncoding').-terminator :: TagParser [Token]-terminator = do- enc <- encGet- case enc of- 0x01 -> word8s [00,00] -- UTF-16- 0x02 -> word8s [00,00] -- UTF-16 BOM- _ -> word8s [00] -- ISO-8859-1 or UTF-8--encPack :: Integer -> [Token] -> String-encPack 0x00 = toString . recode' ISO88591 UTF8 . BS.pack . filter (/=00)-encPack 0x01 = toString . recode' UTF16 UTF8 . BS.pack . filter (/=00)-encPack 0x02 = toString . recode' UTF16 UTF8 . BS.pack . filter (/=00)-encPack _ = Codec.decode . filter (/=00)--recode' from to = Data.Encoding.encodeLazyByteString to . Data.Encoding.decodeLazyByteString from-----,----------------------------------- | Parses a list of values (as [Token]) separated with termination symbol-parseValues :: (Num n) => n -> TagParser [[Token]]-parseValues n = countSepBy' n (many' nonNull) terminator+encPack :: CharEncoding -> [Token] -> String+encPack 0x00 s = Text.unpack $ decodeASCII $ BS.pack s+encPack 0x01 (0xFF:0xFE:s) = Text.unpack $ decodeUtf16LE $ BS.pack s+encPack 0x01 (0xFE:0xFF:s) = Text.unpack $ decodeUtf16BE $ BS.pack s+encPack 0x02 s = Text.unpack $ decodeUtf16BE $ BS.pack s+encPack _ s = Text.unpack $ decodeUtf8 $ BS.pack s -- | Parses one value (as [Token]) till termination symbol-parseValue :: TagParser [Token]-parseValue = nonNull `manyTill'` terminator+parseUntilWord8Null :: TagParser [Token]+parseUntilWord8Null = nonNull `manyTill'` (word8 0x00) +parseUntilWord16Null :: TagParser [Token]+parseUntilWord16Null = do+ s <- sizeGet+ when (s == 1) $ fail "Non-even number of bytes for UTF-16 string"+ if s > 1+ then do+ byte1 <- anyWord8+ byte2 <- anyWord8+ if byte1 == 0x00 && byte2 == 0x00+ then return []+ else do+ rest <- parseUntilWord16Null+ return $ [byte1, byte2] ++ rest+ else return []+ -- any byte except null nonNull = withSize $ satisfy (/=0x00) `adjustErr` (++"\nWTF: nonNull") +-- | Parses a character-encoding "code", a one-byte value that should be 0, 1, 2, or 3+parseEncoding :: TagParser CharEncoding+parseEncoding = anyWord8 >>= (return . toInteger)+ -- | Parses one value and returns it as a 'String'-parseString :: TagParser String-parseString = do- e <- encGet- v <- parseValue- return $ encPack e v -- C.unpack . BS.pack+parseString :: CharEncoding -> TagParser String+parseString enc = do+ v <- case enc of+ 0x01 -> parseUntilWord16Null -- UTF-16+ 0x02 -> parseUntilWord16Null -- UTF-16 BOM+ _ -> parseUntilWord8Null -- ISO-8859-1 or UTF-8+ return $ encPack enc v -- | Parses one value and returns it as a 'Integer' parseNumber :: TagParser Integer-parseNumber = parseValue >>= return . sum . (zipWith (*) (iterate (*10) 1)) . reverse . map toInteger+parseNumber = parseUntilWord8Null >>= return . sum . (zipWith (*) (iterate (*10) 1)) .+ reverse . map toInteger -- | Parses 3 bytes of language value (as a String) and returns a pair ("Language", value) parseLanguage :: TagParser String@@ -244,13 +254,6 @@ lang <- count' 3 anyWord8 return $ encPack 0x03 lang --- | Takes a list of keys and parses a list of values. Result is zip of these two lists.-formValues :: [String] -> TagParser [(String, String)]-formValues keys = do- enc <- encGet- vals <- parseValues (length keys)- return $ zip keys $ map (encPack enc) vals- --(<|>) = onFail ---,--------------------------------@@ -298,12 +301,12 @@ ---,-------------------------------- type Size = Integer--- | 'parseSize_ n' parses n bytes of synchronized size-value, and returns unsynchronized 'Integer' value.-parseSize_ :: Integer -> TagParser Size-parseSize_ n = do+-- | 'parseSize n unsynchDecode' parses n bytes, doing decoding of "unsynchronized" data when unsynchDecode is True, and returns the represented 'Integer' value.+parseSize :: Integer -> Bool -> TagParser Size+parseSize n unsynchDecode = do s <- count n next- let size = unSynchronise s posUpdate (+n)+ let size = if unsynchDecode then unSynchronise s else wordsToInteger s sizeSet size return size
src/ID3/Parser/Header.hs view
@@ -28,12 +28,14 @@ parseHeader_ = do v <- parseVersion `err` "tag version" f <- parseFlags `err` "tag flags"- s <- parseSize `err` "tag size"+ s <- parseTagSize `err` "tag size" return $ initID3Header [tagVersion^=v, tagFlags^=f, tagSize^=s]- -- i.e. return ([major version, revision number], [a, b, c, d], size) parseVersion :: TagParser TagVersion-parseVersion = count 2 anyWord8+parseVersion = do+ v1 <- anyWord8+ v2 <- anyWord8+ return $ (v1, v2) parseFlags :: TagParser TagFlags parseFlags = do@@ -41,8 +43,8 @@ return $ mkFlags [1..4] fs -- i.e. %abcd0000 -parseSize :: TagParser TagSize-parseSize = parseSize_ 4+parseTagSize :: TagParser TagSize+parseTagSize = parseSize 4 True {- | /ID3v2 FOOTER/ (optional)
src/ID3/Parser/NativeFrames.hs view
@@ -58,9 +58,9 @@ textInfo :: String -> TagParser FrameInfo textInfo _ = do- enc <- encRead- info <- parseString- return $ Text enc info --(name, [("Information", encPack enc info)])+ enc <- parseEncoding+ info <- parseString enc+ return $ Text enc info ---- {-- 4.3. URL link frames @@ -79,9 +79,8 @@ -- URL <text string> urlInfo name = do- --info <- formValues ["URL"]- url <- parseString- return $ URL url --(name, info)+ url <- parseString 0+ return $ URL url ---- {-- 4.2.1. Identification frames@@ -483,7 +482,7 @@ ---- --} frameInfo "TCMP" = do- encRead+ parseEncoding -- XXX: Is the encoding really not used here?? f <- parseNumber return $ TCMP (f == 1) @@ -501,10 +500,10 @@ -- Value <text string according to encoding> frameInfo "TXXX" = do- enc <- encRead- descr <- parseString- value <- parseString- return $ TXXX enc descr value -- (name, info)+ enc <- parseEncoding+ descr <- parseString enc+ value <- parseString enc+ return $ TXXX enc descr value ---- --} }}} @@ -570,14 +569,14 @@ -- URL <text string> frameInfo "WXXX" = do- enc <- encRead- descr <- parseString- url <- parseString- return $ WXXX enc descr url-- (name, info)+ enc <- parseEncoding+ descr <- parseString enc+ url <- parseString 0 -- URLs are always encoded in ISO-8859-1 (I think)+ return $ WXXX enc descr url ---- --} }}} ----- {-- TODO: 4.4. Music CD identifier +---- {-- 4.4. Music CD identifier -- This frame is intended for music that comes from a CD, so that the CD -- can be identified in databases such as the CDDB [CDDB]. The frame@@ -593,6 +592,12 @@ -- <Header for 'Music CD identifier', ID: "MCDI"> -- CD TOC <binary data>++frameInfo "MCDI" = do+ sz <- sizeGet+ tocData <- many' anyWord8+ return $ MCDI tocData+ ---- --} ---- {-- TODO: 4.5. Event timing codes @@ -750,11 +755,11 @@ -- Lyrics/text <full text string according to encoding> frameInfo "USLT" = do- enc <- encRead+ enc <- parseEncoding lang <- parseLanguage- descr <- parseString- text <- parseString- return $ USLT enc lang descr text-- ("Unsynchronised lyrics: "++name, lang:info )+ descr <- parseString enc+ text <- parseString enc+ return $ USLT enc lang descr text ---- --} @@ -850,12 +855,12 @@ -- The actual text <full text string according to encoding> frameInfo "COMM" = do- enc <- encRead+ enc <- parseEncoding lang <- parseLanguage- descr <- parseString- text <- parseString- many' terminator -- ???!!!!- return $ COMM enc lang descr text-- ("Comment: "++name, lang:info )+ descr <- parseString enc+ text <- parseString enc+ --many' terminator -- ???!!!!+ return $ COMM enc lang descr text ---- --} @@ -998,12 +1003,12 @@ -- Picture data <binary data> frameInfo "APIC" = do- enc <- encRead- mime <- parseString+ enc <- parseEncoding+ mime <- parseString 0 -- mimetype always encoded in ISO-8859-1 picType <- anyWord8- descr <- parseString+ descr <- parseString enc picData <- many' anyWord8- return $ APIC enc mime picType descr picData -- ("Attached picture", [])+ return $ APIC enc mime picType descr picData -- Picture type: $00 Other -- $01 32x32 pixels 'file icon' (PNG only)@@ -1089,11 +1094,11 @@ -- Counter $xx xx xx xx (xx ...) frameInfo "POPM" = do- encSet 0x00- mail <- parseString+ --encSet 0x00+ mail <- parseString 0 rate <- anyWord8 cnt <- many' anyWord8- return $ POPM mail (toInteger rate) (wordsToInteger cnt)-- ("Popularimeter: "++email, [("Rating", pack [rating]), ("Counter", pack counter)])+ return $ POPM mail (toInteger rate) (wordsToInteger cnt) ---- --} @@ -1355,7 +1360,7 @@ -- Group dependent data <binary data> ---- --} ----- {-- TODO: 4.27. Private frame+---- {-- 4.27. Private frame -- This frame is used to contain information from a software producer -- that its program uses and does not fit into the other frames. The@@ -1370,6 +1375,12 @@ -- <Header for 'Private frame', ID: "PRIV"> -- Owner identifier <text string> $00 -- The private data <binary data>++frameInfo "PRIV" = do+ ownerId <- parseString 0+ privateData <- many' anyWord8+ return $ PRIV ownerId privateData+ ---- --} ---- {-- TODO: 4.28. Signature frame@@ -1447,6 +1458,35 @@ -- Oi = (Fi/2^b)*L (rounded up to the nearest integer) ---- --} +{------------------------------------------------------------------------+ - Legacy ID3v2.3 Frames+ - The following frames are not officially supported in ID3v2.4, but we+ - will at least support reading it in, both because we want to support + - reading of ID3v2.3 tags and because some MP3/ID3 encoders seem to+ - illegally include the frames in ID3v2.4 tags.+ ------------------------------------------------------------------------}++-- TYER+-- The 'Year' frame is a numeric string with a year of the recording.+-- +frameInfo "TYER" = textInfo "Year"++-- TYER+-- The 'Date' frame is a numeric string in the DDMM format containing+-- the date for the recording.+-- This frame, like 'TYER', is also not formally supported in ID3v2.4.+frameInfo "TDAT" = textInfo "Date"++-- TSIZ+-- The 'Size' frame contains the size of the audiofile in bytes,+-- excluding the ID3v2 tag, represented as a numeric string.+frameInfo "TSIZ" = textInfo "Size of file (in bytes) excluding ID3v2 tag"++{------------------------------------------------------------------------}++-- XXX: This default implementation of frameInfo does not work correctly+-- for some reason... All frames coming after the unidentified frame+-- will fail to be read. frameInfo _ = do- cont <- parseString+ cont <- parseString 0 return $ Unknown cont
src/ID3/Parser/Tag.hs view
@@ -1,13 +1,4 @@-module ID3.Parser.Tag- (- -- * Tag type- ID3Tag(..)- -- * Tag parser- , run- , parseTag- , parseTag_- )-where+module ID3.Parser.Tag (parseTag, parseTag_) where {-- IMPORTS -} import Data.Accessor@@ -27,6 +18,8 @@ parseTag_ :: ID3Header -> TagParser ID3Tag parseTag_ h = do+ tagVersionSet (h^.tagVersion)+ flagsSet (h^.tagFlags) ext <- if (h^.tagFlags^.extended) then parseExtHeader >>= return . Just else return Nothing `err` "ext header"
src/ID3/ReadTag.hs view
@@ -1,11 +1,10 @@-module ID3.ReadTag-( hReadTag-, readTag-) where+module ID3.ReadTag (hReadTag, readTag) where import Data.Accessor+import ID3.Parser (run) import ID3.Parser.Tag import ID3.Parser.Header+import ID3.Type (ID3Tag) import ID3.Type.Header import System.IO import System.IO.Binary
src/ID3/Type/Flags.hs view
@@ -7,7 +7,7 @@ import Data.Word (Word8) import ID3.Type.Unparse -data Flags = Flags [Bool] deriving Eq+data Flags = Flags [Bool] deriving (Eq, Show) emptyFlags :: Flags emptyFlags = Flags $ replicate 8 False
src/ID3/Type/Frame.hs view
@@ -289,10 +289,11 @@ where inf = case id of "UFID" -> UFID "" "" "TXXX" -> TXXX 03 "" ""+ "TCMP" -> TCMP False ('T':_)-> Text 03 "" "WXXX" -> WXXX 03 "" "" ('W':_)-> URL ""- --MCDI -- TODO+ "MCDI" -> MCDI [] --ETCO -- { format :: Integer -- , events :: [Event]} -- TODO --MLLT -- TODO@@ -316,8 +317,7 @@ --COMR -- TODO --ENCR -- TODO --GRID -- TODO- --PRIV -- TODO+ "PRIV" -> PRIV "" [] --SIGN -- TODO --ASPI -- TODO- "TCMP" -> TCMP False _ -> Unknown ""
src/ID3/Type/FrameInfo.hs view
@@ -17,7 +17,7 @@ | WXXX { enc :: Integer , descr :: String , url :: String }- | MCDI -- TODO+ | MCDI { tocData :: [Word8] } | ETCO -- { format :: Integer -- , events :: [Event]} -- TODO | MLLT -- TODO@@ -59,7 +59,8 @@ | COMR -- TODO | ENCR -- TODO | GRID -- TODO- | PRIV -- TODO+ | PRIV { ownerId :: String+ , privateData :: [Word8] } | SIGN -- TODO | ASPI -- TODO @@ -81,7 +82,7 @@ TXXX enc descr text -> (fromInteger enc) : (encode descr) ++ [0x00] ++ (encode text) URL url -> encode url WXXX enc descr url -> (fromInteger enc) : (encode descr) ++ [0x00] ++ (encode url)- --MCDI -- TODO+ MCDI tocData -> tocData --ETCO -- { format :: Integer -- , events :: [Event]} -- TODO --MLLT -- TODO@@ -105,7 +106,7 @@ --COMR -- TODO --ENCR -- TODO --GRID -- TODO- --PRIV -- TODO+ PRIV ownerId privateData -> (encode ownerId) ++ privateData --SIGN -- TODO --ASPI -- TODO TCMP isPart -> 0x03 : (encode $ if isPart then "1" else "0")
src/ID3/Type/Header.hs view
@@ -28,12 +28,12 @@ five or higher it should simply ignore the whole tag. -} data ID3Header = ID3Header- { tagVersion_ :: TagVersion -- ^ id3v2 version: @[major version, revision number]@- , tagFlags_ :: TagFlags -- ^ header flags as Bool values- , tagSize_ :: TagSize -- ^ full size of tag- } deriving Eq+ { tagVersion_ :: TagVersion -- ^ id3v2 version: @[major version, revision number]@+ , tagFlags_ :: TagFlags -- ^ header flags as Bool values+ , tagSize_ :: TagSize -- ^ full size of tag+ } deriving Eq -emptyID3Header = ID3Header [4,0] emptyFlags 0+emptyID3Header = ID3Header (4, 0) emptyFlags 0 initID3Header = flip compose emptyID3Header tagVersion = accessor tagVersion_ (\v h -> h {tagVersion_ = v})@@ -44,18 +44,17 @@ show header = "ID3v2"++"."++(show v1)++"."++(show v2)++"\n"++ (showTagFlags $ header^.tagFlags)++ "Tag size: "++(show $ header^.tagSize )++" bytes\n"- where (v1:v2:_) = header^.tagVersion+ where (v1, v2) = header^.tagVersion instance Parsed ID3Header where- unparse h = (unparse $ Str "ID3" ) ++- ( h^.tagVersion) ++- (unparse $ h^.tagFlags ) ++- (unparse $ h^.tagSize )+ unparse h = (unparse $ Str "ID3") ++ [v1, v2] +++ (unparse $ h^.tagFlags) ++ (unparse $ h^.tagSize)+ where (v1, v2) = h^.tagVersion {- | id3v2 version @major version . revision number@ -}-type TagVersion = [Word8]+type TagVersion = (Word8, Word8) {- | /MEANING OF FLAGS/