-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathMain.hs
207 lines (184 loc) · 7.2 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import Control.DeepSeq
import Control.Monad
import Data.Binary
import Data.Bits
import Data.String
import Data.Version
import Data.Monoid
import Network.HTTP
import Options.Applicative
import System.Environment
import System.Exit
import System.IO
import System.Process
import CD.AccurateRip
import CD.CDDB
import Paths_flaccuraterip
progName :: String
progName = "flAccurateRip"
intro :: String
intro = progName ++ " " ++ showVersion version ++
"\n\
\Copyright (C) 2012-2016 Nicola Squartini.\n\
\License GPLv3+: GNU GPL version 3 or \
\later <http://gnu.org/licenses/gpl.html>\n\
\This is free software: you are free to change and redistribute it.\n\
\There is NO WARRANTY, to the extent permitted by law."
bugReport :: String
bugReport = "Report bugs to: <https://github.com/tensor5/flAccurateRip/issues>"
data Options = Options
{ opt30Samples :: Bool
, optOffset :: Int
, optShowEntry :: Bool
, optVerbose :: Bool
, optFiles :: [String]
}
options :: Parser Options
options = Options
<$> switch (short 'c'
<> long "with-30-samples-correction"
<> help "Add 30 samples to the offset (use if the CD was \
\ripped using the correct offset, which is 30 \
\samples less than that in \
\<http://www.accuraterip.com/driveoffsets.htm>)"
)
<*> option auto (short 'o'
<> long "with-sample-offset"
<> metavar "N"
<> value 0
<> help "Set ripping offset to N"
)
<*> switch (short 's'
<> long "show-database-entry"
<> help "Show the AccurateRip database entry for this rip"
)
<*> switch (short 'v'
<> long "verbose"
<> help "Be verbose"
)
<*> some (argument str $
metavar "track01.flac track02.flac ... trackNN.flac"
)
versionOption :: Parser (a -> a)
versionOption = infoOption intro (long "version"
<> help "Show version information and exit")
lengthsToOffsets :: Num a => [a] -> [a]
lengthsToOffsets = scanl1 (+)
offsetsToLengths :: Num a => [a] -> [a]
offsetsToLengths [] = []
offsetsToLengths [x] = [x]
offsetsToLengths xs = offsetsToLengths i ++ [last xs - last i]
where i = init xs
getFlacSampleNumber :: FilePath -> IO Int
getFlacSampleNumber file = do
(_,b, _) <- readProcessWithExitCode
"metaflac"
["--show-total-samples", file]
""
return (read b)
getOffsetsFromFlacs :: [FilePath] -> IO [Int]
getOffsetsFromFlacs [file] = do
(a,_,_) <- readProcessWithExitCode
"metaflac"
["--export-cuesheet-to=-", file]
""
if a == ExitSuccess
then do
prog <- getProgName
putStrLn (prog ++ ": sorry, rips to single file are not supported yet")
exitFailure
else do
l <- getFlacSampleNumber file
return [quot l samplesPerSector]
getOffsetsFromFlacs filelist = do
s <- mapM getFlacSampleNumber filelist
return $ lengthsToOffsets (map (`quot` samplesPerSector) s)
stringToWord32List :: String -> [Word32]
stringToWord32List [] = []
stringToWord32List (x1:x2:x3:x4:xs) =
fourCharToWord32 x1 x2 x3 x4 : stringToWord32List xs
where fourCharToWord32 c1 c2 c3 c4 =
fromIntegral (fromEnum c1) +
fromIntegral (fromEnum c2) `shiftL` 8 +
fromIntegral (fromEnum c3) `shiftL` 16 +
fromIntegral (fromEnum c4) `shiftL` 24
stringToWord32List _ = error "stringToWord32List: length of String should be \
\divisible by 4."
pipeFlacs :: [FilePath] -> IO Handle
pipeFlacs xs = do
(_,Just b,_,_) <- createProcess (CreateProcess
(RawCommand "flac" ("-s":"-c":"-d":
"--force-raw-format":
"--endian=little":
"--sign=signed":xs))
Nothing
Nothing
Inherit
CreatePipe
Inherit
True
True
False
False
False
False
Nothing
Nothing
False)
return b
showAccuracy :: ArData -> RipHash -> IO ()
showAccuracy ar hash = showRes (verifyTracks ar hash) (ripCrc hash)
showRes :: [(Word,Word)] -> [(CRC,CRC)] -> IO ()
showRes x y = do
putStrLn "Track No.\tAccuracy (v.1 sum)\tAccuracy (v.2 sum)\n"
sR (1::Int) x y
where sR _ [] _ = return ()
sR _ _ [] = return ()
sR i ((c1,c2):cs) ((s1,s2):ss) = do
putStrLn ("Track " ++ show i ++ ":\t" ++
show c1 ++ " (" ++ show s1 ++ ")\t\t" ++
show c2 ++ " (" ++ show s2 ++ ")")
sR (i+1) cs ss
main :: IO ()
main = do
opts <- execParser $ info (helper <*> versionOption <*> options)
(fullDesc
<> progDesc "Verify the given FLAC files"
<> footer bugReport)
let flaclist = optFiles opts
verbose = optVerbose opts
list <- getOffsetsFromFlacs flaclist
putStr "Connecting to AccurateRip database"
when verbose $ putStr (" (<" ++ arUrl list ++ ">)")
putStr "..."
hFlush stdout
res <- simpleHTTP (getRequest $ arUrl list)
case res of
Right (Response (4,0,4) _ _ _) -> do
putStrLn " no pressing found."
exitFailure
_ -> do
ardatastr <- getResponseBody res
putStrLn " OK"
let ardata = decode $ fromString ardatastr
when (optShowEntry opts) $ do
let n = length $ unArData ardata
if n == 1
then do putStrLn "Found 1 pressing:\n"
putStrLn $ showArData ardata
else do putStrLn ("Found " ++ show n ++ " pressings:\n")
putStrLn $ showArData ardata
let offset = optOffset opts + 30 * fromEnum (opt30Samples opts)
putStr "Computing track checksums from input files"
when verbose $ putStr (" (using " ++ show offset ++ " samples offset)")
putStrLn "..."
a <- pipeFlacs flaclist
hSetBinaryMode a True
fl <- hGetContents a
let cs = listPairCrcs offset
(map (\n -> samplesPerSector * n) (offsetsToLengths list))
(stringToWord32List fl)
(showAccuracy ardata .
RipHash (discId1 list) (discId2 list) (cddbDiscId list))
$!! cs
hClose a