def get_drive(self, regex, line): """Get the name of the ripping drive used.""" re_drive = re.compile(fmt_ptn(self.patterns['drive']) + regex) result = re_drive.match(line) if result: return result.group(1).strip() raise UnrecognizedException('Could not parse ripping drive')
def eval_offset(log, offset): """Validate the offset used by the ripped drive.""" if not re.match('-?[0-9]+', offset): raise UnrecognizedException('Could not parse drive offset.') if not offset.startswith('-') and offset != '0': offset = '+' + offset if check_for_virtual_drives(log): log.add_deduction('Virtual drive') log.flagged = True return drivestr = prep_drive_name(log) if not drivestr: return results = drive_db_query(drivestr) if not results: # Drive not in database if offset == '0': log.add_deduction('Zero offset') log.unindexed_drive = True return offsets = {row[0] for row in results} if offset not in offsets: log.add_deduction('Drive offset', extra_phrase='correct offsets are: {}'.format( ', '.join(offsets)))
def check(self, main_log): """Checks the EAC logs.""" logs = combined.split_combined(main_log) for log in logs: if len(log.concat_contents) < 20: raise UnrecognizedException('Cannot parse log file; log file too short') log.version = self.check_version(log) log.album = log.concat_contents[2] log.drive = self.check_drive(log) self.index_log(log) self.evaluate_settings(log) parsers.index_toc(log) self.is_there_a_htoa(log) self.check_tracks(log) parsers.parse_checksum(log, self.patterns['checksum'], 'V1.0 beta 1', 'EAC <1.0') if self.markup: markup(log, self.patterns, self.translation) main_log = combined.defragment(logs) validation.validate_track_count(main_log) validation.validate_track_settings(main_log) self.deduct_and_score(main_log) return main_log
def validate_track_settings(log, xld=False): """Also verify that each track contains the required data.""" if xld: if log.range: required_settings = ['copy crc'] else: required_settings = ['filename', 'copy crc'] else: required_settings = ['filename', 'peak', 'copy crc'] for track in log.tracks: if track in log.track_errors['Aborted copy']: pass elif not all(setting in log.tracks[track] for setting in required_settings): raise UnrecognizedException( 'Unable to confirm presence of required track data') # Check for Test & Copy and CRC Mismatches if not all('test crc' in track for track in log.tracks.values()): if log.has_deduction('HTOA extracted') or log.has_deduction( 'HTOA not ripped twice'): # Verify that every track minus HTOA is T&C (Range based is index 0) if all('test crc' in track for i, track in log.tracks.items() if i): return log.add_deduction('Test & Copy')
def check(self, log): """Checks the XLD logs.""" if len(log.contents) < 25: raise UnrecognizedException('Cannot parse log file; log file too short') log.version = self.check_version(log) log.album = log.concat_contents[2] log.drive = self.check_drive(log) self.check_cdr(log) self.index_log(log) self.evaluate_settings(log) parsers.index_toc(log) self.check_tracks(log) self.is_there_a_htoa(log) validation.validate_track_count(log) validation.validate_track_settings(log, xld=True) parsers.parse_checksum( log, self.patterns['checksum'], '20121222', 'XLD pre-142.2' ) self.deduct_and_score(log) if self.markup: markup(log, self.patterns, self.translation) return log
def verify_version(self, regex, line, ripper): """Verify that the version of the log is legitimate.""" result = regex.search(line) if result: version, date = result.group(1), result.group(2) if (version, date) in VERSIONS[ripper]: return version raise UnrecognizedException('Unrecognized {} version'.format(ripper))
def validate_track_count(log): """Verify the presence of all tracks and check for a data track.""" if not log.range: # Data tracks have one extra ToC entry, but one less ripped track if max(log.tracks.keys()) + 1 == max(log.toc.keys()): log.add_deduction('Data track detected') elif len(log.tracks) != len(log.toc): if not log.ripper == 'XLD' or not log.has_deduction('HTOA extracted'): raise UnrecognizedException('Not all tracks are represented in the log')
def get_track_number(log, index, track_word): """Get the track number from the header line of a track block.""" result = re.search(r'{} ([0-9]+)'.format(fmt_ptn(track_word)), log.contents[index]) if result: return int(result.group(1)) elif log.range: # EAC range rip has no track number return 0 else: raise UnrecognizedException('A track has an invalid block header')
def get_ripper(contents): """Determine the ripper used in the log.""" if not contents: # Is file empty? raise UnrecognizedException('Empty log file') # Even foreign EAC logs start with this line. eac_regex = re.compile(r'Exact Audio Copy V[0-1]\.[0-9]+.*?from.*') xld_regex = re.compile( r'X Lossless Decoder version ([0-9abc]+) \([0-9\.]+\)') if eac_regex.match(contents[0]): return 'EAC' elif xld_regex.match(contents[0]): return 'XLD' else: # Unfortunately, not all EAC <=0.95 logs are English, so a compiled multi-language ripper # regex pattern is necessary. re_95 = re.compile('|'.join(list(EAC_RIPLINES.values()))) if re_95.match(contents[0]): return 'EAC95' else: raise UnrecognizedException('Unrecognized ripper')
def evaluate_unmatched_settings(self, log, settings): """Override super to account for burst mode not having some settings.""" burst_no_exist = ['Accurate stream', 'Audio cache', 'C2 pointers'] if log.has_deduction('Read mode'): if any(setting not in settings for setting in burst_no_exist): raise UnrecognizedException('Invalid rip settings for a burst/fast mode rip') for setting in burst_no_exist: del settings[setting] if log.has_deduction('Combined offset') and 'Drive offset' in settings: del settings['Drive offset'] super().evaluate_unmatched_settings(log, settings)
def determine_language(log): """Determine the language of the log file, and verify that it is an EAC log file.""" if log.ripper == 'XLD': return 'english' useful_contents = [ re.sub(r'\s+', ' ', l.rstrip()) for l in log.contents if l.strip() ] for line in useful_contents[:2]: for language, line_starter in EAC_RIPLINES.items(): if re.match(line_starter, line): return language raise UnrecognizedException('Unrecognized/unsupported language')
def check_cdr(self, log): """Check the log to see if CD-R is flagged.""" result = re.search( fmt_ptn(self.patterns['disc type']) + r' : (.*)', log.concat_contents[4], ) if result: if result.group(1) == 'Pressed CD': return elif result.group(1) == 'CD-Recordable': log.cdr = True log.flagged = True log.add_deduction('CD-R') else: raise UnrecognizedException('Unknown disc type')
def evaluate_unmatched_settings(self, log, settings): """Evaluate all unmatched settings and deduct for them.""" for key in settings: if log.range and key == 'Gap handling': log.add_deduction('Could not verify gap handling') elif key == 'Gap handling': log.add_deduction('Gap handling') elif key == 'ID3 tags': log.add_deduction('Could not verify presence of ID3 tags') elif key == 'Null samples': log.add_deduction('Could not verify usage of null samples') else: raise UnrecognizedException( 'One or more required settings could not be found')
def validate_indices(self, log): """Validate the indices of notable lines in the log.""" if not log.track_indices: raise UnrecognizedException('No tracks found') # +4 to compensate for range rip lines for i in range(max(log.track_indices) + 4, len(log.contents)): # Need to check the full_contents and add the extra space for AR summary leading space. if re.match(r' ?\w', log.full_contents[i]): log.track_indices.append(i - 1) log.index_footer = i break log.index_tracks = min(log.track_indices) if not log.index_toc: log.index_toc = log.index_tracks
def check(self, main_log): """Checks the EAC logs.""" logs = combined.split_combined(main_log) for log in logs: if len(log.concat_contents) < 12: raise UnrecognizedException( 'Cannot parse log file; log file too short') log.version = 'EAC <=0.95' log.album = log.concat_contents[1] log.drive = self.check_drive(log) self.index_log(log, ninety_five=True) self.evaluate_settings(log) self.check_tracks(log) if self.markup: markup(log, self.patterns, self.translation) main_log = combined.defragment(logs, eac95=True) validation.validate_track_settings(main_log) self.deduct_and_score(main_log) return main_log