]> git.0d.be Git - botaradio.git/blob - media/file.py
781e39c5a6609dcc1093b13c033676a312b62951
[botaradio.git] / media / file.py
1 import logging
2 import os
3 import re
4 from io import BytesIO
5 import base64
6 import hashlib
7 import mutagen
8 from PIL import Image
9 import json
10
11 import util
12 import variables as var
13 from media.item import BaseItem, item_builders, item_loaders, item_id_generators
14 import constants
15
16 '''
17 type : file
18     id
19     path
20     title
21     artist
22     duration
23     thumbnail
24     user
25 '''
26
27 def file_item_builder(bot, **kwargs):
28     return FileItem(bot, kwargs['path'])
29
30 def file_item_loader(bot, _dict):
31     return FileItem(bot, "", _dict)
32
33 def file_item_id_generator(**kwargs):
34     return hashlib.md5(kwargs['path'].encode()).hexdigest()
35
36 item_builders['file'] = file_item_builder
37 item_loaders['file'] = file_item_loader
38 item_id_generators['file'] = file_item_id_generator
39
40
41 class FileItem(BaseItem):
42     def __init__(self, bot, path, from_dict=None):
43         if not from_dict:
44             super().__init__(bot)
45             self.path = path
46             self.title = ""
47             self.artist = ""
48             self.thumbnail = None
49             if self.path:
50                 self.id = hashlib.md5(path.encode()).hexdigest()
51                 if os.path.exists(self.uri()):
52                     self._get_info_from_tag()
53                     self.ready = "yes"
54         else:
55             super().__init__(bot, from_dict)
56             self.path = from_dict['path']
57             self.title = from_dict['title']
58             self.artist = from_dict['artist']
59             self.thumbnail = from_dict['thumbnail']
60             if not self.validate():
61                 self.ready = "failed"
62
63         self.type = "file"
64
65     def uri(self):
66         return var.music_folder + self.path if self.path[0] != "/" else self.path
67
68     def is_ready(self):
69         return True
70
71     def validate(self):
72         if not os.path.exists(self.uri()):
73             self.log.info(
74                 "file: music file missed for %s" % self.format_debug_string())
75             self.send_client_message(constants.strings('file_missed', file=self.path))
76             return False
77
78         self.version = 1 # 0 -> 1, notify the wrapper to save me when validate() is visited the first time
79         self.ready = "yes"
80         return True
81
82     def _get_info_from_tag(self):
83         match = re.search("(.+)\.(.+)", self.uri())
84         assert match is not None
85
86         file_no_ext = match[1]
87         ext = match[2]
88
89         try:
90             im = None
91             path_thumbnail = file_no_ext + ".jpg"
92             if os.path.isfile(path_thumbnail):
93                 im = Image.open(path_thumbnail)
94
95             if ext == "mp3":
96                 # title: TIT2
97                 # artist: TPE1, TPE2
98                 # album: TALB
99                 # cover artwork: APIC:
100                 tags = mutagen.File(self.uri())
101                 if 'TIT2' in tags:
102                     self.title = tags['TIT2'].text[0]
103                 if 'TPE1' in tags:  # artist
104                     self.artist = tags['TPE1'].text[0]
105
106                 if im is None:
107                     if "APIC:" in tags:
108                         im = Image.open(BytesIO(tags["APIC:"].data))
109
110             elif ext == "m4a" or ext == "m4b" or ext == "mp4" or ext == "m4p":
111                 # title: ©nam (\xa9nam)
112                 # artist: ©ART
113                 # album: ©alb
114                 # cover artwork: covr
115                 tags = mutagen.File(self.uri())
116                 if '©nam' in tags:
117                     self.title = tags['©nam'][0]
118                 if '©ART' in tags:  # artist
119                     self.artist = tags['©ART'][0]
120
121                 if im is None:
122                     if "covr" in tags:
123                         im = Image.open(BytesIO(tags["covr"][0]))
124
125             if im:
126                 self.thumbnail = self._prepare_thumbnail(im)
127         except:
128             pass
129
130         if not self.title:
131             self.title = os.path.basename(file_no_ext)
132
133     def _prepare_thumbnail(self, im):
134         im.thumbnail((100, 100), Image.ANTIALIAS)
135         buffer = BytesIO()
136         im = im.convert('RGB')
137         im.save(buffer, format="JPEG")
138         return base64.b64encode(buffer.getvalue()).decode('utf-8')
139
140     def to_dict(self):
141         dict = super().to_dict()
142         dict['type'] = 'file'
143         dict['path'] = self.path
144         dict['title'] = self.title
145         dict['artist'] = self.artist
146         dict['thumbnail'] = self.thumbnail
147         return dict
148
149     def format_debug_string(self):
150         return "[file] {descrip} ({path})".format(
151             descrip=self.format_short_string(),
152             path=self.path
153         )
154
155     def format_song_string(self, user):
156         return constants.strings("file_item",
157                                     title=self.title,
158                                     artist=self.artist,
159                                     user=user
160                                     )
161
162     def format_current_playing(self, user):
163         display = constants.strings("now_playing", item=self.format_song_string(user))
164         if self.thumbnail:
165             thumbnail_html = '<img width="80" src="data:image/jpge;base64,' + \
166                              self.thumbnail + '"/>'
167             display += "<br />" +  thumbnail_html
168
169         return display
170
171     def format_short_string(self):
172         title = self.title if self.title else self.path
173         if self.artist:
174             return self.artist + " - " + title
175         else:
176             return title
177
178     def display_type(self):
179         return constants.strings("file")