import logging
import os
import random
import shutil
import sys
from datetime import timedelta
from functools import partial
from pathlib import Path
from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple
from urllib.parse import urlparse
import bleach
try:
import osxmmkeys
tap_imported = True
except Exception:
tap_imported = False
from gi.repository import Gdk, GdkPixbuf, Gio, GLib, Gtk
try:
import gi
gi.require_version("Notify", "0.7")
from gi.repository import Notify
glib_notify_exists = True
except Exception:
# I really don't care what kind of exception it is, all that matters is the
# import failed for some reason.
logging.warning(
"Unable to import Notify from GLib. Notifications will be disabled."
)
glib_notify_exists = False
from .adapters import (
AdapterManager,
AlbumSearchQuery,
CacheMissError,
DownloadProgress,
Result,
SongCacheStatus,
)
from .adapters.api_objects import Playlist, PlayQueue, Song
from .config import AppConfiguration, ProviderConfiguration
from .dbus import dbus_propagate, DBusManager
from .players import PlayerDeviceEvent, PlayerEvent, PlayerManager
from .ui.configure_provider import ConfigureProviderDialog
from .ui.main import MainWindow
from .ui.state import RepeatType, UIState
from .util import resolve_path
class SublimeMusicApp(Gtk.Application):
def __init__(self, config_file: Path):
super().__init__(application_id="app.sublimemusic.SublimeMusic")
if glib_notify_exists:
Notify.init("Sublime Music")
self.window: Optional[Gtk.Window] = None
self.app_config = AppConfiguration.load_from_file(config_file)
self.dbus_manager: Optional[DBusManager] = None
self.connect("shutdown", self.on_app_shutdown)
player_manager: Optional[PlayerManager] = None
exiting: bool = False
def do_startup(self):
Gtk.Application.do_startup(self)
def add_action(name: str, fn: Callable, parameter_type: str = None):
"""Registers an action with the application."""
if type(parameter_type) == str:
parameter_type = GLib.VariantType(parameter_type)
action = Gio.SimpleAction.new(name, parameter_type)
action.connect("activate", fn)
self.add_action(action)
# Add action for menu items.
add_action("add-new-music-provider", self.on_add_new_music_provider)
add_action("edit-current-music-provider", self.on_edit_current_music_provider)
add_action(
"switch-music-provider", self.on_switch_music_provider, parameter_type="s"
)
add_action(
"remove-music-provider", self.on_remove_music_provider, parameter_type="s"
)
# Add actions for player controls
add_action("play-pause", self.on_play_pause)
add_action("next-track", self.on_next_track)
add_action("prev-track", self.on_prev_track)
add_action("repeat-press", self.on_repeat_press)
add_action("shuffle-press", self.on_shuffle_press)
# Navigation actions.
add_action("play-next", self.on_play_next, parameter_type="as")
add_action("add-to-queue", self.on_add_to_queue, parameter_type="as")
add_action("go-to-album", self.on_go_to_album, parameter_type="s")
add_action("go-to-artist", self.on_go_to_artist, parameter_type="s")
add_action("browse-to", self.browse_to, parameter_type="s")
add_action("go-to-playlist", self.on_go_to_playlist, parameter_type="s")
add_action("go-online", self.on_go_online)
add_action("refresh-devices", self.on_refresh_devices)
add_action(
"refresh-window",
lambda *a: self.on_refresh_window(None, {}, True),
)
add_action("mute-toggle", self.on_mute_toggle)
add_action(
"update-play-queue-from-server",
lambda a, p: self.update_play_state_from_server(),
)
if tap_imported:
self.tap = osxmmkeys.Tap()
self.tap.on("play_pause", self.on_play_pause)
self.tap.on("next_track", self.on_next_track)
self.tap.on("prev_track", self.on_prev_track)
self.tap.start()
def do_activate(self):
# We only allow a single window and raise any existing ones
if self.window:
self.window.present()
return
# Configure Icons
default_icon_theme = Gtk.IconTheme.get_default()
for adapter in AdapterManager.available_adapters:
if icon_dir := adapter.get_ui_info().icon_dir:
default_icon_theme.append_search_path(str(icon_dir))
icon_dirs = [resolve_path("ui/icons"), resolve_path("adapters/icons")]
for icon_dir in icon_dirs:
default_icon_theme.append_search_path(str(icon_dir))
# Windows are associated with the application when the last one is
# closed the application shuts down.
self.window = MainWindow(application=self, title="Sublime Music")
# Configure the CSS provider so that we can style elements on the
# window.
css_provider = Gtk.CssProvider()
css_provider.load_from_path(str(resolve_path("ui/app_styles.css")))
context = Gtk.StyleContext()
screen = Gdk.Screen.get_default()
context.add_provider_for_screen(
screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER
)
self.window.show_all()
self.window.present()
# Load the state for the server, if it exists.
self.app_config.load_state()
# If there is no current provider, use the first one if there are any
# configured, and if none are configured, then show the dialog to create a new
# one.
if self.app_config.provider is None:
if len(self.app_config.providers) == 0:
self.show_configure_servers_dialog()
# If they didn't add one with the dialog, close the window.
if len(self.app_config.providers) == 0:
self.window.close()
return
AdapterManager.reset(self.app_config, self.on_song_download_progress)
# Connect after we know there's a server configured.
self.window.stack.connect("notify::visible-child", self.on_stack_change)
self.window.connect("song-clicked", self.on_song_clicked)
self.window.connect("songs-removed", self.on_songs_removed)
self.window.connect("refresh-window", self.on_refresh_window)
self.window.connect("notification-closed", self.on_notification_closed)
self.window.connect("go-to", self.on_window_go_to)
self.window.connect("key-press-event", self.on_window_key_press)
self.window.player_controls.connect("song-scrub", self.on_song_scrub)
self.window.player_controls.connect("device-update", self.on_device_update)
self.window.player_controls.connect("volume-change", self.on_volume_change)
# Configure the players
self.last_play_queue_update = timedelta(0)
self.loading_state = False
self.should_scrobble_song = False
def on_timepos_change(value: Optional[float]):
if (
self.loading_state
or not self.window
or not self.app_config.state.current_song
):
return
if value is None:
self.last_play_queue_update = timedelta(0)
return
self.app_config.state.song_progress = timedelta(seconds=value)
GLib.idle_add(
self.window.player_controls.update_scrubber,
self.app_config.state.song_progress,
self.app_config.state.current_song.duration,
self.app_config.state.song_stream_cache_progress,
)
if (self.last_play
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
sublime_music-0.11.12.tar.gz (71个子文件)
sublime_music-0.11.12
PKG-INFO 9KB
README.rst 7KB
pyproject.toml 2KB
LICENSE 34KB
setup.py 9KB
sublime_music
app.py 56KB
dbus
manager.py 16KB
__init__.py 94B
mpris_specs
org.mpris.MediaPlayer2.TrackList.xml 15KB
org.mpris.MediaPlayer2.Playlists.xml 10KB
org.mpris.MediaPlayer2.Player.xml 27KB
org.mpris.MediaPlayer2.xml 9KB
util.py 735B
__main__.py 2KB
players
manager.py 6KB
mpv.py 4KB
chromecast.py 12KB
__init__.py 147B
base.py 7KB
__init__.py 24B
ui
main.py 46KB
images
play-queue-play.svg 213B
play-queue-play.png 903B
artists.py 23KB
app_styles.css 5KB
player_controls.py 32KB
albums.py 35KB
util.py 15KB
icons
chromecast-connecting-1-symbolic.svg 402B
chromecast-connected-symbolic.svg 325B
queue-front-symbolic.svg 388B
chromecast-symbolic.svg 276B
server-offline-symbolic.svg 157B
cloud-offline-symbolic.svg 391B
server-error-symbolic.svg 155B
chromecast-connecting-0-symbolic.svg 402B
chromecast-connecting-2-symbolic.svg 372B
server-connected-symbolic.svg 157B
queue-back-symbolic.svg 392B
state.py 4KB
playlists.py 35KB
__init__.py 0B
common
song_list_column.py 679B
album_with_songs.py 13KB
load_error.py 2KB
__init__.py 394B
spinner_image.py 2KB
icon_button.py 3KB
browse.py 17KB
configure_provider.py 9KB
adapters
subsonic
adapter.py 28KB
icons
subsonic-offline-symbolic.svg 1KB
subsonic-symbolic.svg 1KB
subsonic-connected-symbolic.svg 1KB
subsonic-error-symbolic.svg 1KB
__init__.py 69B
api_objects.py 11KB
images
default-album-art.png 6KB
default-album-art.svg 3KB
manager.py 52KB
configure_server_form.py 14KB
adapter_base.py 33KB
icons
config-ok-symbolic.svg 299B
config-error-symbolic.svg 348B
__init__.py 613B
filesystem
models.py 7KB
adapter.py 34KB
sqlite_extensions.py 4KB
__init__.py 73B
api_objects.py 6KB
config.py 8KB
共 71 条
- 1
资源评论
挣扎的蓝藻
- 粉丝: 13w+
- 资源: 15万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功