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 json 

22import logging 

23from typing import List, Optional, Union, Type 

24from puffotter.prompt import yn_prompt 

25from toktokkie.metadata.base.Metadata import Metadata 

26from toktokkie.metadata.tv.Tv import Tv 

27from toktokkie.metadata.book.Book import Book 

28from toktokkie.metadata.book_series.BookSeries import BookSeries 

29from toktokkie.metadata.comic.Comic import Comic 

30from toktokkie.metadata.music.Music import Music 

31from toktokkie.metadata.movie.Movie import Movie 

32from toktokkie.metadata.game.Game import Game 

33from toktokkie.enums import MediaType 

34from toktokkie.exceptions import MissingMetadata, InvalidMetadata 

35from puffotter.os import listdir 

36 

37 

38class Directory: 

39 """ 

40 Class that encapsulates all of toktokkie's functionality 

41 """ 

42 

43 logger = logging.getLogger(__file__) 

44 """ 

45 Logger for the directory class 

46 """ 

47 

48 def __init__(self, path: str, no_validation: bool = False): 

49 """ 

50 Initializes the metadata of the directory 

51 :param path: The directory's path 

52 :param no_validation: Disables validation 

53 :except MissingMetadataException, 

54 InvalidMetadataException, 

55 MetadataMismatch 

56 """ 

57 self.metadata = self.get_metadata(path, no_validation) 

58 

59 @property 

60 def path(self) -> str: 

61 """ 

62 :return: The path to the directory 

63 """ 

64 return self.metadata.directory_path 

65 

66 def reload(self): 

67 """ 

68 Reloads the metadata from the metadata file 

69 :return: None 

70 """ 

71 self.metadata = self.get_metadata(self.metadata.directory_path) 

72 

73 def save(self): 

74 """ 

75 Updates the metadata file with the current contents of the metadata 

76 :return: None 

77 """ 

78 self.metadata.write() 

79 

80 @classmethod 

81 def prompt(cls, path: str, metadata_type: Union[str, MediaType]) \ 

82 -> Optional["Directory"]: 

83 """ 

84 Prompts the user for metadata information 

85 :param path: The path to the directory for which to prompt 

86 :param metadata_type: The metadata type to generate 

87 :return: The generated directory, or None if aborted 

88 """ 

89 try: 

90 existing: Optional[Directory] = Directory(path, True) 

91 except (InvalidMetadata, MissingMetadata): 

92 existing = None 

93 

94 if existing is not None: 

95 prompt = yn_prompt("Metadata File already exists. " 

96 "Continuing will delete the previous data. " 

97 "Continue?") 

98 if not prompt: 

99 cls.logger.warning("Aborting") 

100 return None 

101 

102 metadata = cls.create_metadata(path, metadata_type) 

103 metadata.write() 

104 return cls(path) 

105 

106 @classmethod 

107 def load_directories( 

108 cls, 

109 paths: List[str], 

110 restrictions: Optional[List[MediaType]] = None 

111 ) -> List["Directory"]: 

112 """ 

113 Loads the toktokkie Media Directory objects based on paths 

114 :param paths: The directories to turn into toktokkie Directory objs 

115 :param restrictions: Restricts the found media directories to media 

116 directories with a specific media type 

117 :return: The list of Media Directories 

118 """ 

119 if restrictions is None: 

120 restrictions = [x for x in MediaType] 

121 

122 logger = logging.getLogger(cls.__name__) 

123 

124 directories = [] # type: List[Directory] 

125 for path in paths: 

126 try: 

127 logger.debug("Loading directory {}".format(path)) 

128 directory = Directory(path) 

129 if directory.metadata.media_type() not in restrictions: 

130 logger.info( 

131 "Skipping directory {} with incorrect type {}" 

132 .format(path, directory.metadata.media_type()) 

133 ) 

134 continue 

135 directories.append(directory) 

136 

137 except MissingMetadata: 

138 logger.warning("{} has no metadata file.".format(path)) 

139 except InvalidMetadata as e: 

140 logger.warning("{}'s metadata is invalid.".format(path)) 

141 logger.warning(e.reason) 

142 

143 return directories 

144 

145 @classmethod 

146 def load_child_directories( 

147 cls, 

148 parent_dir: str, 

149 restrictions: Optional[List[MediaType]] = None 

150 ) -> List["Directory"]: 

151 """ 

152 Loads all media directories in a directory 

153 :param parent_dir: The directory to search for media directories 

154 :param restrictions: Restricts the found media directories to media 

155 directories with a specific media type 

156 :return: The list of Media Directories 

157 """ 

158 paths = [x[1] for x in listdir(parent_dir)] 

159 return cls.load_directories(paths, restrictions) 

160 

161 @staticmethod 

162 def get_metadata(directory: str, no_validation: bool = False) -> Metadata: 

163 """ 

164 Automatically resolves the metadata of a directory 

165 :param directory: The directory for which to generate the metadata 

166 :param no_validation: Disables Validation 

167 :return: The generated metadata 

168 :raises InvalidMetadataException: If the metadata is invalid 

169 """ 

170 info_file = os.path.join(directory, ".meta/info.json") 

171 try: 

172 with open(info_file, "r") as f: 

173 media_type = json.load(f)["type"] 

174 metadata_cls = Directory.get_metadata_class(media_type) 

175 return metadata_cls(directory, no_validation=no_validation) 

176 except (KeyError, ValueError) as e: 

177 raise InvalidMetadata(f"Missing/Invalid attribute: {e}") 

178 except FileNotFoundError: 

179 raise MissingMetadata() 

180 

181 @staticmethod 

182 def create_metadata(directory: str, media_type: Union[str, MediaType]) \ 

183 -> Metadata: 

184 """ 

185 Generates a new metadata object using user prompts 

186 :param directory: The directory for which to generate the metadata 

187 :param media_type: The media type of the metadata 

188 :return: The generated metadata 

189 """ 

190 metadata_cls = Directory.get_metadata_class(media_type) 

191 metadata = metadata_cls.from_prompt(directory) 

192 metadata.write() 

193 return metadata 

194 

195 @staticmethod 

196 def get_metadata_class(media_type: Union[str, MediaType]) \ 

197 -> Type[Metadata]: 

198 """ 

199 Retrieves the metadata class for a given media type 

200 :param media_type: The media type for which to get the metadata class 

201 :return: The metadata class 

202 """ 

203 if isinstance(media_type, str): 

204 media_type = MediaType(media_type) 

205 

206 mapping = { 

207 x.media_type(): x 

208 for x in [ 

209 Book, 

210 BookSeries, 

211 Comic, 

212 Movie, 

213 Tv, 

214 Music, 

215 Game 

216 ] 

217 } 

218 return mapping[media_type]