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>
4This file is part of toktokkie.
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.
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.
16You should have received a copy of the GNU General Public License
17along with toktokkie. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
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
38class Directory:
39 """
40 Class that encapsulates all of toktokkie's functionality
41 """
43 logger = logging.getLogger(__file__)
44 """
45 Logger for the directory class
46 """
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)
59 @property
60 def path(self) -> str:
61 """
62 :return: The path to the directory
63 """
64 return self.metadata.directory_path
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)
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()
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
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
102 metadata = cls.create_metadata(path, metadata_type)
103 metadata.write()
104 return cls(path)
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]
122 logger = logging.getLogger(cls.__name__)
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)
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)
143 return directories
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)
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()
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
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)
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]