Перейти к содержимому

Глобальные методы UI

PotokSDK.ui — рендер, HUD, плеер, навигация, регистрация.

ui.render(component, slotId?) — компиляция разметки и отправка хосту. slotId — проекция в слот; без него — экран песочницы.

Метод ui.showHUD(type: string, message: string) вызывает кратковременный системный баннер в углу экрана. Поддерживаемые типы: “success”, “error”, “info”, “warning”.

ui.playVideo(playbackInfo) — полноэкранный плеер. Поля playbackInfo:

ui.playVideo({
streamUrl: "http://example.com/video.m3u8",
streamType: "m3u8",
title: "Название фильма",
season: 1,
episode: 3,
torrentHash: "abc123def456",
fileIndex: "0",
audios: [
{ id: "ru", name: "Русский дубляж", url: "http://example.com/video_ru.m3u8" },
{ id: "en", name: "Английский оригинал", url: "http://example.com/video_en.m3u8" }
],
headers: { "User-Agent": "PotokPlayer" },
providerId: "my-torrents",
voice: "dub",
subtitles: [
{
id: "ru-vtt",
src: "http://example.com/subs_ru.vtt",
label: "Русские",
language: "ru",
isDefault: true,
format: "vtt",
name: "Русские",
srclang: "ru",
url: "http://example.com/subs_ru.vtt"
}
],
session: {
keepaliveUrl: "http://example.com/session/keepalive",
stopUrl: "http://example.com/session/stop",
intervalSec: 30,
hash: "abc123def456",
file: "0",
statusUrl: "http://example.com/session/status",
statusIntervalSec: 5
},
duration: 7200,
introStart: 0,
introEnd: 90,
outroStart: 7080,
outroEnd: 7200,
thumbnails: {
urlTemplate: "http://example.com/thumbs/{time}.jpg",
intervalSec: 5
},
requiresBuffering: false
});

Метод ui.showEpisodeSelector(config) вызывает системное всплывающее окно для выбора сезонов и серий, разработанное специально для сериалов.

ui.showEpisodeSelector({
title: "Имя сериала",
seasons: [
{
id: 1,
seasonNumber: 1,
season_number: 1,
episodes: [
{
id: 101,
episodeNumber: 1,
episode_number: 1,
name: "Серия 1",
stillPath: "https://image.tmdb.org/t/p/w500/example.jpg",
still_path: "https://image.tmdb.org/t/p/w500/example.jpg",
airDate: "2011-04-17",
air_date: "2011-04-17",
overview: "Описание серии"
}
]
}
],
episodes: [
{
id: "s01e01",
season: 1,
episode: 1,
rawSeason: 1,
rawEpisode: 1,
title: "Серия 1",
fileName: "Show.S01E01.mkv",
stillPath: "https://image.tmdb.org/t/p/w500/example.jpg",
airDate: "2011-04-17",
isWatched: false,
sizeLabel: "1.2 GB",
audios: [
{ id: "ru", name: "Русский дубляж", url: "http://example.com/s01e01_ru.m3u8" },
{ id: "en", name: "Английский оригинал", url: "http://example.com/s01e01_en.m3u8" }
],
url: "http://example.com/s01e01.m3u8"
}
],
seasonsLoading: false,
isSaving: false,
tmdbSeasonsCount: 8,
onPlay: (ep, audioId) => {
console.log("Playing:", ep.season, ep.episode, audioId);
},
onStartEditing: () => {
console.log("Editing started");
},
onApplyOverride: (seasonNum, epNum) => {
console.log("Override applied:", seasonNum, epNum);
},
onClose: () => {
console.log("Selector closed");
}
});

ui.navigateTo(to, state?) — переход в раздел хоста (/settings, /library, …):

ui.navigateTo("/settings");
ui.navigateTo("/library", { filter: "watchlist" });

Кастомные темы и акцент:

  • ui.registerThemes(themes) — регистрация Theme[] на хосте.
  • ui.setAccentTheme(themeId) — переключение акцента.

Внедрение или замена глобального стиля в хосте для переоформления любого встроенного компонента. Требует разрешение custom-css.

  • ui.injectHostCss(id, css) — добавить или заменить глобальный CSS-слой; он идёт после стилей приложения и выигрывает при равной специфичности.
  • Тот же id заменяет слой; пустая строка удаляет его.
  • Сочетается с .style(‘my-class’) у компонента для точного таргетинга.
  • Кнопка настроек и менеджер расширений остаются видимыми — плагин не заблокирует сам себя.
// Свой класс на компоненте
const card = Card().style("my-hero-card").child(Text("Featured"));
ui.render(card);
// Стилизуем его глобально + перекрашиваем встроенный класс
ui.injectHostCss("cards", `
.my-hero-card { border-radius: 1.5rem; box-shadow: 0 8px 24px rgba(0,0,0,.4); }
.media-card-overlay {
background: linear-gradient(to top, #000, transparent) !important;
}
`);
// Тот же id заменяет слой; пустая строка — удаляет
ui.injectHostCss("cards", "");

Метод ui.block(blockName) возвращает билдер для декларативного изменения существующих блоков интерфейса хоста. Через element(id) можно скрыть, отредактировать, вставить до/после или заменить элемент; append/prepend добавляют разметку; apply() отправляет мутации хосту.

  • element(id).hide() — скрывает элемент блока.
  • element(id).edit(props) — изменяет свойства элемента.
  • element(id).before(ui) / after(ui) / replace(ui) — вставка или замена разметки.
  • append(ui) / prepend(ui) — добавляет разметку в начало или конец блока.
  • apply() — регистрирует накопленные мутации на хосте.

Метод ui.onBlockContextUpdate(callback) подписывает плагин на обновления контекста активного блока интерфейса. Коллбек получает (blockName, context) при смене данных экрана (например, при переходе к другому фильму). Возвращает функцию отписки.

Регистрация в хосте:

Функция API Описание
registerPlugin(meta) Метаданные плагина (id, name, version). Вызывать первой при загрузке.
registerSource(config) Регистрирует плагин как поисковый провайдер (источник медиа-потоков). Хост обращается к зарегистрированному источнику при поиске видеофайлов.
registerSlotContribution(config) Регистрирует графический вклад в указанный интерфейсный слот (например, в кнопки действий или под описание медиафайла).
onSettingsChanged(callback) Подписывает плагин на интерактивное изменение полей формы его настроек на хосте в реальном времени. В коллбек передаются (key, value, currentSettings).
updateSettingsForm(updates) Отправляет команду хосту обновить значения полей формы настроек в реальном времени (например, для автозаполнения пресетов, сброса или валидации). Принимает объект обновлений { updates }.

Вклад UI в слот. Хост вызывает render при монтировании экрана.

Поля config:

  • id (string) — id вклада, как в slots манифеста.

  • slotName (string) — media-actions, details-bottom, extension-page, …

  • render(props) (function) — возвращает { label, icon?, layout }.

  • label (string) — Название/подпись для вклада.

  • icon (string, опционально) — Имя иконки Lucide.

  • layout (UIComponent) — Дерево UI компонентов (создается с помощью билдеров Card(), VStack(), Button() и т.д.).

Кнопка трейлера в media-actions, блок в details-bottom:

my-easy-plugin/
├── manifest.json # Метаданные и объявление слотов
└── index.js # Исполняемый JS код плагина
{
"id": "my-easy-plugin",
"name": "Простой Просмотрщик",
"version": "1.0.0",
"description": "Добавляет кнопку просмотра трейлера и блок в деталях",
"permissions": ["ui-notifications"],
"slots": [
{
"id": "trailer-action-button",
"slotName": "media-actions",
"title": "Кнопка Трейлера"
},
{
"id": "extra-details-info",
"slotName": "details-bottom",
"title": "Блок Информации"
}
]
}
import { PotokSDK } from 'potok-sdk';
PotokSDK.registerPlugin({
id: "my-easy-plugin",
name: "Простой Просмотрщик"
});
PotokSDK.registerSlotContribution({
id: "trailer-action-button",
slotName: "media-actions",
render(props) {
const { Button } = PotokSDK.ui.components;
return {
label: "Смотреть Трейлер",
icon: "play",
layout: Button("Смотреть Трейлер")
.variant("primary")
.onClick(() => {
PotokSDK.ui.playVideo({
streamUrl: "http://example.com/video.m3u8",
streamType: "m3u8",
title: "Название фильма",
season: 1,
episode: 3,
torrentHash: "abc123def456",
fileIndex: "0",
audios: [
{ id: "ru", name: "Русский дубляж", url: "http://example.com/video_ru.m3u8" },
{ id: "en", name: "Английский оригинал", url: "http://example.com/video_en.m3u8" }
],
headers: { "User-Agent": "PotokPlayer" },
providerId: "my-torrents",
voice: "dub",
subtitles: [
{
id: "ru-vtt",
src: "http://example.com/subs_ru.vtt",
label: "Русские",
language: "ru",
isDefault: true,
format: "vtt",
name: "Русские",
srclang: "ru",
url: "http://example.com/subs_ru.vtt"
}
],
session: {
keepaliveUrl: "http://example.com/session/keepalive",
stopUrl: "http://example.com/session/stop",
intervalSec: 30,
hash: "abc123def456",
file: "0",
statusUrl: "http://example.com/session/status",
statusIntervalSec: 5
},
duration: 7200,
introStart: 0,
introEnd: 90,
outroStart: 7080,
outroEnd: 7200,
thumbnails: {
urlTemplate: "http://example.com/thumbs/{time}.jpg",
intervalSec: 5
},
requiresBuffering: false
});
})
};
}
});
PotokSDK.registerSlotContribution({
id: "extra-details-info",
slotName: "details-bottom",
render(props) {
const { Card, VStack, Text, Badge, HStack } = PotokSDK.ui.components;
return {
label: "Дополнительно",
icon: "info",
layout: Card()
.title("Рекомендовано плагином")
.subtitle(`Кинопоиск ID: ${props.mediaId}`)
.child(
VStack()
.spacing(8)
.child(Text(`Вы просматриваете страницу "${props.title}". Этот блок встроил кастомный плагин.`).variant("secondary"))
.child(
HStack()
.spacing(6)
.child(Badge("Качество 1080p").color("success"))
.child(Badge("Лицензия").color("info"))
)
)
};
}
});

Протестировать оверлеи в Sandbox