Fetches image from track metadata if available.
Introduced new extension variable
folder_image_names
to configure the image names for folder-based cover arts.Fetches folder based image if present and name matches one of the names in
folder_image_names
.Returns a tuple of Image for each uri.
The image uri is base64 encoded so that it is accessible to clients other than localhost.
Added error handling incase _scanner.scan fails
Showing 1 of 3 files from the diff.
mopidy/file/library.py
changed.
Other files ignored by Codecov
mopidy/file/__init__.py
has changed.
mopidy/file/ext.conf
has changed.
@@ -1,10 +1,10 @@
Loading
1 | 1 | import base64 |
|
2 | - | import imghdr |
|
3 | 2 | import logging |
|
4 | 3 | import os |
|
5 | 4 | ||
6 | 5 | from mopidy import backend, exceptions, models |
|
7 | 6 | from mopidy.audio import scan, tags |
|
7 | + | from mopidy.exceptions import ScannerError |
|
8 | 8 | from mopidy.internal import path |
|
9 | 9 | from mopidy.models import Image |
|
10 | 10 |
@@ -30,7 +30,6 @@
Loading
30 | 30 | def __init__(self, backend, config): |
|
31 | 31 | super().__init__(backend) |
|
32 | 32 | self._media_dirs = list(self._get_media_dirs(config)) |
|
33 | - | self._cache_dir = config["core"]["cache_dir"] |
|
34 | 33 | self._show_dotfiles = config["file"]["show_dotfiles"] |
|
35 | 34 | self._excluded_file_extensions = tuple( |
|
36 | 35 | file_ext.lower() |
@@ -40,6 +39,8 @@
Loading
40 | 39 | ||
41 | 40 | self._scanner = scan.Scanner(timeout=config["file"]["metadata_timeout"]) |
|
42 | 41 | ||
42 | + | self._folder_image_names = config["file"].get("folder_image_names", []) |
|
43 | + | ||
43 | 44 | def browse(self, uri): |
|
44 | 45 | logger.debug("Browsing files at: %s", uri) |
|
45 | 46 | result = [] |
@@ -154,68 +155,28 @@
Loading
154 | 155 | def get_images(self, uris): |
|
155 | 156 | images = {} |
|
156 | 157 | for uri in uris: |
|
157 | - | result = self._scanner.scan(uri) |
|
158 | - | if "image" in result.tags and len(result.tags["image"]) > 0: |
|
158 | + | images[uri] = () |
|
159 | + | try: |
|
160 | + | result = self._scanner.scan(uri) |
|
161 | + | except ScannerError as e: |
|
162 | + | logger.warning(str(e)) |
|
163 | + | continue |
|
164 | + | if len(result.tags.get("image", [])) > 0: |
|
159 | 165 | image = result.tags["image"][0] |
|
160 | - | extension = imghdr.what("", h=image) |
|
161 | 166 | images[uri] = ( |
|
162 | 167 | Image( |
|
163 | - | uri=f"data:image/{extension};base64, " |
|
164 | - | f'{base64.b64encode(image).decode("utf-8")}' |
|
165 | - | ), |
|
166 | - | ) |
|
167 | - | elif os.path.exists( |
|
168 | - | os.path.join( |
|
169 | - | os.path.dirname(path.uri_to_path(uri)), "folder.png" |
|
170 | - | ) |
|
171 | - | ): |
|
172 | - | with open( |
|
173 | - | os.path.join( |
|
174 | - | os.path.dirname(path.uri_to_path(uri)), "folder.png" |
|
168 | + | uri=f"data:;base64, {base64.b64encode(image).decode('utf-8')}" |
|
175 | 169 | ), |
|
176 | - | "rb", |
|
177 | - | ) as f: |
|
178 | - | images[uri] = ( |
|
179 | - | Image( |
|
180 | - | uri=f"data:image/png;base64, " |
|
181 | - | f'{base64.b64encode(f.read()).decode("utf-8")}' |
|
182 | - | ), |
|
183 | - | ) |
|
184 | - | elif os.path.exists( |
|
185 | - | os.path.join( |
|
186 | - | os.path.dirname(path.uri_to_path(uri)), "folder.jpg" |
|
187 | 170 | ) |
|
188 | - | ): |
|
189 | - | with open( |
|
190 | - | os.path.join( |
|
191 | - | os.path.dirname(path.uri_to_path(uri)), "folder.jpg" |
|
192 | - | ), |
|
193 | - | "rb", |
|
194 | - | ) as f: |
|
195 | - | images[uri] = ( |
|
196 | - | Image( |
|
197 | - | uri=f"data:image/jpg;base64, " |
|
198 | - | f'{base64.b64encode(f.read()).decode("utf-8")}' |
|
199 | - | ), |
|
200 | - | ) |
|
201 | - | elif os.path.exists( |
|
202 | - | os.path.join( |
|
203 | - | os.path.dirname(path.uri_to_path(uri)), "folder.jpeg" |
|
204 | - | ) |
|
205 | - | ): |
|
206 | - | with open( |
|
207 | - | os.path.join( |
|
208 | - | os.path.dirname(path.uri_to_path(uri)), "folder.jpeg" |
|
209 | - | ), |
|
210 | - | "rb", |
|
211 | - | ) as f: |
|
212 | - | images[uri] = ( |
|
213 | - | Image( |
|
214 | - | uri=f"data:image/jpeg;base64, " |
|
215 | - | f'{base64.b64encode(f.read()).decode("utf-8")}' |
|
216 | - | ), |
|
217 | - | ) |
|
218 | - | ||
219 | - | else: |
|
220 | - | images[uri] = () |
|
171 | + | continue |
|
172 | + | for i in self._folder_image_names: |
|
173 | + | if (path.uri_to_path(uri).parent / i).exists(): |
|
174 | + | with open(path.uri_to_path(uri).parent / i, "rb",) as f: |
|
175 | + | images[uri] = ( |
|
176 | + | Image( |
|
177 | + | uri=f"data:;base64," |
|
178 | + | f"{base64.b64encode(f.read()).decode('utf-8')}" |
|
179 | + | ), |
|
180 | + | ) |
|
181 | + | break |
|
221 | 182 | return images |
Files | Coverage |
---|---|
mopidy | 76.52% |
Project Totals (55 files) | 76.52% |
2195
2193
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file.
The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files.
The size and color of each slice is representing the number of statements and the coverage, respectively.