-"""Apple Music musicprovider support for MusicAssistant."""
+"""
+Apple Music musicprovider support for MusicAssistant.
+
+TODO MUSIC_APP_TOKEN expires after 6 months so should have a distribution mechanism outside
+ compulsory application updates. It is only a semi-private key in JWT format so code be refreshed
+ daily by a GitHub action and downloaded by the provider each initialise.
+TODO Widevine keys can be obtained dynamically from Apple Music API rather than copied into Docker
+ build. This is undocumented but @maxlyth has a working example.
+TODO MUSIC_USER_TOKEN must be refreshed (~min 180 days) and needs mechanism to prompt user to
+ re-authenticate in browser.
+TODO Current provider ignores private tracks that are not available in the storefront catalog as
+ streamable url is derived from the catalog id. It is undecumented but @maxlyth has a working
+ example to get a streamable url from the library id.
+"""
from __future__ import annotations
import os
import pathlib
import re
+import time
from typing import TYPE_CHECKING, Any
import aiofiles
CONF_MUSIC_APP_TOKEN = "music_app_token"
CONF_MUSIC_USER_TOKEN = "music_user_token"
+CONF_MUSIC_USER_TOKEN_TIMESTAMP = "music_user_token_timestamp"
async def setup(
default_app_token_valid = True
# Action is to launch MusicKit flow
- if action == "CONF_ACTION_AUTH":
- # TODO: check the developer token is valid otherwise user is going to have bad experience
- async with AuthenticationHelper(mass, values["session_id"]) as auth_helper:
+ if action == "CONF_ACTION_AUTH" and default_app_token_valid:
+ callback_method = "POST"
+ async with AuthenticationHelper(mass, values["session_id"], callback_method) as auth_helper:
callback_url = auth_helper.callback_url
flow_base_path = f"apple_music_auth/{values['session_id']}/"
flow_timeout = 600
parent_file_path = pathlib.Path(__file__).parent.resolve()
+ base_url = f"{mass.webserver.base_url}/{flow_base_path}"
+ flow_base_url = f"{base_url}index.html"
async def serve_mk_auth_page(request: web.Request) -> web.Response:
auth_html_path = parent_file_path.joinpath("musickit_auth/musickit_wrapper.html")
- return web.FileResponse(auth_html_path, headers={"content-type": "text/html"})
+ return web.FileResponse(
+ auth_html_path,
+ headers={"content-type": "text/html"},
+ )
async def serve_mk_auth_css(request: web.Request) -> web.Response:
auth_css_path = parent_file_path.joinpath("musickit_auth/musickit_wrapper.css")
- return web.FileResponse(auth_css_path, headers={"content-type": "text/css"})
+ return web.FileResponse(
+ auth_css_path,
+ headers={
+ "content-type": "text/css",
+ },
+ )
async def serve_mk_glue(request: web.Request) -> web.Response:
- return_html = f"const app_token='{values[CONF_MUSIC_APP_TOKEN]}';"
- return_html += f"const user_token='{values[CONF_MUSIC_USER_TOKEN]}';"
- return_html += f"const return_url='{callback_url}';"
- return_html += f"const flow_timeout={flow_timeout - 10};"
- return_html += f"const mass_buid='{mass.version}';"
- return web.Response(body=return_html, headers={"content-type": "text/javascript"})
+ return_html = f"""
+ const return_url='{callback_url}';
+ const base_url='{base_url}';
+ const app_token='{values[CONF_MUSIC_APP_TOKEN]}';
+ const callback_method='{callback_method}';
+ const user_token='{
+ values[CONF_MUSIC_USER_TOKEN]
+ if validate_user_token(values[CONF_MUSIC_USER_TOKEN])
+ else ""
+ }';
+ const user_token_timestamp='{values[CONF_MUSIC_USER_TOKEN_TIMESTAMP]}';
+ const flow_timeout={max([flow_timeout - 10, 60])};
+ const flow_start_time={int(time.time())};
+ const mass_version='{mass.version}';
+ """
+ return web.Response(
+ body=return_html,
+ headers={
+ "content-type": "text/javascript",
+ },
+ )
mass.webserver.register_dynamic_route(
f"/{flow_base_path}index.html", serve_mk_auth_page
)
mass.webserver.register_dynamic_route(f"/{flow_base_path}index.css", serve_mk_auth_css)
mass.webserver.register_dynamic_route(f"/{flow_base_path}index.js", serve_mk_glue)
- flow_base_url = f"{mass.webserver.base_url}/{flow_base_path}index.html"
+
try:
- values[CONF_MUSIC_USER_TOKEN] = (
- await auth_helper.authenticate(flow_base_url, flow_timeout)
- )["music-user-token"]
+ result = await auth_helper.authenticate(flow_base_url, flow_timeout)
+ values[CONF_MUSIC_USER_TOKEN] = result["music-user-token"]
+ values[CONF_MUSIC_USER_TOKEN_TIMESTAMP] = result["music-user-token-timestamp"]
except KeyError:
- # no music-user-token URL param was found so user probably cancelled the auth
+ # no music-user-token URL param was found so likely user cancelled the auth
pass
except Exception as error:
raise LoginFailed(f"Failed to authenticate with Apple '{error}'.")
label="Music User Token",
required=True,
action="CONF_ACTION_AUTH",
- description="You need to authenticate on Apple Music.",
+ description="Authenticate with Apple Music to retrieve a valid music user token.",
action_label="Authenticate with Apple Music",
- value=values.get(CONF_MUSIC_USER_TOKEN) if values else None,
+ value=values.get(CONF_MUSIC_USER_TOKEN)
+ if (
+ values
+ and isinstance(values.get(CONF_MUSIC_USER_TOKEN_TIMESTAMP), int)
+ and (
+ values.get(CONF_MUSIC_USER_TOKEN_TIMESTAMP) > (time.time() - (3600 * 24 * 150))
+ )
+ )
+ else None,
+ ),
+ ConfigEntry(
+ key=CONF_MUSIC_USER_TOKEN_TIMESTAMP,
+ type=ConfigEntryType.INTEGER,
+ description="Timestamp music user token was updated.",
+ label="Music User Token Timestamp",
+ hidden=True,
+ required=True,
+ default_value=0,
+ value=values.get(CONF_MUSIC_USER_TOKEN_TIMESTAMP) if values else 0,
),
)
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Music Assistant MusicKit Redirect</title>
+ <!-- Required to ensure authentication works behind a reverse proxy -->
+ <meta name="referrer" content="strict-origin-when-cross-origin">
+ <title>Apple Music Sign-in for Music Assistant</title>
+ <link rel="stylesheet" href="./index.css">
<script src="./index.js"></script>
<script src="https://js-cdn.music.apple.com/musickit/v3/musickit.js" data-web-components async></script>
- <link rel="stylesheet" href="./index.css">
<script>
var music = null;
- var music_user_token = "";
+ var music_user_token = (typeof user_token !== 'undefined' ) ? user_token : "";
+ var music_user_token_timestamp = (typeof user_token_timestamp !== 'undefined' ) ? user_token_timestamp : 0;
+ var callbackConsumed = false;
+ var closing = false;
// The underlying MA auth helper times-out after fixed period so this window can no longer pass back any data so just close it.
// Self closing windows is anti-pattern user experience but better than leaving non-functioning UI presented
- setTimeout(() => {
- window.close()
- }, flow_timeout * 1000);
- function mkSignIn() {
- document.getElementById('signin_button').disabled = true;
- document.getElementById('message').innerHTML = "Apple Signin window should open…";
- document.getElementById('sub-message').innerHTML = "If the Apple Music authentication window does not open, please check the MusicKit token and try again.";
- music.authorize().then(function (token) {
- music_user_token = encodeURIComponent(token);
- document
- .getElementById('close_button')
- .style
- .display = "inline-block";
- window.location.href = return_url + "?music-user-token=" + music_user_token;
- })
- }
- function mkSignOut() {
- document.getElementById('message').innerHTML = "This window should close…";
- music
- .unauthorize()
- document
- .getElementById('close_button')
- .style
- .display = "inline-block";
- window.location.href = return_url;
- }
- function interceptWindowClose() {
- var xmlHttp = new XMLHttpRequest();
- xmlHttp.open("GET", return_url + (music_user_token ? "?music-user-token=" + music_user_token : ""), true);
- xmlHttp.send(null);
+ if (typeof flow_timeout !== 'undefined' && flow_timeout > 0) {
+ setTimeout(sendTokenAndCloseWindow, flow_timeout * 1000);
}
- // Attempt to intercept window close and redirect back to the auth helper which will in turn close the helper panel
- window.addEventListener("beforeunload", interceptWindowClose, false);
+ // Attempt to intercept window close and send a request to auth helper before closing the window.
+ // This is not always possible, but we try.
+ window.addEventListener("unload", sendTokenAndCloseWindow, false);
- document.addEventListener('musickitloaded', function () { // MusicKit global is now defined
+ document.addEventListener('musickitloaded', function () {
+ // MusicKit global is now defined
document.getElementById('message').innerHTML = "Apple MusicKit loaded";
- document.getElementById('signin_button').disabled = false;
MusicKit.configure({
developerToken: app_token,
app: {
name: 'Music Assistant',
- build: mass_buid
+ build: (typeof mass_version !== 'undefined' ) ? mass_version : '0.0.0'
}
}).then(() => {
music = MusicKit.getInstance();
+ document.getElementById('signin_button').disabled = false;
if (music.isAuthorized) {
+ // user token must have been issued in last 6 months
+ music_user_token_timestamp = Math.max(music_user_token_timestamp, (Date.now() / 1000) - (3600 * 24 * 180));
document.getElementById('message').innerHTML = "Already signed in";
- document.getElementById('sub-message').innerHTML = "Choose Continue to refresh token or Sign out to switch accounts.";
+ document.getElementById('sub-message').innerHTML = "Choose Continue to refresh token or Sign Out to remove token.";
document.getElementById('signin_button').innerHTML = "Continue";
- document
- .getElementById('signout_button')
- .style
- .display = "inline-block";
+ document.getElementById('signout_button').style.display = "inline-block";
} else {
document.getElementById('message').innerHTML = "Not signed in";
- document
- .getElementById('signin_button')
- .style
- .display = "inline-block";
- document
- .getElementById('signout_button')
- .style
- .display = "none";
+ document.getElementById('signin_button').style.display = "inline-block";
+ document.getElementById('signout_button').style.display = "none";
}
});
});
+
+ document.addEventListener("DOMContentLoaded", function () {
+ if (typeof mass_version !== 'undefined' && mass_version) {
+ document.getElementById('mass_version').innerHTML = `v${mass_version} `;
+ }
+ });
+
+ function sendTokenAndCloseWindow() {
+ if (closing) {
+ // anti race condition
+ return;
+ }
+ closing = true;
+ if (callbackConsumed) {
+ window.close();
+ return;
+ }
+ var xmlHttp = new XMLHttpRequest();
+ var url = new URL(return_url);
+ var data = null;
+ if (callback_method === "POST") {
+ data = JSON.stringify({'music-user-token': music_user_token, 'music-user-token-timestamp': music_user_token_timestamp});
+ } else {
+ if (music_user_token) {
+ url.searchParams.set('music-user-token', music_user_token);
+ url.searchParams.set('music-user-token-timestamp', music_user_token_timestamp);
+ }
+ }
+ xmlHttp.open(callback_method, url, true);
+ xmlHttp.onreadystatechange = () => {
+ if (xmlHttp.readyState === XMLHttpRequest.DONE) {
+ callbackConsumed = true;
+ if (xmlHttp.status === 200) {
+ document.getElementById('message').innerHTML = "Apple MusicKit token sent to Music Assistant";
+ document.getElementById('sub-message').innerHTML = "You can now close this window.";
+ document.getElementById('signin_button').style.display = "none";
+ document.getElementById('signout_button').style.display = "none";
+ document.getElementById('close_button').innerHTML = "Closing…";
+ setTimeout(() => {
+ window.close();
+ }, 500);
+ } else if (xmlHttp.status >= 400) {
+ console.error("Error sending token to Music Assistant: ", xmlHttp.statusText);
+ document.getElementById('message').innerHTML = "Error sending token to Music Assistant";
+ document.getElementById('sub-message').innerHTML = "Please try again.";
+ document.getElementById('signin_button').style.display = "inline-block";
+ document.getElementById('signout_button').style.display = "none";
+ }
+ }
+ };
+ xmlHttp.send(data);
+ }
+
+ function mkSignInButton() {
+ document.getElementById('signin_button').disabled = true;
+ document.getElementById('message').innerHTML = "Apple Signin window should open…";
+ document.getElementById('sub-message').innerHTML = "If the Apple Music authentication window does not open, please check the MusicKit token and try again.";
+ music.authorize().then(function (token) {
+ music_user_token = token;
+ music_user_token_timestamp = Math.floor(Date.now() / 1000);
+ document.getElementById('message').innerHTML = "Successfully signed in to Apple Music";
+ document.getElementById('close_button').style.display = "inline-block";
+ sendTokenAndCloseWindow();
+ }).catch(function (error) {
+ console.error("Error signing in: ", error);
+ document.getElementById('message').innerHTML = "Error signing in to Apple Music";
+ document.getElementById('sub-message').innerHTML = "Please try again.";
+ document.getElementById('signin_button').disabled = false;
+ document.getElementById('signin_button').style.display = "inline-block";
+ document.getElementById('signout_button').style.display = "none";
+ });
+ }
+
+ function mkSignOutButton() {
+ music.unauthorize();
+ music_user_token = "";
+ music_user_token_timestamp = 0;
+ document.getElementById('signout_button').disabled = true;
+ document.getElementById('message').innerHTML = "This window should close…";
+ document.getElementById('close_button').style.display = "inline-block";
+ sendTokenAndCloseWindow();
+ }
+
+ function mkCloseButton() {
+ sendTokenAndCloseWindow();
+ }
+
+ function mkSwitchAccountButton() {
+ music.unauthorize()
+ music_user_token = "";
+ music_user_token_timestamp = 0;
+ document.getElementById('message').innerHTML = "Not signed in";
+ document.getElementById('sub-message').innerHTML = "Sign in with Apple to allow Music Assistant to access your Apple Music account.";
+ document.getElementById('signin_button').innerHTML = "Sign In";
+ document.getElementById('signin_button').style.display = "inline-block";
+ document.getElementById('signout_button').style.display = "none";
+ }
</script>
</head>
-<body onunload="interceptWindowClose()">
+<body onunload="sendTokenAndCloseWindow();">
<div id="app" class="container">
<div class="base-content-wrapper theme-music base-authorization-request"
data-test="oauth-authorization-request-third-party">
<div class="icons base-authorization-request__icons-container">
<div class="base-authorization-request__icon">
<div class="base-icon">
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
- id="Artwork" x="0px" y="0px" width="76px" height="76px" viewbox="0 0 361 361"
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ x="0px" y="0px" width="76px" height="76px" viewbox="0 0 361 361"
style="enable-background:new 0 0 361 361;" xml:space="preserve">
<style type="text/css">
- .st0 {
- fill-rule: evenodd;
- clip-rule: evenodd;
- fill: url('#SVGID_1_');
- }
-
- .st1 {
- fill-rule: evenodd;
- clip-rule: evenodd;
- fill: #FFFFFF;
- }
+ .st0 {fill-rule: evenodd;clip-rule: evenodd;fill: url('#SVGID_1_');}
+ .st1 {fill-rule: evenodd;clip-rule: evenodd;fill: #FFFFFF;}
</style>
- <g id="Layer_5">
- <lineargradient id="SVGID_1_" gradientunits="userSpaceOnUse" x1="180" y1="358.6047" x2="180"
- y2="7.7586">
- <stop offset="0" style="stop-color:#FA233B" />
- <stop offset="1" style="stop-color:#FB5C74" />
- </lineargradient>
- <path class="st0"
- d="M360,112.61c0-4.3,0-8.6-0.02-12.9c-0.02-3.62-0.06-7.24-0.16-10.86c-0.21-7.89-0.68-15.84-2.08-23.64c-1.42-7.92-3.75-15.29-7.41-22.49c-3.6-7.07-8.3-13.53-13.91-19.14c-5.61-5.61-12.08-10.31-19.15-13.91c-7.19-3.66-14.56-5.98-22.47-7.41c-7.8-1.4-15.76-1.87-23.65-2.08c-3.62-0.1-7.24-0.14-10.86-0.16C255.99,0,251.69,0,247.39,0H112.61c-4.3,0-8.6,0-12.9,0.02c-3.62,0.02-7.24,0.06-10.86,0.16C80.96,0.4,73,0.86,65.2,2.27c-7.92,1.42-15.28,3.75-22.47,7.41c-7.07,3.6-13.54,8.3-19.15,13.91c-5.61,5.61-10.31,12.07-13.91,19.14c-3.66,7.2-5.99,14.57-7.41,22.49c-1.4,7.8-1.87,15.76-2.08,23.64c-0.1,3.62-0.14,7.24-0.16,10.86C0,104.01,0,108.31,0,112.61v134.77c0,4.3,0,8.6,0.02,12.9c0.02,3.62,0.06,7.24,0.16,10.86c0.21,7.89,0.68,15.84,2.08,23.64c1.42,7.92,3.75,15.29,7.41,22.49c3.6,7.07,8.3,13.53,13.91,19.14c5.61,5.61,12.08,10.31,19.15,13.91c7.19,3.66,14.56,5.98,22.47,7.41c7.8,1.4,15.76,1.87,23.65,2.08c3.62,0.1,7.24,0.14,10.86,0.16c4.3,0.03,8.6,0.02,12.9,0.02h134.77c4.3,0,8.6,0,12.9-0.02c3.62-0.02,7.24-0.06,10.86-0.16c7.89-0.21,15.85-0.68,23.65-2.08c7.92-1.42,15.28-3.75,22.47-7.41c7.07-3.6,13.54-8.3,19.15-13.91c5.61-5.61,10.31-12.07,13.91-19.14c3.66-7.2,5.99-14.57,7.41-22.49c1.4-7.8,1.87-15.76,2.08-23.64c0.1-3.62,0.14-7.24,0.16-10.86c0.03-4.3,0.02-8.6,0.02-12.9V112.61z" />
- </g>
- <g id="Glyph_2_">
- <g>
- <path class="st1"
- d="M254.5,55c-0.87,0.08-8.6,1.45-9.53,1.64l-107,21.59l-0.04,0.01c-2.79,0.59-4.98,1.58-6.67,3c-2.04,1.71-3.17,4.13-3.6,6.95c-0.09,0.6-0.24,1.82-0.24,3.62c0,0,0,109.32,0,133.92c0,3.13-0.25,6.17-2.37,8.76c-2.12,2.59-4.74,3.37-7.81,3.99c-2.33,0.47-4.66,0.94-6.99,1.41c-8.84,1.78-14.59,2.99-19.8,5.01c-4.98,1.93-8.71,4.39-11.68,7.51c-5.89,6.17-8.28,14.54-7.46,22.38c0.7,6.69,3.71,13.09,8.88,17.82c3.49,3.2,7.85,5.63,12.99,6.66c5.33,1.07,11.01,0.7,19.31-0.98c4.42-0.89,8.56-2.28,12.5-4.61c3.9-2.3,7.24-5.37,9.85-9.11c2.62-3.75,4.31-7.92,5.24-12.35c0.96-4.57,1.19-8.7,1.19-13.26l0-116.15c0-6.22,1.76-7.86,6.78-9.08c0,0,88.94-17.94,93.09-18.75c5.79-1.11,8.52,0.54,8.52,6.61l0,79.29c0,3.14-0.03,6.32-2.17,8.92c-2.12,2.59-4.74,3.37-7.81,3.99c-2.33,0.47-4.66,0.94-6.99,1.41c-8.84,1.78-14.59,2.99-19.8,5.01c-4.98,1.93-8.71,4.39-11.68,7.51c-5.89,6.17-8.49,14.54-7.67,22.38c0.7,6.69,3.92,13.09,9.09,17.82c3.49,3.2,7.85,5.56,12.99,6.6c5.33,1.07,11.01,0.69,19.31-0.98c4.42-0.89,8.56-2.22,12.5-4.55c3.9-2.3,7.24-5.37,9.85-9.11c2.62-3.75,4.31-7.92,5.24-12.35c0.96-4.57,1-8.7,1-13.26V64.46C263.54,58.3,260.29,54.5,254.5,55z" />
- </g>
- </g>
+ <lineargradient id="SVGID_1_" gradientunits="userSpaceOnUse" x1="180" y1="358.6047" x2="180" y2="7.7586">
+ <stop offset="0" style="stop-color:#FA233B" />
+ <stop offset="1" style="stop-color:#FB5C74" />
+ </lineargradient>
+ <path class="st0" d="M360,112.61c0-4.3,0-8.6-0.02-12.9c-0.02-3.62-0.06-7.24-0.16-10.86c-0.21-7.89-0.68-15.84-2.08-23.64c-1.42-7.92-3.75-15.29-7.41-22.49c-3.6-7.07-8.3-13.53-13.91-19.14c-5.61-5.61-12.08-10.31-19.15-13.91c-7.19-3.66-14.56-5.98-22.47-7.41c-7.8-1.4-15.76-1.87-23.65-2.08c-3.62-0.1-7.24-0.14-10.86-0.16C255.99,0,251.69,0,247.39,0H112.61c-4.3,0-8.6,0-12.9,0.02c-3.62,0.02-7.24,0.06-10.86,0.16C80.96,0.4,73,0.86,65.2,2.27c-7.92,1.42-15.28,3.75-22.47,7.41c-7.07,3.6-13.54,8.3-19.15,13.91c-5.61,5.61-10.31,12.07-13.91,19.14c-3.66,7.2-5.99,14.57-7.41,22.49c-1.4,7.8-1.87,15.76-2.08,23.64c-0.1,3.62-0.14,7.24-0.16,10.86C0,104.01,0,108.31,0,112.61v134.77c0,4.3,0,8.6,0.02,12.9c0.02,3.62,0.06,7.24,0.16,10.86c0.21,7.89,0.68,15.84,2.08,23.64c1.42,7.92,3.75,15.29,7.41,22.49c3.6,7.07,8.3,13.53,13.91,19.14c5.61,5.61,12.08,10.31,19.15,13.91c7.19,3.66,14.56,5.98,22.47,7.41c7.8,1.4,15.76,1.87,23.65,2.08c3.62,0.1,7.24,0.14,10.86,0.16c4.3,0.03,8.6,0.02,12.9,0.02h134.77c4.3,0,8.6,0,12.9-0.02c3.62-0.02,7.24-0.06,10.86-0.16c7.89-0.21,15.85-0.68,23.65-2.08c7.92-1.42,15.28-3.75,22.47-7.41c7.07-3.6,13.54-8.3,19.15-13.91c5.61-5.61,10.31-12.07,13.91-19.14c3.66-7.2,5.99-14.57,7.41-22.49c1.4-7.8,1.87-15.76,2.08-23.64c0.1-3.62,0.14-7.24,0.16-10.86c0.03-4.3,0.02-8.6,0.02-12.9V112.61z" />
+ <path class="st1" d="M254.5,55c-0.87,0.08-8.6,1.45-9.53,1.64l-107,21.59l-0.04,0.01c-2.79,0.59-4.98,1.58-6.67,3c-2.04,1.71-3.17,4.13-3.6,6.95c-0.09,0.6-0.24,1.82-0.24,3.62c0,0,0,109.32,0,133.92c0,3.13-0.25,6.17-2.37,8.76c-2.12,2.59-4.74,3.37-7.81,3.99c-2.33,0.47-4.66,0.94-6.99,1.41c-8.84,1.78-14.59,2.99-19.8,5.01c-4.98,1.93-8.71,4.39-11.68,7.51c-5.89,6.17-8.28,14.54-7.46,22.38c0.7,6.69,3.71,13.09,8.88,17.82c3.49,3.2,7.85,5.63,12.99,6.66c5.33,1.07,11.01,0.7,19.31-0.98c4.42-0.89,8.56-2.28,12.5-4.61c3.9-2.3,7.24-5.37,9.85-9.11c2.62-3.75,4.31-7.92,5.24-12.35c0.96-4.57,1.19-8.7,1.19-13.26l0-116.15c0-6.22,1.76-7.86,6.78-9.08c0,0,88.94-17.94,93.09-18.75c5.79-1.11,8.52,0.54,8.52,6.61l0,79.29c0,3.14-0.03,6.32-2.17,8.92c-2.12,2.59-4.74,3.37-7.81,3.99c-2.33,0.47-4.66,0.94-6.99,1.41c-8.84,1.78-14.59,2.99-19.8,5.01c-4.98,1.93-8.71,4.39-11.68,7.51c-5.89,6.17-8.49,14.54-7.67,22.38c0.7,6.69,3.92,13.09,9.09,17.82c3.49,3.2,7.85,5.56,12.99,6.6c5.33,1.07,11.01,0.69,19.31-0.98c4.42-0.89,8.56-2.22,12.5-4.55c3.9-2.3,7.24-5.37,9.85-9.11c2.62-3.75,4.31-7.92,5.24-12.35c0.96-4.57,1-8.7,1-13.26V64.46C263.54,58.3,260.29,54.5,254.5,55z" />
</svg>
<p class="base-icon__label">Apple Music</p>
</div>
</div>
<div class="base-authorization-request__arrows"></div>
<div class="base-authorization-request__icon">
- <div class="base-icon"><img
- src="data:image/png;base64,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"
+ <div class="base-icon">
+ <img src="data:image/png;base64,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"
height="76px" width="76px">
<p class="base-icon__label">Music Assistant</p>
</div>
<h1 class="base-content-wrapper__title typography-label">
<span id="message">Apple MusicKit is loading…</span>
</h1>
- <p id="sub-message" class="base-content-wrapper__description typography-body-tight">Sign
- in with Apple to allow Music Assistant to access your Apple Music
- account.</p>
+ <p id="sub-message" class="base-content-wrapper__description typography-body-tight">
+ Sign in with Apple to allow Music Assistant to access your Apple Music account.</p>
<p>
<button id="signin_button"
class="button button-block button-elevated button-primary base-button base-button--primary base-content-wrapper__button"
- onclick="mkSignIn();" disabled>Sign In</button>
+ onclick="mkSignInButton();" disabled>Sign In</button>
</p>
<p>
<button id="signout_button"
class="button button-block button-elevated button-primary base-button base-button--primary base-content-wrapper__button"
- onclick="mkSignOut();" style="display: none">Sign Out</button>
+ onclick="mkSignOutButton();" style="display: none">Sign Out</button>
</p>
<p>
<button id="close_button"
class="button button-block button-elevated button-secondary base-button base-button--secondary base-content-wrapper__button"
- onclick="window.location.href = return_url;">Close</button>
+ onclick="mkCloseButton();">Close</button>
</p>
</section>
<div class="privacy-summary__link-container">
<a class="privacy-summary__link-text privacy-summary__link-text--margin-top typography-caption"
- onclick="music.unauthorize(); location.reload();" href="#">Use a
- different Apple Account</a>
+ onclick="mkSwitchAccountButton();" href="#">Use a different Apple Account</a>
</div>
<footer class="base-footer">
- <p class="base-footer__copyright">© 2025 Music Assistant - Part of the
- <a href="https://www.openhomefoundation.org">Open
- Home Foundation</a>. Support us on
- <a href="https://github.com/sponsors/music-assistant">GitHub</a>.
+ <p class="base-footer__copyright">© 2025 Music Assistant <span id="mass_version"></span>- Part of the
+ <a href="https://www.openhomefoundation.org" target="_blank">Open Home Foundation</a>. Support us on
+ <a href="https://github.com/sponsors/music-assistant" target="_blank">GitHub</a>.
</p>
</footer>
</div>
</div>
</body>
-
</html>