def report(*args, **kwargs): force = False if 'force' in kwargs: force = kwargs['force'] kwargs.pop('force') if force or not silent: cyan(*args, **kwargs)
def print_tags(self, detailed=False, as_json=False, as_pprint=False, to_file=None, extended=False, header=True, subscript=None, highlight_title=False): if header and not to_file: stdout() if subscript: green(self.full_path(), end='') cyan(' (' + subscript + ')') else: green(self.full_path()) track = self.get('TRACKNUMBER') fix_track = False if track.startswith('T'): track = track[1:] fix_track = True if fix_track: self.set('TRACKNUMBER', track) if detailed: self.print(extended, highlight_title) elif as_json or as_pprint: self.print_json(extended, as_pprint, to_file) else: self.echo()
def test_file_manipulation_1(): dryrun = False verbose = False silent = True safe_remove = False new_file('test1.txt', 'test1\n') duplicate_file('.', 'test1.txt', 'test2.txt', dryrun=dryrun, verbose=verbose, silent=silent) rename_file('.', 'test1.txt', 'test3.txt', dryrun=dryrun, verbose=verbose, silent=silent) rename_file( '.', 'test2.txt', 'test3.txt', # will give test3 (1).txt dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3.txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3 (1).txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) cyan('test_file_manipulation_1 OK')
def diff(self, other, my_name=None, other_name=None, extended=False, print_on_diff=True, print_on_diff_f=None): diff = False tags = self.get_tag_keys(extended) my_name = my_name or self.name() other_name = other_name or other.name() for tag in tags: if self.get(tag) != other.get(tag): if not diff and print_on_diff: if print_on_diff_f: stdout(print_on_diff_f) else: cyan(tag, 'diff:', self.get(tag), '(' + my_name + ') !=', other.get(tag), '(' + other_name + ')') diff = True break return diff
def test_file_manipulation_2(): dryrun = False verbose = False silent = True safe_remove = False new_file('test1.txt', 'test1\n') copy_file('test1.txt', 'test2.txt', dryrun=dryrun, verbose=verbose, silent=silent) move_file('test1.txt', 'test3.txt', dryrun=dryrun, verbose=verbose, silent=silent) move_file( 'test2.txt', 'test3.txt', # will vive test3 (1).txt file dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3.txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3 (1).txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) cyan('test_file_manipulation_2 OK')
def test_to_camel_case(): assert_equal('Max Don\'t Have Sex With Your Ex', to_camel_case("Max Don’t Have Sex With Your Ex")) assert_equal('Please Don\'t Go', to_camel_case('Please Don\'t Go')) assert_equal('An Angel \'s Love', to_camel_case('An angel \'s love')) assert_equal('The Cook And The Thief', to_camel_case('The cook and the thief')) assert_equal('Kris\'s Book', to_camel_case('Kris\'s book')) assert_equal('I\'m Singin\' ABC', to_camel_case('I\'M singin\' ABC')) assert_equal('I Am The DJ', to_camel_case('I am the DJ')) assert_equal('I\'m From The 80ies', to_camel_case('I\'m from the 80ies')) assert_equal('You\'re From The 70s', to_camel_case('You\'re from the 70s')) assert_equal('DON\'T YOU', to_camel_case('DON\'T YOU')) assert_equal('L\'Envie Des Etoilles', to_camel_case('L\'Envie des Etoilles')) assert_equal('S\'Express', to_camel_case('S\'Express')) assert_equal('Charlotte De Witte', to_camel_case('Charlotte de Witte')) assert_equal('X (Extasy)', to_camel_case('x (Extasy)')) assert_equal('Annie x Billie', to_camel_case('Annie x Billie')) assert_equal('deadmau5 Preserved', to_camel_case('deadmau5 preserved', ['deadmau5'])) assert_equal('As Is feat.', to_camel_case('As is feat.')) assert_equal('Du Bist Hanz', to_camel_case('du bist hanz')) cyan('test_to_camel_case OK')
def report(*args, **kwargs): if not silent: cyan(*args, **kwargs)
def report(*report_args, **report_kwargs): if not silent: cyan(*report_args, **report_kwargs)
def fix_indexed_songs(path, song_fs, dryrun=True, safe_remove=True, verbose=False, silent=False, debug=False): def report(*args, **kwargs): force = False if 'force' in kwargs: force = kwargs['force'] kwargs.pop('force') if force or not silent: cyan(*args, **kwargs) def retag(song, new_title): if not silent: stdout('Retagging to', new_title) song.set('TITLE', new_title) song.save(dryrun) cnt = 0 for song_f in song_fs: for i in range(99): suffix = ' (' + str(i) + ')' + SONG_FILE_EXT if song_f.endswith(suffix): stripped = song_f[:-len(suffix)] stripped_f = stripped + SONG_FILE_EXT s = Song(path=path, song_f=song_f, debug=debug) if stripped_f in song_fs: report('Resolving conflict with', stripped_f) t = s.parse_track_nbr() s_stripped = Song(path=path, song_f=stripped_f, debug=debug) t_stripped = s_stripped.parse_track_nbr() stripped_j = song_f[:-len(SONG_FILE_EXT)] # reasonable stripped_j_f = song_f # - defaults for j in range(2, 99): stripped_j = stripped + ' -' + str(j) + '-' stripped_j_f = stripped_j + SONG_FILE_EXT if stripped_j_f not in song_fs: break if t < t_stripped: report( 'New', song_f, 'becomes', stripped_f, # and original stripped becomes stripped_j 'as its track number is lower', force=True) retag(s, stripped) rename_file(path, song_f, stripped_f, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) retag(s_stripped, stripped_j) rename_file(path, stripped_f, stripped_j_f, dryrun=dryrun, safe_rename=not dryrun, verbose=verbose, silent=silent, debug=debug) elif t > t_stripped: report( 'New', song_f, 'becomes', stripped_j_f, # and original stripped remains 'as its track number is higher', force=True) retag(s, stripped_j) rename_file(path, song_f, stripped_j_f, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) else: report('Track numbers match!') report('Comparing sample rates') s1 = int(s.get('#samplerate')) s2 = int(s_stripped.get('#samplerate')) if s1 > s2: report('New', song_f, 'wins (' + str(s1), 'over', str(s2) + ')', force=True) alert('Removing', path, stripped_f) remove_file(path, stripped_f, safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) retag(s, stripped) rename_file(path, song_f, stripped, dryrun=dryrun, safe_rename=not dryrun, verbose=verbose, silent=silent, debug=debug) elif s1 < s2: report('Existing', stripped_f, 'wins (' + str(s2), 'over', str(s1) + ')', force=True) alert('Removing', path, song_f) remove_file(path, song_f, safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) else: report('Sample rates match!') report('Comparing length') l1 = int(s.get('#length')) l2 = int(s_stripped.get('#length')) if l1 > l2: cyan('New', song_f, 'wins (' + str(l1), 'over', str(l2) + ')', force=True) alert('Removing', path, song_f) remove_file(path, stripped_f, safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) retag(s, stripped) rename_file(path, song_f, stripped_f, dryrun=dryrun, safe_rename=not dryrun, verbose=verbose, silent=silent, debug=debug) else: report('Existing', stripped_f, 'wins (' + str(l2), 'over', str(l1) + ')', force=True) alert('Removing', path, song_f) remove_file(path, song_f, safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) else: report('Fixing', song_f, 'to', stripped + SONG_FILE_EXT) retag(s, stripped) rename_file(path, song_f, stripped_f, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) cnt += 1 if not dryrun and cnt: break if not dryrun and cnt: break return cnt
def main(): program = 'review_songs' description = ''' Review songs based on user input ''' parser = argparse.ArgumentParser(prog=program, description=description) parser.add_argument('-j', '--project-filter', type=str, help='Set project filter') parser.add_argument('-a', '--artist-filter', type=str, help='Set artist filter') parser.add_argument('-b', '--album-filter', type=str, help='Set album filter') parser.add_argument('-c', '-t', '--title-filter', type=str, help='Set title filter') parser.add_argument('-x', '--path', type=str, help='Sets root path') parser.add_argument('-r', '--review-stamps', help='Create/Use review stamps', action='store_true') parser.add_argument('-v', '--verbose', help='Enable verbose', action='store_true') parser.add_argument('-s', '--silent', help='Enable silence', action='store_true') parser.add_argument('-rs', '--radio-silent', help='Enable radio silence', action='store_true') parser.add_argument('-d', '--debug', help='Enable debug', action='store_true') args = parser.parse_args() root = args.path or get_default_root() radio_silent = args.radio_silent or get_env_var('RADIO_SILENT') silent = args.silent or get_env_var('SILENT') or radio_silent debug = args.debug or get_env_var('DEBUG') verbose = args.verbose or get_env_var('VERBOSE') or debug assert_non_empty_dir(root) project_filter = args.project_filter artist_filter = args.artist_filter album_filter = args.album_filter title_filter = args.title_filter review_stamps = args.review_stamps if title_filter and title_filter.lower().endswith(SONG_FILE_EXT): title_filter = title_filter[:-len(SONG_FILE_EXT)] for dir_name, _, song_fs in os.walk(root): # gotcha... this doesn't work if i specify a full detailed path, up # to the album dir ... process, project, artist, album = process_songs_dir( root, dir_name, project_filter, artist_filter, album_filter) if process: path = root + '/' + project + '/' + artist + '/' + album reviewed_mark = path + '/.reviewed' if review_stamps and is_file(reviewed_mark): if debug: cyan('Skipping', project, '/', artist, '/', album) else: review_songs(path, song_fs, title_filter, verbose=verbose, silent=silent, radio_silent=radio_silent, debug=debug) if review_stamps: new_mark_file(reviewed_mark)
def test_file_extension(): f = 'grease/is/the.word' f_base, f_ext = split_file_extension(f) assert_equal('grease/is/the', f_base) assert_equal('.word', f_ext) cyan('test_file_extension OK')
def test_error(): error('wooo...bad (just kidding)') error('wooo...bad', end=' (just kidding!)\n') cyan('test_error OK')
def test_windows(): f_yellow('You are on {}Windows', '' if is_windows() else 'non-') cyan('test_windows OK')
def test_colours(): stdout('USA is ', end='') red('stars ', end='') stdout('and ', end='') blue('stripes') cyan('test_colours OK')
def audit_song(root, project, artist_d, album_d, song_f, reset_artist_ref, reset_album_ref, dir_structure_as_ref=False, set_artist=None, set_album=None, set_song=None, force_write=False, dryrun=True, verbose=False, silent=False, debug=False): global errors_fixed, errors_unresolved, sung_it def debug_out(*args, **kwargs): if debug: stdout(ConsoleColors.ENDC, end='') stdout(*args, **kwargs) debug_out('audit_song:', root, project, artist_d, album_d, song_f) song = Song(root, project, artist_d, album_d, song_f=song_f, debug=debug) title_f = song_f[:-len(SONG_FILE_EXT)] pre_errors_fixed = errors_fixed keep = {} init_the_song = '*** UNINITIALIZED ***' sung_it = False def keep_it(): for _tag in ('TITLE', 'ALBUM', 'ARTIST', 'ALBUMARTIST'): keep[_tag] = song.get(_tag) def force_it(): keep['TITLE'] = '' sing_it() cyan('FORCE-WRITING TITLE ', end='') def save_it(): song.save(dryrun) def print_it(p_tags=None, offset=4): p_tags = p_tags or song.get_tags() for _tag in Song.get_tag_keys(): if _tag == 'TRACKNUMBER': continue # skip stdout(' ' * offset + _tag + ': \'' + p_tags[_tag] + '\'') def sing_it(force=False): global sung_it # python 3 can do nonlocal but in py2 is readonly elaborated = False if not silent and (force or not sung_it): if elaborated: stdout(init_the_song, end='') # sing it else: yellow('Fixing', project, '/', artist_d, '/', album_d, '/', song_f) sung_it = True def did_it(): for _tag in ('TITLE', 'ALBUM', 'ARTIST', 'ALBUMARTIST'): if song.get(_tag) != keep[_tag]: return True return False def report(*args, **kwargs): if not silent: cyan(*args, **kwargs) def replace_name(name): set_name = None suffix = None if name.endswith('-'): # -X- assumed for i in range(999): s = ' -{}-'.format(i) if name.endswith(s): suffix = s truncated_name = name[:-len(s)] debug_out('Checking', truncated_name, 'instead of', name) name = truncated_name break e = to_camel_case(name, PRESERVED_NAMES) if e != name: set_name = e name = set_name done = False while not done: done = True for c in (' ', ',', ';', ':', '&'): while name.endswith(c): set_name = name[:-1] done = False name = set_name if name == '?': set_name = 'Q' else: prev_name = name for c in REPLACE_CHARS: name = name.replace(c, REPLACE_CHARS[c]) if name != prev_name: set_name = name if set_name and suffix: set_name += suffix if set_name: debug_out('replace_name: replaced', name, 'to', set_name) return set_name def fix_dir_name(name): if name.endswith('.'): # WINDOWS DOESN'T DEAL WITH DOTS AT END OF A DIRECTORY... name = name[:-1] + '. ()' return name def tag_to_filesystem_name(name, is_dir): if is_dir: name = fix_dir_name(name) for c in TOLERATED_CHARS_IN_TAGS_REPLACED_TO_IN_FILESYSTEM: name = name.replace( c, TOLERATED_CHARS_IN_TAGS_REPLACED_TO_IN_FILESYSTEM[c]) for c in ascii_letters: name = name.replace(c + '- ', c + ' - ') return name def fix_artist_tag(): artist = song.get('ALBUMARTIST') t = FIX_ARTIST_TAGS.get(artist) if t: sing_it() report('FIXING ARTIST ', end='') song.set('ALBUMARTIST', t) return t else: return None def fix_artist_dir(): d = FIX_ARTIST_DIRS.get(artist_d) if d: sing_it() report('FIX_ARTIST_DIR [Renaming dir', artist_d, 'to', d + ']') # mind, actual rename is not done here! return d return None def fix_album_tag(): album = song.get('ALBUM') t = FIX_ALBUM_TAGS.get(album) if t: sing_it() report('FIXING ALBUM ', end='') song.set('ALBUM', t) return t else: return None def fix_tags_for_camel_case_and_more(): for tag in ('TITLE', 'ALBUM', 'ARTIST', 'ALBUMARTIST'): t_tag = song.get(tag) debug_out('Processing', tag, '=', t_tag) if t_tag: new_tag = replace_name(t_tag) # replace comma's in artist if tag == 'ALBUMARTIST': t_cur = new_tag if new_tag else t_tag t_new = t_cur for c in REPLACE_CHARS_IN_ARTISTS_ONLY: t_new = t_new.replace(c, REPLACE_CHARS_IN_ARTISTS_ONLY[c]) if t_new != t_cur: new_tag = t_new sing_it() report('FIXING ALBUMARTIST [' + new_tag + '] ', end='') song.set(tag, new_tag, straight_overwrite_artist=True) # extra logic (eventually can go) def retag(name): for r in REPLACE_CHARS_IN_TAGS_ONLY: name = name.replace(r, REPLACE_CHARS_IN_TAGS_ONLY[r]) return name if new_tag: new_tag = retag(new_tag) else: old_t = t_tag t_tag = retag(t_tag) if t_tag != old_t: new_tag = t_tag # end of extra logic (eventually can go) if new_tag: sing_it() report('FIXING NAME [' + new_tag + '] ', end='') song.set(tag, new_tag) def fix_dirs_for_camel_case_and_more(): debug_out('fix_dirs_for_camel_case_and_more') return (replace_name(set_artist if set_artist else artist_d), replace_name(set_album if set_album else album_d), replace_name(set_song if set_song else title_f)) def fix_collection_albums(): debug_out('fix_collection_albums') if song.get('ALBUM') in COLLECTION_ALBUMS: album_t = song.get('ALBUM') overwrite = u(a_else_b(COLLECTION_ALBUMS[album_t], album_t)) if song.get('ALBUMARTIST') != overwrite: sing_it() report('COLLECTION:RENAMING ', end='') song.set('ALBUM', overwrite) song.set('ALBUMARTIST', overwrite) if overwrite not in song.get('ARTIST'): sing_it() if song.get('ARTIST').endswith(keep['ALBUM']): report('COLLECTION:STRIPPING ', end='') song.set('ARTIST', song.get('ARTIST')[:-(len(keep['ALBUM']) + 2)]) song.set('ARTIST', song.get('ARTIST') + ', ' + overwrite) report('COLLECTION:MARKING ', end='') def truncate_artists(): while True: song_artist = song.get('ALBUMARTIST') artist = song.get('ARTIST') if song_artist: if ',' in song_artist: sing_it() report('TRUNCATING ALBUMARTIST', end='') song.set('ALBUMARTIST', song.get('ALBUMARTIST').split(',')[0]) if song.get('ALBUMARTIST') not in song.get('ARTIST'): song.set( 'ARTIST', song.get('ARTIST') + ', ' + song.get('ALBUMARTIST')) break elif artist: sing_it() report('FIXING ALBUMARTIST ', end='') if ',' in artist: song.set('ALBUMARTIST', song.get('ARTIST').split(',')[0]) else: song.set('ALBUMARTIST', song.get('ARTIST')) else: error('No ARTIST and no ALBUMARTIST?') error(project, '/', artist_d, '/', album_d, '/', song_f, '\n') break def finish_it(): global errors_fixed debug_out('finish_it') artist = song.get('ALBUMARTIST') contributing_artists = song.get('ARTIST') album = song.get('ALBUM') title = song.get('TITLE') if sung_it: report('OK') if did_it(): if silent: green('Retagged', project, '/', artist, '(' + contributing_artists + ') /', album, '/', title) else: stdout('WAS:') print_it(keep) stdout('BECAME:') print_it() save_it() # this is the moment where we save! errors_fixed += 1 def audit_entity(entity_tag, unrooted_entity_path_root, is_file, file_extension, entity_d, entity, set_entity): global errors_fixed, errors_unresolved debug_out('audit_entity:', entity_tag, unrooted_entity_path_root, is_file, file_extension, entity_d, entity, set_entity) def retag(to): green('Retagging', unrooted_entity_path_root.replace('/', ' / '), '/', entity, 'to', to) # TODO(do we need to save here?) song.set(entity_tag, to, save=True, dryrun=dryrun) def rename(to): rooted_entity_path_root = root + '/' + unrooted_entity_path_root if is_file: green('Renaming', unrooted_entity_path_root.replace('/', ' / '), '/', entity_d + file_extension, 'to', to + file_extension) rename_file(rooted_entity_path_root, entity_d + file_extension, to + file_extension, safe_rename=True, add_suffix_if_exists=True, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) elif (path_exists(rooted_entity_path_root + '/' + to) and not (is_windows() and entity_d.lower() == to.lower())): green('Merging', unrooted_entity_path_root, '/', entity_d, 'into', to) merge_dir(rooted_entity_path_root + '/' + entity_d, rooted_entity_path_root + '/' + to, safe_merge=True, add_suffix_if_exists=True, merge_subdirs=True, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) else: green('Renaming', unrooted_entity_path_root.replace('/', ' / '), '/', entity_d, 'to', to) rename_dir(rooted_entity_path_root, entity_d, to, safe_rename=False, dryrun=dryrun, verbose=verbose, silent=silent, debug=debug) if set_entity: debug_out('audit_entity:', entity_tag, 'set to', set_entity) modified = False if entity != set_entity: sing_it() retag(set_entity) errors_fixed += 1 modified = True c_set_entity = tag_to_filesystem_name(set_entity, not is_file) if entity_d != c_set_entity: sing_it() rename(c_set_entity) errors_fixed += 1 modified = True return c_set_entity if modified else None else: c_entity = tag_to_filesystem_name(entity, not is_file) if c_entity != entity_d and entity_d != JOKER_VALUE: if dir_structure_as_ref: sing_it() retag(entity_d) errors_fixed += 1 return entity_d else: sing_it() rename(c_entity) errors_fixed += 1 return c_entity def audit_artist(set_it): artist = song.get('ALBUMARTIST') return audit_entity('ALBUMARTIST', project, False, None, artist_d, artist, set_it) def audit_album(set_it): album = song.get('ALBUM') return audit_entity('ALBUM', project + '/' + artist_d, False, None, album_d, album, set_it) def audit_title(set_it): title = song.get('TITLE') return audit_entity('TITLE', project + '/' + artist_d + '/' + album_d, True, SONG_FILE_EXT, title_f, title, set_it) orig_artist_d = artist_d orig_album_d = album_d orig_title_d = title_f if dir_structure_as_ref: artist_mod = fix_artist_dir() if artist_mod: set_artist = set_artist or artist_mod if not dryrun: cyan('(Resetting ARTIST)') reset_artist_ref[0] = set_artist artist_mod, album_mod, title_mod = fix_dirs_for_camel_case_and_more() if artist_mod: set_artist = set_artist or artist_mod # mind, despite efforts to # correct it, when was pre- # set, we stick to what was # set (manually or via # fix_artist_dir) if not dryrun and not reset_artist_ref[0]: cyan('(Resetting ARTIST)') reset_artist_ref[0] = set_artist if album_mod: set_album = set_album or album_mod # mind, same here, but then # only for previously manually # set in this case if not dryrun: cyan('(Resetting ALBUM)') reset_album_ref[0] = set_album if title_mod: set_song = set_song or title_mod else: keep_it() if force_write: force_it() fix_artist_tag() fix_album_tag() fix_tags_for_camel_case_and_more() fix_collection_albums() truncate_artists() finish_it() modified_to = audit_artist(set_artist) if modified_to and not dir_structure_as_ref: artist_d = modified_to if not dryrun: cyan('(Resetting ARTIST)') reset_artist_ref[0] = modified_to artist_d = fix_dir_name(artist_d) # always modified_to = audit_album(set_album) if modified_to and not dir_structure_as_ref: album_d = modified_to if not dryrun: cyan('(Resetting ALBUM)') reset_album_ref[0] = modified_to album_d = fix_dir_name(album_d) # always modified_to = audit_title(set_song) if modified_to and not dir_structure_as_ref: title_f = modified_to number_modified = errors_fixed > pre_errors_fixed if number_modified: fixed_songs.append(root + '/' + project + '/' + # if dryrun, files aren't actually changed (orig_artist_d if dryrun else artist_d) + '/' + (orig_album_d if dryrun else album_d) + '/' + (orig_title_d if dryrun else title_f) + SONG_FILE_EXT) return number_modified
def review_songs(path, song_files, title_filter, detailed=True, sort=True, verbose=False, silent=False, radio_silent=False, debug=False): songs = [] filtered_songs, missed_songs, _, _, _ = get_filtered_songs( path, song_files, title_filter=title_filter, sort_per_track=sort, post_process_songs=sort, print_header=not silent, verbose=verbose, silent=silent, no_warn=radio_silent, debug=debug) if not filtered_songs: return for track_nbr in sorted(filtered_songs): for song in filtered_songs[track_nbr]: songs.append(song) PAGE_SIZE = 20 done = False idx = 1 def option_title(title_idx): s = songs[title_idx - 1] # noinspection PyTypeChecker title = s.get('TITLE') if detailed: # noinspection PyTypeChecker track_nr = s.get('TRACKNUMBER') title += (' (' + track_nr + ')') option(title_idx, title) while not done: if idx > len(songs): break default = idx - 1 stdout() option(default, 'None') for _ in range(PAGE_SIZE): if idx > len(songs): done = True break option_title(idx) idx += 1 def add_options(_idx): _add_tags = None def _add_option(_s, _i): option(_i, _s) return _i, _i + 1 if missed_songs: _add_tags, _idx = _add_option('-- ADD .missing TAGS --', _idx) _reload, _idx = _add_option('-- RELOAD --', _idx) _exit_the_game, _idx = _add_option('-- EXIT --', _idx) return _add_tags, _reload, _exit_the_game add_tags, reload, exit_the_game = add_options(idx) stdout() while True: c = numerical_input('Song to edit', default, exit_the_game) if missed_songs and c == add_tags: stdout() for missed in missed_songs: add_missing_file(missed[0], missed[1], dryrun=False) stdout() c = None elif c == reload: stdout() option(default, 'None') for i in range(default + 2, idx): option_title(i) add_options(idx) cyan('(mind, not re-sorted)') cyan() c = None elif c == exit_the_game: cyan('Bye.') end() elif c == default: break if c is not None: stdout() song_idx = c - 1 songs[song_idx] = edit_song(songs[song_idx], debug=debug)
def force_it(): keep['TITLE'] = '' sing_it() cyan('FORCE-WRITING TITLE ', end='')
dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3.txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) remove_file('.', 'test3 (1).txt', safe_remove=safe_remove, dryrun=dryrun, verbose=verbose, silent=silent) cyan('test_file_manipulation_2 OK') if __name__ == "__main__": test_colours() test_windows() test_error() test_to_camel_case() test_file_extension() test_file_manipulation_1() test_file_manipulation_2() green('ALL TESTS PASS. Happy day :)') cyan('Final test is a fatal error:') fatal_error('The world exploded') error('SHOULD NOT HAVE COME HERE!')