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 logging 

22import mimetypes 

23from typing import Dict, Any, List 

24from puffotter.os import listdir 

25from toktokkie.enums import IdType 

26from toktokkie.exceptions import InvalidMetadata 

27from toktokkie.metadata.base.IdHelper import IdHelper 

28from toktokkie.metadata.base.components.JsonComponent import JsonComponent 

29from toktokkie.metadata.music.components.MusicSong import MusicSong 

30from toktokkie.metadata.music.components.MusicVideo import MusicVideo 

31 

32 

33class MusicAlbum(JsonComponent): 

34 """ 

35 Class that defines attributes of music albums 

36 """ 

37 

38 def __init__( 

39 self, 

40 parent_path: str, 

41 parent_ids: Dict[IdType, List[str]], 

42 ids: Dict[IdType, List[str]], 

43 name: str, 

44 genre: str, 

45 year: int 

46 ): 

47 """ 

48 Initializes the MusicAlbum object 

49 :param parent_path: The path to the parent directory 

50 :param parent_ids: The IDs associated with the parent 

51 :param ids: The specific IDs for this album 

52 :param name: The name of the album 

53 :param genre: The genre of the album 

54 :param year: The year this album was released 

55 """ 

56 self.logger = logging.getLogger(self.__class__.__name__) 

57 

58 self.parent_path = parent_path 

59 self.parent_ids = parent_ids 

60 

61 self.artist_name = os.path.basename(os.path.abspath(parent_path)) 

62 

63 self.name = name 

64 self.genre = genre 

65 self.year = year 

66 self.path = os.path.join(parent_path, self.name) 

67 

68 self.ids = IdHelper.fill_ids( 

69 ids, [IdType.MUSICBRAINZ_RELEASE], parent_ids 

70 ) 

71 

72 @property 

73 def json(self) -> Dict[str, Any]: 

74 """ 

75 Converts the component into a JSON-compatible dictionary 

76 :return: The JSON-compatible dictionary 

77 """ 

78 return { 

79 "name": self.name, 

80 "genre": self.genre, 

81 "year": self.year, 

82 "ids": IdHelper.stringify_ids( 

83 IdHelper.minimize_ids(self.ids, self.parent_ids) 

84 ) 

85 } 

86 

87 @classmethod 

88 def from_json( 

89 cls, 

90 parent_path: str, 

91 parent_ids: Dict[IdType, List[str]], 

92 json_data: Dict[str, Any] 

93 ) -> "MusicAlbum": 

94 """ 

95 Generates a new MusicAlbum object based on JSON data 

96 :param parent_path: The path to the parent metadata directory 

97 :param parent_ids: The IDs of the parent metadata 

98 :param json_data: The JSON data 

99 :return: The generated object 

100 :raises InvalidMetadataException: If the provided JSON is invalid 

101 """ 

102 try: 

103 return cls( 

104 parent_path, 

105 parent_ids, 

106 IdHelper.objectify_ids(json_data["ids"]), 

107 json_data["name"], 

108 json_data["genre"], 

109 json_data["year"] 

110 ) 

111 except KeyError as e: 

112 raise InvalidMetadata(f"Attribute missing: {e}") 

113 

114 def load_songs(self, tracknumber_sort: bool = True) -> List["MusicSong"]: 

115 """ 

116 Loads songs from this album 

117 :param tracknumber_sort: Whether or not to sort the songs by 

118 tracknumber 

119 Needed to avoid infinite recursion 

120 in MusicSong class 

121 :return: All songs in this album 

122 """ 

123 song_files = self.__get_files("audio") 

124 songs = [MusicSong(x, self) for x in song_files] 

125 songs.sort(key=lambda x: x.path) 

126 if tracknumber_sort: 

127 songs.sort(key=lambda x: x.tracknumber[0]) 

128 return songs 

129 

130 @property 

131 def songs(self) -> List["MusicSong"]: 

132 """ 

133 :return: All songs in this album 

134 """ 

135 return self.load_songs(True) 

136 

137 @property 

138 def videos(self) -> List["MusicVideo"]: 

139 """ 

140 :return: All music videos in this album 

141 """ 

142 video_files = self.__get_files("video") 

143 videos = [MusicVideo(x, self) for x in video_files] 

144 videos.sort(key=lambda x: x.title) 

145 return videos 

146 

147 def __get_files(self, mimetype: str) -> List[str]: 

148 """ 

149 Retrieves the files in the album directory based on their mime type 

150 :param mimetype: The mime type of the files to find 

151 :return: The files with that mimetype 

152 """ 

153 files = [] 

154 for _file, path in listdir(self.path, no_dirs=True): 

155 guess = str(mimetypes.MimeTypes().guess_type(path)[0]) 

156 if guess.startswith(mimetype): 

157 files.append(path) 

158 return files