diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml
index 292899014..44f879a2d 100644
--- a/.github/workflows/build-desktop.yml
+++ b/.github/workflows/build-desktop.yml
@@ -100,7 +100,11 @@ jobs:
librsvg2-dev \
patchelf \
gstreamer1.0-plugins-base \
- gstreamer1.0-plugins-good
+ gstreamer1.0-plugins-good \
+ gstreamer1.0-plugins-bad \
+ gstreamer1.0-plugins-ugly \
+ gstreamer1.0-libav \
+ gstreamer1.0-gl
- name: Install frontend dependencies
run: npm ci
diff --git a/api/youtube/live.js b/api/youtube/live.js
index 10593d341..97902421d 100644
--- a/api/youtube/live.js
+++ b/api/youtube/live.js
@@ -15,6 +15,28 @@ export default async function handler(request) {
}
const url = new URL(request.url);
const channel = url.searchParams.get('channel');
+ const videoIdParam = url.searchParams.get('videoId');
+
+ // Video ID lookup: resolve author name via oembed
+ if (videoIdParam && /^[A-Za-z0-9_-]{11}$/.test(videoIdParam)) {
+ try {
+ const oembedRes = await fetch(
+ `https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v=${videoIdParam}&format=json`,
+ { headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' } },
+ );
+ if (oembedRes.ok) {
+ const data = await oembedRes.json();
+ return new Response(JSON.stringify({ channelName: data.author_name || null, title: data.title || null, videoId: videoIdParam }), {
+ status: 200,
+ headers: { ...cors, 'Content-Type': 'application/json', 'Cache-Control': 'public, max-age=3600, s-maxage=3600' },
+ });
+ }
+ } catch {}
+ return new Response(JSON.stringify({ channelName: null, title: null, videoId: videoIdParam }), {
+ status: 200,
+ headers: { ...cors, 'Content-Type': 'application/json' },
+ });
+ }
if (!channel) {
return new Response(JSON.stringify({ error: 'Missing channel parameter' }), {
@@ -47,6 +69,16 @@ export default async function handler(request) {
// Channel exists if the page contains canonical channel metadata
const channelExists = html.includes('"channelId"') || html.includes('og:url');
+ // Extract channel name from page metadata (prefer channel name over video title)
+ let channelName = null;
+ const ownerMatch = html.match(/"ownerChannelName"\s*:\s*"([^"]+)"/);
+ if (ownerMatch) {
+ channelName = ownerMatch[1];
+ } else {
+ const authorMatch = html.match(/"author"\s*:\s*"([^"]+)"/);
+ if (authorMatch) channelName = authorMatch[1];
+ }
+
// Scope both fields to the same videoDetails block so we don't
// combine a videoId from one object with isLive from another.
let videoId = null;
@@ -60,7 +92,7 @@ export default async function handler(request) {
}
}
- return new Response(JSON.stringify({ videoId, isLive: videoId !== null, channelExists }), {
+ return new Response(JSON.stringify({ videoId, isLive: videoId !== null, channelExists, channelName }), {
status: 200,
headers: {
'Content-Type': 'application/json',
diff --git a/src/components/LiveNewsPanel.ts b/src/components/LiveNewsPanel.ts
index 49b981368..e90c5e4a4 100644
--- a/src/components/LiveNewsPanel.ts
+++ b/src/components/LiveNewsPanel.ts
@@ -72,7 +72,7 @@ const TECH_LIVE_CHANNELS: LiveChannel[] = [
{ id: 'bloomberg', name: 'Bloomberg', handle: '@Bloomberg', fallbackVideoId: 'iEpJwprxDdk' },
{ id: 'yahoo', name: 'Yahoo Finance', handle: '@YahooFinance', fallbackVideoId: 'KQp-e_XQnDE' },
{ id: 'cnbc', name: 'CNBC', handle: '@CNBC', fallbackVideoId: '9NyxcX3rhQs' },
- { id: 'nasa', name: 'NASA TV', handle: '@NASA', fallbackVideoId: 'fO9e9jnhYK8', useFallbackOnly: true },
+ { id: 'nasa', name: 'Sen Space Live', handle: '@NASA', fallbackVideoId: 'fO9e9jnhYK8', useFallbackOnly: true },
];
// Optional channels users can add from the "Available Channels" tab UI
@@ -85,6 +85,8 @@ export const OPTIONAL_LIVE_CHANNELS: LiveChannel[] = [
{ id: 'cbs-news', name: 'CBS News', handle: '@CBSNews' },
{ id: 'nbc-news', name: 'NBC News', handle: '@NBCNews' },
// Europe
+ { id: 'bbc-news', name: 'BBC News', handle: '@BBCNews' },
+ { id: 'france24-en', name: 'France 24 English', handle: '@FRANCE24English' },
{ id: 'welt', name: 'WELT', handle: '@WELTNachrichtensender' },
{ id: 'rtve', name: 'RTVE 24H', handle: '@RTVENoticias', fallbackVideoId: '7_srED6k0bE' },
{ id: 'trt-haber', name: 'TRT Haber', handle: '@trthaber' },
@@ -111,6 +113,12 @@ export const OPTIONAL_LIVE_CHANNELS: LiveChannel[] = [
{ id: 'vtc-now', name: 'VTC NOW', handle: '@VTCNOW' },
{ id: 'cna-asia', name: 'CNA (NewsAsia)', handle: '@channelnewsasia' },
{ id: 'nhk-world', name: 'NHK World Japan', handle: '@NHKWORLDJAPAN' },
+ // Middle East
+ { id: 'al-hadath', name: 'Al Hadath', handle: '@AlHadath', fallbackVideoId: 'xWXpl7azI8k', useFallbackOnly: true },
+ { id: 'sky-news-arabia', name: 'Sky News Arabia', handle: '@skynewsarabia' },
+ { id: 'trt-world', name: 'TRT World', handle: '@taborrtworld' },
+ { id: 'iran-intl', name: 'Iran International', handle: '@IranIntl' },
+ { id: 'cgtn-arabic', name: 'CGTN Arabic', handle: '@CGTNArabic' },
// Africa
{ id: 'africanews', name: 'Africanews', handle: '@africanews' },
{ id: 'channels-tv', name: 'Channels TV', handle: '@channelstv' },
@@ -121,9 +129,10 @@ export const OPTIONAL_LIVE_CHANNELS: LiveChannel[] = [
export const OPTIONAL_CHANNEL_REGIONS: { key: string; labelKey: string; channelIds: string[] }[] = [
{ key: 'na', labelKey: 'components.liveNews.regionNorthAmerica', channelIds: ['livenow-fox', 'fox-news', 'newsmax', 'abc-news', 'cbs-news', 'nbc-news'] },
- { key: 'eu', labelKey: 'components.liveNews.regionEurope', channelIds: ['welt', 'rtve', 'trt-haber', 'ntv-turkey', 'cnn-turk', 'tv-rain'] },
+ { key: 'eu', labelKey: 'components.liveNews.regionEurope', channelIds: ['bbc-news', 'france24-en', 'welt', 'rtve', 'trt-haber', 'ntv-turkey', 'cnn-turk', 'tv-rain'] },
{ key: 'latam', labelKey: 'components.liveNews.regionLatinAmerica', channelIds: ['cnn-brasil', 'jovem-pan', 'record-news', 'band-jornalismo', 'tn-argentina', 'c5n', 'milenio', 'noticias-caracol', 'ntn24', 't13'] },
{ key: 'asia', labelKey: 'components.liveNews.regionAsia', channelIds: ['tbs-news', 'ann-news', 'ntv-news', 'cti-news', 'wion', 'vtc-now', 'cna-asia', 'nhk-world'] },
+ { key: 'me', labelKey: 'components.liveNews.regionMiddleEast', channelIds: ['al-hadath', 'sky-news-arabia', 'trt-world', 'iran-intl', 'cgtn-arabic'] },
{ key: 'africa', labelKey: 'components.liveNews.regionAfrica', channelIds: ['africanews', 'channels-tv', 'ktn-news', 'enca', 'sabc-news'] },
];
diff --git a/src/live-channels-window.ts b/src/live-channels-window.ts
index a16b9b207..7c2cb8cfc 100644
--- a/src/live-channels-window.ts
+++ b/src/live-channels-window.ts
@@ -26,6 +26,35 @@ function customChannelIdFromHandle(handle: string): string {
return 'custom-' + normalized;
}
+/** Parse YouTube URL into a handle or video ID. Returns null if not a YouTube URL. */
+function parseYouTubeInput(raw: string): { handle: string } | { videoId: string } | null {
+ let url: URL;
+ try {
+ url = new URL(raw);
+ } catch {
+ return null;
+ }
+ if (!url.hostname.match(/^(www\.)?(youtube\.com|youtu\.be)$/)) return null;
+
+ // youtu.be/VIDEO_ID
+ if (url.hostname.includes('youtu.be')) {
+ const vid = url.pathname.slice(1);
+ if (/^[A-Za-z0-9_-]{11}$/.test(vid)) return { videoId: vid };
+ return null;
+ }
+ // youtube.com/watch?v=VIDEO_ID
+ const v = url.searchParams.get('v');
+ if (v && /^[A-Za-z0-9_-]{11}$/.test(v)) return { videoId: v };
+ // youtube.com/@Handle
+ const handleMatch = url.pathname.match(/^\/@([\w.-]{3,30})$/);
+ if (handleMatch) return { handle: `@${handleMatch[1]}` };
+ // youtube.com/c/ChannelName or /channel/ID
+ const channelMatch = url.pathname.match(/^\/(c|channel)\/([\w.-]+)$/);
+ if (channelMatch) return { handle: `@${channelMatch[2]}` };
+
+ return null;
+}
+
// Persist active region tab across re-renders
let activeRegionTab = OPTIONAL_CHANNEL_REGIONS[0]?.key ?? 'na';
@@ -341,8 +370,8 @@ export function initLiveChannelsWindow(containerEl?: HTMLElement): void {
${escapeHtml(t('components.liveNews.customChannel') ?? 'Custom channel')}
-
-
+
+
@@ -379,7 +408,53 @@ export function initLiveChannelsWindow(containerEl?: HTMLElement): void {
const nameInput = document.getElementById('liveChannelsName') as HTMLInputElement | null;
const raw = handleInput?.value?.trim();
if (!raw) return;
- const handle = raw.startsWith('@') ? raw : `@${raw}`;
+ if (handleInput) handleInput.classList.remove('invalid');
+
+ // Try parsing as a YouTube URL first
+ const parsed = parseYouTubeInput(raw);
+
+ // Direct video URL (watch?v= or youtu.be/)
+ if (parsed && 'videoId' in parsed) {
+ const videoId = parsed.videoId;
+ const id = `custom-vid-${videoId}`;
+ if (channels.some((c) => c.id === id)) return;
+
+ if (addBtn) {
+ addBtn.disabled = true;
+ addBtn.textContent = t('components.liveNews.verifying') ?? 'Verifying…';
+ }
+
+ // Try to resolve video/channel title via our proxy (YouTube oembed has no CORS)
+ let resolvedName = nameInput?.value?.trim() || '';
+ if (!resolvedName) {
+ try {
+ const baseUrl = isDesktopRuntime() ? getRemoteApiBaseUrl() : '';
+ const res = await fetch(`${baseUrl}/api/youtube/live?videoId=${encodeURIComponent(videoId)}`);
+ if (res.ok) {
+ const data = await res.json();
+ resolvedName = data.channelName || data.title || '';
+ }
+ } catch { /* use fallback */ }
+ }
+ if (!resolvedName) resolvedName = `Video ${videoId}`;
+
+ if (addBtn) {
+ addBtn.disabled = false;
+ addBtn.textContent = t('components.liveNews.addChannel') ?? 'Add channel';
+ }
+
+ channels.push({ id, name: resolvedName, handle: `@video`, fallbackVideoId: videoId, useFallbackOnly: true });
+ saveChannelsToStorage(channels);
+ renderList(listEl);
+ if (handleInput) handleInput.value = '';
+ if (nameInput) nameInput.value = '';
+ return;
+ }
+
+ // Extract handle from URL, or treat raw input as handle
+ const handle = parsed && 'handle' in parsed
+ ? parsed.handle
+ : raw.startsWith('@') ? raw : `@${raw}`;
// Validate YouTube handle format: @<3-30 alphanumeric/dot/hyphen/underscore chars>
if (!/^@[\w.-]{3,30}$/i.test(handle)) {
@@ -393,13 +468,13 @@ export function initLiveChannelsWindow(containerEl?: HTMLElement): void {
const id = customChannelIdFromHandle(handle);
if (channels.some((c) => c.id === id)) return;
- // Validate channel exists on YouTube
+ // Validate channel exists on YouTube + resolve name
if (addBtn) {
addBtn.disabled = true;
addBtn.textContent = t('components.liveNews.verifying') ?? 'Verifying…';
}
- if (handleInput) handleInput.classList.remove('invalid');
+ let resolvedName = '';
try {
const baseUrl = isDesktopRuntime() ? getRemoteApiBaseUrl() : '';
const res = await fetch(`${baseUrl}/api/youtube/live?channel=${encodeURIComponent(handle)}`);
@@ -412,6 +487,7 @@ export function initLiveChannelsWindow(containerEl?: HTMLElement): void {
}
return;
}
+ resolvedName = data.channelName || '';
}
// Non-OK status (429, 5xx) or ambiguous response — allow adding anyway
} catch (e) {
@@ -424,7 +500,7 @@ export function initLiveChannelsWindow(containerEl?: HTMLElement): void {
}
}
- const name = nameInput?.value?.trim() || handle;
+ const name = nameInput?.value?.trim() || resolvedName || handle;
channels.push({ id, name, handle });
saveChannelsToStorage(channels);
renderList(listEl);
diff --git a/src/locales/ar.json b/src/locales/ar.json
index b60b10449..d80f4ccb8 100644
--- a/src/locales/ar.json
+++ b/src/locales/ar.json
@@ -1268,6 +1268,7 @@
"addChannel": "إضافة قناة",
"remove": "إزالة",
"youtubeHandle": "معرّف YouTube (مثال: @Channel)",
+ "youtubeHandleOrUrl": "رابط أو معرّف يوتيوب",
"displayName": "اسم العرض (اختياري)",
"openPanelSettings": "إعدادات عرض اللوحة",
"channelSettings": "إعدادات القناة",
@@ -1282,6 +1283,7 @@
"regionEurope": "أوروبا",
"regionLatinAmerica": "أمريكا اللاتينية",
"regionAsia": "آسيا",
+ "regionMiddleEast": "الشرق الأوسط",
"regionAfrica": "أفريقيا"
}
},
diff --git a/src/locales/de.json b/src/locales/de.json
index 92733867f..7c7eaee78 100644
--- a/src/locales/de.json
+++ b/src/locales/de.json
@@ -1268,6 +1268,7 @@
"addChannel": "Kanal hinzufügen",
"remove": "Entfernen",
"youtubeHandle": "YouTube-Handle (z. B. @Channel)",
+ "youtubeHandleOrUrl": "YouTube-Handle oder URL",
"displayName": "Anzeigename (optional)",
"openPanelSettings": "Panelanzeige-Einstellungen",
"channelSettings": "Kanaleinstellungen",
@@ -1282,6 +1283,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "Lateinamerika",
"regionAsia": "Asien",
+ "regionMiddleEast": "Naher Osten",
"regionAfrica": "Afrika"
}
},
diff --git a/src/locales/el.json b/src/locales/el.json
index bd9243810..c1ed5a7db 100644
--- a/src/locales/el.json
+++ b/src/locales/el.json
@@ -1310,6 +1310,7 @@
"regionEurope": "Ευρώπη",
"regionLatinAmerica": "Λατινική Αμερική",
"regionAsia": "Ασία",
+ "regionMiddleEast": "Μέση Ανατολή",
"regionAfrica": "Αφρική"
}
},
diff --git a/src/locales/en.json b/src/locales/en.json
index b25a37581..67e6a22c4 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -1360,6 +1360,7 @@
"addChannel": "Add channel",
"remove": "Remove",
"youtubeHandle": "YouTube handle (e.g. @Channel)",
+ "youtubeHandleOrUrl": "YouTube handle or URL",
"displayName": "Display name (optional)",
"openPanelSettings": "Panel display settings",
"channelSettings": "Channel Settings",
@@ -1374,6 +1375,7 @@
"regionEurope": "Europe",
"regionLatinAmerica": "Latin America",
"regionAsia": "Asia",
+ "regionMiddleEast": "Middle East",
"regionAfrica": "Africa",
"invalidHandle": "Enter a valid YouTube handle (e.g. @ChannelName)",
"channelNotFound": "YouTube channel not found",
diff --git a/src/locales/es.json b/src/locales/es.json
index da6d87f2c..11ca7ddd4 100644
--- a/src/locales/es.json
+++ b/src/locales/es.json
@@ -1268,6 +1268,7 @@
"addChannel": "Añadir canal",
"remove": "Eliminar",
"youtubeHandle": "Handle de YouTube (ej. @Channel)",
+ "youtubeHandleOrUrl": "Identificador o URL de YouTube",
"displayName": "Nombre para mostrar (opcional)",
"openPanelSettings": "Configuración de visualización del panel",
"channelSettings": "Configuración del canal",
@@ -1282,6 +1283,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "Latinoamérica",
"regionAsia": "Asia",
+ "regionMiddleEast": "Oriente Medio",
"regionAfrica": "África"
}
},
diff --git a/src/locales/fr.json b/src/locales/fr.json
index 747ae3236..cd43b917f 100644
--- a/src/locales/fr.json
+++ b/src/locales/fr.json
@@ -1268,6 +1268,7 @@
"addChannel": "Ajouter une chaîne",
"remove": "Supprimer",
"youtubeHandle": "Handle YouTube (ex. @Channel)",
+ "youtubeHandleOrUrl": "Identifiant ou URL YouTube",
"displayName": "Nom d'affichage (optionnel)",
"openPanelSettings": "Paramètres d'affichage du panneau",
"channelSettings": "Paramètres de la chaîne",
@@ -1282,6 +1283,7 @@
"regionEurope": "Europe",
"regionLatinAmerica": "Amérique latine",
"regionAsia": "Asie",
+ "regionMiddleEast": "Moyen-Orient",
"regionAfrica": "Afrique"
}
},
diff --git a/src/locales/it.json b/src/locales/it.json
index 5d76960fe..44ff9451a 100644
--- a/src/locales/it.json
+++ b/src/locales/it.json
@@ -1268,6 +1268,7 @@
"addChannel": "Aggiungi canale",
"remove": "Rimuovi",
"youtubeHandle": "Handle YouTube (es. @Channel)",
+ "youtubeHandleOrUrl": "Handle o URL di YouTube",
"displayName": "Nome visualizzato (opzionale)",
"openPanelSettings": "Impostazioni visualizzazione pannello",
"channelSettings": "Impostazioni canale",
@@ -1282,6 +1283,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "America Latina",
"regionAsia": "Asia",
+ "regionMiddleEast": "Medio Oriente",
"regionAfrica": "Africa"
}
},
diff --git a/src/locales/ja.json b/src/locales/ja.json
index 2fa1140e7..006c10ff2 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -1268,6 +1268,7 @@
"addChannel": "チャンネルを追加",
"remove": "削除",
"youtubeHandle": "YouTube ハンドル(例: @Channel)",
+ "youtubeHandleOrUrl": "YouTubeハンドルまたはURL",
"displayName": "表示名(任意)",
"openPanelSettings": "表示パネル設定",
"channelSettings": "チャンネル設定",
@@ -1282,6 +1283,7 @@
"regionEurope": "ヨーロッパ",
"regionLatinAmerica": "中南米",
"regionAsia": "アジア",
+ "regionMiddleEast": "中東",
"regionAfrica": "アフリカ"
}
},
diff --git a/src/locales/nl.json b/src/locales/nl.json
index 482be8a7e..ebb127368 100644
--- a/src/locales/nl.json
+++ b/src/locales/nl.json
@@ -1127,6 +1127,7 @@
"addChannel": "Kanaal toevoegen",
"remove": "Verwijderen",
"youtubeHandle": "YouTube-handle (bijv. @Channel)",
+ "youtubeHandleOrUrl": "YouTube-handle of URL",
"displayName": "Weergavenaam (optioneel)",
"openPanelSettings": "Paneelweergave-instellingen",
"channelSettings": "Kanaalinstellingen",
@@ -1141,6 +1142,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "Latijns-Amerika",
"regionAsia": "Azië",
+ "regionMiddleEast": "Midden-Oosten",
"regionAfrica": "Afrika"
}
},
diff --git a/src/locales/pl.json b/src/locales/pl.json
index 27fe279a5..b53f43681 100644
--- a/src/locales/pl.json
+++ b/src/locales/pl.json
@@ -1268,6 +1268,7 @@
"addChannel": "Dodaj kanał",
"remove": "Usuń",
"youtubeHandle": "Handle YouTube (np. @Channel)",
+ "youtubeHandleOrUrl": "Uchwyt lub URL YouTube",
"displayName": "Nazwa wyświetlana (opcjonalnie)",
"openPanelSettings": "Ustawienia wyświetlania panelu",
"channelSettings": "Ustawienia kanału",
@@ -1282,6 +1283,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "Ameryka Łacińska",
"regionAsia": "Azja",
+ "regionMiddleEast": "Bliski Wschód",
"regionAfrica": "Afryka"
}
},
diff --git a/src/locales/pt.json b/src/locales/pt.json
index cdb1b3580..85ffd3eb8 100644
--- a/src/locales/pt.json
+++ b/src/locales/pt.json
@@ -1127,6 +1127,7 @@
"addChannel": "Adicionar canal",
"remove": "Remover",
"youtubeHandle": "Handle do YouTube (ex.: @Channel)",
+ "youtubeHandleOrUrl": "Identificador ou URL do YouTube",
"displayName": "Nome de exibição (opcional)",
"openPanelSettings": "Configurações de exibição do painel",
"channelSettings": "Configurações do canal",
@@ -1141,6 +1142,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "América Latina",
"regionAsia": "Ásia",
+ "regionMiddleEast": "Oriente Médio",
"regionAfrica": "África"
}
},
diff --git a/src/locales/ru.json b/src/locales/ru.json
index 9492d608b..955bf5aa4 100644
--- a/src/locales/ru.json
+++ b/src/locales/ru.json
@@ -1268,6 +1268,7 @@
"addChannel": "Добавить канал",
"remove": "Удалить",
"youtubeHandle": "YouTube- handle (напр. @Channel)",
+ "youtubeHandleOrUrl": "Имя канала или URL YouTube",
"displayName": "Отображаемое имя (необяз.)",
"openPanelSettings": "Настройки отображения панели",
"channelSettings": "Настройки канала",
@@ -1282,6 +1283,7 @@
"regionEurope": "Европа",
"regionLatinAmerica": "Латинская Америка",
"regionAsia": "Азия",
+ "regionMiddleEast": "Ближний Восток",
"regionAfrica": "Африка"
}
},
diff --git a/src/locales/sv.json b/src/locales/sv.json
index 51cdc9a4e..9a7df4661 100644
--- a/src/locales/sv.json
+++ b/src/locales/sv.json
@@ -1127,6 +1127,7 @@
"addChannel": "Lägg till kanal",
"remove": "Ta bort",
"youtubeHandle": "YouTube-handtag (t.ex. @Channel)",
+ "youtubeHandleOrUrl": "YouTube-handtag eller URL",
"displayName": "Visningsnamn (valfritt)",
"openPanelSettings": "Panelvisningsinställningar",
"channelSettings": "Kanalinställningar",
@@ -1141,6 +1142,7 @@
"regionEurope": "Europa",
"regionLatinAmerica": "Latinamerika",
"regionAsia": "Asien",
+ "regionMiddleEast": "Mellanöstern",
"regionAfrica": "Afrika"
}
},
diff --git a/src/locales/th.json b/src/locales/th.json
index 52a0121a7..b07ceba1f 100644
--- a/src/locales/th.json
+++ b/src/locales/th.json
@@ -1268,6 +1268,7 @@
"addChannel": "เพิ่มช่อง",
"remove": "ลบ",
"youtubeHandle": "YouTube handle (เช่น @Channel)",
+ "youtubeHandleOrUrl": "แฮนเดิล YouTube หรือ URL",
"displayName": "ชื่อที่แสดง (ไม่บังคับ)",
"openPanelSettings": "การตั้งค่าการแสดงผลแผง",
"channelSettings": "การตั้งค่าช่อง",
@@ -1282,6 +1283,7 @@
"regionEurope": "ยุโรป",
"regionLatinAmerica": "ละตินอเมริกา",
"regionAsia": "เอเชีย",
+ "regionMiddleEast": "ตะวันออกกลาง",
"regionAfrica": "แอฟริกา"
}
},
diff --git a/src/locales/tr.json b/src/locales/tr.json
index 4131790bb..d79be2396 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -1268,6 +1268,7 @@
"addChannel": "Kanal ekle",
"remove": "Kaldır",
"youtubeHandle": "YouTube tanıtıcı (örn. @Channel)",
+ "youtubeHandleOrUrl": "YouTube tanıtıcısı veya URL",
"displayName": "Görünen ad (isteğe bağlı)",
"openPanelSettings": "Panel görüntü ayarları",
"channelSettings": "Kanal ayarları",
@@ -1282,6 +1283,7 @@
"regionEurope": "Avrupa",
"regionLatinAmerica": "Latin Amerika",
"regionAsia": "Asya",
+ "regionMiddleEast": "Orta Doğu",
"regionAfrica": "Afrika"
}
},
diff --git a/src/locales/vi.json b/src/locales/vi.json
index a09458406..8b9ddc995 100644
--- a/src/locales/vi.json
+++ b/src/locales/vi.json
@@ -1268,6 +1268,7 @@
"addChannel": "Thêm kênh",
"remove": "Xóa",
"youtubeHandle": "Handle YouTube (vd: @Channel)",
+ "youtubeHandleOrUrl": "Tên kênh hoặc URL YouTube",
"displayName": "Tên hiển thị (tùy chọn)",
"openPanelSettings": "Cài đặt hiển thị bảng",
"channelSettings": "Cài đặt kênh",
@@ -1282,6 +1283,7 @@
"regionEurope": "Châu Âu",
"regionLatinAmerica": "Mỹ Latinh",
"regionAsia": "Châu Á",
+ "regionMiddleEast": "Trung Đông",
"regionAfrica": "Châu Phi"
}
},
diff --git a/src/locales/zh.json b/src/locales/zh.json
index c8c243fa6..1a79e3781 100644
--- a/src/locales/zh.json
+++ b/src/locales/zh.json
@@ -1268,6 +1268,7 @@
"addChannel": "添加频道",
"remove": "删除",
"youtubeHandle": "YouTube 句柄(如 @Channel)",
+ "youtubeHandleOrUrl": "YouTube 频道名称或链接",
"displayName": "显示名称(可选)",
"openPanelSettings": "面板显示设置",
"channelSettings": "频道设置",
@@ -1282,6 +1283,7 @@
"regionEurope": "欧洲",
"regionLatinAmerica": "拉丁美洲",
"regionAsia": "亚洲",
+ "regionMiddleEast": "中东",
"regionAfrica": "非洲"
}
},