Added support for replaygain option
This commit is contained in:
@@ -11,6 +11,7 @@ gi.require_version('Notify', '0.7')
|
||||
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk, Notify
|
||||
|
||||
from .cache_manager import CacheManager
|
||||
from .config import ReplayGainType
|
||||
from .dbus_manager import dbus_propagate, DBusManager
|
||||
from .players import ChromecastPlayer, MPVPlayer, PlayerEvent
|
||||
from .server.api_objects import Child, Directory, Playlist
|
||||
@@ -431,6 +432,8 @@ class SublimeMusicApp(Gtk.Application):
|
||||
'prefetch_amount'].get_value_as_int()
|
||||
self.state.config.concurrent_download_limit = dialog.data[
|
||||
'concurrent_download_limit'].get_value_as_int()
|
||||
self.state.config.replay_gain = ReplayGainType.from_string(
|
||||
dialog.data['replay_gain'].get_active_id())
|
||||
self.state.save_config()
|
||||
self.reset_state()
|
||||
dialog.destroy()
|
||||
|
@@ -348,6 +348,7 @@ class CacheManager(metaclass=Singleton):
|
||||
logging.info('Migrating cache to version 1.')
|
||||
cover_art_re = re.compile(r'(\d+)_(\d+)')
|
||||
abs_path = self.calculate_abs_path('cover_art/')
|
||||
abs_path.mkdir(parents=True, exist_ok=True)
|
||||
for cover_art_file in Path(abs_path).iterdir():
|
||||
match = cover_art_re.match(cover_art_file.name)
|
||||
if match:
|
||||
|
@@ -1,10 +1,29 @@
|
||||
import logging
|
||||
import os
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import keyring
|
||||
|
||||
|
||||
class ReplayGainType(Enum):
|
||||
NO = 0
|
||||
TRACK = 1
|
||||
ALBUM = 2
|
||||
|
||||
def as_string(self) -> str:
|
||||
return ['no', 'track', 'album'][self.value]
|
||||
|
||||
@staticmethod
|
||||
def from_string(replay_gain_type: str) -> 'ReplayGainType':
|
||||
return {
|
||||
'no': ReplayGainType.NO,
|
||||
'disabled': ReplayGainType.NO,
|
||||
'track': ReplayGainType.TRACK,
|
||||
'album': ReplayGainType.ALBUM,
|
||||
}[replay_gain_type.lower()]
|
||||
|
||||
|
||||
class ServerConfiguration:
|
||||
version: int
|
||||
name: str
|
||||
@@ -61,19 +80,23 @@ class AppConfiguration:
|
||||
prefetch_amount: int = 3
|
||||
concurrent_download_limit: int = 5
|
||||
port_number: int = 8282
|
||||
version: int = 2
|
||||
version: int = 3
|
||||
serve_over_lan: bool = True
|
||||
replay_gain: ReplayGainType = ReplayGainType.NO
|
||||
|
||||
def to_json(self) -> Dict[str, Any]:
|
||||
exclude = ('servers')
|
||||
exclude = ('servers', 'replay_gain')
|
||||
json_object = {
|
||||
k: getattr(self, k)
|
||||
for k in self.__annotations__.keys()
|
||||
if k not in exclude
|
||||
}
|
||||
json_object.update({
|
||||
'servers': [s.__dict__ for s in self.servers],
|
||||
})
|
||||
json_object.update(
|
||||
{
|
||||
'servers': [s.__dict__ for s in self.servers],
|
||||
'replay_gain':
|
||||
getattr(self, 'replay_gain', ReplayGainType.NO).value,
|
||||
})
|
||||
return json_object
|
||||
|
||||
def migrate(self):
|
||||
@@ -85,7 +108,12 @@ class AppConfiguration:
|
||||
logging.info('Setting serve_over_lan to True')
|
||||
self.serve_over_lan = True
|
||||
|
||||
self.version = 2
|
||||
if (getattr(self, 'version') or 0) < 3:
|
||||
logging.info('Migrating app configuration to version 3.')
|
||||
logging.info('Setting replay_gain to ReplayGainType.NO')
|
||||
self.replay_gain = ReplayGainType.NO
|
||||
|
||||
self.version = 3
|
||||
|
||||
@property
|
||||
def cache_location(self) -> str:
|
||||
|
@@ -123,11 +123,19 @@ class Player:
|
||||
|
||||
|
||||
class MPVPlayer(Player):
|
||||
def __init__(self, *args):
|
||||
super().__init__(*args)
|
||||
def __init__(
|
||||
self,
|
||||
on_timepos_change: Callable[[Optional[float]], None],
|
||||
on_track_end: Callable[[], None],
|
||||
on_player_event: Callable[[PlayerEvent], None],
|
||||
config: AppConfiguration,
|
||||
):
|
||||
super().__init__(
|
||||
on_timepos_change, on_track_end, on_player_event, config)
|
||||
|
||||
self.mpv = mpv.MPV()
|
||||
self.mpv.audio_client_name = 'sublime-music'
|
||||
self.mpv.replaygain = config.replay_gain.as_string()
|
||||
self.progress_value_lock = threading.Lock()
|
||||
self.progress_value_count = 0
|
||||
self._muted = False
|
||||
@@ -301,11 +309,7 @@ class ChromecastPlayer(Player):
|
||||
config: AppConfiguration,
|
||||
):
|
||||
super().__init__(
|
||||
on_timepos_change,
|
||||
on_track_end,
|
||||
on_player_event,
|
||||
config,
|
||||
)
|
||||
on_timepos_change, on_track_end, on_player_event, config)
|
||||
self._timepos = 0.0
|
||||
self.time_incrementor_running = False
|
||||
self._can_hotswap_source = False
|
||||
|
@@ -160,16 +160,22 @@ class ApplicationState:
|
||||
os.makedirs(os.path.dirname(self.state_filename), exist_ok=True)
|
||||
|
||||
# Save the state
|
||||
state_json = json.dumps(self.to_json(), indent=2, sort_keys=True)
|
||||
if not state_json:
|
||||
return
|
||||
with open(self.state_filename, 'w+') as f:
|
||||
f.write(json.dumps(self.to_json(), indent=2, sort_keys=True))
|
||||
f.write(state_json)
|
||||
|
||||
def save_config(self):
|
||||
# Make the necessary directories before writing the config.
|
||||
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
||||
|
||||
config_json = json.dumps(
|
||||
self.config.to_json(), indent=2, sort_keys=True)
|
||||
if not config_json:
|
||||
return
|
||||
with open(self.config_file, 'w+') as f:
|
||||
f.write(
|
||||
json.dumps(self.config.to_json(), indent=2, sort_keys=True))
|
||||
f.write(config_json)
|
||||
|
||||
def get_config(self, filename: str) -> AppConfiguration:
|
||||
if not os.path.exists(filename):
|
||||
|
@@ -4,16 +4,20 @@ import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gtk
|
||||
|
||||
TextFieldDescription = Tuple[str, str, bool]
|
||||
BooleanFieldDescription = Tuple[str, str]
|
||||
NumericFieldDescription = Tuple[str, str, Tuple[int, int, int], int]
|
||||
OptionFieldDescription = Tuple[str, str, Tuple[str, ...]]
|
||||
|
||||
|
||||
class EditFormDialog(Gtk.Dialog):
|
||||
entity_name: str
|
||||
title: str
|
||||
initial_size: Tuple[int, int]
|
||||
text_fields: List[Tuple[str, str, bool]] = []
|
||||
boolean_fields: List[Tuple[str, str]] = []
|
||||
text_fields: List[TextFieldDescription] = []
|
||||
boolean_fields: List[BooleanFieldDescription] = []
|
||||
numeric_fields: List[NumericFieldDescription] = []
|
||||
option_fields: List[OptionFieldDescription] = []
|
||||
extra_label: Optional[str] = None
|
||||
extra_buttons: List[Gtk.Button] = []
|
||||
|
||||
@@ -75,6 +79,30 @@ class EditFormDialog(Gtk.Dialog):
|
||||
|
||||
i += 1
|
||||
|
||||
for label, value_field_name, options in self.option_fields:
|
||||
entry_label = Gtk.Label(label=label + ':')
|
||||
entry_label.set_halign(Gtk.Align.START)
|
||||
content_grid.attach(entry_label, 0, i, 1, 1)
|
||||
|
||||
options_store = Gtk.ListStore(str)
|
||||
for option in options:
|
||||
options_store.append([option])
|
||||
|
||||
combo = Gtk.ComboBox.new_with_model(options_store)
|
||||
combo.set_id_column(0)
|
||||
renderer_text = Gtk.CellRendererText()
|
||||
combo.pack_start(renderer_text, True)
|
||||
combo.add_attribute(renderer_text, "text", 0)
|
||||
|
||||
field_value = getattr(existing_object, value_field_name)
|
||||
if field_value:
|
||||
combo.set_active(field_value.value)
|
||||
|
||||
content_grid.attach(combo, 1, i, 1, 1)
|
||||
self.data[value_field_name] = combo
|
||||
|
||||
i += 1
|
||||
|
||||
# Add the boolean entries to the content area.
|
||||
for label, value_field_name in self.boolean_fields:
|
||||
entry_label = Gtk.Label(label=label + ':')
|
||||
|
@@ -41,6 +41,9 @@ class SettingsDialog(EditFormDialog):
|
||||
5,
|
||||
),
|
||||
]
|
||||
option_fields = [
|
||||
('Replay Gain', 'replay_gain', ('Disabled', 'Track', 'Album')),
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.extra_label = Gtk.Label(
|
||||
|
Reference in New Issue
Block a user