def get_metadata(self): """ Read metadata from csv db """ with open(DB_PATH, 'r') as items: rows = csv.reader(items) for row in rows: if row[0] == self.serial: logging.debug('Item {0} found in {1}.'.format(self.serial, DB_PATH)) self.artist = row[1] self.album = row[2] self.media_type = row[3].lower() self.option = row[4].lower() self.format = row[5].lower() self.image = row[6] if row[6] else self.serial self.image_path = os.path.join(IMAGES_FOLDER, self.image) + '.jpg' self.copies = int(row[7]) if row[7] else 1 self.date_received = row[8] self.customer_notes = row[9] self.private_notes = row[10] self.split_point = int(self.private_notes[1:3]) if self.private_notes and self.private_notes[0] == '_' else None if self.split_point: if self.name[-2] is not '_': raise Exception('Error! Split file expected for {0}'.format(self.name)) self.side = self.name[-1] else: self.side = None self.tracks = [] # Deluxe / 45 stuff if 'std' not in self.option: tracks = [] for track_num in range(1, 31): if row[10 + track_num]: tracks.append(row[10 + track_num]) else: break else: logging.warning('Careful! 30+ tracks for Deluxe item {0}.'.format(self.name)) # Read track times from audio file cues = split.read_markers(self.path) if not cues: raise Exception('Error! No markers found in audio for Deluxe item {0}'.format(self.name)) if not tracks: logging.info('No tracks found in db for Deluxe item {0}.'.format(self.name)) for cue_num in range(1, len(cues)): tracks.append('Track {0}'.format(cue_num)) if self.side is '1': tracks = tracks[:self.split_point] elif self.side is '2': tracks = tracks[self.split_point:] if len(tracks) is not len(cues) - 1: raise Exception('Error! Item {0} expected {1} tracks, found {2} in audio file.'.format(self.name, len(tracks), len(cues) - 1)) Track = namedtuple('Track', ['num', 'title', 'duration']) for i in range(1, len(cues)): duration = cues[i] - cues[i - 1] m, s = divmod(duration / 44100, 60) duration = '{0}:{1}'.format(str(m), str(s).zfill(2)) self.tracks.append(Track(num=i, title=tracks[i - 1], duration=duration)) return else: logging.warning('Row {0} not found in {1}.'.format(self.serial, DB_PATH))
def split_cd(input_filename): def write_marked_file(output_filename_fullpath, audio, cues): def write_header_new(initlength): WAVE_FORMAT_PCM = 0x0001 assert not new._headerwritten new._file.write('RIFF') if not new._nframes: new._nframes = initlength / (new._nchannels * new._sampwidth) new._datalength = new._nframes * new._nchannels * new._sampwidth new._form_length_pos = new._file.tell() new._file.write(struct.pack('<L4s4sLHHLLHH', 36 + 24*len(cues) + 12 + new._datalength, 'WAVE', 'fmt ', 16, WAVE_FORMAT_PCM, new._nchannels, new._framerate, new._nchannels * new._framerate * new._sampwidth, new._nchannels * new._sampwidth, new._sampwidth * 8, )) # Cues chunk wrapper (length: 12 + 24n) new._file.write(struct.pack('<4sLL', 'cue ', #chunkID 24*len(cues) + 4, #chunkDataSize len(cues))) #cuePointsCount # Cue chunks (length: 24 each) for num, cue in enumerate(cue_offsets): new._file.write(struct.pack('<LL4sLLL', num, #cuePointID cue, #playOrderPosition (no playlist) **According to the spec, this should be 0. But who reads specs, anyway?** 'data', #dataChunkID (not silence) 0, #chunkStart (standard 'data' chunk) 0, #blockStart (uncompressed) cue)) #sampleOffset (cue position) print 'Cue', num, 'written @', cue, '/', new._nframes, '(' + str(round(cue/float(new._nframes)*100, 2)) + '%)' # Data chunk header new._data_length_pos = new._file.tell() new._file.write(struct.pack('<4sL', 'data', new._datalength)) new._headerwritten = True """ Write new file with markers """ cue_offsets = cues new = wave.open(output_filename_fullpath, 'wb') new._write_header = write_header_new new.setnchannels(audio.channels) new.setsampwidth(audio.sample_width) new.setframerate(audio.frame_rate) new.setnframes(int(audio.frame_count())) new.writeframesraw(audio._data) new.close() i = 0 cd_counter = 0 print 'input_filename:', input_filename if not out_path: output_path = input_path input_filename_fullpath = os.path.join(input_path, input_filename) output_filename_fullpath = os.path.join(output_path, input_filename) audio = pydub.AudioSegment.from_wav(input_filename_fullpath) # Check CD-type xfer (not mp3) with open(db_path, 'r') as db: rows = csv.reader(db) audio_serial = os.path.basename(input_filename_fullpath).rsplit('_clean')[0] for row in rows: if row[0] == audio_serial: # Check for CD-safe recording length (78:20) if 'cd' in row[5].lower() and len(audio) > 4700000: print '(Double!)' markers = read_markers(input_filename) if not markers: raise SystemError('Hey! {0} needs markers!'.format(audio_serial)) if not cd_counter: cd_counter = 1 # Write file for sides A and B if 'std' in row[4]: if markers[0] == 0: split_point = 1 else: split_point = 0 elif row[10].startswith('_'): split_point = int(row[10][1:3]) else: split_point = 1 for i in range(2): temp_filename = output_filename_fullpath.rsplit('_clean')[0] + '_' + str(cd_counter) + '_clean.wav' pydub_markers = [int(marker / 44.1) for marker in markers] print 'temp_filename:', temp_filename print 'markers:', markers print 'pydub_markers:', pydub_markers print 'split_point:', split_point if i == 0: split_markers = markers[:split_point + 1] split_audio = audio[:pydub_markers[split_point]] else: split_markers = [marker - markers[split_point] for marker in markers[split_point:]] split_audio = audio[pydub_markers[split_point]:] print 'split_markers:', split_markers print 'len(split_audio):', len(split_audio) write_marked_file(temp_filename, split_audio, split_markers) cd_counter += 1 # Delete big original file #os.remove(input_filename_fullpath) #os.remove(os.path.splitext(input_filename_fullpath)[0] + '.pkf') return break else: print 'Row', audio_serial, 'not found in', db_path + '.' return
def add_image(position): try: img = images_iter.next() except StopIteration: return audio_serial = img.rsplit('_')[0] audio_path = os.path.join(folder, img).rsplit('_')[0] with open(db_path, 'r') as items: # Read spreadsheet rows = csv.reader(items) for row in rows: if row[0] == audio_serial: if '45' in row[3]: # DO THE 45 THANG break else: break else: print 'Row', audio_serial, 'not found in', db_path + '.' img_path = os.path.join(images_folder,img).rsplit('_')[0] + '.jpg' # Image positioning if position == 'top': img_adjustment = 118 * mm label_adjustment = 10.2 * inch elif position == 'bottom': img_adjustment = 0 label_adjustment = 0 # Draw Ameryn logo watermark c.drawImage('Q:/Images/ameryn-logo-watermark-2.jpg', 157 * mm, 25*mm + img_adjustment, width=16 * mm, height=11 * mm) # Draw artist/album titles line2 = '' if row[1] and row[2]: line1 = row[1] line2 = row[2] spine = line1 + ' - ' + line2 elif row[1]: line1 = row[1] spine = line1 elif row[2]: line1 = row[2] spine = line1 else: line1 = 'Untitled' # Add disc # for multi-disc sets if img[-2] == '_': spine += ' (Disc ' + img[-1] + ')' if line2: line2 += ' (Disc ' + img[-1] + ')' else: line2 = 'Disc ' + img[-1] # Line 1 c.setFont('Garamond', 18) if pdfmetrics.stringWidth(line1, 'Garamond', 18) > 125 * mm: c.setFont('Garamond', 16) if pdfmetrics.stringWidth(line1, 'Garamond', 16) > 125 * mm: c.setFont('Garamond', 14) if pdfmetrics.stringWidth(line1, 'Garamond', 14) > 125 * mm: c.setFont('Garamond', 12) if pdfmetrics.stringWidth(line1, 'Garamond', 12) > 125 * mm: c.setFont('Garamond', 11) if pdfmetrics.stringWidth(line1, 'Garamond', 11) > 125 * mm: c.setFont('Garamond', 10) c.drawString(48 * mm, 125*mm + img_adjustment, line1) # Line 2 if line2: c.setFont('Garamond', 14) if pdfmetrics.stringWidth(line2, 'Garamond', 14) > 125 * mm: c.setFont('Garamond', 12) if pdfmetrics.stringWidth(line2, 'Garamond', 12) > 125 * mm: c.setFont('Garamond', 11) if pdfmetrics.stringWidth(line2, 'Garamond', 11) > 125 * mm: c.setFont('Garamond', 10) c.drawString(48 * mm, 118*mm + img_adjustment, line2) # Horizontal divider c.setLineWidth(2) c.line(48 * mm, 112*mm + img_adjustment, 168 * mm, 112*mm + img_adjustment) # Set spine label font size c.saveState() c.rotate(90) fontsize = 9 if pdfmetrics.stringWidth(spine, 'Garamond', 9) > 116 * mm: fontsize = 8 if pdfmetrics.stringWidth(spine, 'Garamond', 8) > 116 * mm: fontsize = 7 c.setFont('Garamond', fontsize) # Left spine label x = (118*mm - pdfmetrics.stringWidth(spine, 'Garamond', fontsize))/2 + 21.7*mm + img_adjustment y = -37.3 * mm c.drawString(x, y, spine) # Right spine label c.rotate(180) x = (118*mm - pdfmetrics.stringWidth(spine, 'Garamond', fontsize))/2 - 139.7*mm - img_adjustment y = 178.7 * mm c.drawString(x, y, spine) c.restoreState() # Get track durations print audio_path cues = split.read_markers(audio_path + '_clean.wav') durations = [] for i in range(1, len(cues)): duration = cues[i] - cues[i - 1] m, s = divmod(duration / 44100, 60) durations.append(str(m) + ':' + str(s).zfill(2)) print durations # Get track listing tracks = [] # No track names if not row[11]: for i in range(len(durations)): tracks.append('Track ' + str(i + 1)) else: for i in range(30): if row[11 + i]: tracks.append(row[11 + i]) else: break # Compile track data if len(tracks) != len(durations): err = 'Track/marker mismatch for ' + audio_serial + ': ' + str(len(tracks)) + ' on spreadsheet; ' + str(len(durations)) + ' on file.' raise SystemError(err) track_data = [] for i in range(len(durations)): track_data.append([(str(i + 1) + '.'), tracks[i], durations[i]]) # Draw track listing table = Table(track_data, colWidths=[6 * mm, 100 * mm, 10 * mm]) fontsize = 11 padding = 2 if len(tracks) > 14: fontsize = 10 if len(tracks) > 16: fontsize = 9 padding = 1 table.setStyle(TableStyle([ ('FONT', (0,0), (-1,-1), 'Garamond', fontsize), ('TOPPADDING', (0,0), (-1,-1), padding), ('BOTTOMPADDING', (0,0), (-1,-1), padding), ])) w, h = table.wrap(width, height) table.wrapOn(c, width, height) table.drawOn(c, 49 * mm, 108*mm - h + img_adjustment) # Draw crop lines c.setLineWidth(0.5) c.rect(33 * mm, 21.7*mm + img_adjustment, 150 * mm , 118 * mm, fill=0) c.line(39 * mm, 21.7*mm + img_adjustment, 39 * mm, 139.7*mm + img_adjustment) c.line(177 * mm, 21.7*mm + img_adjustment, 177 * mm, 139.7*mm + img_adjustment) # Draw image label c.setFont('Helvetica', 12) c.drawString(3.8 * inch, 0.3*inch + label_adjustment, img)
def get_metadata(self): """ Read metadata from csv db """ with open(DB_PATH, "r") as items: rows = csv.reader(items) for row in rows: if row[0] == self.serial: logging.debug("Item {0} found in {1}.".format(self.serial, DB_PATH)) self.artist = row[1] self.album = row[2] self.media_type = row[3].lower() self.option = row[4].lower() self.format = row[5].lower() for digital_format in DIGITAL_FORMATS: if digital_format in self.format: self.digital_ext = digital_format break else: self.digital_ext = None self.image = row[6] if row[6] else self.serial self.image_path = os.path.join(IMAGES_FOLDER, self.image) + ".jpg" self.copies = int(row[7]) if row[7] else 1 self.date_received = row[8] self.customer_notes = row[9] self.private_notes = row[10] self.split_point = ( int(self.private_notes[1:3]) if self.private_notes and self.private_notes[0] == "_" else None ) if self.split_point: if self.name[-2] is not "_": raise Exception("Error! Split file expected for {0}".format(self.name)) self.side = self.name[-1] else: self.side = None self.tracks = [] # Deluxe / 45 stuff if "std" not in self.option: tracks = [] for track_num in range(1, 31): if row[10 + track_num]: tracks.append(row[10 + track_num]) else: break else: if not self.side: logging.warning("Careful! 30+ tracks for Deluxe item {0}.".format(self.name)) # Read track times from audio file self.cues = split.read_markers(self.path) if not self.cues: raise Exception("Error! No markers found in audio for Deluxe item {0}".format(self.name)) if not tracks: logging.info("No tracks found in db for Deluxe item {0}.".format(self.name)) for cue_num in range(1, len(self.cues)): tracks.append("Track {0}".format(cue_num)) if self.side is "1": tracks = tracks[: self.split_point] elif self.side is "2": tracks = tracks[self.split_point :] if len(tracks) is not len(self.cues) - 1: raise Exception( "Error! Item {0} expected {1} tracks, found {2} in audio file.".format( self.name, len(tracks), len(self.cues) - 1 ) ) for i in range(1, len(self.cues)): duration = self.cues[i] - self.cues[i - 1] m, s = divmod(duration / 44100, 60) duration = "{0}:{1}".format(str(m), str(s).zfill(2)) self.tracks.append(Track(num=i, title=tracks[i - 1], duration=duration)) return else: logging.warning("Row {0} not found in {1}.".format(self.serial, DB_PATH))
def get_metadata(self): """ Read metadata from csv db """ with open(db_path, 'r') as items: rows = csv.reader(items) for row in rows: if row[0] == self.serial: logging.debug('Item {0} found in {1}.'.format(self.serial, db_path)) self.artist = row[1] self.album = row[2] self.media_type = row[3].lower() self.option = row[4].lower() self.format = row[5].lower() for digital_format in digital_formats: if digital_format in self.format: self.digital_ext = digital_format break else: self.digital_ext = None self.bitrate = '320k' if '320' in self.format else mp3_bitrate if row[6].lower() == 'none' and 'cd' not in self.format: self.image = None self.image_path = None self.thumb = None self.thumb_path = None else: if row[6]: self.image = row[6] else: self.image = self.serial if os.path.isfile(os.path.join(images_folder, self.image) + '.jpg'): self.image_path = os.path.join(images_folder, self.image) + '.jpg' elif os.path.isfile(os.path.join(images_folder, 'Archived cover pictures/', self.image) + '.jpg'): self.image_path = os.path.join(images_folder, 'Archived cover pictures/', self.image) + '.jpg' else: self.metadata_errors.append('Error! No image found for {0}'.format(self.name)) self.copies = int(row[7]) if row[7] else 1 self.date_received = row[8] self.customer_notes = row[9] self.private_notes = row[10] self.split_point = int(self.private_notes[1:3]) if self.private_notes and self.private_notes[0] == '_' else None # Check if long (>78m) with closing(wave.open(self.path, 'r')) as audio: self.duration = audio.getnframes() / audio.getframerate() if self.duration > 4680: self.long = True if 'std' not in self.option and not self.split_point: self.metadata_errors.append('Error! Split point needed for Tracked long item {0}'.format(self.name)) return elif 'std' in self.option: self.split_point = 1 else: self.long = False self.side = None self.tracks = [] self.compilation_counter = None self.compilation_cd_counter = None self.compilation_cds = 0 self.compilation_items = 0 self.image_counter = 0 # Tracks stuff if 'std' not in self.option or self.long: tracks = [track for track in row[12:59] if track] if not self.long and 'cd' in self.format and len(tracks) > 30: logging.warning('Careful! >30 tracks for Tracked {0} {1}.'.format(self.media_type, self.name)) # Read track times from audio file self.cues = split.read_markers(self.path) if len(self.cues) < 3 and 'std' not in self.option: self.metadata_errors.append('Error! No markers found in audio for Tracked {0} {1}'.format(self.media_type, self.name)) return elif len(self.cues) < 3 and self.long: self.metadata_errors.append('Error! No markers found in audio for long (>78m) {0} {1}'.format( self.media_type, self.name)) return if not tracks: if 'std' not in self.option: logging.info('No tracks found in db for Tracked {0} {1}.'.format(self.media_type, self.name)) for cue_num in range(1, len(self.cues)): tracks.append('Track {0}'.format(cue_num)) elif not tracks: # Long std CD tracks = ['Side A', 'Side B'] if len(tracks) is not len(self.cues) - 1: self.metadata_errors.append('Error! Item {0} expected {1} tracks, found {2} in audio file.'.format( self.name, len(tracks), len(self.cues) - 1)) return # Set file, track durations for i in range(1, len(self.cues)): duration = self.cues[i] - self.cues[i - 1] m, s = divmod(duration / 44100, 60) duration = '{0}:{1}'.format(str(m), str(s).zfill(2)) self.tracks.append(Track(num=i, title=tracks[i - 1], duration=duration, start=self.cues[i - 1], stop=self.cues[i])) return else: logging.warning('Row {0} not found in {1}.'.format(self.serial, db_path))
def get_metadata(self): """ Read metadata from csv db """ with open(db_path, 'r') as items: rows = csv.reader(items) for row in rows: if row[0] == self.serial: logging.debug('Item {0} found in {1}.'.format(self.serial, db_path)) self.artist = row[1] self.album = row[2] self.media_type = row[3].lower() self.option = row[4].lower() self.format = row[5].lower() for digital_format in digital_formats: if digital_format in self.format: self.digital_ext = digital_format break else: self.digital_ext = None self.bitrate = '320k' if '320' in self.format else mp3_bitrate if row[6].lower() == 'none' and 'cd' not in self.format: self.image = None self.image_path = None self.thumb = None self.thumb_path = None else: if row[6]: self.image = row[6] else: self.image = self.serial if os.path.isfile(os.path.join(images_folder, self.image) + '.jpg'): self.image_path = os.path.join(images_folder, self.image) + '.jpg' elif os.path.isfile(os.path.join(images_folder, 'Archived cover pictures/', self.image) + '.jpg'): self.image_path = os.path.join(images_folder, 'Archived cover pictures/', self.image) + '.jpg' else: metadata_errors.append('Error! No image found for {0}'.format(self.name)) self.copies = int(row[7]) if row[7] else 1 self.date_received = row[8] self.customer_notes = row[9] self.private_notes = row[10] self.split_point = int(self.private_notes[1:3]) if self.private_notes and self.private_notes[0] == '_' else None if self.split_point: if self.name[-2] is not '_': metadata_errors.append('Error! Split file expected for {0}'.format(self.name)) return self.side = self.name[-1] else: self.side = None self.tracks = [] self.compilation_counter = None self.compilation_cd_counter = None self.compilation_cds = 0 self.compilation_items = 0 self.image_counter = 0 # Deluxe / 45 stuff if 'std' not in self.option: tracks = [track for track in row[11:58] if track] if not self.side and 'cd' in self.format and len(tracks) > 30: logging.warning('Careful! >30 tracks for Deluxe item {0}.'.format(self.name)) # Read track times from audio file self.cues = split.read_markers(self.path) if not self.cues: metadata_errors.append('Error! No markers found in audio for Deluxe item {0}'.format(self.name)) return if not tracks: logging.info('No tracks found in db for Deluxe item {0}.'.format(self.name)) for cue_num in range(1, len(self.cues)): tracks.append('Track {0}'.format(cue_num)) if self.side is '1': tracks = tracks[:self.split_point] elif self.side is '2': tracks = tracks[self.split_point:] if len(tracks) is not len(self.cues) - 1: metadata_errors.append('Error! Item {0} expected {1} tracks, found {2} in audio file.'.format( self.name, len(tracks), len(self.cues) - 1)) return # Set file, track durations self.duration = self.cues[-1] / 44100 # seconds for i in range(1, len(self.cues)): duration = self.cues[i] - self.cues[i - 1] m, s = divmod(duration / 44100, 60) duration = '{0}:{1}'.format(str(m), str(s).zfill(2)) self.tracks.append(Track(num=i, title=tracks[i - 1], duration=duration)) return else: logging.warning('Row {0} not found in {1}.'.format(self.serial, db_path))
def get_metadata(self): """ Read metadata from csv db """ with open(config.db_path, 'r') as items: rows = csv.reader(items) for row in rows: if row[0] == self.serial: logging.debug('Item {0} found in {1}.'.format(self.serial, config.db_path)) self.artist = row[1] self.album = row[2] self.media_type = row[3].lower() self.option = row[4].lower() self.format = row[5].lower() for digital_format in config.digital_formats: if digital_format in self.format: self.digital_ext = digital_format break else: self.digital_ext = None # Bitrate if 'vbr' in self.format: self.bitrate = None self.export_parameters = ['-q:a', '2'] # LAME VBR -V2 elif '320' in self.format: self.bitrate = '320k' self.export_parameters = None elif '128' in self.format: self.bitrate = '128k' self.export_parameters = None else: self.bitrate = config.mp3_bitrate self.export_parameters = None if row[6].lower() == 'none' and 'cd' not in self.format: self.image = None self.image_path = None self.thumb = None self.thumb_path = None else: if row[6]: self.image = row[6] else: self.image = self.serial if os.path.isfile(os.path.join(config.images_folder, self.image) + '.jpg'): self.image_path = os.path.join(config.images_folder, self.image) + '.jpg' elif os.path.isfile(os.path.join(config.images_folder, 'Archived cover pictures/', self.image) + '.jpg'): self.image_path = os.path.join(config.images_folder, 'Archived cover pictures/', self.image) + '.jpg' elif '45' not in self.media_type: self.metadata_errors.append('Error! No image found for {0}'.format(self.name)) self.counter_image_path = None self.copies = int(row[7]) if row[7] else 1 self.copy_counter = None self.date_received = row[8] self.customer_notes = row[9] self.private_notes = row[10] split_points = re.search('(?:_)([0-9]+)', self.private_notes) # ex. '_9' or '_21' self.split_point = int(split_points.group(1)) if split_points else None self.side = None self.tracks = [] self.compilation_counter = None self.compilation_cd_item_counter = None self.compilation_cd_counter = None self.compilation_cds = 0 self.compilation_items = 0 self.image_counter = 0 # Tracks stuff, if files allegedly have tracks added if self.tracks_added: # Check if long (>78m) if self.split_point: self.long = True else: with closing(wave.open(self.path, 'r')) as audio: self.duration = audio.getnframes() / audio.getframerate() if self.duration > 4680 and 'cd' in self.format: self.long = True if 'std' not in self.option and not self.split_point and len([track for track in row[12:59] if track]) != 2: self.metadata_errors.append('Error! Split point needed for Tracked long item {0}'.format(self.name)) return elif not self.split_point: self.split_point = 1 else: self.long = False if 'std' not in self.option or self.long: tracks = [track for track in row[12:79] if track] if not self.long and 'cd' in self.format and len(tracks) > 30: logging.warning('Careful! >30 tracks for Tracked {0} {1}.'.format(self.media_type, self.name)) # Read track times from audio file self.cues = split.read_markers(self.path) # Remove duplicated cues (length <3s) for cue_num in range(1, len(self.cues)): if self.cues[cue_num] - self.cues[cue_num - 1] < 132300: print 'Short track (duplicate marker?) (len {0}) for item {1}'.format( self.cues[cue_num] - self.cues[cue_num - 1], self.name) if cue_num == 1: del self.cues[1] else: del self.cues[cue_num - 1] if len(self.cues) < 2 and 'std' not in self.option: self.metadata_errors.append('Error! No markers found in audio for Tracked {0} {1}'.format( self.media_type, self.name)) return elif len(self.cues) < 3 and self.long: self.metadata_errors.append('Error! No markers found in audio for long (>78m) {0} {1}'.format( self.media_type, self.name)) return if not tracks: if 'std' in self.option and len(self.cues) == 3: tracks = ['Side A', 'Side B'] else: logging.info('No tracks found in db for Tracked {0} {1}.'.format(self.media_type, self.name)) for cue_num in range(1, len(self.cues)): tracks.append('Track {0}'.format(cue_num)) if len(tracks) is not len(self.cues) - 1: self.metadata_errors.append('Error! Item {0} expected {1} tracks, found {2} in audio file.'.format( self.name, len(tracks), len(self.cues) - 1)) return # Set file, track durations; truncate filename at 190 chars for i in range(1, len(self.cues)): duration = self.cues[i] - self.cues[i - 1] m, s = divmod(duration / 44100, 60) duration = '{0}:{1}'.format(str(m), str(s).zfill(2)) self.tracks.append(Track(num=i, title=tracks[i - 1][:190], \ duration=duration, start=self.cues[i - 1], stop=self.cues[i])) return else: logging.warning('Row {0} not found in {1}.'.format(self.serial, config.db_path)) self.metadata_errors.append('Row {0} not found in {1}.'.format(self.serial, config.db_path))