Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""LICENSE 

2Copyright 2015 Hermann Krumrey <hermann@krumreyh.com> 

3 

4This file is part of toktokkie. 

5 

6toktokkie is free software: you can redistribute it and/or modify 

7it under the terms of the GNU General Public License as published by 

8the Free Software Foundation, either version 3 of the License, or 

9(at your option) any later version. 

10 

11toktokkie is distributed in the hope that it will be useful, 

12but WITHOUT ANY WARRANTY; without even the implied warranty of 

13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

14GNU General Public License for more details. 

15 

16You should have received a copy of the GNU General Public License 

17along with toktokkie. If not, see <http://www.gnu.org/licenses/>. 

18LICENSE""" 

19 

20import os 

21import argparse 

22import shutil 

23from typing import List, cast 

24from toktokkie.commands.Command import Command 

25from toktokkie.enums import MediaType 

26from toktokkie.Directory import Directory 

27from toktokkie.exceptions import MissingMetadata 

28from toktokkie.metadata.music.Music import Music 

29from puffotter.os import listdir 

30 

31 

32class MusicMergeCommand(Command): 

33 """ 

34 Class that encapsulates behaviour of the music-merge command 

35 """ 

36 

37 @classmethod 

38 def name(cls) -> str: 

39 """ 

40 :return: The command name 

41 """ 

42 return "music-merge" 

43 

44 @classmethod 

45 def help(cls) -> str: 

46 """ 

47 :return: The help message for the command 

48 """ 

49 return "Combines multiple music directories into one" 

50 

51 @classmethod 

52 def prepare_parser(cls, parser: argparse.ArgumentParser): 

53 """ 

54 Prepares an argumentparser for this command 

55 :param parser: The parser to prepare 

56 :return: None 

57 """ 

58 parser.add_argument("target", 

59 help="Target directory. If not a toktokkie media" 

60 "directory, will try to merge subfolders") 

61 parser.add_argument("sources", nargs="+", 

62 help="Directores containing directories to merge") 

63 parser.add_argument("--keep", action="store_true", 

64 help="If set, does not delete merged directories") 

65 

66 def execute(self): 

67 """ 

68 Executes the commands 

69 :return: None 

70 """ 

71 try: 

72 Directory(self.args.target) 

73 targets = [self.args.target] 

74 single_artist_mode = True 

75 except MissingMetadata: 

76 targets = [x[1] for x in listdir(self.args.target)] 

77 single_artist_mode = False 

78 

79 sources = [] # type: List[str] 

80 for path in self.args.sources: 

81 try: 

82 Directory(path) 

83 sources.append(path) 

84 except MissingMetadata: 

85 sources += [x[1] for x in listdir(path)] 

86 

87 target_artists = Directory.load_directories( 

88 targets, restrictions=[MediaType.MUSIC_ARTIST] 

89 ) 

90 source_artists = Directory.load_directories( 

91 sources, restrictions=[MediaType.MUSIC_ARTIST] 

92 ) 

93 

94 if single_artist_mode: 

95 for source in source_artists: 

96 self.merge_artists(target_artists[0], source) 

97 

98 else: 

99 target_map = {x.metadata.name: x for x in target_artists} 

100 

101 for source_artist in source_artists: 

102 source_metadata = cast(Music, source_artist.metadata) 

103 

104 if source_metadata.name not in target_map: 

105 new_path = os.path.join( 

106 self.args.target, source_metadata.name 

107 ) 

108 shutil.copytree(source_artist.path, new_path) 

109 if not self.args.keep: 

110 shutil.rmtree(source_artist.path) 

111 

112 else: 

113 target_artist = \ 

114 target_map[source_metadata.name] # type: Directory 

115 self.merge_artists(target_artist, source_artist) 

116 

117 def merge_artists( 

118 self, 

119 target_artist: Directory, 

120 source_artist: Directory 

121 ): 

122 """ 

123 Merges two artists 

124 :param target_artist: The target artist 

125 :param source_artist: The artist to merge into the target 

126 :return: None 

127 """ 

128 source_metadata = cast(Music, source_artist.metadata) 

129 target_metadata = cast(Music, target_artist.metadata) 

130 

131 target_albums = {x.name: x for x in target_metadata.albums} 

132 source_themes = {x.name: x for x in source_metadata.theme_songs} 

133 

134 for source_album in source_metadata.albums: 

135 if source_album.name in target_albums: 

136 self.logger.warning( 

137 "Duplicate album: {}".format(source_album.name) 

138 ) 

139 else: 

140 source_path = os.path.join( 

141 source_artist.path, source_album.name 

142 ) 

143 target_path = os.path.join( 

144 target_artist.path, source_album.name 

145 ) 

146 shutil.copytree(source_path, target_path) 

147 target_metadata.add_album(source_album) 

148 

149 theme_song = source_themes.get(source_album.name) 

150 if theme_song is not None: 

151 target_metadata.add_theme_song(theme_song) 

152 

153 target_metadata.write() 

154 if not self.args.keep: 

155 shutil.rmtree(source_artist.path)