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
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
31class MusicExtras(MetadataBase, ABC):
32 """
33 Additional methods and attributes for music metadata objects
34 """
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 ))
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}
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
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 ))
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)
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)
92 if "theme_songs" not in self.json:
93 self.json["theme_songs"] = []
95 self.json["theme_songs"].append(theme_song.json)
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:
106 title = song.filename.rsplit(".", 1)[0]
107 if title.split(" - ", 1)[0].isnumeric():
108 title = title.split(" - ", 1)[1]
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
118 song.save_tags()
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")
126 if cover_file is not None and os.path.isfile(cover_file):
128 if song.format != "mp3":
129 continue
131 id3 = ID3(song.path)
133 for key in list(id3.keys()):
134 if str(key).startswith("APIC") \
135 and key != "APIC:Cover":
136 id3.pop(key)
138 if "APIC:Cover" not in id3.keys() or force_art_refresh:
139 with open(cover_file, "rb") as f:
140 img = f.read()
142 apic = APIC(
143 3,
144 "image/jpeg",
145 3,
146 "Cover",
147 img
148 )
149 id3.add(apic)
151 id3.save()