def test_read_standardize_write_file(self): path = self.create_test_file(TEST_TAG_LIST) au_file = audio_file.scan(path) self.assertTrue(au_file is not None) au_file.volume = TEST_VOL au_file.import_timestamp = TEST_TS au_file.album_id = 77777 # Inject the necessary UFID tag. au_file.mutagen_id3[constants.UFID_OWNER_IDENTIFIER] = ufid.ufid_tag( TEST_VOL, TEST_TS, au_file.fingerprint) import_file.standardize_file(au_file) # Do some basic checks for tag in au_file.mutagen_id3.values(): self.assertTrue( (tag.FrameID in constants.ID3_TAG_WHITELIST or tag.HashKey in constants.ID3_TAG_WHITELIST)) for frame_id in constants.ID3_TAG_REQUIRED: self.assertTrue(frame_id in au_file.mutagen_id3) # Write the standardized file out, then re-read it and make sure # that everything is as we expected. alt_prefix = os.path.join(self.prefix, "alt") new_path = import_file.write_file(au_file, alt_prefix) new_au_file = audio_file.scan(new_path) self.assertEqual(sorted(au_file.mutagen_id3.keys()), sorted(new_au_file.mutagen_id3.keys())) for key in au_file.mutagen_id3.keys(): self.assertEqual(repr(au_file.mutagen_id3[key]), repr(new_au_file.mutagen_id3[key])) self.assertEqual(au_file.fingerprint, new_au_file.fingerprint) self.assertEqual(au_file.payload, new_au_file.payload)
def test_read_standardize_write_file(self): path = self.create_test_file(TEST_TAG_LIST) au_file = audio_file.scan(path) self.assertTrue(au_file is not None) au_file.volume = TEST_VOL au_file.import_timestamp = TEST_TS au_file.album_id = 77777 # Inject the necessary UFID tag. au_file.mutagen_id3[constants.UFID_OWNER_IDENTIFIER] = ufid.ufid_tag( TEST_VOL, TEST_TS, au_file.fingerprint) import_file.standardize_file(au_file) # Do some basic checks for tag in au_file.mutagen_id3.values(): self.assertTrue((tag.FrameID in constants.ID3_TAG_WHITELIST or tag.HashKey in constants.ID3_TAG_WHITELIST)) for frame_id in constants.ID3_TAG_REQUIRED: self.assertTrue(frame_id in au_file.mutagen_id3) # Write the standardized file out, then re-read it and make sure # that everything is as we expected. alt_prefix = os.path.join(self.prefix, "alt") new_path = import_file.write_file(au_file, alt_prefix) new_au_file = audio_file.scan(new_path) self.assertEqual(sorted(au_file.mutagen_id3.keys()), sorted(new_au_file.mutagen_id3.keys())) for key in au_file.mutagen_id3.keys(): self.assertEqual(repr(au_file.mutagen_id3[key]), repr(new_au_file.mutagen_id3[key])) self.assertEqual(au_file.fingerprint, new_au_file.fingerprint) self.assertEqual(au_file.payload, new_au_file.payload)
def test_scan_has_chirp_tags(self): path = os.path.join(TESTDATA, "has_chirp_tags.mp3") fast_au_file = audio_file.scan_fast(path) slow_au_file = audio_file.scan(path) self.assertEqual(path, fast_au_file.path) self.assertEqual(path, slow_au_file.path) # This volume and timestamp is extracted from the UFID. self.assertEqual(123, fast_au_file.volume) self.assertEqual(1230519180, fast_au_file.import_timestamp) self.assertEqual(3918, fast_au_file.duration_ms) self.assertEqual(slow_au_file.duration_ms, fast_au_file.duration_ms) self.assertEqual(slow_au_file.fingerprint, fast_au_file.fingerprint) # Test file contains 150 frames for a total of 137,173 bytes. self.assertEqual(150, slow_au_file.frame_count) self.assertEqual(137173, slow_au_file.frame_size) self.assertEqual(path, slow_au_file.path) # The fast scan picks up an album ID stored in the tags, # the slow scan doesn't. The test file is marked as having # an album ID of 123454321. self.assertEqual(123454321, fast_au_file.album_id) self.assertEqual(None, slow_au_file.album_id)
def test_scan_no_chirp_tags(self): path = os.path.join(TESTDATA, "no_chirp_tags.mp3") fast_au_file = audio_file.scan_fast(path) self.assertEqual(None, fast_au_file.volume) self.assertEqual(None, fast_au_file.import_timestamp) self.assertEqual(None, fast_au_file.fingerprint) self.assertEqual(None, fast_au_file.frame_count) self.assertEqual(None, fast_au_file.frame_size) # File doesn't have a TLEN tag. self.assertEqual(None, fast_au_file.duration_ms) self.assertEqual(path, fast_au_file.path) slow_au_file = audio_file.scan(path) # The file's fingerprint is stashed in the UFID:test tag. fp = slow_au_file.mutagen_id3.get("UFID:test").data self.assertEqual(None, slow_au_file.volume) self.assertEqual(None, slow_au_file.import_timestamp) self.assertEqual(fp, slow_au_file.fingerprint) # Test file contains 150 frames for a total of 137,173 bytes. self.assertEqual(150, slow_au_file.frame_count) self.assertEqual(137173, slow_au_file.frame_size) self.assertEqual(path, slow_au_file.path)
def from_directory(dirpath, fast=False): """Creates Album objects from the files in a directory. Found audio files are grouped into albums based on their TALB tags. Non-audio files are silently ignored. Args: dirpath: The path to the directory to scan for audio files. fast: If True, do a fast scan when analyzing the audio files. Returns: A list of Album objects. """ by_talb = {} for basename in os.listdir(dirpath): file_path = os.path.join(dirpath, basename) # Skip anything that isn't a regular file. if not os.path.isfile(file_path): continue # Skip dotfiles if basename.startswith("."): continue # Must have mp3 as the extension. if not basename.lower().endswith(".mp3"): continue if fast: au_file = audio_file.scan_fast(file_path) else: au_file = audio_file.scan(file_path) # Silently skip anything that seems bogus. if not au_file: continue if not "TALB" in au_file.mutagen_id3: raise AlbumError("Missing TALB tag on %s" % file_path) talb = au_file.mutagen_id3["TALB"].text[0] by_talb.setdefault(talb, []).append(au_file) return [Album(all_au_files) for all_au_files in by_talb.values()]
""" # Make sure the canonical directory exists. try: os.makedirs(au_file.canonical_directory(prefix)) except OSError, ex: if ex.errno != errno.EEXIST: raise path = au_file.canonical_path(prefix) if os.path.exists(path): raise ImportFileError(["File exists: " + path]) au_file.mutagen_id3.save(path) assert au_file.payload is not None out_fh = open(path, "a") out_fh.write(au_file.payload) out_fh.close() # Now make sure that the file we just wrote passes our checks. new_au_file = audio_file.scan(path) if new_au_file is None: raise ImportFileError(["New file damaged!"]) new_au_file.volume = au_file.volume new_au_file.import_timestamp = au_file.import_timestamp post_write_tagging_errors = checker.find_tags_errors(new_au_file) if post_write_tagging_errors: os.unlink(path) raise ImportFileError(["Found post-write errors!"] + post_write_tagging_errors) return path
def ensure_payloads(self): for au in self.all_au_files: if au.payload is None: new_au = audio_file.scan(au.path) assert new_au.fingerprint == au.fingerprint au.payload = new_au.payload
def __iter__(self): """Iterator that yields a sequence of crawled MP3s. Yields: An AudioFile object. """ self._reset() yielded_size = 0 for root_path in self._all_roots: for self._current_dir, dirnames, filenames in os.walk(root_path): # We do not recursively descend into these directoryies. dirnames[:] = self._remove_ignored_directories( self._current_dir, dirnames) # If a directory filter has been specified, use it to know # when to silently skip any files in a single directory. if (self._directory_filter and not self._directory_filter(self._current_dir)): continue # Now walk across each file in this dir, yielding a # stream of AudioFile objects. for name in filenames: full_path = os.path.join(self._current_dir, name) # Skip files with the wrong sorts of names. These are # not logged. if name.startswith("."): continue if not name.lower().endswith(".mp3"): self.skipped_files.append( (full_path, "Invalid filename")) continue # Stat the file, and skip the files when that # operation fails. try: stat_obj = os.stat(full_path) except (IOError, OSError), ex: self.skipped_files.append((full_path, str(ex))) continue try: if self._fast: au_file = audio_file.scan_fast(full_path) else: au_file = audio_file.scan(full_path) except Exception, ex: # TODO(trow): Here we should really only catch # the exceptions we expect audio_file.scan and # .scan_fast to raise. self.skipped_files.append((full_path, str(ex))) logging.error("Skipping file %s: %s", full_path, str(ex)) continue if au_file is None: self.skipped_files.append( (full_path, "Not an MP3 (No tags?)")) continue # Remember this directory, then yield the AudioFile. self.directories_seen.add(self._current_dir) yield au_file
def __iter__(self): """Iterator that yields a sequence of crawled MP3s. Yields: An AudioFile object. """ self._reset() yielded_size = 0 for root_path in self._all_roots: for self._current_dir, dirnames, filenames in os.walk(root_path): # We do not recursively descend into these directoryies. dirnames[:] = self._remove_ignored_directories( self._current_dir, dirnames) # If a directory filter has been specified, use it to know # when to silently skip any files in a single directory. if (self._directory_filter and not self._directory_filter(self._current_dir)): continue # Now walk across each file in this dir, yielding a # stream of AudioFile objects. for name in filenames: full_path = os.path.join(self._current_dir, name) # Skip files with the wrong sorts of names. These are # not logged. if name.startswith("."): continue if not name.lower().endswith(".mp3"): self.skipped_files.append( (full_path, "Invalid filename")) continue # Stat the file, and skip the files when that # operation fails. try: stat_obj = os.stat(full_path) except (IOError, OSError), ex: self.skipped_files.append((full_path, str(ex))) continue try: if self._fast: au_file = audio_file.scan_fast(full_path) else: au_file = audio_file.scan(full_path) except Exception, ex: # TODO(trow): Here we should really only catch # the exceptions we expect audio_file.scan and # .scan_fast to raise. self.skipped_files.append((full_path, str(ex))) logging.error("Skipping file %s: %s", full_path, str(ex)) continue if au_file is None: self.skipped_files.append((full_path, "Not an MP3 (No tags?)")) continue # Remember this directory, then yield the AudioFile. self.directories_seen.add(self._current_dir) yield au_file