Skip to content

Commit

Permalink
Merge branch 'music-assistant:main' into deezer/new-logo
Browse files Browse the repository at this point in the history
  • Loading branch information
arctixdev authored Nov 16, 2023
2 parents f2802a6 + 6f12531 commit 1bf7f32
Show file tree
Hide file tree
Showing 47 changed files with 397 additions and 177 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/pre-commit-updater.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ jobs:
auto-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/[email protected].0
uses: actions/[email protected].1
with:
python-version: '3.10'
- name: Install pre-commit
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ jobs:
runs-on: ubuntu-latest
steps:
# Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v5.24.0
- uses: release-drafter/release-drafter@v5.25.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
34 changes: 23 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
outputs:
version: ${{ steps.vars.outputs.tag }}
steps:
- uses: actions/checkout@v3.5.3
- uses: actions/checkout@v4
- name: Get tag
id: vars
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: Set up Python 3.10
uses: actions/[email protected].0
uses: actions/[email protected].1
with:
python-version: "3.10"
- name: Install build
Expand Down Expand Up @@ -54,15 +54,15 @@ jobs:
- name: Sleep for 60 seconds (to prevent race condition with the pypi upload)
run: sleep 60s
shell: bash
- uses: actions/checkout@v3.5.3
- uses: actions/checkout@v4
- name: Log in to the GitHub container registry
uses: docker/login-action@v2.2.0
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2.9.1
uses: docker/setup-buildx-action@v3.0.0
- name: Version number for tags
id: tags
shell: bash
Expand All @@ -79,13 +79,13 @@ jobs:
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/${{ github.repository_owner }}/server
- name: Build and Push images
uses: docker/build-push-action@v4.1.1
uses: docker/build-push-action@v5.0.0
with:
context: .
platforms: linux/amd64,linux/arm64
Expand All @@ -101,12 +101,24 @@ jobs:
build-args: |
"MASS_VERSION=${{ needs.build-and-publish-pypi.outputs.version }}"
addon-version-update:
name: Server repo PR
release-notes-update:
name: Updates the release notes and changelog
needs: [ build-and-publish-pypi, build-and-push-container-image ]
runs-on: ubuntu-latest
steps:
- uses: music-assistant/addon-update-action@main
- name: Update changelog and release notes including frontend notes
uses: music-assistant/release-notes-merge-action@main
with:
github_token: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
release_tag: ${{ needs.build-and-publish-pypi.outputs.version }}

addon-version-update:
name: Updates the Addon repository with the new version
needs: [ build-and-publish-pypi, build-and-push-container-image, release-notes-update ]
runs-on: ubuntu-latest
steps:
- name: Push new version number to addon config
uses: music-assistant/addon-update-action@main
with:
github_token: ${{ secrets.PRIVILEGED_GITHUB_TOKEN }}
new_release_version: ${{ needs.build-and-publish-pypi.outputs.version }}
new_server_version: ${{ needs.build-and-publish-pypi.outputs.version }}
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:

steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v4
- name: Set up Python
uses: actions/[email protected].0
uses: actions/[email protected].1
with:
python-version: "3.11"
- name: Install dependencies
Expand All @@ -41,9 +41,9 @@ jobs:

steps:
- name: Check out code from GitHub
uses: actions/checkout@v3.5.3
uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/[email protected].0
uses: actions/[email protected].1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
Expand All @@ -10,18 +10,18 @@ repos:
- --branch=main
- id: debug-statements
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.284'
rev: 'v0.1.5'
hooks:
- id: ruff
- repo: https://github.com/psf/black
rev: 23.7.0
rev: 23.11.0
hooks:
- id: black
args:
- --safe
- --quiet
- repo: https://github.com/codespell-project/codespell
rev: v2.2.5
rev: v2.2.6
hooks:
- id: codespell
args: []
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ By far the most convenient way to run the Music Assistant Server is to install t
1. Add the Music Assistant repository to your Home Assistant instance.
2. Install the Music Assistant add-on.

[![Add repository on my Home Assistant][repository-badge]][repository-url]
[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Fmusic-assistant%2Fhome-assistant-addon)


### Alternative method: Docker image
Expand Down
2 changes: 1 addition & 1 deletion music_assistant/client/music.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ async def get_album_tracks(
return [
Track.from_dict(item)
for item in await self.client.send_command(
"music/album/tracks",
"music/album/album_tracks",
item_id=item_id,
provider_instance_id_or_domain=provider_instance_id_or_domain,
)
Expand Down
8 changes: 5 additions & 3 deletions music_assistant/common/models/config_entries.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ def update(self, update: dict[str, ConfigValueType]) -> set[str]:
changed_keys.add(key)

# config entry values
for key, new_val in update.items():
values = update.get("values", update)
for key, new_val in values.items():
if key in root_values:
continue
cur_val = self.values[key].value if key in self.values else None
Expand Down Expand Up @@ -364,9 +365,10 @@ class CoreConfig(Config):
key=CONF_VOLUME_NORMALIZATION_TARGET,
type=ConfigEntryType.INTEGER,
range=(-30, 0),
default_value=-14,
default_value=-17,
label="Target level for volume normalization",
description="Adjust average (perceived) loudness to this target level, " "default is -14 LUFS",
description="Adjust average (perceived) loudness to this target level, "
"default is -17 LUFS \n\n WARNING: Setting levels higher than this may result in clipping",
depends_on=CONF_VOLUME_NORMALIZATION,
advanced=True,
)
Expand Down
2 changes: 1 addition & 1 deletion music_assistant/common/models/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ProviderManifest(DataClassORJSONMixin):
builtin: bool = False
# hidden: hide entry in the UI
hidden: bool = False
# load_by_default: load this provider by default (mostly used together with `builtin`)
# load_by_default: load this provider by default (may be used together with `builtin`)
load_by_default: bool = False
# depends_on: depends on another provider to function
depends_on: str | None = None
Expand Down
28 changes: 28 additions & 0 deletions music_assistant/server/controllers/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,28 @@ async def remove_provider_config(self, instance_id: str) -> None:
existing = self.get(conf_key)
if not existing:
raise KeyError(f"Provider {instance_id} does not exist")
prov_manifest = self.mass.get_provider_manifest(existing["domain"])
if prov_manifest.load_by_default and instance_id == prov_manifest.domain:
# Guard for a provider that is loaded by default
LOGGER.warning(
"Provider %s can not be removed, disabling instead...", prov_manifest.name
)
existing["enabled"] = False
await self._update_provider_config(instance_id, existing)
return
if prov_manifest.builtin:
raise RuntimeError(f"Builtin provider {prov_manifest.name} can not be removed.")
self.remove(conf_key)
await self.mass.unload_provider(instance_id)
if existing["type"] == "music":
# cleanup entries in library
await self.mass.music.cleanup_provider(instance_id)
if existing["type"] == "player":
# cleanup entries in player manager
for player in self.mass.players:
if player.provider != instance_id:
continue
self.mass.players.remove(player.player_id, cleanup_config=True)

async def remove_provider_config_value(self, instance_id: str, key: str) -> None:
"""Remove/reset single Provider config value."""
Expand Down Expand Up @@ -619,11 +636,22 @@ async def _update_provider_config(
await self._load_provider_config(config)
else:
# disable provider
prov_manifest = self.mass.get_provider_manifest(config.domain)
if prov_manifest.builtin:
raise RuntimeError("Builtin provider can not be disabled.")
# also unload any other providers dependent of this provider
for dep_prov in self.mass.providers:
if dep_prov.manifest.depends_on == config.domain:
await self.mass.unload_provider(dep_prov.instance_id)
await self.mass.unload_provider(config.instance_id)
if config.type == ProviderType.PLAYER:
# cleanup entries in player manager
for player in self.mass.players.all(
return_unavailable=True, return_hidden=True, return_disabled=True
):
if player.provider != instance_id:
continue
self.mass.players.remove(player.player_id, cleanup_config=False)
# load succeeded, save new config
config.last_error = None
conf_key = f"{CONF_PROVIDERS}/{instance_id}"
Expand Down
2 changes: 1 addition & 1 deletion music_assistant/server/controllers/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ async def get_playlist_metadata(self, playlist: Playlist) -> None:
# if playlist has no default image (e.g. a local playlist)
if images and (not playlist.image or playlist.image.provider != "url"):
if playlist.image and self.mass.storage_path in playlist.image.path:
# re-use previous created path
# reuse previous created path
img_path = playlist.image.path
else:
img_path = os.path.join(self.mass.storage_path, f"{uuid4().hex}.png")
Expand Down
24 changes: 13 additions & 11 deletions music_assistant/server/controllers/music.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class MusicController(CoreController):

domain: str = "music"
database: DatabaseConnection | None = None
config: CoreConfig

def __init__(self, *args, **kwargs) -> None:
"""Initialize class."""
Expand Down Expand Up @@ -99,11 +100,12 @@ async def get_config_entries(

async def setup(self, config: CoreConfig) -> None:
"""Async initialize of module."""
self.config = config
# setup library database
await self._setup_database()
sync_interval = config.get_value(CONF_SYNC_INTERVAL)
self.logger.info("Setting up the sync interval to %s minutes.", sync_interval)
self._sync_task = self.mass.create_task(self.start_sync(reschedule=sync_interval))
self.logger.info("Using a sync interval of %s minutes.", sync_interval)
self._schedule_sync()

async def close(self) -> None:
"""Cleanup on exit."""
Expand All @@ -117,11 +119,10 @@ def providers(self) -> list[MusicProvider]:
return self.mass.get_providers(ProviderType.MUSIC)

@api_command("music/sync")
async def start_sync(
def start_sync(
self,
media_types: list[MediaType] | None = None,
providers: list[str] | None = None,
reschedule: int | None = None,
) -> None:
"""Start running the sync of (all or selected) musicproviders.
Expand All @@ -138,13 +139,6 @@ async def start_sync(
continue
self._start_provider_sync(provider.instance_id, media_types)

# reschedule task if needed
def create_sync_task():
self.mass.create_task(self.start_sync, media_types, providers, reschedule)

if reschedule is not None:
self.mass.loop.call_later(reschedule, create_sync_task)

@api_command("music/synctasks")
def get_running_sync_tasks(self) -> list[SyncTask]:
"""Return list with providers that are currently (scheduled for) syncing."""
Expand Down Expand Up @@ -620,6 +614,14 @@ async def cleanup_provider(self, provider_instance: str) -> None:
for item in prov_items:
await ctrl.remove_provider_mappings(item.item_id, provider_instance)

def _schedule_sync(self) -> None:
"""Schedule the periodic sync."""
self.start_sync()
sync_interval = self.config.get_value(CONF_SYNC_INTERVAL)
# we reschedule ourselves right after execution
# NOTE: sync_interval is stored in minutes, we need seconds
self.mass.loop.call_later(sync_interval * 60, self._schedule_sync)

async def _setup_database(self):
"""Initialize database."""
db_path = os.path.join(self.mass.storage_path, "library.db")
Expand Down
Loading

0 comments on commit 1bf7f32

Please sign in to comment.