Add extract media url service to media extractor (#100780)
* Exclude manifest files from youtube media extraction * Add media_extractor service to extract media * Fix snapshot * Run ytdlp async * Add icon * Fix * Fix
This commit is contained in:
parent
7cd0fe3c5f
commit
e9894f8e91
13 changed files with 2045 additions and 7 deletions
|
@ -17,18 +17,29 @@ from homeassistant.components.media_player import (
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_ENTITY_ID
|
from homeassistant.const import ATTR_ENTITY_ID
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
ServiceResponse,
|
||||||
|
SupportsResponse,
|
||||||
|
)
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
ATTR_FORMAT_QUERY,
|
||||||
|
ATTR_URL,
|
||||||
|
DEFAULT_STREAM_QUERY,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF_CUSTOMIZE_ENTITIES = "customize"
|
CONF_CUSTOMIZE_ENTITIES = "customize"
|
||||||
CONF_DEFAULT_STREAM_QUERY = "default_query"
|
CONF_DEFAULT_STREAM_QUERY = "default_query"
|
||||||
|
|
||||||
DEFAULT_STREAM_QUERY = "best"
|
|
||||||
DOMAIN = "media_extractor"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.Schema(
|
DOMAIN: vol.Schema(
|
||||||
|
@ -47,10 +58,62 @@ CONFIG_SCHEMA = vol.Schema(
|
||||||
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
def setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
"""Set up the media extractor service."""
|
"""Set up the media extractor service."""
|
||||||
|
|
||||||
|
async def extract_media_url(call: ServiceCall) -> ServiceResponse:
|
||||||
|
"""Extract media url."""
|
||||||
|
youtube_dl = YoutubeDL(
|
||||||
|
{"quiet": True, "logger": _LOGGER, "format": call.data[ATTR_FORMAT_QUERY]}
|
||||||
|
)
|
||||||
|
|
||||||
|
def extract_info() -> dict[str, Any]:
|
||||||
|
return cast(
|
||||||
|
dict[str, Any],
|
||||||
|
youtube_dl.extract_info(
|
||||||
|
call.data[ATTR_URL], download=False, process=False
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await hass.async_add_executor_job(extract_info)
|
||||||
|
if "entries" in result:
|
||||||
|
_LOGGER.warning("Playlists are not supported, looking for the first video")
|
||||||
|
entries = list(result["entries"])
|
||||||
|
if entries:
|
||||||
|
selected_media = entries[0]
|
||||||
|
else:
|
||||||
|
raise HomeAssistantError("Playlist is empty")
|
||||||
|
else:
|
||||||
|
selected_media = result
|
||||||
|
if "formats" in selected_media:
|
||||||
|
if selected_media["extractor"] == "youtube":
|
||||||
|
url = get_best_stream_youtube(selected_media["formats"])
|
||||||
|
else:
|
||||||
|
url = get_best_stream(selected_media["formats"])
|
||||||
|
else:
|
||||||
|
url = cast(str, selected_media["url"])
|
||||||
|
return {"url": url}
|
||||||
|
|
||||||
def play_media(call: ServiceCall) -> None:
|
def play_media(call: ServiceCall) -> None:
|
||||||
"""Get stream URL and send it to the play_media service."""
|
"""Get stream URL and send it to the play_media service."""
|
||||||
MediaExtractor(hass, config[DOMAIN], call.data).extract_and_send()
|
MediaExtractor(hass, config[DOMAIN], call.data).extract_and_send()
|
||||||
|
|
||||||
|
default_format_query = config.get(DOMAIN, {}).get(
|
||||||
|
CONF_DEFAULT_STREAM_QUERY, DEFAULT_STREAM_QUERY
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL,
|
||||||
|
extract_media_url,
|
||||||
|
schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_URL): cv.string,
|
||||||
|
vol.Optional(
|
||||||
|
ATTR_FORMAT_QUERY, default=default_format_query
|
||||||
|
): cv.string,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
||||||
|
|
||||||
hass.services.register(
|
hass.services.register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
|
|
9
homeassistant/components/media_extractor/const.py
Normal file
9
homeassistant/components/media_extractor/const.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"""Constants for media_extractor."""
|
||||||
|
|
||||||
|
DEFAULT_STREAM_QUERY = "best"
|
||||||
|
DOMAIN = "media_extractor"
|
||||||
|
|
||||||
|
ATTR_URL = "url"
|
||||||
|
ATTR_FORMAT_QUERY = "format_query"
|
||||||
|
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL = "extract_media_url"
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"services": {
|
"services": {
|
||||||
"play_media": "mdi:play"
|
"play_media": "mdi:play",
|
||||||
|
"extract_media_url": "mdi:link"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,3 +19,14 @@ play_media:
|
||||||
- "MUSIC"
|
- "MUSIC"
|
||||||
- "TVSHOW"
|
- "TVSHOW"
|
||||||
- "VIDEO"
|
- "VIDEO"
|
||||||
|
extract_media_url:
|
||||||
|
fields:
|
||||||
|
url:
|
||||||
|
required: true
|
||||||
|
example: "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
format_query:
|
||||||
|
example: "best"
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
|
@ -13,6 +13,20 @@
|
||||||
"description": "The type of the content to play. Must be one of MUSIC, TVSHOW, VIDEO, EPISODE, CHANNEL or PLAYLIST MUSIC."
|
"description": "The type of the content to play. Must be one of MUSIC, TVSHOW, VIDEO, EPISODE, CHANNEL or PLAYLIST MUSIC."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"extract_media_url": {
|
||||||
|
"name": "Get Media URL",
|
||||||
|
"description": "Extract media url from a service.",
|
||||||
|
"fields": {
|
||||||
|
"url": {
|
||||||
|
"name": "Media URL",
|
||||||
|
"description": "URL where the media can be found."
|
||||||
|
},
|
||||||
|
"format_query": {
|
||||||
|
"name": "Format query",
|
||||||
|
"description": "Youtube-dl query to select the quality of the result."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,13 @@ class MockYoutubeDL:
|
||||||
"""Initialize mock object for YoutubeDL."""
|
"""Initialize mock object for YoutubeDL."""
|
||||||
self.params = params
|
self.params = params
|
||||||
|
|
||||||
def extract_info(self, url: str, *, process: bool = False) -> dict[str, Any]:
|
def extract_info(
|
||||||
|
self, url: str, *, download: bool = True, process: bool = False
|
||||||
|
) -> dict[str, Any]:
|
||||||
"""Return info."""
|
"""Return info."""
|
||||||
self._fixture = _get_base_fixture(url)
|
self._fixture = _get_base_fixture(url)
|
||||||
|
if not download:
|
||||||
|
return load_json_object_fixture(f"media_extractor/{self._fixture}.json")
|
||||||
return load_json_object_fixture(f"media_extractor/{self._fixture}_info.json")
|
return load_json_object_fixture(f"media_extractor/{self._fixture}_info.json")
|
||||||
|
|
||||||
def process_ie_result(
|
def process_ie_result(
|
||||||
|
|
87
tests/components/media_extractor/fixtures/no_formats.json
Normal file
87
tests/components/media_extractor/fixtures/no_formats.json
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
"id": "223644256",
|
||||||
|
"uploader": "BRUTTOBAND",
|
||||||
|
"uploader_id": "111488150",
|
||||||
|
"uploader_url": "https://soundcloud.com/bruttoband",
|
||||||
|
"timestamp": 1442140228,
|
||||||
|
"title": "BRUTTO - \u0420\u043e\u0434\u043d\u044b \u043a\u0440\u0430\u0439",
|
||||||
|
"description": "",
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"id": "mini",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-mini.jpg",
|
||||||
|
"width": 16,
|
||||||
|
"height": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tiny",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-tiny.jpg",
|
||||||
|
"width": 20,
|
||||||
|
"height": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "small",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-small.jpg",
|
||||||
|
"width": 32,
|
||||||
|
"height": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "badge",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-badge.jpg",
|
||||||
|
"width": 47,
|
||||||
|
"height": 47
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t67x67",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t67x67.jpg",
|
||||||
|
"width": 67,
|
||||||
|
"height": 67
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "large",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-large.jpg",
|
||||||
|
"width": 100,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t300x300",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t300x300.jpg",
|
||||||
|
"width": 300,
|
||||||
|
"height": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "crop",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-crop.jpg",
|
||||||
|
"width": 400,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t500x500",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t500x500.jpg",
|
||||||
|
"width": 500,
|
||||||
|
"height": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "original",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-original.jpg",
|
||||||
|
"preference": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"duration": 229.089,
|
||||||
|
"webpage_url": "https://soundcloud.com/bruttoband/brutto-11",
|
||||||
|
"license": "all-rights-reserved",
|
||||||
|
"view_count": 291779,
|
||||||
|
"like_count": 3347,
|
||||||
|
"comment_count": 14,
|
||||||
|
"repost_count": 59,
|
||||||
|
"genre": "Brutto",
|
||||||
|
"url": "https://cf-media.sndcdn.com/50remGX1OqRY.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vNTByZW1HWDFPcVJZLjEyOC5tcDMqIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNjk0Nzk4NTkzfX19XX0_&Signature=flALJvEBnzS0ZOOhf0-07Ap~NURw2Gn2OqkeKKTTMX5HRGJw9eXFay79tcC4GsMMXWUgWoCx-n3yelpyilE2MOEIufBNUbjqRfMSJaX5YhYxjQdoDYuiU~gqBzJyPw9pKzr6P8~5HNKL3Idr0CNhUzdV6FQLaUPKMMibq9ghV833mUmdyvdk1~GZBc8MOg9GrTdcigGgpPzd-vrIMICMvFzFnwBOeOotxX2Vfqf9~wVekBKGlvB9A~7TlZ71lv9Fl9u4m8rse9E-mByweVc1M784ehJV3~tRPjuF~FXXWKP8x0nGJmoq7RAnG7iFIt~fQFmsfOq2o~PG7dHMRPh7hw__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ",
|
||||||
|
"ext": "mp3",
|
||||||
|
"original_url": "https://soundcloud.com/bruttoband/brutto-11",
|
||||||
|
"webpage_url_basename": "brutto-11",
|
||||||
|
"webpage_url_domain": "soundcloud.com",
|
||||||
|
"extractor": "soundcloud",
|
||||||
|
"extractor_key": "Soundcloud",
|
||||||
|
"heatmap": [],
|
||||||
|
"automatic_captions": {}
|
||||||
|
}
|
114
tests/components/media_extractor/fixtures/soundcloud.json
Normal file
114
tests/components/media_extractor/fixtures/soundcloud.json
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
{
|
||||||
|
"id": "223644255",
|
||||||
|
"uploader": "BRUTTOBAND",
|
||||||
|
"uploader_id": "111488150",
|
||||||
|
"uploader_url": "https://soundcloud.com/bruttoband",
|
||||||
|
"timestamp": 1442140228,
|
||||||
|
"title": "BRUTTO - \u0420\u043e\u0434\u043d\u044b \u043a\u0440\u0430\u0439",
|
||||||
|
"description": "",
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"id": "mini",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-mini.jpg",
|
||||||
|
"width": 16,
|
||||||
|
"height": 16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tiny",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-tiny.jpg",
|
||||||
|
"width": 20,
|
||||||
|
"height": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "small",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-small.jpg",
|
||||||
|
"width": 32,
|
||||||
|
"height": 32
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "badge",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-badge.jpg",
|
||||||
|
"width": 47,
|
||||||
|
"height": 47
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t67x67",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t67x67.jpg",
|
||||||
|
"width": 67,
|
||||||
|
"height": 67
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "large",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-large.jpg",
|
||||||
|
"width": 100,
|
||||||
|
"height": 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t300x300",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t300x300.jpg",
|
||||||
|
"width": 300,
|
||||||
|
"height": 300
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "crop",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-crop.jpg",
|
||||||
|
"width": 400,
|
||||||
|
"height": 400
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "t500x500",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-t500x500.jpg",
|
||||||
|
"width": 500,
|
||||||
|
"height": 500
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "original",
|
||||||
|
"url": "https://i1.sndcdn.com/artworks-000129385232-ysbisn-original.jpg",
|
||||||
|
"preference": 10
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"duration": 229.089,
|
||||||
|
"webpage_url": "https://soundcloud.com/bruttoband/brutto-11",
|
||||||
|
"license": "all-rights-reserved",
|
||||||
|
"view_count": 291779,
|
||||||
|
"like_count": 3347,
|
||||||
|
"comment_count": 14,
|
||||||
|
"repost_count": 59,
|
||||||
|
"genre": "Brutto",
|
||||||
|
"formats": [
|
||||||
|
{
|
||||||
|
"url": "https://cf-hls-media.sndcdn.com/playlist/50remGX1OqRY.128.mp3/playlist.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLWhscy1tZWRpYS5zbmRjZG4uY29tL3BsYXlsaXN0LzUwcmVtR1gxT3FSWS4xMjgubXAzL3BsYXlsaXN0Lm0zdTgqIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNjk1NTAzOTYzfX19XX0_&Signature=eNeIoSTgRZL89YBJYXpmRg0AVGk3M0gV4E4rYPYbFw6pTePHO4o8Mv6HwdK85FOMsaUHZvYgzc35uWPhAr1SUqqjnm--xwN8VUrDkCPgdv97Vrs9qJ9QElHKnlWhK2-BDs3Y7sDcAurA00L2uReB-vjI-4K65WBApYBTaUGnOACimoVAOWHmtigO0Ap5DxlEh7fqqwi88enEvVDE-98v5uX9FcV9lq9AfVwEtfqbPsjVJyh6WbWAB3PJDJElvV13RgKmzVvbFluLElYlDud9WMsHjztdWhdaRzGOj1AfcQcwkQbQlBRiAKMtqrRlzAAXnBfLvMF3DOvdYWeCwJeCXA__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ",
|
||||||
|
"ext": "mp3",
|
||||||
|
"abr": 128,
|
||||||
|
"format_id": "hls_mp3_128",
|
||||||
|
"protocol": "m3u8_native",
|
||||||
|
"preference": null,
|
||||||
|
"vcodec": "none"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://cf-media.sndcdn.com/50remGX1OqRY.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vNTByZW1HWDFPcVJZLjEyOC5tcDMqIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNjk1NTAzOTYzfX19XX0_&Signature=JAG~zJ~2NOWOgiuHLCSYWwdjUVuYWR2fBvmxPGSnLzMgX2xqu5~WfOk-gOyRUbHhnKnybUbP70cr6~t~Qx0KEU5mwIy2H0YhOXDHFX5RJVQlj1iCVuko-hAFJc7RtZuKTP5oCWOM-R2a6HfYN88YAIqgwWbGvTKin1CAgHaICeoM2p5O50n-kp05KgCw3RKcRutkYT-RVcWkmXtY4D4Jtw~LuBERDNyErseTHzmruDCkaYkVNlTcaIdgygQjgxVlgZiIRj-p0vRNO0qv5Bc0LfNMBzYm9fTAr86c~TzxyvQRhwHOPYp-DCXcs1K6i9x4BVvHWLOSHr0Dhd3X4fe5kw__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ",
|
||||||
|
"ext": "mp3",
|
||||||
|
"abr": 128,
|
||||||
|
"format_id": "http_mp3_128",
|
||||||
|
"protocol": "http",
|
||||||
|
"preference": null,
|
||||||
|
"vcodec": "none"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://cf-hls-opus-media.sndcdn.com/playlist/50remGX1OqRY.64.opus/playlist.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLWhscy1vcHVzLW1lZGlhLnNuZGNkbi5jb20vcGxheWxpc3QvNTByZW1HWDFPcVJZLjY0Lm9wdXMvcGxheWxpc3QubTN1OCoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2OTU1MDM5NjR9fX1dfQ__&Signature=HqyeKoK4gx1Bd3jId5v1-9ltDY2SN7fzGp6M7tW3kWluv8Ns0SgANPKG3~Tzo8hUrQzAYVvwMbO2F75b6NBeBr4xx0SRCeKotZdArzBFT4Wtrz4HtEddLyPjp12vWYCDuOshd1sTjrvaBAd9TAFTZuwyal1OKpLMsqK0QN-KFH-5GsmLDZTPWcoVkMqC7XBmNWTq0G1mtVeP57TN~9T7qEYqRLFIDXdm2HKSPSR4BB2gjaZUK22pBUPSGVr-ziBSpNVHvNISy2QdObjS5zjuAe8bl-npQ3PlFatYECDI3Gc~wjCNIJPdTpCbHUsX36SSR4dnKlgW1nYGx~eED7dppA__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ",
|
||||||
|
"ext": "opus",
|
||||||
|
"abr": 64,
|
||||||
|
"format_id": "hls_opus_64",
|
||||||
|
"protocol": "m3u8_native",
|
||||||
|
"preference": null,
|
||||||
|
"vcodec": "none"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"original_url": "https://soundcloud.com/bruttoband/brutto-11",
|
||||||
|
"webpage_url_basename": "brutto-11",
|
||||||
|
"webpage_url_domain": "soundcloud.com",
|
||||||
|
"extractor": "soundcloud",
|
||||||
|
"extractor_key": "Soundcloud",
|
||||||
|
"heatmap": [],
|
||||||
|
"automatic_captions": {}
|
||||||
|
}
|
1430
tests/components/media_extractor/fixtures/youtube_1.json
Normal file
1430
tests/components/media_extractor/fixtures/youtube_1.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"id": "PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJO",
|
||||||
|
"title": "Very important videos",
|
||||||
|
"availability": "public",
|
||||||
|
"channel_follower_count": null,
|
||||||
|
"description": "Not original",
|
||||||
|
"tags": [],
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwEwCKgBEF5IWvKriqkDIwgBFQAAiEIYAfABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLDBCH5IQ0obogxXhAzIH8pE0d7r1Q",
|
||||||
|
"height": 94,
|
||||||
|
"width": 168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwEwCMQBEG5IWvKriqkDIwgBFQAAiEIYAfABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLAybhgn-CoPMjBE-0VfBDqvy0jyOQ",
|
||||||
|
"height": 110,
|
||||||
|
"width": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwExCPYBEIoBSFryq4qpAyMIARUAAIhCGAHwAQH4Ad4DgALoAooCDAgAEAEYPCBlKD8wDw==&rs=AOn4CLDutIdjr5zTE9G78eWf83-mGXYnUA",
|
||||||
|
"height": 138,
|
||||||
|
"width": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwExCNACELwBSFryq4qpAyMIARUAAIhCGAHwAQH4Ad4DgALoAooCDAgAEAEYPCBlKD8wDw==&rs=AOn4CLD2884fHuvAv8ysHA48LD3uArB6bA",
|
||||||
|
"height": 188,
|
||||||
|
"width": 336
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_date": "20230813",
|
||||||
|
"view_count": 5770834,
|
||||||
|
"playlist_count": 3,
|
||||||
|
"channel": "ZulTarx",
|
||||||
|
"channel_id": "UChOLuQpsxxmJiJUeSU2tSTw",
|
||||||
|
"uploader_id": "@Armand314",
|
||||||
|
"uploader": "ZulTarx",
|
||||||
|
"channel_url": "https://www.youtube.com/channel/UChOLuQpsxxmJiJUeSU2tSTw",
|
||||||
|
"uploader_url": "https://www.youtube.com/@Armand314",
|
||||||
|
"_type": "playlist",
|
||||||
|
"entries": [],
|
||||||
|
"extractor_key": "YoutubeTab",
|
||||||
|
"extractor": "youtube:tab",
|
||||||
|
"webpage_url": "https://www.youtube.com/playlist?list=PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJO",
|
||||||
|
"original_url": "https://www.youtube.com/playlist?list=PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJO",
|
||||||
|
"webpage_url_basename": "playlist",
|
||||||
|
"webpage_url_domain": "youtube.com",
|
||||||
|
"heatmap": [],
|
||||||
|
"automatic_captions": {}
|
||||||
|
}
|
179
tests/components/media_extractor/fixtures/youtube_playlist.json
Normal file
179
tests/components/media_extractor/fixtures/youtube_playlist.json
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
{
|
||||||
|
"id": "PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJP",
|
||||||
|
"title": "Very important videos",
|
||||||
|
"availability": "public",
|
||||||
|
"channel_follower_count": null,
|
||||||
|
"description": "Not original",
|
||||||
|
"tags": [],
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwEwCKgBEF5IWvKriqkDIwgBFQAAiEIYAfABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLDBCH5IQ0obogxXhAzIH8pE0d7r1Q",
|
||||||
|
"height": 94,
|
||||||
|
"width": 168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwEwCMQBEG5IWvKriqkDIwgBFQAAiEIYAfABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLAybhgn-CoPMjBE-0VfBDqvy0jyOQ",
|
||||||
|
"height": 110,
|
||||||
|
"width": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwExCPYBEIoBSFryq4qpAyMIARUAAIhCGAHwAQH4Ad4DgALoAooCDAgAEAEYPCBlKD8wDw==&rs=AOn4CLDutIdjr5zTE9G78eWf83-mGXYnUA",
|
||||||
|
"height": 138,
|
||||||
|
"width": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwExCNACELwBSFryq4qpAyMIARUAAIhCGAHwAQH4Ad4DgALoAooCDAgAEAEYPCBlKD8wDw==&rs=AOn4CLD2884fHuvAv8ysHA48LD3uArB6bA",
|
||||||
|
"height": 188,
|
||||||
|
"width": 336
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified_date": "20230813",
|
||||||
|
"view_count": 5770834,
|
||||||
|
"playlist_count": 3,
|
||||||
|
"channel": "ZulTarx",
|
||||||
|
"channel_id": "UChOLuQpsxxmJiJUeSU2tSTw",
|
||||||
|
"uploader_id": "@Armand314",
|
||||||
|
"uploader": "ZulTarx",
|
||||||
|
"channel_url": "https://www.youtube.com/channel/UChOLuQpsxxmJiJUeSU2tSTw",
|
||||||
|
"uploader_url": "https://www.youtube.com/@Armand314",
|
||||||
|
"_type": "playlist",
|
||||||
|
"entries": [
|
||||||
|
{
|
||||||
|
"_type": "url",
|
||||||
|
"ie_key": "Youtube",
|
||||||
|
"id": "q6EoRBvdVPQ",
|
||||||
|
"url": "https://www.youtube.com/watch?v=q6EoRBvdVPQ",
|
||||||
|
"title": "Yee",
|
||||||
|
"description": null,
|
||||||
|
"duration": 10,
|
||||||
|
"channel_id": "UC-fD_qwTEQQ1L-MUWx_mNvg",
|
||||||
|
"channel": "revergo",
|
||||||
|
"channel_url": "https://www.youtube.com/channel/UC-fD_qwTEQQ1L-MUWx_mNvg",
|
||||||
|
"uploader": "revergo",
|
||||||
|
"uploader_id": "@revergo",
|
||||||
|
"uploader_url": "https://www.youtube.com/@revergo",
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwE1CKgBEF5IVfKriqkDKAgBFQAAiEIYAXABwAEG8AEB-AHeA4AC6AKKAgwIABABGDwgZSg_MA8=&rs=AOn4CLAJYg16HMBdEsv9lYBJyNqA5G3anQ",
|
||||||
|
"height": 94,
|
||||||
|
"width": 168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwE1CMQBEG5IVfKriqkDKAgBFQAAiEIYAXABwAEG8AEB-AHeA4AC6AKKAgwIABABGDwgZSg_MA8=&rs=AOn4CLAgCNP9UuQas-D59hHHM-RqkUvA6g",
|
||||||
|
"height": 110,
|
||||||
|
"width": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwE2CPYBEIoBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLCTWaY5897XxhcpRyVtGQQNuMHfTg",
|
||||||
|
"height": 138,
|
||||||
|
"width": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/q6EoRBvdVPQ/hqdefault.jpg?sqp=-oaymwE2CNACELwBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgB3gOAAugCigIMCAAQARg8IGUoPzAP&rs=AOn4CLCeS6NC75yTYvyP4DsehZ3oXNuxMQ",
|
||||||
|
"height": 188,
|
||||||
|
"width": 336
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": null,
|
||||||
|
"release_timestamp": null,
|
||||||
|
"availability": null,
|
||||||
|
"view_count": 96000000,
|
||||||
|
"live_status": null,
|
||||||
|
"channel_is_verified": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_type": "url",
|
||||||
|
"ie_key": "Youtube",
|
||||||
|
"id": "8YWl7tDGUPA",
|
||||||
|
"url": "https://www.youtube.com/watch?v=8YWl7tDGUPA",
|
||||||
|
"title": "color red",
|
||||||
|
"description": null,
|
||||||
|
"duration": 17,
|
||||||
|
"channel_id": "UCbYMTn6xKV0IKshL4pRCV3g",
|
||||||
|
"channel": "Alex Jimenez",
|
||||||
|
"channel_url": "https://www.youtube.com/channel/UCbYMTn6xKV0IKshL4pRCV3g",
|
||||||
|
"uploader": "Alex Jimenez",
|
||||||
|
"uploader_id": "@alexjimenez1237",
|
||||||
|
"uploader_url": "https://www.youtube.com/@alexjimenez1237",
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/8YWl7tDGUPA/hqdefault.jpg?sqp=-oaymwE1CKgBEF5IVfKriqkDKAgBFQAAiEIYAXABwAEG8AEB-AG2BIACwAKKAgwIABABGGUgXShUMA8=&rs=AOn4CLBqzngIx-4i_HFvqloetUfeN8yrYw",
|
||||||
|
"height": 94,
|
||||||
|
"width": 168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/8YWl7tDGUPA/hqdefault.jpg?sqp=-oaymwE1CMQBEG5IVfKriqkDKAgBFQAAiEIYAXABwAEG8AEB-AG2BIACwAKKAgwIABABGGUgXShUMA8=&rs=AOn4CLB7mWPQmdL2QBLxTHhrgbFj2jFaCg",
|
||||||
|
"height": 110,
|
||||||
|
"width": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/8YWl7tDGUPA/hqdefault.jpg?sqp=-oaymwE2CPYBEIoBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgBtgSAAsACigIMCAAQARhlIF0oVDAP&rs=AOn4CLA9YAIO3g_DnClsuc5LjMQn4O9ZQQ",
|
||||||
|
"height": 138,
|
||||||
|
"width": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/8YWl7tDGUPA/hqdefault.jpg?sqp=-oaymwE2CNACELwBSFXyq4qpAygIARUAAIhCGAFwAcABBvABAfgBtgSAAsACigIMCAAQARhlIF0oVDAP&rs=AOn4CLDPHY6aG08hlTJMlc-LJt9ywtpWEg",
|
||||||
|
"height": 188,
|
||||||
|
"width": 336
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": null,
|
||||||
|
"release_timestamp": null,
|
||||||
|
"availability": null,
|
||||||
|
"view_count": 30000000,
|
||||||
|
"live_status": null,
|
||||||
|
"channel_is_verified": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"_type": "url",
|
||||||
|
"ie_key": "Youtube",
|
||||||
|
"id": "6bnanI9jXps",
|
||||||
|
"url": "https://www.youtube.com/watch?v=6bnanI9jXps",
|
||||||
|
"title": "Terrible Mall Commercial",
|
||||||
|
"description": null,
|
||||||
|
"duration": 31,
|
||||||
|
"channel_id": "UCLmnB20wsih9F5N0o5K0tig",
|
||||||
|
"channel": "quantim",
|
||||||
|
"channel_url": "https://www.youtube.com/channel/UCLmnB20wsih9F5N0o5K0tig",
|
||||||
|
"uploader": "quantim",
|
||||||
|
"uploader_id": "@Potatoflesh",
|
||||||
|
"uploader_url": "https://www.youtube.com/@Potatoflesh",
|
||||||
|
"thumbnails": [
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/6bnanI9jXps/hqdefault.jpg?sqp=-oaymwEbCKgBEF5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLAsyI0ZJA9STG8vlSdRkKk55ls5Dg",
|
||||||
|
"height": 94,
|
||||||
|
"width": 168
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/6bnanI9jXps/hqdefault.jpg?sqp=-oaymwEbCMQBEG5IVfKriqkDDggBFQAAiEIYAXABwAEG&rs=AOn4CLD2bZ9S8AB4UGsZlx_8TjBoL72enA",
|
||||||
|
"height": 110,
|
||||||
|
"width": 196
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/6bnanI9jXps/hqdefault.jpg?sqp=-oaymwEcCPYBEIoBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLCKNlgvl_7lKoFq8vyDYZRtTs4woA",
|
||||||
|
"height": 138,
|
||||||
|
"width": 246
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://i.ytimg.com/vi/6bnanI9jXps/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLBeZv8F8IyICmKD9qjo9pTMJmM8ug",
|
||||||
|
"height": 188,
|
||||||
|
"width": 336
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": null,
|
||||||
|
"release_timestamp": null,
|
||||||
|
"availability": null,
|
||||||
|
"view_count": 26000000,
|
||||||
|
"live_status": null,
|
||||||
|
"channel_is_verified": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extractor_key": "YoutubeTab",
|
||||||
|
"extractor": "youtube:tab",
|
||||||
|
"webpage_url": "https://www.youtube.com/playlist?list=PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJP",
|
||||||
|
"original_url": "https://www.youtube.com/playlist?list=PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJP",
|
||||||
|
"webpage_url_basename": "playlist",
|
||||||
|
"webpage_url_domain": "youtube.com",
|
||||||
|
"heatmap": [],
|
||||||
|
"automatic_captions": {}
|
||||||
|
}
|
|
@ -1,4 +1,24 @@
|
||||||
# serializer version: 1
|
# serializer version: 1
|
||||||
|
# name: test_extract_media_service[https://soundcloud.com/bruttoband/brutto-11]
|
||||||
|
dict({
|
||||||
|
'url': 'https://cf-hls-opus-media.sndcdn.com/playlist/50remGX1OqRY.64.opus/playlist.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLWhscy1vcHVzLW1lZGlhLnNuZGNkbi5jb20vcGxheWxpc3QvNTByZW1HWDFPcVJZLjY0Lm9wdXMvcGxheWxpc3QubTN1OCoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2OTU1MDM5NjR9fX1dfQ__&Signature=HqyeKoK4gx1Bd3jId5v1-9ltDY2SN7fzGp6M7tW3kWluv8Ns0SgANPKG3~Tzo8hUrQzAYVvwMbO2F75b6NBeBr4xx0SRCeKotZdArzBFT4Wtrz4HtEddLyPjp12vWYCDuOshd1sTjrvaBAd9TAFTZuwyal1OKpLMsqK0QN-KFH-5GsmLDZTPWcoVkMqC7XBmNWTq0G1mtVeP57TN~9T7qEYqRLFIDXdm2HKSPSR4BB2gjaZUK22pBUPSGVr-ziBSpNVHvNISy2QdObjS5zjuAe8bl-npQ3PlFatYECDI3Gc~wjCNIJPdTpCbHUsX36SSR4dnKlgW1nYGx~eED7dppA__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_extract_media_service[https://test.com/abc]
|
||||||
|
dict({
|
||||||
|
'url': 'https://cf-media.sndcdn.com/50remGX1OqRY.128.mp3?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiKjovL2NmLW1lZGlhLnNuZGNkbi5jb20vNTByZW1HWDFPcVJZLjEyOC5tcDMqIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNjk0Nzk4NTkzfX19XX0_&Signature=flALJvEBnzS0ZOOhf0-07Ap~NURw2Gn2OqkeKKTTMX5HRGJw9eXFay79tcC4GsMMXWUgWoCx-n3yelpyilE2MOEIufBNUbjqRfMSJaX5YhYxjQdoDYuiU~gqBzJyPw9pKzr6P8~5HNKL3Idr0CNhUzdV6FQLaUPKMMibq9ghV833mUmdyvdk1~GZBc8MOg9GrTdcigGgpPzd-vrIMICMvFzFnwBOeOotxX2Vfqf9~wVekBKGlvB9A~7TlZ71lv9Fl9u4m8rse9E-mByweVc1M784ehJV3~tRPjuF~FXXWKP8x0nGJmoq7RAnG7iFIt~fQFmsfOq2o~PG7dHMRPh7hw__&Key-Pair-Id=APKAI6TU7MMXM5DG6EPQ',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_extract_media_service[https://www.youtube.com/playlist?list=PLZ4DbyIWUwCq4V8bIEa8jm2ozHZVuREJP]
|
||||||
|
dict({
|
||||||
|
'url': 'https://www.youtube.com/watch?v=q6EoRBvdVPQ',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
|
# name: test_extract_media_service[https://www.youtube.com/watch?v=dQw4w9WgXcQ]
|
||||||
|
dict({
|
||||||
|
'url': 'https://rr2---sn-5hnekn7k.googlevideo.com/videoplayback?expire=1695524274&ei=UlEPZeSINMKn6dsPt6mr8Ac&ip=45.93.75.130&id=o-AA7xSUDqvzXPAn6m_Jd8d0jhyk4er7c5zjbyZCHyREaQ&itag=22&source=youtube&requiressl=yes&mh=7c&mm=31%2C29&mn=sn-5hnekn7k%2Csn-5hne6nzy&ms=au%2Crdu&mv=m&mvi=2&pl=22&initcwndbps=1311250&spc=UWF9f6JvGMIlJJFOWUL6L4bH2pl28C4&vprv=1&svpuc=1&mime=video%2Fmp4&cnr=14&ratebypass=yes&dur=212.091&lmt=1694045086815467&mt=1695502401&fvip=3&fexp=24007246&beids=24350018&c=ANDROID&txp=4532434&sparams=expire%2Cei%2Cip%2Cid%2Citag%2Csource%2Crequiressl%2Cspc%2Cvprv%2Csvpuc%2Cmime%2Ccnr%2Cratebypass%2Cdur%2Clmt&sig=AOq0QJ8wRAIgHVwajP0J2fiJ1ERoAonpxghXGzDmEXh3rvJ399UEMWECIFdBjiVUOk7QdiFBxQ4QqojJd8p_PfL25TV_8TBrp_Kb&lsparams=mh%2Cmm%2Cmn%2Cms%2Cmv%2Cmvi%2Cpl%2Cinitcwndbps&lsig=AG3C_xAwRgIhANJRDHqeYwWk4W8XXGlTNmYA3OOiuzK3PEWtVpK4NimlAiEAwK0nj8vGtcP0IJMZVBGh8SC5KRFzPfx_yJn41ZOUGyQ%3D',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_no_target_entity
|
# name: test_no_target_entity
|
||||||
ReadOnlyDict({
|
ReadOnlyDict({
|
||||||
'device_id': list([
|
'device_id': list([
|
||||||
|
|
|
@ -9,9 +9,14 @@ import pytest
|
||||||
from syrupy import SnapshotAssertion
|
from syrupy import SnapshotAssertion
|
||||||
from yt_dlp import DownloadError
|
from yt_dlp import DownloadError
|
||||||
|
|
||||||
from homeassistant.components.media_extractor import DOMAIN
|
from homeassistant.components.media_extractor.const import (
|
||||||
|
ATTR_URL,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL,
|
||||||
|
)
|
||||||
from homeassistant.components.media_player import SERVICE_PLAY_MEDIA
|
from homeassistant.components.media_player import SERVICE_PLAY_MEDIA
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import load_json_object_fixture
|
from tests.common import load_json_object_fixture
|
||||||
|
@ -30,6 +35,58 @@ async def test_play_media_service_is_registered(hass: HomeAssistant) -> None:
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.services.has_service(DOMAIN, SERVICE_PLAY_MEDIA)
|
assert hass.services.has_service(DOMAIN, SERVICE_PLAY_MEDIA)
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_EXTRACT_MEDIA_URL)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"url",
|
||||||
|
[
|
||||||
|
YOUTUBE_VIDEO,
|
||||||
|
SOUNDCLOUD_TRACK,
|
||||||
|
NO_FORMATS_RESPONSE,
|
||||||
|
YOUTUBE_PLAYLIST,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_extract_media_service(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_youtube_dl: MockYoutubeDL,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
empty_media_extractor_config: dict[str, Any],
|
||||||
|
url: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test play media service is registered."""
|
||||||
|
await async_setup_component(hass, DOMAIN, empty_media_extractor_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert (
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL,
|
||||||
|
{ATTR_URL: url},
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
== snapshot
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_extracting_playlist_no_entries(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_youtube_dl: MockYoutubeDL,
|
||||||
|
empty_media_extractor_config: dict[str, Any],
|
||||||
|
) -> None:
|
||||||
|
"""Test extracting a playlist without entries."""
|
||||||
|
|
||||||
|
await async_setup_component(hass, DOMAIN, empty_media_extractor_config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
with pytest.raises(HomeAssistantError):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_EXTRACT_MEDIA_URL,
|
||||||
|
{ATTR_URL: YOUTUBE_EMPTY_PLAYLIST},
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
|
|
Loading…
Add table
Reference in a new issue