Merge branch 'master' of gitlab.com:sumner/sublime-music
This commit is contained in:
@@ -855,16 +855,19 @@ class SublimeMusicApp(Gtk.Application):
|
|||||||
if order_token != self.song_playing_order_token:
|
if order_token != self.song_playing_order_token:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add the image to the notification, and re-draw the
|
# Add the image to the notification, and re-show the
|
||||||
# notification.
|
# notification.
|
||||||
song_notification.set_image_from_pixbuf(
|
song_notification.set_image_from_pixbuf(
|
||||||
GdkPixbuf.Pixbuf.new_from_file(cover_art_filename))
|
GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||||
|
cover_art_filename, 70, 70, True))
|
||||||
song_notification.show()
|
song_notification.show()
|
||||||
|
|
||||||
def get_cover_art_filename(order_token):
|
def get_cover_art_filename(order_token):
|
||||||
cover_art_future = CacheManager.get_cover_art_filename(
|
return (
|
||||||
song.coverArt, size=70)
|
CacheManager.get_cover_art_filename(
|
||||||
return cover_art_future.result(), order_token
|
song.coverArt).result(),
|
||||||
|
order_token,
|
||||||
|
)
|
||||||
|
|
||||||
self.song_playing_order_token += 1
|
self.song_playing_order_token += 1
|
||||||
cover_art_future = CacheManager.create_future(
|
cover_art_future = CacheManager.create_future(
|
||||||
|
@@ -4,6 +4,7 @@ import itertools
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import threading
|
import threading
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@@ -252,6 +253,7 @@ class CacheManager(metaclass=Singleton):
|
|||||||
CacheManager.should_exit = True
|
CacheManager.should_exit = True
|
||||||
logging.info('CacheManager shutdown start')
|
logging.info('CacheManager shutdown start')
|
||||||
CacheManager.executor.shutdown()
|
CacheManager.executor.shutdown()
|
||||||
|
CacheManager._instance.save_cache_info()
|
||||||
logging.info('CacheManager shutdown complete')
|
logging.info('CacheManager shutdown complete')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -335,7 +337,30 @@ class CacheManager(metaclass=Singleton):
|
|||||||
try:
|
try:
|
||||||
meta_json = json.load(f)
|
meta_json = json.load(f)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
return
|
# Just continue with the default meta_json.
|
||||||
|
pass
|
||||||
|
|
||||||
|
cache_version = meta_json.get('version', 0)
|
||||||
|
|
||||||
|
if cache_version < 1:
|
||||||
|
logging.info('Migrating cache to version 1.')
|
||||||
|
cover_art_re = re.compile(r'(\d+)_(\d+)')
|
||||||
|
abs_path = self.calculate_abs_path('cover_art/')
|
||||||
|
for cover_art_file in Path(abs_path).iterdir():
|
||||||
|
match = cover_art_re.match(cover_art_file.name)
|
||||||
|
if match:
|
||||||
|
art_id, dimensions = map(int, match.groups())
|
||||||
|
if dimensions == 1000:
|
||||||
|
no_dimens = cover_art_file.parent.joinpath(
|
||||||
|
'{art_id}')
|
||||||
|
logging.debug(
|
||||||
|
f'Moving {cover_art_file} to {no_dimens}')
|
||||||
|
shutil.move(cover_art_file, no_dimens)
|
||||||
|
else:
|
||||||
|
logging.debug(f'Deleting {cover_art_file}')
|
||||||
|
cover_art_file.unlink()
|
||||||
|
|
||||||
|
self.cache['version'] = 1
|
||||||
|
|
||||||
cache_configs = [
|
cache_configs = [
|
||||||
# Playlists
|
# Playlists
|
||||||
@@ -476,7 +501,7 @@ class CacheManager(metaclass=Singleton):
|
|||||||
return CacheManager.executor.submit(fn, *args)
|
return CacheManager.executor.submit(fn, *args)
|
||||||
|
|
||||||
def delete_cached_cover_art(self, id: int):
|
def delete_cached_cover_art(self, id: int):
|
||||||
relative_path = f'cover_art/*{id}_*'
|
relative_path = f'cover_art/*{id}*'
|
||||||
|
|
||||||
abs_path = self.calculate_abs_path(relative_path)
|
abs_path = self.calculate_abs_path(relative_path)
|
||||||
|
|
||||||
@@ -698,12 +723,12 @@ class CacheManager(metaclass=Singleton):
|
|||||||
'2a96cbd8b46e442fc41c2b86b821562f.png')):
|
'2a96cbd8b46e442fc41c2b86b821562f.png')):
|
||||||
if isinstance(artist, (ArtistWithAlbumsID3, ArtistID3)):
|
if isinstance(artist, (ArtistWithAlbumsID3, ArtistID3)):
|
||||||
return CacheManager.get_cover_art_filename(
|
return CacheManager.get_cover_art_filename(
|
||||||
artist.coverArt, size=300)
|
artist.coverArt)
|
||||||
elif (isinstance(artist, Directory)
|
elif (isinstance(artist, Directory)
|
||||||
and len(artist.child) > 0):
|
and len(artist.child) > 0):
|
||||||
# Retrieve the first album's cover art
|
# Retrieve the first album's cover art
|
||||||
return CacheManager.get_cover_art_filename(
|
return CacheManager.get_cover_art_filename(
|
||||||
artist.child[0].coverArt, size=300)
|
artist.child[0].coverArt)
|
||||||
|
|
||||||
if lastfm_url == '':
|
if lastfm_url == '':
|
||||||
return CacheManager.Result.from_data('')
|
return CacheManager.Result.from_data('')
|
||||||
@@ -876,17 +901,16 @@ class CacheManager(metaclass=Singleton):
|
|||||||
self,
|
self,
|
||||||
id: str,
|
id: str,
|
||||||
before_download: Callable[[], None] = lambda: None,
|
before_download: Callable[[], None] = lambda: None,
|
||||||
size: Union[str, int] = 200,
|
|
||||||
force: bool = False,
|
force: bool = False,
|
||||||
allow_download: bool = True,
|
allow_download: bool = True,
|
||||||
) -> 'CacheManager.Result[Optional[str]]':
|
) -> 'CacheManager.Result[Optional[str]]':
|
||||||
if id is None:
|
if id is None:
|
||||||
art_path = 'ui/images/default-album-art.png'
|
default_art_path = 'ui/images/default-album-art.png'
|
||||||
return CacheManager.Result.from_data(
|
return CacheManager.Result.from_data(
|
||||||
str(Path(__file__).parent.joinpath(art_path)))
|
str(Path(__file__).parent.joinpath(default_art_path)))
|
||||||
return self.return_cached_or_download(
|
return self.return_cached_or_download(
|
||||||
f'cover_art/{id}_{size}',
|
f'cover_art/{id}',
|
||||||
lambda: self.server.get_cover_art(id, str(size)),
|
lambda: self.server.get_cover_art(id),
|
||||||
before_download=before_download,
|
before_download=before_download,
|
||||||
force=force,
|
force=force,
|
||||||
allow_download=allow_download,
|
allow_download=allow_download,
|
||||||
|
@@ -277,11 +277,11 @@ class DBusManager:
|
|||||||
'x',
|
'x',
|
||||||
(song.duration or 0) * self.second_microsecond_conversion,
|
(song.duration or 0) * self.second_microsecond_conversion,
|
||||||
)
|
)
|
||||||
track_cover = CacheManager.get_cover_art_url(song.coverArt, 1000)
|
|
||||||
return {
|
return {
|
||||||
'mpris:trackid': trackid,
|
'mpris:trackid': trackid,
|
||||||
'mpris:length': duration,
|
'mpris:length': duration,
|
||||||
'mpris:artUrl': track_cover,
|
'mpris:artUrl': CacheManager.get_cover_art_url(song.coverArt),
|
||||||
'xesam:album': song.album or '',
|
'xesam:album': song.album or '',
|
||||||
'xesam:albumArtist': [song.artist or ''],
|
'xesam:albumArtist': [song.artist or ''],
|
||||||
'xesam:artist': [song.artist or ''],
|
'xesam:artist': [song.artist or ''],
|
||||||
|
@@ -428,7 +428,7 @@ class ChromecastPlayer(Player):
|
|||||||
force_stream=True,
|
force_stream=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
cover_art_url = CacheManager.get_cover_art_url(song.coverArt, 1000)
|
cover_art_url = CacheManager.get_cover_art_url(song.coverArt)
|
||||||
self.chromecast.media_controller.play_media(
|
self.chromecast.media_controller.play_media(
|
||||||
file_or_url,
|
file_or_url,
|
||||||
# Just pretend that whatever we send it is mp3, even if it isn't.
|
# Just pretend that whatever we send it is mp3, even if it isn't.
|
||||||
|
@@ -840,7 +840,7 @@ class Server:
|
|||||||
"""
|
"""
|
||||||
return self.do_download(self._make_url('download'), id=id)
|
return self.do_download(self._make_url('download'), id=id)
|
||||||
|
|
||||||
def get_cover_art(self, id: str, size: str = None) -> bytes:
|
def get_cover_art(self, id: str, size: int = 1000):
|
||||||
"""
|
"""
|
||||||
Returns the cover art image in binary form.
|
Returns the cover art image in binary form.
|
||||||
|
|
||||||
@@ -850,9 +850,9 @@ class Server:
|
|||||||
return self.do_download(
|
return self.do_download(
|
||||||
self._make_url('getCoverArt'), id=id, size=size)
|
self._make_url('getCoverArt'), id=id, size=size)
|
||||||
|
|
||||||
def get_cover_art_url(self, id: str, size: str = None) -> str:
|
def get_cover_art_url(self, id: str, size: int = 1000):
|
||||||
"""
|
"""
|
||||||
Returns the cover art image in binary form.
|
Returns the URL of the cover art image.
|
||||||
|
|
||||||
:param id: The ID of a song, album or artist.
|
:param id: The ID of a song, album or artist.
|
||||||
:param size: If specified, scale image to this size.
|
:param size: If specified, scale image to this size.
|
||||||
|
@@ -183,6 +183,7 @@ class ArtistDetailPanel(Gtk.Box):
|
|||||||
loading=False,
|
loading=False,
|
||||||
image_name='artist-album-artwork',
|
image_name='artist-album-artwork',
|
||||||
spinner_name='artist-artwork-spinner',
|
spinner_name='artist-artwork-spinner',
|
||||||
|
image_size=300,
|
||||||
)
|
)
|
||||||
self.big_info_panel.pack_start(self.artist_artwork, False, False, 0)
|
self.big_info_panel.pack_start(self.artist_artwork, False, False, 0)
|
||||||
|
|
||||||
|
@@ -57,7 +57,6 @@ class AlbumWithSongs(Gtk.Box):
|
|||||||
cover_art_filename_future = CacheManager.get_cover_art_filename(
|
cover_art_filename_future = CacheManager.get_cover_art_filename(
|
||||||
album.coverArt,
|
album.coverArt,
|
||||||
before_download=lambda: artist_artwork.set_loading(True),
|
before_download=lambda: artist_artwork.set_loading(True),
|
||||||
size=cover_art_size,
|
|
||||||
)
|
)
|
||||||
cover_art_filename_future.add_done_callback(
|
cover_art_filename_future.add_done_callback(
|
||||||
lambda f: GLib.idle_add(cover_art_future_done, f))
|
lambda f: GLib.idle_add(cover_art_future_done, f))
|
||||||
|
@@ -391,7 +391,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
|||||||
util.esc(song.artist),
|
util.esc(song.artist),
|
||||||
)
|
)
|
||||||
cover_art_future = CacheManager.get_cover_art_filename(
|
cover_art_future = CacheManager.get_cover_art_filename(
|
||||||
song.coverArt, size=50)
|
song.coverArt)
|
||||||
self.song_results.add(
|
self.song_results.add(
|
||||||
self._create_search_result_row(
|
self._create_search_result_row(
|
||||||
label_text, 'song', song, cover_art_future))
|
label_text, 'song', song, cover_art_future))
|
||||||
@@ -407,7 +407,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
|||||||
util.esc(album.artist),
|
util.esc(album.artist),
|
||||||
)
|
)
|
||||||
cover_art_future = CacheManager.get_cover_art_filename(
|
cover_art_future = CacheManager.get_cover_art_filename(
|
||||||
album.coverArt, size=50)
|
album.coverArt)
|
||||||
self.album_results.add(
|
self.album_results.add(
|
||||||
self._create_search_result_row(
|
self._create_search_result_row(
|
||||||
label_text, 'album', album, cover_art_future))
|
label_text, 'album', album, cover_art_future))
|
||||||
|
@@ -137,7 +137,6 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
self.cover_art_update_order_token += 1
|
self.cover_art_update_order_token += 1
|
||||||
self.update_cover_art(
|
self.update_cover_art(
|
||||||
state.current_song.coverArt,
|
state.current_song.coverArt,
|
||||||
size='70',
|
|
||||||
order_token=self.cover_art_update_order_token,
|
order_token=self.cover_art_update_order_token,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -194,9 +193,7 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
|
|
||||||
# Cover Art
|
# Cover Art
|
||||||
cover_art_future = CacheManager.get_cover_art_filename(
|
cover_art_future = CacheManager.get_cover_art_filename(
|
||||||
song_details.coverArt,
|
song_details.coverArt)
|
||||||
size=50,
|
|
||||||
)
|
|
||||||
if cover_art_result.is_future:
|
if cover_art_result.is_future:
|
||||||
# We don't have the cover art already cached.
|
# We don't have the cover art already cached.
|
||||||
cover_art_result.add_done_callback(
|
cover_art_result.add_done_callback(
|
||||||
@@ -227,9 +224,7 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
label = calculate_label(song_details)
|
label = calculate_label(song_details)
|
||||||
|
|
||||||
cover_art_result = CacheManager.get_cover_art_filename(
|
cover_art_result = CacheManager.get_cover_art_filename(
|
||||||
song_details.coverArt,
|
song_details.coverArt)
|
||||||
size=50,
|
|
||||||
)
|
|
||||||
if cover_art_result.is_future:
|
if cover_art_result.is_future:
|
||||||
# We don't have the cover art already cached.
|
# We don't have the cover art already cached.
|
||||||
cover_art_result.add_done_callback(
|
cover_art_result.add_done_callback(
|
||||||
@@ -655,7 +650,8 @@ class PlayerControls(Gtk.ActionBar):
|
|||||||
if not filename:
|
if not filename:
|
||||||
cell.set_property('icon_name', '')
|
cell.set_property('icon_name', '')
|
||||||
return
|
return
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(
|
||||||
|
filename, 50, 50, True)
|
||||||
|
|
||||||
# If this is the playing song, then overlay the play icon.
|
# If this is the playing song, then overlay the play icon.
|
||||||
if model.get_value(iter, 2):
|
if model.get_value(iter, 2):
|
||||||
|
@@ -264,6 +264,7 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
|||||||
self.playlist_artwork = SpinnerImage(
|
self.playlist_artwork = SpinnerImage(
|
||||||
image_name='playlist-album-artwork',
|
image_name='playlist-album-artwork',
|
||||||
spinner_name='playlist-artwork-spinner',
|
spinner_name='playlist-artwork-spinner',
|
||||||
|
image_size=200,
|
||||||
)
|
)
|
||||||
self.big_info_panel.pack_start(self.playlist_artwork, False, False, 0)
|
self.big_info_panel.pack_start(self.playlist_artwork, False, False, 0)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user