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 

21from abc import ABC 

22from typing import Dict, Any, List 

23from puffotter.prompt import prompt_comma_list 

24from toktokkie.enums import IdType 

25from toktokkie.metadata.base.MetadataBase import MetadataBase 

26from toktokkie.utils.IdFetcher import IdFetcher 

27from toktokkie.exceptions import InvalidDirectoryState 

28 

29 

30class Prompter(MetadataBase, ABC): 

31 """ 

32 Class that's responsible for defining the metadata creation prompts 

33 """ 

34 

35 id_prompt_order = [ 

36 IdType.TVDB, 

37 IdType.IMDB, 

38 IdType.ISBN, 

39 IdType.VNDB, 

40 IdType.MUSICBRAINZ_ARTIST, 

41 IdType.MUSICBRAINZ_RELEASE, 

42 IdType.MUSICBRAINZ_RECORDING, 

43 IdType.MYANIMELIST, 

44 IdType.ANILIST, 

45 IdType.KITSU, 

46 IdType.MANGADEX 

47 ] 

48 """ 

49 The order in which ID types should be prompted 

50 """ 

51 

52 @classmethod 

53 def prompt(cls, directory_path: str) -> Dict[str, Any]: 

54 """ 

55 Generates a new Metadata object using prompts for a directory 

56 :param directory_path: The path to the directory for which to generate 

57 the metadata object 

58 :return: The generated metadata object 

59 """ 

60 name = os.path.basename(os.path.abspath(directory_path)) 

61 id_fetcher = cls.create_id_fetcher(directory_path) 

62 print(f"Generating metadata for {name} " 

63 f"(type: {cls.media_type().value}):") 

64 

65 cls.pre_prompt_check(directory_path) 

66 

67 data = { 

68 "type": cls.media_type().value, 

69 "tags": prompt_comma_list("Tags"), 

70 "ids": cls.prompt_ids( 

71 cls.valid_id_types(), 

72 cls.required_id_types(), 

73 {}, 

74 id_fetcher 

75 ) 

76 } 

77 return data 

78 

79 @classmethod 

80 def create_id_fetcher(cls, directory: str) -> IdFetcher: 

81 """ 

82 Creates an ID fetcher 

83 :param directory: The directory for which to generate the ID fetcher 

84 :return: The generated ID fetcher 

85 """ 

86 return IdFetcher( 

87 os.path.basename(os.path.abspath(directory)), 

88 cls.media_type() 

89 ) 

90 

91 @classmethod 

92 def prompt_ids( 

93 cls, 

94 valid_ids: List[IdType], 

95 required_ids: List[IdType], 

96 defaults: Dict[str, List[str]], 

97 id_fetcher: IdFetcher, 

98 mincount: int = 1 

99 ) -> Dict[str, List[str]]: 

100 """ 

101 Prompts the user for any valid IDs the metadata may contain 

102 :param valid_ids: IDs that are valid for the prompt 

103 :param required_ids: IDs that are required to be provided 

104 :param defaults: Any potential default values for the IDs 

105 :param id_fetcher: An ID fetcher 

106 :param mincount: Minimal amount of IDs that the user needs to provide 

107 :return: The IDs in a dictionary mapping the ID names to their IDs 

108 """ 

109 ids = {} # type: Dict[str, List[str]] 

110 for id_type in cls.id_prompt_order: 

111 if id_type not in valid_ids: 

112 continue 

113 else: 

114 defaults = cls._load_default_ids( 

115 valid_ids, defaults, id_fetcher 

116 ) 

117 

118 default = defaults.get(id_type.value) 

119 is_int = id_type in cls.int_id_types() 

120 

121 min_count = 0 

122 if id_type in required_ids: 

123 min_count = 1 

124 

125 prompted = prompt_comma_list( 

126 "{} IDs".format(id_type.value), 

127 min_count=min_count, 

128 default=default, 

129 primitive_type=int if is_int else lambda x: str(x) 

130 ) 

131 prompted = [str(x) for x in prompted] 

132 non_default = prompted != default 

133 

134 if len(prompted) > 0: 

135 ids[id_type.value] = prompted 

136 defaults[id_type.value] = prompted 

137 

138 # Update anilist IDs if myanimelist IDs were updated 

139 if id_type == IdType.MYANIMELIST and non_default: 

140 if IdType.ANILIST.value in defaults: 

141 defaults.pop(IdType.ANILIST.value) 

142 

143 if len(ids) < mincount: 

144 print("Please enter at least {} ID(s)".format(mincount)) 

145 return cls.prompt_ids( 

146 valid_ids, required_ids, defaults, id_fetcher, 

147 mincount=mincount 

148 ) 

149 else: 

150 return ids 

151 

152 @classmethod 

153 def _load_default_ids( 

154 cls, 

155 valid_ids: List[IdType], 

156 defaults: Dict[str, List[str]], 

157 id_fetcher: IdFetcher 

158 ) -> Dict[str, List[str]]: 

159 """ 

160 Tries to load any missing default IDs using the name of the directory 

161 and/or other default IDs 

162 :param valid_ids: List of valid ID types 

163 :param defaults: The current default IDs 

164 :param id_fetcher: An ID fetcher 

165 :return: The updated IDs 

166 """ 

167 for id_type in valid_ids: 

168 if id_type.value in defaults: 

169 continue 

170 else: 

171 _defaults = cls.objectify_ids(defaults) 

172 ids = id_fetcher.fetch_ids(id_type, _defaults) 

173 if ids is not None: 

174 defaults[id_type.value] = ids 

175 

176 return defaults 

177 

178 @classmethod 

179 def prompt_component_ids( 

180 cls, 

181 valid_ids: List[IdType], 

182 previous_ids: Dict[str, List[str]], 

183 id_fetcher: IdFetcher 

184 ) -> Dict[str, List[str]]: 

185 """ 

186 Prompts for IDs for a component (for example, a season of a tv series) 

187 Strips away any IDs that are the same as the root metadata ids 

188 :param valid_ids: ID Types that are valid for the kind of metadata 

189 :param previous_ids: The IDs previously aquired 

190 :param id_fetcher: An ID fetcher 

191 :return: The prompted IDs, mapped to id type strings 

192 """ 

193 

194 defaults = previous_ids.copy() 

195 ids = cls.prompt_ids(valid_ids, [], defaults, id_fetcher, mincount=0) 

196 

197 # Strip unnecessary IDs 

198 for key in list(ids.keys()): 

199 value = ids[key] 

200 if previous_ids.get(key) == value: 

201 ids.pop(key) 

202 

203 return ids 

204 

205 @classmethod 

206 def pre_prompt_check(cls, directory_path: str): 

207 """ 

208 Performs checks before prompting 

209 :return: None 

210 """ 

211 if not os.path.isdir(directory_path): 

212 raise InvalidDirectoryState(f"{directory_path} does not exist")