diff --git a/.gitignore b/.gitignore
index f347387..e7e4d11 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,4 +128,8 @@ dmypy.json
# Pyre type checker
.pyre/
-.vscode/
\ No newline at end of file
+<<<<<<< HEAD
+.vscode/
+=======
+.vscode/
+>>>>>>> dev
diff --git a/README.md b/README.md
index cf8bb9f..b0aa053 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,8 @@ https://github.com/Binaryify/NeteaseCloudMusicApi
不想动手不想操心,也可以付费使用由我部署维护的接口服务(每年30)
+**注意:关联媒体播放器调整为在集成选项中选择**
+
## 使用 - [插件图片预览](https://github.com/shaonianzhentan/image/blob/main/ha_cloud_music/README.md)
> **指定ID播放**
@@ -47,21 +49,10 @@ https://github.com/Binaryify/NeteaseCloudMusicApi
> **登录后播放**
- [x] 每日推荐 `cloudmusic://163/my/daily`
+- [x] 我喜欢的音乐 `cloudmusic://163/my/ilike`
+
-configuration.yaml
-```yaml
-homeassistant:
- customize: !include customize.yaml
-```
-customize.yaml
-```yaml
-media_player.yun_yin_le:
- media_player:
- - media_player.源实体1
- - media_player.源实体2
- - media_player.源实体3
-```
## 关联项目
@@ -74,4 +65,4 @@ media_player.yun_yin_le:
|
#### 关注我的微信订阅号,了解更多HomeAssistant相关知识
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/custom_components/ha_cloud_music/browse_media.py b/custom_components/ha_cloud_music/browse_media.py
index 77fb8c6..1eadcab 100644
--- a/custom_components/ha_cloud_music/browse_media.py
+++ b/custom_components/ha_cloud_music/browse_media.py
@@ -86,6 +86,7 @@ class CloudMusicRouter():
my_login = f'{cloudmusic_protocol}my/login'
my_daily = f'{cloudmusic_protocol}my/daily'
+ my_ilike = f'{cloudmusic_protocol}my/ilike'
my_recommend_resource = f'{cloudmusic_protocol}my/recommend_resource'
my_cloud = f'{cloudmusic_protocol}my/cloud'
my_created = f'{cloudmusic_protocol}my/created'
@@ -762,6 +763,8 @@ async def async_play_media(media_player, cloud_music, media_content_id):
playlist = await cloud_music.async_get_playlist(id)
elif media_content_id.startswith(CloudMusicRouter.my_daily):
playlist = await cloud_music.async_get_dailySongs()
+ elif media_content_id.startswith(CloudMusicRouter.my_ilike):
+ playlist = await cloud_music.async_get_ilinkSongs()
elif media_content_id.startswith(CloudMusicRouter.my_cloud):
playlist = await cloud_music.async_get_cloud()
elif media_content_id.startswith(CloudMusicRouter.artist_playlist):
diff --git a/custom_components/ha_cloud_music/cloud_music.py b/custom_components/ha_cloud_music/cloud_music.py
index 9f88116..6b08aa1 100644
--- a/custom_components/ha_cloud_music/cloud_music.py
+++ b/custom_components/ha_cloud_music/cloud_music.py
@@ -258,6 +258,13 @@ class CloudMusic():
return list(map(format_playlist, res['data']['dailySongs']))
+ # 获取我喜欢的音乐
+ async def async_get_ilinkSongs(self):
+ uid = self.userinfo.get('uid')
+ if uid is not None:
+ res = await self.netease_cloud_music(f'/user/playlist?uid={uid}')
+ return await self.async_get_playlist(res['playlist'][0]['id'])
+
# 乐听头条
async def async_ting_playlist(self, catalog_id):
diff --git a/custom_components/ha_cloud_music/config_flow.py b/custom_components/ha_cloud_music/config_flow.py
index ed3a9c1..c45bac9 100644
--- a/custom_components/ha_cloud_music/config_flow.py
+++ b/custom_components/ha_cloud_music/config_flow.py
@@ -12,14 +12,14 @@ from homeassistant.core import callback
import os
from .manifest import manifest
-from .http_api import http_cookie
+from .http_api import fetch_data
from homeassistant.util.json import save_json
DOMAIN = manifest.domain
class SimpleConfigFlow(ConfigFlow, domain=DOMAIN):
- VERSION = 1
+ VERSION = 2
async def async_step_user(
self, user_input: dict[str, Any] | None = None
@@ -30,8 +30,15 @@ class SimpleConfigFlow(ConfigFlow, domain=DOMAIN):
errors = {}
if user_input is not None:
url = user_input.get(CONF_URL).strip('/')
- user_input[CONF_URL] = url
- return self.async_create_entry(title=DOMAIN, data=user_input)
+ # 检查接口是否可用
+ try:
+ res = await fetch_data(f'{url}/login/status')
+ if res['data']['code'] == 200:
+ user_input[CONF_URL] = url
+ return self.async_create_entry(title=DOMAIN, data=user_input)
+ except Exception as ex:
+ print(ex)
+ errors = { 'base': 'api_failed' }
else:
user_input = {}
@@ -45,7 +52,6 @@ class SimpleConfigFlow(ConfigFlow, domain=DOMAIN):
def async_get_options_flow(entry: ConfigEntry):
return OptionsFlowHandler(entry)
-
class OptionsFlowHandler(OptionsFlow):
def __init__(self, config_entry: ConfigEntry):
self.config_entry = config_entry
@@ -57,20 +63,22 @@ class OptionsFlowHandler(OptionsFlow):
options = self.config_entry.options
errors = {}
if user_input is not None:
- username = user_input.get(CONF_USERNAME)
- password = user_input.get(CONF_PASSWORD)
+ return self.async_create_entry(title='', data=user_input)
+
+ media_states = self.hass.states.async_all('media_player')
+ media_entities = {}
- cloud_music = self.hass.data['cloud_music']
+ for state in media_states:
+ friendly_name = state.attributes.get('friendly_name')
+ platform = state.attributes.get('platform')
+ entity_id = state.entity_id
+ value = f'{friendly_name}({entity_id})'
- result = await cloud_music.login(username, password)
- if result is not None:
- return self.async_create_entry(title='', data=user_input)
- else:
- errors['base'] = 'login_failed'
+ if platform != 'cloud_music':
+ media_entities[entity_id] = value
DATA_SCHEMA = vol.Schema({
- vol.Required(CONF_USERNAME, default=options.get(CONF_USERNAME)): str,
- vol.Required(CONF_PASSWORD, default=options.get(CONF_PASSWORD)): str
+ vol.Required('media_player', default=options.get('media_player')): vol.In(media_entities)
})
return self.async_show_form(step_id="user", data_schema=DATA_SCHEMA, errors=errors)
\ No newline at end of file
diff --git a/custom_components/ha_cloud_music/http_api.py b/custom_components/ha_cloud_music/http_api.py
index 4b9dfb3..f8adad9 100644
--- a/custom_components/ha_cloud_music/http_api.py
+++ b/custom_components/ha_cloud_music/http_api.py
@@ -24,7 +24,6 @@ async def http_cookie(url):
}
async def http_get(url, COOKIES={}):
- print(url)
headers = {'Referer': url, **HEADERS}
jar = aiohttp.CookieJar(unsafe=True)
async with aiohttp.ClientSession(headers=headers, cookies=COOKIES, cookie_jar=jar) as session:
@@ -39,4 +38,10 @@ async def http_get(url, COOKIES={}):
async def http_code(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
- return response.status
\ No newline at end of file
+ return response.status
+
+async def fetch_data(url):
+ timeout = aiohttp.ClientTimeout(total=5)
+ async with aiohttp.ClientSession(timeout=timeout) as session:
+ async with session.get(url) as response:
+ return await response.json()
\ No newline at end of file
diff --git a/custom_components/ha_cloud_music/manifest.json b/custom_components/ha_cloud_music/manifest.json
index 2394e10..4ff6afa 100644
--- a/custom_components/ha_cloud_music/manifest.json
+++ b/custom_components/ha_cloud_music/manifest.json
@@ -1,7 +1,7 @@
{
"domain": "ha_cloud_music",
"name": "\u4E91\u97F3\u4E50",
- "version": "2023.8.13",
+ "version": "2023.11.1",
"config_flow": true,
"documentation": "https://github.com/shaonianzhentan/ha_cloud_music",
"requirements": [
diff --git a/custom_components/ha_cloud_music/media_player.py b/custom_components/ha_cloud_music/media_player.py
index 890771e..51d91a3 100644
--- a/custom_components/ha_cloud_music/media_player.py
+++ b/custom_components/ha_cloud_music/media_player.py
@@ -13,7 +13,6 @@ from homeassistant.components.media_player.const import (
SUPPORT_VOLUME_SET,
SUPPORT_VOLUME_MUTE,
SUPPORT_SELECT_SOURCE,
- SUPPORT_SELECT_SOUND_MODE,
SUPPORT_PLAY_MEDIA,
SUPPORT_PLAY,
SUPPORT_PAUSE,
@@ -51,7 +50,6 @@ DOMAIN = manifest.domain
_LOGGER = logging.getLogger(__name__)
SUPPORT_FEATURES = SUPPORT_VOLUME_STEP | SUPPORT_VOLUME_MUTE | SUPPORT_VOLUME_SET | \
- SUPPORT_SELECT_SOURCE | SUPPORT_SELECT_SOUND_MODE | \
SUPPORT_PLAY_MEDIA | SUPPORT_PLAY | SUPPORT_PAUSE | SUPPORT_PREVIOUS_TRACK | SUPPORT_NEXT_TRACK | \
SUPPORT_BROWSE_MEDIA | SUPPORT_SEEK | SUPPORT_CLEAR_PLAYLIST | SUPPORT_SHUFFLE_SET | SUPPORT_REPEAT_SET
@@ -63,14 +61,14 @@ async def async_setup_entry(
entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
- media_player = CloudMusicMediaPlayer(hass)
+ media_player = CloudMusicMediaPlayer(hass, entry)
await hass.async_add_executor_job(track_time_interval, hass, media_player.interval, TIME_BETWEEN_UPDATES)
async_add_entities([ media_player ], True)
class CloudMusicMediaPlayer(MediaPlayerEntity):
- def __init__(self, hass):
+ def __init__(self, hass, entry):
self.hass = hass
self._attributes = {
'platform': 'cloud_music'
@@ -81,9 +79,7 @@ class CloudMusicMediaPlayer(MediaPlayerEntity):
self._attr_supported_features = SUPPORT_FEATURES
# default attribute
- self._attr_source_list = []
- self._attr_sound_mode = None
- self._attr_sound_mode_list = []
+ self.source_media_player = entry.options.get('media_player')
self._attr_name = manifest.name
self._attr_unique_id = manifest.documentation
self._attr_state = STATE_ON
@@ -140,8 +136,8 @@ class CloudMusicMediaPlayer(MediaPlayerEntity):
@property
def media_player(self):
- if self.entity_id is not None and self._attr_sound_mode is not None:
- return self.hass.states.get(self._attr_sound_mode)
+ if self.entity_id is not None and self.source_media_player is not None:
+ return self.hass.states.get(self.source_media_player)
@property
def device_info(self):
@@ -162,15 +158,6 @@ class CloudMusicMediaPlayer(MediaPlayerEntity):
async def async_browse_media(self, media_content_type=None, media_content_id=None):
return await self.cloud_music.async_browse_media(self, media_content_type, media_content_id)
- async def async_select_source(self, source):
- if self._attr_source_list.count(source) > 0:
- self._attr_source = source
-
- async def async_select_sound_mode(self, sound_mode):
- if self._attr_sound_mode_list.count(sound_mode) > 0:
- await self.async_media_pause()
- self._attr_sound_mode = sound_mode
-
async def async_volume_up(self):
await self.async_call('volume_up')
@@ -240,18 +227,7 @@ class CloudMusicMediaPlayer(MediaPlayerEntity):
# 更新属性
async def async_update(self):
- if self.entity_id is not None:
- state = self.hass.states.get(self.entity_id)
- entities = state.attributes.get('media_player')
- if entities is not None:
- # 兼容初版
- if isinstance(entities, str):
- entities = [ entities ]
-
- if len(entities) > 0:
- self._attr_sound_mode_list = entities
- if self._attr_sound_mode is None:
- self._attr_sound_mode = entities[0]
+ pass
# 调用服务
async def async_call(self, service, service_data={}):
diff --git a/custom_components/ha_cloud_music/translations/en.json b/custom_components/ha_cloud_music/translations/en.json
index b96664d..05568d1 100644
--- a/custom_components/ha_cloud_music/translations/en.json
+++ b/custom_components/ha_cloud_music/translations/en.json
@@ -1,35 +1,35 @@
{
- "title": "云音乐",
- "config": {
- "abort": {
- "single_instance_allowed": "仅允许单个配置"
- },
- "step": {
- "user": {
- "title": "接口配置",
- "description": "为防止你的账号密码泄露,建议自行部署API接口服务 \n免费部署文档:https://neteasecloudmusicapi.vercel.app \n实在是搞不来,也可以付费使用由我部署维护持续更新的接口服务😊",
- "data": {
- "url": "网易云音乐API"
- }
- }
- },
- "error": {
- "login_failed": "登录失败"
- }
+ "title": "云音乐",
+ "config": {
+ "abort": {
+ "single_instance_allowed": "仅允许单个配置"
},
- "options": {
- "step": {
- "user": {
- "title": "网易云音乐",
- "description": "登录后会将cookie保存在HomeAssistant存储目录之中",
- "data": {
- "username": "邮箱/手机号",
- "password": "网易云音乐密码"
- }
- }
- },
- "error": {
- "login_failed": "登录失败"
+ "step": {
+ "user": {
+ "title": "接口配置",
+ "description": "为防止你的账号密码泄露,建议自行部署API接口服务 \n免费部署文档:https://neteasecloudmusicapi.vercel.app \n实在是搞不来,也可以付费使用由我部署维护持续更新的接口服务😊",
+ "data": {
+ "url": "网易云音乐API"
}
+ }
+ },
+ "error": {
+ "login_failed": "登录失败",
+ "api_failed": "接口地址不正确"
}
+ },
+ "options": {
+ "step": {
+ "user": {
+ "title": "配置",
+ "description": "关联的媒体播放器必须支持自定义音乐资源,可通过TTS插件自行测试是否可用",
+ "data": {
+ "media_player": "关联媒体播放器"
+ }
+ }
+ },
+ "error": {
+ "login_failed": "登录失败"
+ }
+ }
}
\ No newline at end of file