8 STRING
= "('([^']|\\')*'|\"([^\"]|\\\")*\"|("+NONWS
+'+))'
10 FILE_FORMAT
= '(BINARY|MOTOROLA|AIFF|WAVE|MP3)'
11 TRACK_MODE
= '(AUDIO|MODE1_2048|MODE1_2352|MODE2_2048|MODE2_2332|MODE2_2336|MODE2_2342|MODE2_2352)'
12 TRACK_FLAG
= '(PRE|DCP|FOUR_CH|SCMS)'
13 TRACK_FLAGS
= '('+TRACK_FLAG
+WS
+')*'+TRACK_FLAG
14 TIME
= '('+NUMBER
+':'+NUMBER
+':'+NUMBER
+')'
16 REM
= re
.compile(LB
+'REM.*$')
17 EMPTY
= re
.compile('^'+WS
+'*$')
18 CATALOG
= re
.compile(LB
+'CATALOG' +WS
+STRING
+LE
)
19 CDTEXTFILE
= re
.compile(LB
+'CDTEXTFILE'+WS
+STRING
+LE
)
21 TITLE
= re
.compile(LB
+'TITLE' +WS
+STRING
+LE
)
22 PERFORMER
= re
.compile(LB
+'PERFORMER' +WS
+STRING
+LE
)
23 SONGWRITER
= re
.compile(LB
+'SONGWRITER'+WS
+STRING
+LE
)
24 COMPOSER
= re
.compile(LB
+'COMPOSER' +WS
+STRING
+LE
)
25 ARRANGER
= re
.compile(LB
+'ARRANGER' +WS
+STRING
+LE
)
26 MESSAGE
= re
.compile(LB
+'MESSAGE' +WS
+STRING
+LE
)
27 DISC_ID
= re
.compile(LB
+'DISC_ID' +WS
+STRING
+LE
)
28 GENRE
= re
.compile(LB
+'GENRE' +WS
+STRING
+LE
)
29 TOC_INFO1
= re
.compile(LB
+'TOC_INFO1' +WS
+STRING
+LE
)
30 TOC_INFO2
= re
.compile(LB
+'TOC_INFO2' +WS
+STRING
+LE
)
31 UPC_EAN
= re
.compile(LB
+'UPC_EAN' +WS
+STRING
+LE
)
32 ISRC
= re
.compile(LB
+'ISRC' +WS
+STRING
+LE
)
33 SIZE_INFO
= re
.compile(LB
+'SIZE_INFO' +WS
+STRING
+LE
)
35 FILE
= re
.compile(LB
+'FILE'+WS
+STRING
+WS
+FILE_FORMAT
+LE
)
37 TRACK
= re
.compile(LB
+'TRACK'+WS
+NUMBER
+WS
+TRACK_MODE
+LE
)
38 FLAGS
= re
.compile(LB
+'FLAGS'+WS
+TRACK_FLAGS
+LE
)
39 TRACK_ISRC
= re
.compile(LB
+'TRACK_ISRC'+WS
+STRING
+LE
)
40 PREGAP
= re
.compile(LB
+'PREGAP'+WS
+TIME
+LE
)
41 INDEX
= re
.compile(LB
+'INDEX'+WS
+NUMBER
+WS
+TIME
+LE
)
42 POSTGAP
= re
.compile(LB
+'POSTGAP'+WS
+TIME
+LE
)
45 def match(self
, cre
, s
):
53 def __init__(self
, m
, s
, f
):
58 return minutes
== 0 and seconds
== 0 and frames
== 0
60 return '%02d:%02d:%02d' % (self
.minutes
, self
.seconds
, self
.frames
)
78 def __to_string_list(self
):
81 ss
.append('TITLE "%s"' % self
.title
)
83 ss
.append('PERFORMER "%s"' % self
.performer
)
85 ss
.append('SONGWRITER "%s"' % self
.songwriter
)
87 ss
.append('COMPOSER "%s"' % self
.composer
)
89 ss
.append('ARRANGER "%s"' % self
.arranger
)
91 ss
.append('MESSAGE "%s"' % self
.message
)
93 ss
.append('DISC_ID "%s"' % self
.disc_id
)
95 ss
.append('GENRE "%s"' % self
.genre
)
97 ss
.append('TOC_INFO1 "%s"' % self
.toc_info1
)
99 ss
.append('TOC_INFO2 "%s"' % self
.toc_info2
)
101 ss
.append('UPC_EAN "%s"' % self
.upc_ean
)
103 ss
.append('ISRC "%s"' % self
.isrc
)
105 ss
.append('SIZE_INFO "%s"' % self
.size_info
)
106 if self
.file and self
.file_format
:
107 ss
.append('FILE "%s" %s' % (self
.file, self
.file_format
))
110 class CueTrack(CueCommonData
):
112 pregap
= CueTime(0,0,0)
113 postgap
= CueTime(0,0,0)
116 def __init__(self
, num
, mode
):
119 def add_flag(self
, flag
):
120 self
.flags
.append(flag
)
121 def add_index(self
, num
, time
):
122 self
.indexes
.append((num
, time
))
123 def __to_string_list(self
):
125 ss
.append('TRACK %02d %s' % (self
.num
, self
.mode
))
127 ss
.append('TRACK_ISRC "%s"' % self
.track_isrc
)
128 if not self
.pregap
.is_zero():
129 ss
.append('PREGAP '+str(self
.pregap
))
130 if not self
.postgap
.is_zero():
131 ss
.append('POSTGAP '+str(self
.postgap
))
133 ss
.append('FLAGS '+self
.flags
.join(' '))
135 for index
in self
.indexes
:
136 ss
.append('INDEX %02d %s' % (index
[0], str(index
[1])))
137 ss
= ss
+ self
.CueCommonData
.__to
_string
_list
()
140 class CueSheet(CueCommonData
):
144 def add_track(self
, track
):
145 self
.tracks
.append(track
)
146 def to_string(self
, sep
):
149 ss
.append('CATALOG '+self
.catalog
)
151 ss
.append('CDTEXTFILE '+self
.cdtextfile
)
152 ss
= ss
+ self
.__to
_string
_list
('')
154 ss
= ss
+ track
.__to
_string
_list
()
157 def match_cdtext(s
, o
):
159 if u
.match(TITLE
, s
):
160 o
.title
= u
.m
.group(1)
161 elif u
.match(PERFORMER
, s
):
162 o
.performer
= u
.m
.group(1)
163 elif u
.match(SONGWRITER
, s
):
164 o
.songwriter
= u
.m
.group(1)
165 elif u
.match(COMPOSER
, s
):
166 o
.composer
= u
.m
.group(1)
167 elif u
.match(ARRANGER
, s
):
168 o
.arranger
= u
.m
.group(1)
169 elif u
.match(MESSAGE
, s
):
170 o
.message
= u
.m
.group(1)
171 elif u
.match(DISC_ID
, s
):
172 o
.disc_id
= u
.m
.group(1)
173 elif u
.match(GENRE
, s
):
174 o
.genre
= u
.m
.group(1)
175 elif u
.match(TOC_INFO1
, s
):
176 o
.toc_info1
= u
.m
.group(1)
177 elif u
.match(TOC_INFO2
, s
):
178 o
.toc_info2
= u
.m
.group(1)
179 elif u
.match(UPC_EAN
, s
):
180 o
.upc_ean
= u
.m
.group(1)
181 elif u
.match(ISRC
, s
):
182 o
.isrc
= u
.m
.group(1)
183 elif u
.match(SIZE_INFO
, s
):
184 o
.size_info
= u
.m
.group(1)
189 def match_file(s
, o
):
192 o
.file = u
.m
.group(1)
193 o
.file_format
= u
.m
.group(2)
198 def parse_string_list(slist
):
204 if u
.match(CATALOG
, s
):
205 sheet
.catalog
= u
.m
.group(1)
206 elif u
.match(CDTEXTFILE
, s
):
207 sheet
.cdtextfile
= u
.m
.group(1)
208 elif match_cdtext(s
, sheet
):
210 elif match_file(s
, sheet
):
212 elif u
.match(TRACK
, s
):
214 track
= CueTrack(int(u
.m
.group(1)), u
.m
.group(2))
215 elif u
.match(REM
, s
):
217 elif u
.match(EMPTY
, s
):
220 print 'global error at %s' % s
222 if u
.match(TRACK
, s
):
223 sheet
.add_track(track
)
224 track
= CueTrack(int(u
.m
.group(1)), u
.m
.group(2))
225 elif match_cdtext(s
, track
):
227 elif u
.match(FLAGS
, s
):
228 track
.add_flag(u
.m
.group(1))
229 elif u
.match(TRACK_ISRC
, s
):
230 track
.track_isrc
= u
.m
.group(1)
231 elif u
.match(PREGAP
, s
):
232 track
.pregap(CueTime(u
.m
.group(1)))
233 elif u
.match(INDEX
, s
):
234 time
= CueTime(int(u
.m
.group(3)),int(u
.m
.group(4)),int(u
.m
.group(5)))
235 track
.add_index(int(u
.m
.group(1)), time
)
236 elif u
.match(POSTGAP
, s
):
237 track
.postgap
= CueTime(u
.m
.group(1))
238 elif match_file(s
, track
):
240 elif u
.match(REM
, s
):
242 elif u
.match(EMPTY
, s
):
245 print 'track error at %s' % s
248 def parse_file(filename
,encoding
):
249 f
= open(filename
, 'r')
251 udata
= unicode(data
, encoding
)
252 return parse_string_list(re
.split('\r|\n', udata
))