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 List 

23# noinspection PyUnresolvedReferences,PyProtectedMember 

24from mutagen.id3 import ID3, APIC 

25from toktokkie.metadata.music.components.MusicAlbum import MusicAlbum 

26from toktokkie.metadata.music.components.MusicThemeSong import \ 

27 MusicThemeSong 

28from toktokkie.metadata.base.MetadataBase import MetadataBase 

29 

30 

31class MusicExtras(MetadataBase, ABC): 

32 """ 

33 Additional methods and attributes for music metadata objects 

34 """ 

35 

36 @property 

37 def albums(self) -> List[MusicAlbum]: 

38 """ 

39 :return: All albums in the metadata, regardless if they're just normal 

40 albums or theme songs 

41 """ 

42 return list(map( 

43 lambda x: MusicAlbum.from_json(self.directory_path, self.ids, x), 

44 self.json["albums"] 

45 )) 

46 

47 @property 

48 def theme_songs(self) -> List[MusicThemeSong]: 

49 """ 

50 :return: All theme songs for this music artist 

51 """ 

52 album_map = {x.name: x for x in self.albums} 

53 

54 theme_songs = [] 

55 for theme_song in self.json.get("theme_songs", []): 

56 name = theme_song["name"] 

57 album = album_map[name] 

58 theme_songs.append(MusicThemeSong.from_json(album, theme_song)) 

59 return theme_songs 

60 

61 @property 

62 def non_theme_song_albums(self) -> List[MusicAlbum]: 

63 """ 

64 :return: Any albums that are not also theme songs 

65 """ 

66 theme_song_names = list(map(lambda x: x.name, self.theme_songs)) 

67 return list(filter( 

68 lambda x: x.name not in theme_song_names, 

69 self.albums 

70 )) 

71 

72 def add_album(self, album_data: MusicAlbum): 

73 """ 

74 Adds an album to the metadata 

75 :param album_data: The album metadata to add 

76 :return: None 

77 """ 

78 existing = list(map(lambda x: x.name, self.albums)) 

79 if album_data.name not in existing: 

80 self.json["albums"].append(album_data.json) 

81 

82 def add_theme_song(self, theme_song: MusicThemeSong): 

83 """ 

84 Adds a theme song to the metadata 

85 :param theme_song: The theme song to add 

86 :return: None 

87 """ 

88 existing = list(map(lambda x: x.name, self.theme_songs)) 

89 if theme_song.name not in existing: 

90 self.add_album(theme_song.album) 

91 

92 if "theme_songs" not in self.json: 

93 self.json["theme_songs"] = [] 

94 

95 self.json["theme_songs"].append(theme_song.json) 

96 

97 def apply_tags(self, force_art_refresh: bool = False): 

98 """ 

99 Applies MP3 tags 

100 :param force_art_refresh: Forces refresh of cover art 

101 :return: None 

102 """ 

103 for album in self.albums: 

104 for song in album.songs: 

105 

106 title = song.filename.rsplit(".", 1)[0] 

107 if title.split(" - ", 1)[0].isnumeric(): 

108 title = title.split(" - ", 1)[1] 

109 

110 song.title = title 

111 song.artist_name = album.artist_name 

112 song.album_artist_name = album.artist_name 

113 song.album_name = album.name 

114 song.year = album.year 

115 song.genre = album.genre 

116 song.tracknumber = song.tracknumber 

117 

118 song.save_tags() 

119 

120 cover_file = self.get_icon_file(album.name) 

121 if cover_file is None: 

122 self.logger.warning("No specific cover file for {}" 

123 .format(album.name)) 

124 cover_file = self.get_icon_file("main") 

125 

126 if cover_file is not None and os.path.isfile(cover_file): 

127 

128 if song.format != "mp3": 

129 continue 

130 

131 id3 = ID3(song.path) 

132 

133 for key in list(id3.keys()): 

134 if str(key).startswith("APIC") \ 

135 and key != "APIC:Cover": 

136 id3.pop(key) 

137 

138 if "APIC:Cover" not in id3.keys() or force_art_refresh: 

139 with open(cover_file, "rb") as f: 

140 img = f.read() 

141 

142 apic = APIC( 

143 3, 

144 "image/jpeg", 

145 3, 

146 "Cover", 

147 img 

148 ) 

149 id3.add(apic) 

150 

151 id3.save()