def run(self, args, skip_targets=False): """ Common argument parsing """ args = super(MusaScriptCommand, self).parse_args(args) self.prefixes = TreePrefixes() if skip_targets: return [], [], [] trees, tracks, metadata = [], [], [] for path in args.paths: if os.path.isdir(path): trees.append(Tree(path)) else: try: tracks.append(Track(path)) except TreeError: match = match_metadata(path) if match is not None: metadata.append(match) tracks_found = False for d in trees: if not len(d): continue tracks_found = True break if not tracks_found and not len(tracks) and not len(metadata): return [], [], [] return trees, tracks, metadata
def __init__(self, path): super(Track, self).__init__(path) self.prefixes = TreePrefixes() if self.codec is None: raise TreeError('Not a music file: {0}'.format(self.path)) self.tags_loaded = False self.file_tags = None
def __init__(self, path, iterable): self.log = SoundforestLogger().default_stream self.__next = None self.__iterable = iterable if path in ['.', '']: path = os.path.realpath(path) self.path = path_string(path) self.prefixes = TreePrefixes() self.invalid_paths = [] self.has_been_iterated = False setattr(self, iterable, [])
class Track(AudioFileFormat): """Track Audio file track """ def __init__(self, path): super(Track, self).__init__(path) self.prefixes = TreePrefixes() if self.codec is None: raise TreeError('Not a music file: {0}'.format(self.path)) self.tags_loaded = False self.file_tags = None @property def tags(self): if not self.tags_loaded: try: self.file_tags = Tags(self.path, fileformat=self) self.tags_loaded = True except TagError as e: raise TreeError('Error loading tags: {0}'.format(e)) return self.file_tags def relative_path(self): return self.prefixes.relative_path(os.path.realpath(self.path)) @property def filename_no_extension(self): return os.path.splitext(os.path.basename(self.path))[0] @property def extension(self): return os.path.splitext(self.path)[1][1:] @property def album(self): return Album(os.path.dirname(self.path)) @property def tracknumber_and_title(self): filename = os.path.splitext(os.path.basename(self.path))[0] try: tracknumber, title = filename.split(None, 1) tracknumber = int(tracknumber) except ValueError: tracknumber = None title = filename return tracknumber, title @property def checksum(self): with open(self.path, 'rb') as fd: m = hashlib.md5() m.update(fd.read()) return m.hexdigest() def get_album_tracks(self): path = os.path.dirname(self.path) extensions = CODECS[self.codec]['extensions'] tracks = [] for t in os.listdir(path): if os.path.splitext(t)[1][1:] not in extensions: continue tracks.append(Track(os.path.join(path, t))) return tracks def get_decoder_command(self, wav_path=None): if wav_path is None: wav_path = '{0}.wav'.format(os.path.splitext(self.path)[0]) if wav_path == self.path: raise TreeError('Trying to encode to itself') try: decoder = self.get_available_decoders()[0] except IndexError: raise TreeError('No available decoders for {0}'.format(self.path)) decoder = decoder.split() decoder[decoder.index('OUTFILE')] = wav_path decoder[decoder.index('FILE')] = self.path return decoder def get_encoder_command(self, wav_path=None): if wav_path is None: wav_path = '{0}.wav'.format(os.path.splitext(self.path)[0]) if wav_path == self.path: raise TreeError('Trying to encode to itself') try: encoder = self.get_available_encoders()[0] except IndexError: raise TreeError('No available encoders for {0}'.format(self.path)) encoder = encoder.split() encoder[encoder.index('OUTFILE')] = self.path encoder[encoder.index('FILE')] = wav_path return encoder def get_tester_command(self, tempfile_path): try: tester = self.get_available_testers()[0] except IndexError: raise TreeError('No available testers for {0}'.format(self.path)) tester = tester.split() tester[tester.index('FILE')] = self.path if tester.count('OUTFILE') == 1: tester[tester.index('OUTFILE')] = tempfile_path return tester def test(self, callback): tempfile_path = self.get_temporary_file(prefix='test', suffix='.wav') try: cmd = self.get_tester_command(tempfile_path) except TreeError: callback(self, False, errors='No tester available for {0}'.format(self.extension)) return rv, stdout, stderr = self.execute(cmd) if rv == 0: callback(self, True, stdout=stdout, stderr=stderr) else: callback(self, False, stdout=stdout, stderr=stderr) if os.path.isfile(tempfile_path): try: os.unlink(tempfile_path) except IOError as e: raise TreeError('Error removing temporary file {0}: {1}'.format(tempfile_path, e)) except OSError as e: raise TreeError('Error removing temporary file {0}: {1}'.format(tempfile_path, e)) return rv
class IterableTrackFolder(object): """IterableTrackFolder model Abstract class for various iterable music items """ def __init__(self, path, iterable): self.log = SoundforestLogger().default_stream self.__next = None self.__iterable = iterable if path in ['.', '']: path = os.path.realpath(path) self.path = path_string(path) self.prefixes = TreePrefixes() self.invalid_paths = [] self.has_been_iterated = False setattr(self, iterable, []) def __getitem__(self, item): if not self.has_been_iterated: self.load() iterable = getattr(self, self.__iterable) return iterable[item] def __len__(self): iterable = getattr(self, self.__iterable) if len(iterable) == 0: self.load() if len(iterable) - len(self.invalid_paths) >= 0: return len(iterable) - len(self.invalid_paths) else: return 0 def __iter__(self): return self def next(self): iterable = getattr(self, self.__iterable) if self.__next is None: self.__next = 0 self.has_been_iterated = False if len(iterable) == 0: self.load() try: entry = iterable[self.__next] self.__next += 1 path = os.path.join(entry[0], entry[1]) try: return Track(path) except TreeError: if not self.invalid_paths.count(path): self.invalid_paths.append(path) return self.next() except IndexError: self.__next = None self.has_been_iterated = True raise StopIteration def load(self): """Lazy loader Lazy loader of the iterable item """ iterable = getattr(self, self.__iterable) iterable.__delslice__(0, len(iterable)) self.invalid_paths.__delslice__(0, len(self.invalid_paths)) def relative_path(self, item=None): """Item relative path Returns relative path of this iterable item """ if item is not None: if isinstance(item, Track): return self.prefixes.relative_path(item.path) else: return self.prefixes.relative_path(item) else: return self.prefixes.relative_path(self.path) def remove_empty_path(self, empty): """Remove empty directory Remove empty directory and all empty parent directories """ while True: if not os.path.isdir(empty): # Directory does not exist return if os.listdir(empty): # Directory is not empty return try: os.rmdir(empty) except OSError as e: raise TreeError('Error removing empty directory {0}: {1}'.format(empty, e)) except IOError as e: raise TreeError('Error removing empty directory {0}: {1}'.format(empty, e)) # Try to remove parent empty directory empty = os.path.dirname(empty)