iobroker.assistant 1.0.3 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/admin/custom/.vite/manifest.json +62 -62
- package/admin/custom/assets/Components-BSewCBfS.js +2 -0
- package/admin/custom/assets/{_virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomAssistant__mf_owner__1-A4gakomR.js → _virtual_mf-localSharedImportMap___mfe_internal__ConfigCustomAssistant__mf_owner__1-C5NPM0Of.js} +1 -1
- package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-CwqmfMvn.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare___mf_0_emotion_mf_1_styled__loadShare__.js-IaosaymG.js} +1 -1
- package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-BMuLYe6f.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare___mf_0_iobroker_mf_1_gui_mf_2_components__loadShare__.js-Cdyjt5KT.js} +39 -39
- package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__loadShare__shared-BxjZmnpc.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare__shared-Dsw5zWS8.js} +1 -1
- package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-SgSagTOv.js +122 -0
- package/admin/custom/assets/{hostInit-4roC4hEj.js → hostInit-jJe8bIfV.js} +1 -1
- package/admin/custom/assets/{lib-CchzgTUs.js → lib-CL8mXvI6.js} +1 -1
- package/admin/custom/assets/{mf-entry-bootstrap-0-4806bbe4.js → mf-entry-bootstrap-0-ab227aa6.js} +2 -2
- package/admin/custom/assets/{pendingShares-DLlv8yFY.js → pendingShares-wrGtfliQ.js} +1 -1
- package/admin/custom/customComponents.js +2 -2
- package/admin/custom/i18n/de.json +40 -0
- package/admin/custom/i18n/en.json +40 -0
- package/admin/custom/i18n/es.json +40 -0
- package/admin/custom/i18n/fr.json +40 -0
- package/admin/custom/i18n/it.json +40 -0
- package/admin/custom/i18n/nl.json +40 -0
- package/admin/custom/i18n/pl.json +40 -0
- package/admin/custom/i18n/pt.json +40 -0
- package/admin/custom/i18n/ru.json +40 -0
- package/admin/custom/i18n/uk.json +40 -0
- package/admin/custom/i18n/zh-cn.json +40 -0
- package/admin/custom/index.html +2 -2
- package/admin/custom/mf-manifest.json +1 -1
- package/admin/custom/mf-stats.json +1 -1
- package/admin/i18n/de.json +13 -3
- package/admin/i18n/en.json +13 -3
- package/admin/i18n/es.json +13 -3
- package/admin/i18n/fr.json +13 -3
- package/admin/i18n/it.json +13 -3
- package/admin/i18n/nl.json +13 -3
- package/admin/i18n/pl.json +13 -3
- package/admin/i18n/pt.json +13 -3
- package/admin/i18n/ru.json +13 -3
- package/admin/i18n/uk.json +13 -3
- package/admin/i18n/zh-cn.json +13 -3
- package/admin/jsonConfig.json +61 -2
- package/build/lib/journal.js +298 -2
- package/build/lib/journal.js.map +1 -1
- package/build/lib/llm.js +105 -4
- package/build/lib/llm.js.map +1 -1
- package/build/lib/localLlm.js +47 -10
- package/build/lib/localLlm.js.map +1 -1
- package/build/lib/nlu.js +124 -5
- package/build/lib/nlu.js.map +1 -1
- package/build/lib/tools.js +3 -0
- package/build/lib/tools.js.map +1 -1
- package/build/lib/voice/esphome.js +81 -7
- package/build/lib/voice/esphome.js.map +1 -1
- package/build/lib/voice/esphomeProto.js.map +1 -1
- package/build/lib/voice/mediaServer.js +29 -1
- package/build/lib/voice/mediaServer.js.map +1 -1
- package/build/lib/voice/protocol.js.map +1 -1
- package/build/lib/voice/voiceServer.js +81 -7
- package/build/lib/voice/voiceServer.js.map +1 -1
- package/build/lib/voice/wakeWords.js +284 -0
- package/build/lib/voice/wakeWords.js.map +1 -0
- package/build/lib/voice/wyoming.js +12 -4
- package/build/lib/voice/wyoming.js.map +1 -1
- package/build/lib/weather.js +160 -0
- package/build/lib/weather.js.map +1 -1
- package/build/lib/youtube.js +625 -0
- package/build/lib/youtube.js.map +1 -0
- package/build/main.js +1152 -76
- package/build/main.js.map +1 -1
- package/io-package.json +30 -14
- package/package.json +6 -6
- package/admin/custom/assets/Components-C6qcpaRC.js +0 -1
- package/admin/custom/assets/_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__prebuild___mf_0_iobroker_mf_1_json_mf_2_config__prebuild__-TM4l0OYm.js +0 -122
- /package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-BkH7fzDw.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__loadShare___mf_0_emotion_mf_1_react__loadShare__.js-BkH7fzDw.js} +0 -0
- /package/admin/custom/assets/{_virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__201375093093590__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-M61rjtmp.js → _virtual_mf___mfe_internal__ConfigCustomAssistant__mf_owner__163539842885036__prebuild__react_mf_1_jsx_mf_2_runtime__prebuild__-M61rjtmp.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"youtube.js","sourceRoot":"","sources":["../../src/lib/youtube.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * YouTube on the TV: \"spiele den Kanal Sailing OceanAir ab\" → find the channel, switch the TV on, play it.\n *\n * Playing goes through the `youtube-remote` adapter (YouTube Lounge API, \"Link with TV\"): its `youtube` state\n * takes a video id, a playlist id (`PL…`), a channel id (`UC…`, plays the uploads) or a URL. What it cannot\n * do is turn a spoken name into one of those — handles and names need a search. That search lives here and\n * uses InnerTube, the JSON API the YouTube web page itself talks to: no API key and no quota, unlike the Data\n * API (100 units per search, 10 000 a day). The price is that Google does not document it; the parser\n * therefore walks the whole response for the renderer objects it knows instead of following one fixed path,\n * so a reshuffled page layout still yields results.\n *\n * Which player is meant comes from a settings table (name, player, power state, room). A request without a\n * name goes to the player in the room it came from, then to the first row — \"the TV\" in a one-TV household.\n * A row normally names a youtube-remote **instance**, not its device: the device id comes from the TV's\n * screen id and changes when the TV is paired again, so the device is looked up at runtime\n * ({@link pickDevice}).\n */\n\n/** What to search for. `video` is also the kind of a plain \"play X on YouTube\". */\nexport type YouTubeKind = 'channel' | 'video' | 'playlist';\n\n/** One configured YouTube player. */\nexport interface YouTubePlayer {\n /** As configured; '' until the device of an instance row was found (then the device name). */\n name: string;\n /** The youtube-remote instance the row names (`youtube-remote.0`), '' if the row names a device. */\n instance: string;\n /** Device object of the player, e.g. `youtube-remote.0.tv_8198d994`; '' until resolved. */\n base: string;\n /** The state that takes what to play (`<base>.youtube`); '' until resolved. */\n target: string;\n /** State that switches the TV on (`switch.power`), '' = none. */\n power: string;\n room: string;\n /** Seconds to give the TV after it reported \"on\" before the YouTube app can take a command. */\n delaySec: number;\n}\n\n/** One row of the settings table (everything arrives as text, the number column as number). */\nexport interface YouTubePlayerRow {\n name?: string;\n id?: string;\n power?: string;\n room?: string;\n delay?: number | string;\n}\n\n/** One search result. */\nexport interface YouTubeHit {\n kind: YouTubeKind;\n /** Video id, playlist id or channel id — exactly what the `youtube` state of the player takes. */\n id: string;\n title: string;\n /** Channel of a video or playlist. */\n channel?: string;\n /** `@handle` of a channel, if the result shows it. */\n handle?: string;\n}\n\n/** A play request the rule-based path understood. */\nexport interface YouTubeCommand {\n /** `auto` = nothing said what it is: a channel of that name if there is one, else videos. */\n kind: YouTubeKind | 'auto';\n query: string;\n /** Player name as configured, if the request named one. */\n player?: string;\n}\n\n/** Default boot time of a TV between \"power reports on\" and \"YouTube app answers\". */\nexport const DEFAULT_POWER_DELAY_SEC = 8;\n\n/** Fold a word or name for comparing: lower case, umlauts spelled out, punctuation gone, single spaces. */\nexport function fold(text: unknown): string {\n if (typeof text !== 'string') {\n return '';\n }\n return text\n .toLowerCase()\n .replace(/ä/g, 'ae')\n .replace(/ö/g, 'oe')\n .replace(/ü/g, 'ue')\n .replace(/ß/g, 'ss')\n .replace(/ё/g, 'е')\n .replace(/[^\\p{L}\\p{N}]+/gu, ' ')\n .trim()\n .replace(/\\s+/g, ' ');\n}\n\n/** Same word up to an inflected ending (\"Wohnzimmer\"/\"Wohnzimmers\", \"телевизор\"/\"телевизоре\"). */\nfunction sameWord(a: string, b: string): boolean {\n if (a === b) {\n return true;\n }\n const short = a.length < b.length ? a : b;\n const long = a.length < b.length ? b : a;\n return short.length >= 4 && long.startsWith(short) && long.length - short.length <= 2;\n}\n\n/** `youtube-remote.0` — an instance, as opposed to one of its devices. */\nexport function isInstanceId(id: string): boolean {\n return /^[\\w-]+\\.\\d+$/.test(id);\n}\n\n/**\n * Turn settings-table rows into players; rows without a player are skipped, the first name wins. A row may\n * name the instance (the usual case) or, for an instance with several TVs, one device or its `youtube` state.\n */\nexport function parsePlayerRows(rows: YouTubePlayerRow[] | undefined): YouTubePlayer[] {\n const out: YouTubePlayer[] = [];\n const seen = new Set<string>();\n for (const row of rows || []) {\n const id = String(row?.id ?? '')\n .trim()\n .replace(/\\.youtube$/, '');\n if (!id) {\n continue;\n }\n const instance = isInstanceId(id) ? id : '';\n const room = String(row?.room ?? '').trim();\n // An instance row without name and room is named after its device once that is found.\n const name = String(row?.name ?? '').trim() || room || (instance ? '' : id.split('.').pop() || id);\n const key = fold(name);\n if (key && seen.has(key)) {\n continue;\n }\n seen.add(key);\n const delay = Number(row?.delay);\n out.push({\n name,\n instance,\n base: instance ? '' : id,\n target: instance ? '' : `${id}.youtube`,\n power: String(row?.power ?? '').trim(),\n room,\n delaySec:\n row?.delay === undefined || row.delay === '' || !Number.isFinite(delay)\n ? DEFAULT_POWER_DELAY_SEC\n : Math.min(120, Math.max(0, delay)),\n });\n }\n return out;\n}\n\n/** A paired device of a youtube-remote instance. */\nexport interface YouTubeDevice {\n id: string;\n name: string;\n}\n\n/**\n * The device an instance row means: the one named like the row (name or room), else the first — with one\n * TV per instance, which is the normal case, simply that one. `null` while nothing is paired.\n */\nexport function pickDevice(\n devices: YouTubeDevice[],\n player: Pick<YouTubePlayer, 'name' | 'room'>,\n): YouTubeDevice | null {\n if (!devices.length) {\n return null;\n }\n const sorted = [...devices].sort((a, b) => a.id.localeCompare(b.id));\n return (\n // Every word of the row must be in the device name: a row \"Schlafzimmer TV\" is not the device \"TV\".\n sorted.find(d => !!player.name && nameMatches(player.name, d.name)) ||\n sorted.find(d => !!player.room && nameMatches(player.room, d.name)) ||\n sorted[0]\n );\n}\n\n/** Does a configured name (all its words) match what was said? */\nfunction nameMatches(name: string, wanted: string): boolean {\n const a = fold(name);\n const b = fold(wanted);\n if (!a || !b) {\n return false;\n }\n if (a === b) {\n return true;\n }\n const nameWords = a.split(' ');\n const saidWords = b.split(' ');\n return nameWords.every(n => saidWords.some(s => sameWord(n, s)));\n}\n\n/**\n * Which player is meant: the named one (name, then room), else the one in the room the request came from,\n * else the first row. A name that matches nothing is `null` — playing on the wrong TV is worse than asking.\n */\nexport function findPlayer(players: YouTubePlayer[], wanted?: string, room?: string): YouTubePlayer | null {\n if (!players.length) {\n return null;\n }\n const name = (wanted || '').trim();\n if (name) {\n return (\n players.find(p => fold(p.name) === fold(name)) ||\n players.find(p => p.room && fold(p.room) === fold(name)) ||\n players.find(p => nameMatches(p.name, name) || (!!p.room && nameMatches(p.room, name))) ||\n null\n );\n }\n if (room) {\n const here = players.find(p => (p.room && fold(p.room) === fold(room)) || fold(p.name) === fold(room));\n if (here) {\n return here;\n }\n }\n return players[0];\n}\n\n/** Does a value of a power state mean \"on\"? */\nexport function isPowerOn(value: unknown): boolean {\n if (typeof value === 'boolean') {\n return value;\n }\n if (typeof value === 'number') {\n return value > 0;\n }\n return typeof value === 'string' && /^(on|true|1|an|ein|power_?on|вкл)$/i.test(value.trim());\n}\n\n/**\n * The query as something the player takes without a search: a youtube.com/youtu.be URL, a channel id, a\n * playlist id, or — when a video is asked for — an 11-character video id. `null` = needs a search.\n */\nexport function directYouTubeTarget(query: string, kind: YouTubeKind): string | null {\n const q = query.trim();\n if (/^(https?:\\/\\/)?((www|m|music)\\.)?(youtube\\.com|youtu\\.be)\\//i.test(q)) {\n return q;\n }\n if (/^UC[\\w-]{22}$/.test(q) || /^(PL|UU|OL|FL)[\\w-]{10,}$/.test(q)) {\n return q;\n }\n // A plain word of 11 letters (\"Tagesschaus\") is no id: a real one nearly always mixes in digits,\n // capitals or -/_.\n if (kind === 'video' && /^[\\w-]{11}$/.test(q) && /[\\d_A-Z-]/.test(q.slice(1))) {\n return q;\n }\n return null;\n}\n\n/** InnerTube search filter (`sp` parameter of the results page): only channels / videos / playlists. */\nconst SEARCH_FILTER: Record<YouTubeKind, string> = {\n video: 'EgIQAQ==',\n channel: 'EgIQAg==',\n playlist: 'EgIQAw==',\n};\n\n/** Text of an InnerTube text object (`{simpleText}` or `{runs:[{text}]}`). */\nfunction textOf(node: unknown): string {\n if (!node || typeof node !== 'object') {\n return '';\n }\n const n = node as { simpleText?: unknown; runs?: { text?: unknown }[]; content?: unknown };\n if (typeof n.simpleText === 'string') {\n return n.simpleText;\n }\n if (typeof n.content === 'string') {\n return n.content;\n }\n if (Array.isArray(n.runs)) {\n return n.runs.map(r => (typeof r?.text === 'string' ? r.text : '')).join('');\n }\n return '';\n}\n\n/**\n * Pull the results of one kind out of an InnerTube search response. Walks the whole tree: the renderer\n * objects stay stable for years, the containers around them do not (playlists already moved from\n * `playlistRenderer` to `lockupViewModel`).\n */\nexport function parseSearchResults(response: unknown, kind: YouTubeKind): YouTubeHit[] {\n const hits: YouTubeHit[] = [];\n const seen = new Set<string>();\n const add = (hit: YouTubeHit): void => {\n if (hit.id && !seen.has(hit.id)) {\n seen.add(hit.id);\n hits.push(hit);\n }\n };\n const walk = (node: unknown, depth: number): void => {\n if (!node || typeof node !== 'object' || depth > 40) {\n return;\n }\n if (Array.isArray(node)) {\n for (const item of node) {\n walk(item, depth + 1);\n }\n return;\n }\n for (const [key, value] of Object.entries(node as Record<string, unknown>)) {\n const v = value as Record<string, any>;\n if (key === 'channelRenderer' && kind === 'channel' && typeof v?.channelId === 'string') {\n // YouTube shows the handle in the field that used to hold the subscriber count.\n const handle = [textOf(v.subscriberCountText), textOf(v.videoCountText)].find(t => t.startsWith('@'));\n add({ kind, id: v.channelId, title: textOf(v.title), handle });\n } else if (key === 'videoRenderer' && kind === 'video' && typeof v?.videoId === 'string') {\n add({ kind, id: v.videoId, title: textOf(v.title), channel: textOf(v.ownerText) || undefined });\n } else if (key === 'playlistRenderer' && kind === 'playlist' && typeof v?.playlistId === 'string') {\n add({\n kind,\n id: v.playlistId,\n title: textOf(v.title),\n channel: textOf(v.shortBylineText) || undefined,\n });\n } else if (key === 'lockupViewModel' && typeof v?.contentId === 'string') {\n const type = String(v.contentType || '');\n const meta = v.metadata?.lockupMetadataViewModel;\n if (\n (kind === 'playlist' && type === 'LOCKUP_CONTENT_TYPE_PLAYLIST') ||\n (kind === 'video' && type === 'LOCKUP_CONTENT_TYPE_VIDEO')\n ) {\n add({ kind, id: v.contentId, title: textOf(meta?.title) });\n }\n }\n walk(value, depth + 1);\n }\n };\n walk(response, 0);\n return hits;\n}\n\n/** Edit distance of two short strings (names, not texts — the matrix is small). */\nfunction editDistance(a: string, b: string): number {\n let prev = Array.from({ length: b.length + 1 }, (_, k) => k);\n for (let i = 1; i <= a.length; i++) {\n const cur = [i];\n for (let k = 1; k <= b.length; k++) {\n cur[k] = Math.min(prev[k] + 1, cur[k - 1] + 1, prev[k - 1] + (a[i - 1] === b[k - 1] ? 0 : 1));\n }\n prev = cur;\n }\n return prev[b.length];\n}\n\n/**\n * Is a spoken name the same name? Spaces do not count and a typo or two per eight letters is allowed,\n * because the name went through speech recognition: \"Sailing Ocean Air\" is the channel \"Sailing Oceanaire\".\n */\nexport function similarName(said: string, name: string): boolean {\n const a = fold(said).replace(/ /g, '');\n const b = fold(name).replace(/ /g, '');\n if (!a || !b) {\n return false;\n }\n return a === b || editDistance(a, b) <= Math.max(1, Math.floor(Math.min(a.length, b.length) / 8));\n}\n\n/** Is this channel the one that was named — by its title (without the \" | tagline\") or its handle? */\nexport function isNamedChannel(hit: YouTubeHit, query: string): boolean {\n return similarName(query, spokenTitle(hit)) || (!!hit.handle && similarName(query, hit.handle.replace(/^@/, '')));\n}\n\n/**\n * The hit that was most likely meant: a channel whose name is the query (see {@link isNamedChannel}), a\n * video or playlist titled like it, otherwise YouTube's own first result. The name match matters when a\n * bigger channel outranks the one that was named.\n */\nexport function pickBest(hits: YouTubeHit[], query: string): YouTubeHit | null {\n if (!hits.length) {\n return null;\n }\n const q = fold(query);\n return hits.find(h => (h.kind === 'channel' ? isNamedChannel(h, query) : fold(h.title) === q)) || hits[0];\n}\n\n/** The title as it should be spoken: without the \" | tagline\" channels like to append. */\nexport function spokenTitle(hit: YouTubeHit): string {\n return hit.title.split(' | ')[0].trim() || hit.title;\n}\n\nexport interface SearchOptions {\n /** Interface language of the results (ISO, e.g. `de`). */\n language?: string;\n timeoutMs?: number;\n fetchFn?: typeof fetch;\n}\n\n/** Search YouTube for channels, videos or playlists; throws on a network or HTTP error. */\nexport async function searchYouTube(query: string, kind: YouTubeKind, opts: SearchOptions = {}): Promise<YouTubeHit[]> {\n const fetchFn = opts.fetchFn || fetch;\n const res = await fetchFn('https://www.youtube.com/youtubei/v1/search?prettyPrint=false', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n context: { client: { clientName: 'WEB', clientVersion: '2.20250101.00.00', hl: opts.language || 'en' } },\n query,\n params: SEARCH_FILTER[kind],\n }),\n signal: AbortSignal.timeout(opts.timeoutMs || 10_000),\n });\n if (res.status !== 200) {\n throw new Error(`YouTube search answered HTTP ${res.status}`);\n }\n return parseSearchResults(await res.json(), kind);\n}\n\n// ── Rule-based \"play … on YouTube\" ──────────────────────────────────────────\n\nconst VERBS = new Set([\n 'spiel',\n 'spiele',\n 'spielt',\n 'abspielen',\n 'zeig',\n 'zeige',\n 'starte',\n 'start',\n 'play',\n 'show',\n 'put',\n 'включи',\n 'поставь',\n 'запусти',\n 'покажи',\n 'воспроизведи',\n]);\nconst POLITE = new Set(['bitte', 'mal', 'please', 'пожалуйста']);\nconst FILLERS = new Set([\n 'mir',\n 'uns',\n 'me',\n 'us',\n 'мне',\n 'нам',\n 'den',\n 'das',\n 'die',\n 'einen',\n 'ein',\n 'eine',\n 'the',\n 'a',\n 'an',\n 'some',\n 'mal',\n 'bitte',\n]);\nconst KIND_WORDS: Record<string, YouTubeKind> = {\n kanal: 'channel',\n channel: 'channel',\n канал: 'channel',\n video: 'video',\n clip: 'video',\n видео: 'video',\n ролик: 'video',\n playlist: 'playlist',\n wiedergabeliste: 'playlist',\n плейлист: 'playlist',\n};\nconst OF_WORDS = new Set(['von', 'vom', 'of', 'from', 'by', 'от']);\nconst YOUTUBE_WORDS = new Set(['youtube', 'ютуб', 'ютубе', 'ютюб', 'ютюбе']);\nconst PREPOSITIONS = new Set(['auf', 'im', 'in', 'am', 'an', 'bei', 'dem', 'der', 'on', 'at', 'the', 'на', 'в', 'во']);\n/** Prepositions that only ever say \"where\" — what follows them right after the verb is a place, not a title. */\nconst LOCATIVE = new Set(['im', 'в', 'во']);\nconst ARTICLES = new Set(['der', 'dem', 'den', 'the']);\nconst TRAILING = new Set(['ab', 'please', 'bitte', 'пожалуйста', 'jetzt', 'now']);\n/** A \"query\" that is really a request to repeat — not something to search for. */\nconst NOT_A_QUERY = new Set(['nochmal', 'noch mal', 'weiter', 'again', 'more', 'ещё', 'еще', 'ещё раз', 'еще раз']);\n\n/**\n * Understand \"spiele den Kanal Sailing OceanAir (auf dem Fernseher) ab\", \"play the channel X on the bedroom\n * TV\", \"включи канал X\", \"spiele im Wohnzimmer Sailing Ocean Air auf YouTube\". Needs a kind word\n * (Kanal/channel/канал, Video, Playlist) or the word YouTube — \"spiele Musik im Wohnzimmer\" stays with the\n * rest of the pipeline. Without a kind word the kind is `auto`. A player name or room right after the verb\n * or at the end is cut off the query, as is a place right after the verb (\"im Bad\") — that one is then\n * looked up as a player and answered as unknown, which beats searching YouTube for \"im Bad X\". Everything\n * else stays in the query with its spelling, because it is what gets searched for.\n */\nexport function parseYouTubeCommand(text: string, players: YouTubePlayer[]): YouTubeCommand | null {\n const words = text\n .trim()\n .split(/\\s+/)\n .map(w => w.replace(/^[„\"“”'«»]+|[„\"“”'«».,!?;:]+$/g, ''))\n .filter(Boolean);\n const low = words.map(w => fold(w));\n let i = 0;\n while (i < low.length && POLITE.has(low[i])) {\n i++;\n }\n if (i >= low.length || !VERBS.has(low[i])) {\n return null;\n }\n i++;\n const sawYouTube = low.some(w => YOUTUBE_WORDS.has(w));\n // Player names and rooms, longest first so \"Schlafzimmer TV\" wins over \"TV\".\n const candidates = players\n .flatMap(p => [\n { player: p.name, words: fold(p.name).split(' ') },\n ...(p.room ? [{ player: p.name || p.room, words: fold(p.room).split(' ') }] : []),\n ])\n .filter(c => c.words[0])\n .sort((a, b) => b.words.length - a.words.length);\n const nameAt = (at: number): { player: string; end: number } | null => {\n for (const c of candidates) {\n if (c.words.every((w, k) => at + k < low.length && sameWord(w, low[at + k]))) {\n return { player: c.player, end: at + c.words.length };\n }\n }\n return null;\n };\n /** \"im Wohnzimmer\", \"auf dem Fernseher\", \"в спальне\" starting at `at`; `null` if it is no place. */\n const placeAt = (at: number): { player: string; end: number } | null => {\n let j = at;\n while (j < low.length && PREPOSITIONS.has(low[j])) {\n j++;\n }\n if (j === at || j >= low.length) {\n return null;\n }\n const named = nameAt(j);\n if (named) {\n return named;\n }\n const locative = LOCATIVE.has(low[at]) || (low[at] === 'in' && ARTICLES.has(low[at + 1]));\n return locative ? { player: words[j], end: j + 1 } : null;\n };\n let kind: YouTubeKind | 'auto' | null = null;\n let player: string | undefined;\n // Fillers, \"auf YouTube\", the kind word and the place may come in any order before the name.\n for (;;) {\n if (i < low.length && FILLERS.has(low[i])) {\n i++;\n } else if (i + 1 < low.length && PREPOSITIONS.has(low[i]) && YOUTUBE_WORDS.has(low[i + 1])) {\n i += 2;\n } else if (i < low.length && YOUTUBE_WORDS.has(low[i])) {\n i++;\n } else if (!kind && i < low.length && KIND_WORDS[low[i]]) {\n kind = KIND_WORDS[low[i]];\n i++;\n if (i < low.length && OF_WORDS.has(low[i])) {\n i++;\n }\n } else if (!player && i < low.length) {\n const place = placeAt(i);\n // Only if something is left to search for.\n if (!place || place.end >= low.length) {\n break;\n }\n player = place.player;\n i = place.end;\n } else {\n break;\n }\n }\n if (!kind) {\n if (!sawYouTube) {\n return null;\n }\n kind = 'auto';\n }\n let end = words.length;\n const trimTail = (): void => {\n for (;;) {\n if (end > i && TRAILING.has(low[end - 1])) {\n end--;\n } else if (end > i && YOUTUBE_WORDS.has(low[end - 1])) {\n end--;\n while (end > i && PREPOSITIONS.has(low[end - 1])) {\n end--;\n }\n } else {\n return;\n }\n }\n };\n trimTail();\n // A trailing player: its name or room, preceded by a preposition so a channel called like a room is\n // not eaten (\"Kanal Küche\" without \"in der\" stays a channel).\n for (const c of player ? [] : candidates) {\n const n = c.words.length;\n if (end - n <= i) {\n continue;\n }\n const tail = low.slice(end - n, end);\n if (!c.words.every((w, k) => sameWord(w, tail[k]))) {\n continue;\n }\n let start = end - n;\n if (!PREPOSITIONS.has(low[start - 1])) {\n continue;\n }\n while (start > i && PREPOSITIONS.has(low[start - 1])) {\n start--;\n }\n if (start <= i) {\n continue; // nothing would be left to search for\n }\n player = c.player;\n end = start;\n trimTail();\n break;\n }\n const query = words.slice(i, end).join(' ').trim();\n if (!query || NOT_A_QUERY.has(fold(query))) {\n return null;\n }\n return { kind, query, ...(player ? { player } : {}) };\n}\n\n// ── Spoken replies (de/en/ru) ───────────────────────────────────────────────\n\nexport type YouTubeReplyKey =\n | 'playing'\n | 'poweringOn'\n | 'notFound'\n | 'searchFailed'\n | 'noConfirm'\n | 'adapterDown'\n | 'unknownPlayer'\n | 'noControl'\n | 'noDevice';\n\n/** \"the channel „X“\" in the reply language. */\nfunction describeWhat(lang: string, kind: YouTubeKind, title: string): string {\n if (lang === 'de') {\n return `${kind === 'channel' ? 'den Kanal' : kind === 'playlist' ? 'die Playlist' : 'das Video'} „${title}“`;\n }\n if (lang === 'ru') {\n return `${kind === 'channel' ? 'канал' : kind === 'playlist' ? 'плейлист' : 'видео'} «${title}»`;\n }\n return `the ${kind} \"${title}\"`;\n}\n\n/** What nothing was found of: \"keinen Kanal\", \"no channel\", \"канал\". */\nfunction describeNone(lang: string, kind: YouTubeKind): string {\n if (lang === 'de') {\n return kind === 'channel' ? 'keinen Kanal' : kind === 'playlist' ? 'keine Playlist' : 'kein Video';\n }\n if (lang === 'ru') {\n return kind === 'channel' ? 'канал' : kind === 'playlist' ? 'плейлист' : 'видео';\n }\n return `no ${kind}`;\n}\n\n/** A spoken reply of the YouTube feature. */\nexport function youtubeReply(\n lang: string,\n key: YouTubeReplyKey,\n v: { kind?: YouTubeKind; title?: string; query?: string; player?: string; names?: string; instance?: string } = {},\n): string {\n const kind = v.kind || 'video';\n const what = describeWhat(lang, kind, v.title || v.query || '');\n const player = v.player || '';\n if (lang === 'de') {\n switch (key) {\n case 'playing':\n return `Ich spiele ${what} auf ${player}.`;\n case 'poweringOn':\n return `Ich schalte ${player} ein und spiele dann ${what}.`;\n case 'notFound':\n return `Ich habe auf YouTube ${describeNone(lang, kind)} „${v.query}“ gefunden.`;\n case 'searchFailed':\n return 'Die YouTube-Suche ist gerade nicht erreichbar.';\n case 'noConfirm':\n return `${player} hat nicht reagiert. Ist der Fernseher an und die YouTube-App verbunden?`;\n case 'adapterDown':\n return `Der YouTube-Adapter ${v.instance} läuft nicht.`;\n case 'unknownPlayer':\n return `Einen Player „${v.query}“ kenne ich nicht. Ich kenne: ${v.names}.`;\n case 'noControl':\n return 'Die Gerätesteuerung ist in den Einstellungen ausgeschaltet.';\n case 'noDevice':\n return `Mit ${v.instance} ist noch kein Fernseher gekoppelt.`;\n }\n }\n if (lang === 'ru') {\n switch (key) {\n case 'playing':\n return `Включаю ${what} — ${player}.`;\n case 'poweringOn':\n return `Включаю ${player}, потом запущу ${what}.`;\n case 'notFound':\n return `На YouTube не нашла ${describeNone(lang, kind)} «${v.query}».`;\n case 'searchFailed':\n return 'Поиск YouTube сейчас недоступен.';\n case 'noConfirm':\n return `${player} не ответил. Телевизор включён и приложение YouTube подключено?`;\n case 'adapterDown':\n return `Адаптер YouTube ${v.instance} не запущен.`;\n case 'unknownPlayer':\n return `Плеер «${v.query}» мне не известен. Я знаю: ${v.names}.`;\n case 'noControl':\n return 'Управление устройствами отключено в настройках.';\n case 'noDevice':\n return `С ${v.instance} ещё не связан ни один телевизор.`;\n }\n }\n switch (key) {\n case 'playing':\n return `Playing ${what} on ${player}.`;\n case 'poweringOn':\n return `Switching ${player} on, then playing ${what}.`;\n case 'notFound':\n return `I found ${describeNone(lang, kind)} \"${v.query}\" on YouTube.`;\n case 'searchFailed':\n return 'The YouTube search cannot be reached right now.';\n case 'noConfirm':\n return `${player} did not respond. Is the TV on and the YouTube app linked?`;\n case 'adapterDown':\n return `The YouTube adapter ${v.instance} is not running.`;\n case 'unknownPlayer':\n return `I don't know a player \"${v.query}\". I know: ${v.names}.`;\n case 'noControl':\n return 'Device control is switched off in the settings.';\n case 'noDevice':\n return `No TV is paired with ${v.instance} yet.`;\n }\n return '';\n}\n"]}
|