My Journey to a reliable and enjoyable locally hosted voice assistant

I have created a new pyscript / tool for Assist. This allows it to use the YouTube API to search for videos and then play them on media players.

PyScript
import aiohttp
import os

async def _search_youtube_videos(query: str, api_key: str, max_results: int = 1):
    """Helper function to search for YouTube videos using the YouTube Data API.
    
    Args:
        query: The search query string
        api_key: YouTube Data API key
        max_results: Maximum number of results to return (default: 1)
    """
    base_url = "https://www.googleapis.com/youtube/v3/search"
    
    params = {
        "part": "snippet",
        "q": query,
        "type": "video",
        "maxResults": max_results,
        "key": api_key
    }
    
    # --- Fetch from YouTube Data API using aiohttp (fully async) ---
    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(base_url, params=params, timeout=10) as response:
                if response.status != 200:
                    error_text = await response.text()
                    log.error(f"YouTube API error {response.status}: {error_text}")
                    response.raise_for_status()
                
                data = await response.json()
    except aiohttp.ClientResponseError as e:
        log.error(f"YouTube API request failed: {e}")
        raise
    except Exception as e:
        log.error(f"Unexpected error during YouTube API request: {e}")
        raise
    
    items = data.get("items", [])
    return items

@service(supports_response="only")
async def search_youtube_video(query: str, api_key: str | None = None):
    """This service searches YouTube for videos matching the query and returns the top result.
    
    Args:
        query: The search query string
        api_key: YouTube Data API key (if not provided, will try to get from environment variable YOUTUBE_API_KEY)
    """
    
    if not api_key:
        api_key = os.getenv("YOUTUBE_API_KEY")
        if not api_key:
            return { "error": "YouTube API key is required. Provide it as a parameter or set YOUTUBE_API_KEY environment variable." }
    
    # Search for videos
    try:
        results = await _search_youtube_videos(query, api_key, max_results=1)
    except Exception as e:
        log.error(f"Failed to search YouTube: {e}")
        return { "error": "Failed to search YouTube. Please check API key configuration and ensure YouTube Data API v3 is enabled." }
    
    if not results:
        return { "video": "No videos found for the given query" }
    
    # Extract the top result
    top_result = results[0]
    video_id = top_result.get("id", {}).get("videoId")
    snippet = top_result.get("snippet", {})
    
    video_data = {
        "videoId": video_id,
        "title": snippet.get("title"),
        "channelTitle": snippet.get("channelTitle"),
        "publishedAt": snippet.get("publishedAt"),
        "description": snippet.get("description"),
        "thumbnail": snippet.get("thumbnails", {}).get("default", {}).get("url"),
        "url": f"https://www.youtube.com/watch?v={video_id}" if video_id else None
    }
    
    return { "video": video_data }

HA Script
sequence:
  - action: pyscript.search_youtube_video
    data:
      query: "{{ query }}"
    response_variable: video
  - action: media_player.play_media
    data:
      media:
        media_content_id: "{{ video.video.url }}"
        media_content_type: url
        metadata: {}
    target:
      entity_id: "{{ player }}"
alias: YouTube Video Search
description: >-
  Searches YouTube for videos matching the query and plays the top result on the
  selected media player. Returns video information including title, channel,
  description, and URL.
fields:
  query:
    selector:
      text: null
    name: Search Query
    description: The search query to find YouTube videos.
    required: true
  player:
    selector:
      select:
        options:
          - media_player.xyz
          - media_player.abc
    name: Media Player
    description: The media player device where the YouTube video will be played.
    required: true
icon: mdi:movie-open-play

1 Like