FXML: различия между версиями

Материал из Браузер Fork - Wiki
Перейти к навигации Перейти к поиску
Нет описания правки
Реструктуризация: спецификация FXML для новичков (разделы 1-8)
 
(не показаны 63 промежуточные версии 10 участников)
Строка 1: Строка 1:
'''FXML''' (Fork eXtensible Markup Language) язык разметки документов во Всемирной паутине. Для навигации по таким страницам не требуется мышь или сенсорный экран. Язык '''FXML''' интерпретируется такими приложениями как ForkPlayer, OTT Player, OTT-play. Полученный в результате интерпретации форматированный текст отображается на экране телевизора.  
<div class="toccolours" style="width:100%; max-width:900px; padding:12px 16px; line-height:1.5;">
'''FXML''' — официальная спецификация языка разметки порталов для Fork browser и совместимых приложений.<br />
Формат ответа: '''валидный JSON'''. Навигация — с пульта, без мыши и тач-экрана.
</div>


Например, следующий код (должен быть валидным JSON):
== 1. Область применения ==
FXML (Fork eXtensible Markup Language) описывает структуру страницы, которую сервер отдаёт клиенту. Клиент интерпретирует JSON и показывает список элементов, видео или произвольную вёрстку на экране телевизора.
 
'''Совместимые приложения:''' ForkPlayer, OTT Player, OTT-play.
 
'''Связанные документы:'''
* [https://wiki.forkbrowser.top/wiki/CHANGELOG CHANGELOG] — история изменений протокола
* [https://wiki.forkbrowser.top/wiki/FXMLbaseparser FXMLbaseparser] — PHP-класс для генерации FXML
* [https://github.com/vengo634/kino.pub_forkplayerPHP/blob/master/index.php Пример портала (PHP)]
 
== 2. Быстрый старт ==
Минимальная страница состоит из заголовка и массива элементов <code>channels</code>. У каждого элемента — как минимум <code>title</code> и ссылка (<code>playlist_url</code> или <code>stream_url</code>).
 
'''Минимальный пример (JSON):'''
<pre>{
  "title": "Мой портал",
  "channels": [
    {"title": "Главная", "logo_30x30": "http://example.com/icon.png", "playlist_url": "http://example.com/"},
    {"title": "Фильм", "stream_url": "http://example.com/video.m3u8"}
  ]
}</pre>
 
'''Вывод на PHP:'''
<pre>&lt;?php
$_PL = ["title" => "Мой портал"];
$_CH = [
  ["title" => "Главная", "logo_30x30" => "http://example.com/icon.png", "playlist_url" => "http://example.com/"],
  ["title" => "Фильм", "stream_url" => "http://example.com/video.m3u8"]
];
$_PL["channels"] = $_CH;
header("Content-Type: application/json; charset=utf-8");
print json_encode($_PL, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?&gt;</pre>
 
* <code>playlist_url</code> — открыть другую FXML/XML/M3U страницу
* <code>stream_url</code> — запустить видеоплеер
 
'''Полный пример портала (COOLTV):'''
====Следующий код (валидный JSON):====
  {"title":"COOLTV - портал нового поколения","background-image":"<nowiki>http://cooltv.info/img/tvcool.jpg</nowiki>","typeList":"start","icon":"<nowiki>http://cooltv.info/img/tvcool23.jpg</nowiki>","channels":[{"title":"Вход","logo_30x30":"<nowiki>http://cooltv.info/img/profle22.jpg</nowiki>","playlist_url":"<nowiki>http://cooltv.info/auth</nowiki>"},{"title":"Новости ","logo_30x30":"<nowiki>http://cooltv.info/img/rss-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/news</nowiki>"},{"title":"Поиск ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-search-folder-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media/search.php</nowiki>"},{"title":"Кинозал ","logo_30x30":"<nowiki>http://cooltv.info/img/1icons8-film-reel-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media</nowiki>"},{"title":"AceStream ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-wave-arrows-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/12</nowiki>"},{"title":"Обменник ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-next-100.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media/obmen/</nowiki>"},{"title":"Сервисы ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8s3-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/9</nowiki>"},{"title":"IPTV ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-hdtv-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/8</nowiki>"},{"title":"Мультимедиа ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-documentary-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/7</nowiki>"},{"title":"Чат ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-chat-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/guest</nowiki>"},{"title":"FAQ ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-info-popup-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/faq/</nowiki>"},{"title":"Копилка","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-bad-piggies-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/copilka</nowiki>"}]}
  {"title":"COOLTV - портал нового поколения","background-image":"<nowiki>http://cooltv.info/img/tvcool.jpg</nowiki>","typeList":"start","icon":"<nowiki>http://cooltv.info/img/tvcool23.jpg</nowiki>","channels":[{"title":"Вход","logo_30x30":"<nowiki>http://cooltv.info/img/profle22.jpg</nowiki>","playlist_url":"<nowiki>http://cooltv.info/auth</nowiki>"},{"title":"Новости ","logo_30x30":"<nowiki>http://cooltv.info/img/rss-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/news</nowiki>"},{"title":"Поиск ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-search-folder-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media/search.php</nowiki>"},{"title":"Кинозал ","logo_30x30":"<nowiki>http://cooltv.info/img/1icons8-film-reel-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media</nowiki>"},{"title":"AceStream ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-wave-arrows-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/12</nowiki>"},{"title":"Обменник ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-next-100.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/media/obmen/</nowiki>"},{"title":"Сервисы ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8s3-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/9</nowiki>"},{"title":"IPTV ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-hdtv-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/8</nowiki>"},{"title":"Мультимедиа ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-documentary-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/vdt/7</nowiki>"},{"title":"Чат ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-chat-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/guest</nowiki>"},{"title":"FAQ ","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-info-popup-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/faq/</nowiki>"},{"title":"Копилка","logo_30x30":"<nowiki>http://cooltv.info/img/icons8-bad-piggies-96.png</nowiki>","playlist_url":"<nowiki>http://cooltv.info/copilka</nowiki>"}]}
даст такой результат (в ForkPlayer):
даст такой результат (в ForkPlayer):
Строка 7: Строка 48:




Все допустимые команды можно посмотреть в исходном коде страниц
====Просмотр исходного кода страницы в ForkPlayer====
[[Файл:VmGe53a9.png|мини|без]]


https://github.com/alexkdpu/kino.pub_forkplayerPHP/blob/master/index.php
== 3. Термины и модель данных ==
{| class="wikitable"
! Термин !! Описание
|-
| '''Документ FXML''' || JSON-объект верхнего уровня, который сервер отдаёт клиенту
|-
| '''$_PL''' / '''Playlist''' || Глобальные поля документа (заголовок страницы, стили, команды)
|-
| '''$_CH''' / '''Channel''' || Один элемент списка в массиве <code>channels[]</code>
|-
| <code>playlist_url</code> || Ссылка на следующую страницу (FXML, XML, M3U, команда)
|-
| <code>stream_url</code> || Ссылка на видеопоток; при наличии у channel не используется <code>playlist_url</code>
|-
| <code>typeList</code> || Режим отображения страницы: список (по умолчанию) или плитка (<code>start</code>)
|-
| <code>position</code> || Вид отдельного элемента при <code>typeList="start"</code> (плитка, список, html и др.)
|}


Смотреть Исходный код любой страницы можно непосредственно в ForkPlayer
'''Правила:'''
[[Файл:VmGe53a9.png|мини|без]]
# Ответ сервера — один JSON-объект с ключом <code>channels</code> (массив).
# У channel должен быть непустой <code>title</code>.
# У channel заполняется '''либо''' <code>playlist_url</code>, '''либо''' <code>stream_url</code> (если элемент не служебный).
# Новые теги добавляются по мере развития протокола — см. [https://wiki.forkbrowser.top/wiki/CHANGELOG CHANGELOG].
 
<div class="toccolours mw-collapsible" style="width:100%; max-width:900px;">
<div style="font-weight:bold;">Содержание спецификации</div>
<div class="mw-collapsible-content">
# [[#1. Область применения|1. Область применения]]
# [[#2. Быстрый старт|2. Быстрый старт]] — минимальный пример
# [[#3. Термины и модель данных|3. Термины и модель данных]]
# [[#4. Справочник глобальных тегов ($\_PL)|4. Глобальные теги $_PL]]
# [[#5. Справочник тегов channel ($\_CH)|5. Теги channel $_CH]]
# [[#6. Оформление и вёрстка элементов|6. Оформление и вёрстка]]
# [[#7. Формирование ответа на сервере|7. Формирование ответа на сервере]]
# [[#8. Связанные материалы|8. Связанные материалы]]
</div></div>
 
== 4. Справочник глобальных тегов ($_PL) ==
Теги задают свойства '''всей страницы'''. В PHP соответствуют элементам массива <code>$_PL</code>.
 
{| class="wikitable"
! Тег !! Назначение !! Раздел
|-
| <code>title</code> || Заголовок страницы || —
|-
| <code>typeList</code> || <code>start</code> — плитка; иначе — список || [[#position при заданном $_PL["typeList"]="start";|position]]
|-
| <code>channels</code> || Массив элементов списка || [[#5. Справочник тегов channel ($_CH)|§5]]
|-
| <code>css</code> || CSS-стили страницы (строка) || [[#4.1. Оформление страницы (css, link, align)|§4.1]]
|-
| <code>link[]</code> || Подключение внешних CSS || [[#Подключения css файлов стилей|link]]
|-
| <code>align</code> || Выравнивание плитки (<code>left</code>, по умолчанию — center) || [[#Глобальный тег align|align]]
|-
| <code>cmd</code> || Команда при загрузке страницы || [[#cmd тег (как глобальный так и в playlist_url) - допустимые команды|cmd]]
|-
| <code>ping</code> || Опрос URL до появления channels || [[#Глобальный тег ping|ping]]
|-
| <code>aside</code> || Боковое меню || [[#Глобальный тег aside для вывода бокового меню|aside]]
|-
| <code>advertising</code> || Реклама перед видео || [[#advertising для показа рекламы перед запуском видео|advertising]]
|-
| <code>url_tvg</code> || URL телепрограммы (xmltv) || [[#Глобальный тег url_tvg — своя телепрограмма|url_tvg]]
|-
| <code>menu</code> || Глобальная полоса меню вверху || [[#menu — контекстное меню элемента channel|menu]]
|-
| <code>cacheinfo</code> || <code>nocache</code> — не кешировать страницу || ниже
|-
| <code>setcookie</code> || Cookie для последующих запросов к домену || ниже
|-
| <code>info</code>, <code>confirm</code> || Уведомление или диалог при загрузке || ниже
|}
 
=== 4.1. Оформление страницы (css, link, align) ===
<code>$_PL["css"]</code> — CSS одной строкой. При <code>typeList="start"</code> доступны классы <code>.start</code>, <code>.list</code>, <code>.html</code> и др. (см. [[#position при заданном $_PL["typeList"]="start";|position]]).
 
<code>$_PL["css"]="start";</code> — плиточный вид + поддержка <code>$_CH["position"]</code>.
 
====Стили сайта по умолчанию====
По умолчанию для вашей страницы задаются такие стили (вы их можете изменить задав нужные в $_PL["css"]
 
<div class="toccolours mw-collapsible mw-collapsed" style="width:700px; overflow:auto;">
 
<div style="font-weight:bold;line-height:1.6;">Смотреть CSS стили по умолчанию</div>
 
<div class="mw-collapsible-content">
 
::-webkit-scrollbar-button:single-button {
    background-color: #bbbbbb;
    display: block;
    border-style: solid;
    height: 10px;
    width: 10px;
}
 
::-webkit-scrollbar-button:single-button:vertical:decrement {
    border-width: 5px;
    border-color: transparent transparent #555555;
}
 
::-webkit-scrollbar-button:single-button:vertical:increment {
    border-width: 5px;
    border-color: #555555 transparent transparent transparent;
}
 
::-webkit-scrollbar {
    width: 10px;
}
 
::-webkit-scrollbar-track {
    background: #ddd;
}
 
::-webkit-scrollbar-thumb {
    background: #888;
}
 
::-webkit-scrollbar-thumb:hover {
    background: #777;
}
 
.start {
    position: relative;
    border: 0px;
    text-align: center;
    display: inline-block;
    ;
    overflow: hidden;
    text-align: center;
    margin-left: 4px;
    margin-top: 1px;
    height: 10px;
    width: 10%;
}
 
.start .rating {
    position: absolute;
    display: none;
    text-align: left;
    height: 23px;
    width: 28px;
    padding-top: 5px;
    padding-left: 5px;
    font-size: 11px;
}
 
.start .icon {
    padding: 2px 2px 0px 2px;
    height: 76%;
}
 
.start .name {
    font-size: 17px;
    line-height: 0.9;
    position: absolute;
    bottom: 0px;
    width: 100%;
    max-height: 50%;
}
 
.start .title {
    background: rgba(0, 0, 0, 0.65);
    border-radius: 3px;
    width: auto;
    display: display: inline-block;
    margin: 2px;
    padding: 1px 4px 4px 4px;
}
 
.start .side_icon {
    position: relative;
    padding: 2px;
}
 
.list {
    text-align: left;
    border-radius: 4px 0px 0px 4px;
    cursor: default;
    margin: 2px 0px;
    width: 48.87708333333334%;
    height: 35px;
}
 
.list .contmenu {
    position: relative;
    float: right;
    display: none;
}
 
.list .numb {
    float: left;
    padding-top: 2px;
    font-size: 60%;
    min-width: 38px;
    text-align: center;
}
 
.list .icon {
    margin: 0px 2px;
    padding-right: 2px;
    float: left;
}
 
.list .title {
    display: inline-block;
    float: left;
    position: relative;
    white-space: nowrap;
}
 
.listsearch {
    background-color: #ddd;
    color: #333;
    overflow: hidden;
    border-radius: 4px;
    padding: 3px;
    margin-top: 2px;
    font-size: 85%;
    width: 90%;
    height: 68%;
}
 
.infolinktitle {
    height: 63%;
    font-size: 80%;
    overflow: hidden;
}
 
.infolink {
    padding-left: 3px;
    font-size: 11px;
}
 
.default .contmenu {
    display: none;
}
 
.selected .contmenu {
    display: block;
}
 
.html {
    position: relative;
    display: inline-block;
    vertical-align: top;
}
 
.hlink {
    position: relative;
    border: 0px solid transparent;
    margin: 0px 4px;
    border-radius: 4px;
    display: inline-block;
    overflow: hidden;
}
 
.hlink .title {
    overflow: hidden;
    max-width: 310px;
    font-size: 90%;
    padding: 0px 4px;
    float: left;
    height: 28px;
    text-decoration: underline;
}
 
.hlink .icon {
    float: left;
    height: 22px;
    padding-top: 5px;
}
 
.fulleditline {
    border: 1px solid transparent;
    width: 80%;
    margin: 1px 9%;
    border-radius: 4px;
    display: inline-block;
    overflow: hidden;
}
 
.fulleditline .title {
    overflow: hidden;
    font-size: 24px;
    height: 24px;
    margin: 3px;
    color: #2b2525;
    background-color: #eee;
    padding: 4px;
}
 
.fulleditline .icon {
    float: right;
    height: 22px;
    padding-top: 2px;
}
 
.label {
    position: relative;
    text-align: left;
    border-radius: 4px;
    margin: -2px 4%;
    font-size: 80%;
    width: 92%;
    display: inline-block;
    overflow: hidden;
    height: 26px;
}
 
.label .icon {
    float: left;
    height: 20px;
}
 
.default {
    background: none;
}
 
.selected {
    background: rgba(180, 180, 180, 0.7);
}
 
.listselected {
    color: black;
}
 
.site {
    height: 100%;
    width: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    font-size: 27px;
}
 
body {
    color: rgb(238, 238, 238);
    margin: 0px;
    padding: 0px;
    height: 100%;
    width: 100%;
    overflow: hidden;
    background-color: transparent;
}
 
small {
    font-size: 70%;
    color: gray;
}
 
.buttons {
    position: fixed;
    top: 80%;
    left: 50%;
    width: 47%;
    margin: 2px;
    display: none;
    padding: 0px 3px 0px 3px;
    color: #cccccc;
    font-size: 80%;
}
\#description {
    top: 0px;
    position: fixed;
    margin: 2px;
    overflow-x: hidden;
    overflow-y: auto;
    display: none;
    padding: 0px 3px 0px 3px;
    font-size: 28px;
    left: 51%;
    width: 47.2%;
    word-break: break-word;
}
 
</div></div>
 
===Пример своего стиля (пишите чистый css, он будет преобразован в одну строку)===
 
===Если строка стилей очень большая, их лучше вынести в отдельный css файл===
$_PL["link"][]=["type"=>"text/css","href"=>"http://example.com/main.css"];
 
====#content - стиль страницы (без description)====
$_PL["css"]
 
====#rightHalf- стиль description====
$_PL["css"]="#content {font-size:25px;}  #rightHalf{font-size:10 px;} "; // Установим размер шрифта сайта и описания
 
====.selected - стиль при выделении элемента====
$_PL["css"]=" .selected {  color: black; } "; // Делаем смену цвета выделенного элемента
 
====.default - стиль невыделенного элемента====
$_PL["css"]=" .default{  color: gray; } ";
 
====.contmenu - визуальная кнопка контекстного меню====
$_PL["css"]=" .contmenu{ display:none; } ";  // Скрываем кнопку контекстного меню
 
====.label, .start, .list, .fulleditline, .hlink, .html - стили(верхний уровень) для разных типов элементов $_CH["position"]  [https://wiki.forkbrowser.top/wiki/FXML#position_.D0.BF.D1.80.D0.B8_.D0.B7.D0.B0.D0.B4.D0.B0.D0.BD.D0.BD.D0.BE.D0.BC_.24_PL.5B.22typeList.22.5D.3D.22start.22.3B]====
Если не задан $_CH["position"] элемента то по умолчанию элементы отображаются на странице списком и классом .list
 
Если при этом задано отображение плиткой ($_PL["typeList"]="start" ) то по умолчанию элементы отображаются с классом .start
 
====.labelselected, .startselected, .listselected, .fulleditlineselected, .hlinkselected, .htmlselected - стили(верхний уровень) для выделенных элементов====
 
=====Установим цвет для выделенных элементов только .list=====
$_PL["css"]=" .listselected{color: red; }";
 
====.labeldefault, .startdefault, .listdefault, .fulleditlinedefault, .hlinkdefault, .htmldefault - стили(верхний уровень) для невыделенных элементов====


====.list - зададим свой вид списка====
$_PL["css"]=" .list{color: red; }"; // Меняем цвет


====.title .icon .rating .numb вложенные в верхний уровень стили заголовка, иконки, рейтинга(для .start), порядкового номера(для .list)====
$_PL["css"]=" .title{color: red; }"; // Меняем цвет заголовков


и FXML CMS
$_PL["css"]=" .numb { display:none; } "; // Скрываем отображение номера


<br />
====Комбинация классов, зададим стиль иконок только для списка (.list)====
$_PL["css"]=" .list .icon{background-color:white; margin:2px;}"; // Зададим белую рамку фона для иконок


==Глобальные теги==
$_PL["typeList"]="start"; // Плиточный + другой(с использованием $_CH["position"] ) виды страницы


====Стили сайта====
$_PL["css"]="#content {font-size:25px;}  #site {font-size:22px; }"; // Стили вашего сайта в формате css


$_PL["cacheinfo"]="nocache";  // Не кешировать страницу (при возврате назад страница будет грузиться заново по адресу)
$_PL["cacheinfo"]="nocache";  // Не кешировать страницу (при возврате назад страница будет грузиться заново по адресу)
Строка 34: Строка 482:
$_PL["confirm"]=["title"=>"Открыть вложенный CHannel?","channel"=>["playlist_url"=>"http://.."]];
$_PL["confirm"]=["title"=>"Открыть вложенный CHannel?","channel"=>["playlist_url"=>"http://.."]];


===Отдельные стили элементов при наведении===
=== 4.2. Поведение страницы (cmd, ping, служебные теги) ===
====["channels"]["parent"] - элемент списка, разные стили при выделении и по умолчании====
задать красный цвет фона при выделении
 
$_PL["style"]["channels"]["parent"]["selected"]["backgroundColor"]="red ";
 
задать серый цвет фона не выделенных  элементов
 
$_PL["style"]["channels"]["parent"]["default"]["backgroundColor"]="gray";
<br />


===cmd тег (как глобальный так и в playlist_url) - допустимые команды===
===cmd тег (как глобальный так и в playlist_url) - допустимые команды===
Строка 67: Строка 506:
Например $_CH["playlist_url"]="reload();"; перезагрузит страницу при нажатии
Например $_CH["playlist_url"]="reload();"; перезагрузит страницу при нажатии


==Channel теги==
===Глобальный тег ping===
Для выполнения действий когда они станут доступны. Например в Fork browser можно отслеживать введение кода с телефона и автоматически загружать страницу.
 
$_PL["ping"]["link"]="<nowiki>http://example.com/check.php?hash</nowiki>";
 
Если check.php отдаёт пустой ответ, Fork продолжает проверять страницу каждые 6 сек.
 
Если check.php отдаёт страницу с <code>channels</code>, загружается она, например:
 
$_PL["channels"]=[["location"=>1,"title"=>"Успешно вошли! Нажмите для переадресации...","playlist_url"=>"<nowiki>http://example.com/</nowiki>"]];
 
===Глобальный тег align===
Выравнивание элементов при виде плиткой (<code>$_PL["typeList"]="start"</code>). По умолчанию — по центру.
 
$_PL["align"]="left";
 
===Глобальный тег url_tvg — своя телепрограмма===
Поддерживается только формат xmltv (сжатый и нет).
 
$_PL["url_tvg"]="<nowiki>http://epg.it999.ru/edem.xml.gz</nowiki>";
 
В M3U плейлисте аналогичный параметр задаётся в начале:
 
<nowiki>#</nowiki>EXTM3U url-tvg="<nowiki>http://epg.it999.ru/edem.xml.gz</nowiki>"
 
===Глобальный тег aside для вывода бокового меню===
[[Файл:Aside menu.png|мини|Пример бокового меню Fork]]
$_PL["aside"]["channels"][]=["title"=>"Home","playlist_url"=>"http://site.com/","style"=>"color:blue;","logo_30x30"=>"https://static.vecteezy.com/system/resources/thumbnails/022/013/913/small/home-icon-illustration-image-vector.jpg"];
 
$_PL["aside"]["channels"][]=["title"=>"Category 1","playlist_url"=>"http://site.com/cat1","logo_30x30"=>"https://static.vecteezy.com/system/resources/thumbnails/022/013/913/small/home-icon-illustration-image-vector.jpg"];
 
Изменить стили бокового меню по умолчанию:
 
$_PL["css"].=""#aside{position:absolute;z-index:1;top:0px;left:0px;height:inherit;border-radius: 0px 10px 10px 0px;padding:0px 10px 0px 2px;font-size: 27px;line-height: 27px;white-space: nowrap;overflow-x: hidden;}
 
#aside img{width:27px;height:27px;margin-right:8px;}
 
.aside_default{margin:5px 0px;padding: 4px;border-radius: 8px;border: 2px solid transparent;}
 
.aside_selected{margin:5px 0px;padding: 4px;border-radius: 8px;border: 2px solid #5590e7;}
 
.aside_hide{width:30px;background:none;overflow-y: hidden;}
 
.aside_show{width:auto;overflow-y: auto;background-color:rgba(0, 0, 0, 0.8);}";
 
Если стили свои, то лучше вынести их в отдельный css файл
 
===Подключения css файлов стилей===
 
$_PL["link"][]=["type"=>"text/css","href"=>"http://example.com/main.css"];
 
===advertising для показа рекламы перед запуском видео===
====Видеоролик====
$_PL["advertising"]=["client"=>"video",
    "title"=>"Реклама",// Надпись внизу с названием или описанием обьявления
    "skip"=>3,//Добавьте skip в секундах когда можно будет нажать кнопку пропуск
    "tag"=>"https://mlb2.adriver.ru/mf/0008908/0008908282/0/720_480.mp4"
];
 
====VAST====
XML формат рекламы. Поддерживаются перенаправления, пропуск, отправка событий
$_PL["advertising"]=["client"=>"vast",
    "title"=>"Реклама",
    "tag"=>"https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%203.0%20Samples/Inline_Companion_Tag-test.xml"
];
В настройках форка для тестирования включите Ads: always
 
== 5. Справочник тегов channel ($_CH) ==
Каждый объект в <code>channels[]</code> — элемент списка. Ниже — теги в порядке от базовых к расширенным.
 
{| class="wikitable"
! Уровень !! Тег !! Назначение
|-
| '''Базовый''' || <code>title</code> || Заголовок элемента (обязательно)
|-
| '''Базовый''' || <code>logo_30x30</code> || URL иконки 30×30
|-
| '''Базовый''' || <code>playlist_url</code> || Переход на другую страницу
|-
| '''Базовый''' || <code>stream_url</code> || Запуск видеоплеера
|-
| '''Базовый''' || <code>description</code> || Текст справа (режим списка) или в шаблоне
|-
| Средний || <code>position</code>, <code>br</code>, <code>template</code> || Вид и вёрстка элемента
|-
| Средний || <code>menu</code>, <code>confirm</code> || Меню и диалоги
|-
| Средний || <code>location</code> || Переадресация (1 — заменить URL, 3 — временная)
|-
| Средний || <code>before</code>, <code>after</code> || HTML до/после элемента
|-
| Плеер || <code>poster</code>, <code>label</code>, <code>information</code> || Подписи в видеоплеере
|-
| Плеер || <code>subtitles</code>, <code>event</code>, <code>start_time</code> || Субтитры, события, старт с позиции
|-
| Расширенный || <code>parser</code>, <code>cors</code>, <code>iframe</code> || Загрузка и встраивание данных
|-
| Расширенный || <code>SetTimeInterval</code> || Периодические запросы к серверу
|}
 
=== 5.1. Основные теги ===


===title===
===title===
Заголовок
Заголовок элемента. Отображается в списке и используется как fallback для <code>label</code> в плеере.


===playlist_url или stream_url===
===playlist_url или stream_url===
Адрес страницы или адрес видеопотока (непустым может быть только один из этих тегов)
Адрес страницы или адрес видеопотока. Непустым может быть только один из этих тегов.
 
====Параметры ссылок #direct и #stream_url====
Добавляются в конец ссылки:
 
'''#direct''' — если сайт требует чистой ссылки без добавлений Fork (параметры, cookie, идентификатор):
 
<nowiki>http://pastebin.com/yuUtYu56g#direct</nowiki> → откроется как <nowiki>http://pastebin.com/yuUtYu56g</nowiki>
 
'''#stream_url''' — ссылка откроется сразу в видеоплеере.
 
====AddFavorite и AddSearch====
Команды в <code>playlist_url</code> для добавления портала в закладки или глобальный поиск:
 
$_CH[]=["logo_30x30"=>"none","title"=>"Добавить этот портал в закладки / стартовое меню","playlist_url"=>"AddFavorite(Кинопаб,<nowiki>https://kino.pub/images/logo.png,http://195.88.208.101/kinopub/</nowiki>);"];
 
$_CH[]=["logo_30x30"=>"none","title"=>"Добавить этот портал в Глобальный поиск","playlist_url"=>"AddSearch(Кинопаб,<nowiki>https://kino.pub/images/logo.png,http://195.88.208.101/kinopub/?cat=search</nowiki>);"];
 
(пример всей страницы на PHP: [https://github.com/alexkdpu/kino.pub_forkplayerPHP/blob/master/index.php Кинопаб])
 
====iframe — вставка содержимого другой страницы====
На место элемента списка вставляется содержимое из ссылки. Можно использовать для поиска по нескольким плейлистам (глобальный поиск).
 
$_CH[]=["title"=>"Поиск Terminator в filmix.red","playlist_url"=>"<nowiki>https://filmix.red/fork/search?search=Terminator</nowiki>","iframe"=>"4","timeout"=>8];
 
<code>iframe</code> — обязательный параметр. Число — сколько ссылок из дочерней страницы показать сразу; остальное будет внизу списка. <code>"iframe"=>"0"</code> — показать все.
 
<code>timeout</code> — таймаут ожидания содержимого из дочерней ссылки (секунды).
 
====Альтернативный playlist_url2, время ожидания таймаута, способы получения====
Можно указать запасной источник страницы, время ожидания таймаута, способы получения
 
<?php
$_CH[]= [
    "title" => "Каталог",
    "playlist_url" => "http://domain1.in.net/",
    "playlist_url2" => "http://domain2.in.net/",
    "playlist_url3" => "http://domain3.in.net/",
    "cors" => "directnoheader", // Тип получения страницы(напрямую без заголовков). Если не указать то будет перебор direct, directnoheader, remotefork и потом альтернативный источник
    "timeout" => 4, // В секундах
    "error" => "http://domainstat.in.net//err.php?info=%INFO" // Адрес куда отправлять ошибку если все страницы не откроются
];
 
 
====stream_url мультиссылки с разными качествами и (или) несколькими источниками, Err format json link====
Для видеопотока можно задавать мультиссылки с разным качеством (можно и разные озвучки, но это будет не так явно для пользователя), выбор качества будет браться автоматически с настроек Fork(1080 или 720 и т.д.) и (или) несколькими источниками
 
Пример использования в PHP с json_encode
В данном примере создается массив с различными ссылками на видеопотоки, и с помощью функции json_encode он преобразуется в JSON-формат для последующего использования:
 
<?php
// Массив с данными для разных качеств видео и двумя источниками
$stream_links = [
    "360" => [
        "title" => "360p",
        "url" => [
            "http://server.in/240.mp4",
            "http://server2.in/hls/240.mp4"
        ]
    ],
    "720" => [
        "title" => "720p",
        "url" => [
          "http://server.in/720.mp4",
          "http://server2.in/hls/720.mp4"
        ]
    ]
];
// Преобразование массива в JSON
$json_stream_url = json_encode($stream_links);
// Использование JSON в $_CH["stream_url"]
$_CH["stream_url"] = $json_stream_url;
// Вывод JSON для проверки
echo $_CH["stream_url"];
?>
 
 
Пример с несколькими источниками:
 
<?php
$stream_links = [
            "http://server.in/240.mp4",
            "http://server2.in/hls/240.mp4"
        ];
// Преобразование массива в JSON
$json_stream_url = json_encode($stream_links);
// Использование JSON в $_CH["stream_url"]
$_CH["stream_url"] = $json_stream_url;
// Вывод JSON для проверки
echo $_CH["stream_url"];
?>
 
====Поддержка ссылок Яндекс.Диска====
Ссылка должна быть в поле <code>stream_url</code> или в m3u плейлисте в формате <nowiki>https://yadi.sk/i/idfile</nowiki>
 
====playlist — похожие видео после окончания (YouTube)====
<?php
$PLAYLIST=[];
$PLAYLIST[]=["title"=>"Похожее видео 1","stream_url"=>"<nowiki>http://www.youtube.com/watch?v=ZNLZla2xHUQ</nowiki>"];
$PLAYLIST[]=["title"=>"Похожее видео 2","stream_url"=>"<nowiki>http://www.youtube.com/watch?v=ZNLZla2xHUQ</nowiki>"];
$_CH[]=["title"=>"Основное видео","stream_url"=>"<nowiki>http://www.youtube.com/watch?v=xhFCmwrSxCU</nowiki>","playlist"=>$PLAYLIST];
$_PL["channels"]=$_CH;
print json_encode($_PL);
?>


===logo_30x30===
===logo_30x30===
Адрес иконки
URL иконки элемента (рекомендуемый размер 30×30 px). Используется также как fallback для <code>poster</code> в плеере.
 
=== 5.2. Видеоплеер ===
 
===Теги видеоплеера poster, label, information===
Нужны чтобы по-разному выдавать информацию на сайте и в видеоплеере.
 
$_CH["title"]="Хало 1 сезон 1 серия";
$_CH["label"]="Хало";
$_CH["information"]="1 сезон 1 серия";
 
[[Файл:Halo.png|мини|Вид экрана плеера]]
 
Плеер сначала ищет значение в специализированном теге; если он пустой — берёт из общего тега.
 
====Картинка в видеоплеере слева внизу====
$_CH["poster"]="<nowiki>http://example.com/videoposter.png</nowiki>";
 
если пустой — используется <code>logo_30x30</code>.
 
====Название над полосой прогресса====
$_CH["label"]="Хало";
 
если пустой — используется <code>title</code>.
 
====Информация под прогрессом====
$_CH["information"]="1 сезон 1 серия";
 
если пустой — используется <code>description</code>.
 
===Тег subtitles — субтитры===
$_CH["subtitles"][0]=["UA","<nowiki>https://tortuga.wtf/player/subtitle/18625_ua.vtt</nowiki>"];
$_CH["subtitles"][1]=["EN","<nowiki>https://tortuga.wtf/player/subtitle/18625_en.vtt</nowiki>"];
$_CH["subtitles"][2]=["RU","<nowiki>https://tortuga.wtf/player/subtitle/18625_ru.vtt</nowiki>"];
 
===event — события видеоплеера===
Отправка на сервер событий при старте и остановке видео:
 
$_CH[]=["logo_30x30"=>"hidden","title"=>"Video","stream_url"=>"<nowiki>http://...</nowiki>","event"=>["onstartvideo"=>"$siteurl/?event=onstartvideo&videoid=1","onstopvideo"=>"$siteurl/?event=onstopvideo&curTime=[curTime]&totalTime=[totalTime]&videoid=1"]];
 
<code>[curTime]</code> и <code>[totalTime]</code> заменяются на время остановки и общую длительность видео в секундах.
 
===start_time — начало воспроизведения===
$_CH[]=["logo_30x30"=>"hidden","title"=>"Video","stream_url"=>"<nowiki>http://...</nowiki>","start_time"=>340];
 
<code>start_time</code> — время в секундах. Предлагается на кнопке Play только если пользователь ранее не смотрел это видео; иначе нужно отметить видео непросмотренным в Меню / Отметить непросмотренным.
 
=== 5.3. Интерактивные элементы ===


===confirm - диалоговое окно с действиями при нажатии на элемент===
===confirm - диалоговое окно с действиями при нажатии на элемент===
Строка 83: Строка 772:
$_CH[]=["title"=>"Выйти","playlist_url"=>"confirm","confirm"=>["http://host/?do<nowiki>=exit"],"description"=>"Выйти с аккаунта?"];</nowiki>
$_CH[]=["title"=>"Выйти","playlist_url"=>"confirm","confirm"=>["http://host/?do<nowiki>=exit"],"description"=>"Выйти с аккаунта?"];</nowiki>


===menu - контекстное меню элемента===
===menu — контекстное меню элемента channel===
$menu=[];
 
Массив <code>$_CH["menu"]</code> задаёт '''контекстное меню одного элемента''' списка (<code>channels[]</code>). Каждый пункт меню — мини-channel: <code>title</code>, <code>playlist_url</code> (или <code>stream_url</code>) и опциональные поля оформления.
 
{| class="wikitable"
! Тег !! Область !! Назначение
|-
| <code>$_CH["menu"]</code> || Элемент списка || Меню '''конкретного''' channel (качество, реакция, настройка строки)
|-
| <code>$_PL["menu"]</code> || Страница целиком || Глобальная полоса меню '''вверху''' страницы (зеркала, навигация портала)
|}
 
<div class="toccolours mw-collapsible" style="width:720px;">
'''Содержание раздела'''
* [[#menu — быстрый старт|Быстрый старт]]
* [[#menu — режимы отображения|Режимы отображения]]
* [[#menu — опции type|Опции type (menu[0])]]
* [[#menu — навигация|Навигация и управление]]
* [[#menu — встраивание $menu|Встраивание в template ($menu)]]
* [[#menu — справочник полей|Справочник полей menu[]]]
* [[#menu — плейсхолдеры template|Плейсхолдеры template пункта]]
* [[#menu — подменю|Подменю]]
* [[#menu — ответ сервера cmd|Ответ сервера cmd без перехода]]
* [[#menu — стилизация|Стилизация]]
* [[#menu — совместимость|Совместимость]]
</div>
 
====menu — быстрый старт====
 
Минимальное '''вертикальное popup-меню''' по OK на channel:
 
<pre>$menu = [];
$menu[] = ["title" => "Пункт 1", "playlist_url" => "http://example.com/1"];
$menu[] = ["title" => "Пункт 2", "playlist_url" => "http://example.com/2"];
 
$_CH["title"]  = "Настройки";
$_CH["playlist_url"] = "menu";  // обязательно для открытия меню
$_CH["menu"]    = $menu;</pre>
 
'''Обязательные условия:'''
* у channel: <code>"playlist_url": "menu"</code>;
* массив <code>menu</code> с одним и более пунктов;
* каждый пункт — обычный channel (URL, <code>cmd:...</code>, <code>submenu</code> и т.д.).
 
====menu — режимы отображения====
 
Поведение определяется комбинацией '''<code>menu[0].type</code>''' и наличия '''<code>$menu</code>''' в <code>$_CH["template"]</code>.
 
{| class="wikitable"
! Режим !! Условие !! Вид !! Как открыть
|-
| '''Popup (вертикальный)''' || нет <code>inline</code> в <code>type</code> || Список поверх страницы || OK на channel с <code>playlist_url":"menu"</code>
|-
| '''Inline в строке''' || <code>type</code> содержит <code>inline</code> + в template есть <code>$menu</code> || Горизонтальный ряд кнопок внутри элемента || Кнопки видны сразу; popup не открывается
|-
| '''Inline popup''' || <code>inline</code> + в template '''нет''' <code>$menu</code> || Горизонтальный popup || OK на channel; навигация ←/→
|}
 
'''Схема подключения inline в template:'''
 
# <code>$_CH["position"] = "html";</code>
# В <code>template</code> — плейсхолдер <code>$menu</code>
# Заполнить <code>menu[]</code>
# У первого пункта: <code>"type": "inline"</code> (и при необходимости <code>|nocancel</code>)
 
====menu — опции type====
 
Поле <code>type</code> задаётся '''только у первого пункта''' <code>menu[0]</code>. Несколько опций комбинируются через <code>|</code> (порядок не важен):
 
<pre>"type": "inline"
"type": "inline|nocancel"
"type": "nocancel"</pre>
 
{| class="wikitable"
! Значение !! Описание
|-
| <code>inline</code> || Горизонтальное меню: в строке (<code>$menu</code> в template) или горизонтальный popup
|-
| <code>nocancel</code> || Не добавлять пункт «Отмена» в popup; закрытие — кнопкой '''Назад'''
|}
 
'''Примечание:''' при <code>inline</code> первый пункт меню — обычная кнопка (например «1080p»), а не пустой служебный маркер.
 
====menu — навигация====
 
{| class="wikitable"
! Контекст !! Клавиши !! Действие
|-
| Popup (вертикальный) || ↑ / ↓ || Перемещение между пунктами
|-
| Popup || OK || Выбор пункта
|-
| Popup || Назад || Закрыть меню (без перехода по истории)
|-
| Inline popup || ← / → || Перемещение между пунктами
|-
| Inline в строке (<code>$menu</code>) || OK / клик || Выбор пункта напрямую
|-
| Inline в строке || ← / → || Фокус по горизонтальному ряду (если меню в popup после OK)
|}
 
'''Поле <code>default</code>''' — начальный фокус при первом открытии меню:
 
<pre>"menu": [
  {"title": "Отлично", "playlist_url": "..."},
  {"title": "Нормально", "default": 1, "playlist_url": "..."},
  {"title": "Плохо", "playlist_url": "..."}
]</pre>
 
* у пункта с <code>"default": 1</code> (или любое истинное значение) курсор ставится при открытии;
* если ни у одного пункта нет <code>default</code> — выделяется первый (индекс 0);
* работает в popup-меню и во '''вложенном submenu''';
* пункт «Отмена» в фокус по <code>default</code> не попадает.
 
'''Поле <code>hint</code>''' — подсказка у пункта в popup: появляется через 1 с после фокуса, скрывается через 5 с (жёлтый tooltip у элемента).
 
====menu — встраивание $menu====


$menu[0]=["title"=>"Контекстное меню1","playlist_url"=>"http..."]; //  Аналог обычного $_CH
'''Пример JSON''' (переключатель качества):


$menu[0]=["title"=>"Контекстное меню2","playlist_url"=>"http..."]; //  Аналог обычного $_CH
<pre>{
  "title": "Качество",
  "position": "html",
  "playlist_url": "menu",
  "template": "&lt;div class='setting-item'&gt;&lt;div class='setting-title'&gt;$title&lt;/div&gt;&lt;div class='chmenu'&gt;$menu&lt;/div&gt;&lt;/div&gt;",
  "menu": [
    {
      "type": "inline|nocancel",
      "title": "1080p",
      "logo_30x30": "http://example.com/icons/hd.png",
      "playlist_url": "http://example.com/?q=1080",
      "default": 1,
      "class": "pill active",
      "hint": "Full HD",
      "template": "&lt;div&gt;&lt;img src=$logo_30x30&gt; $title&lt;/div&gt;"
    },
    {"title": "720p", "playlist_url": "http://example.com/?q=720", "class": "pill"},
    {"title": "480p", "playlist_url": "http://example.com/?q=480", "class": "pill"}
  ]
}</pre>


$_CH["menu"]=$menu;
'''Пример PHP:'''


===position===
<pre>&lt;?php
$_CH["position"]="fulleditline"; //- Поле ввода текста шириной почти на всю страницу
$_CH["title"] = "Качество";
$_CH["position"] = "html";
$_CH["playlist_url"] = "menu";
$_CH["template"] = '&lt;div class="setting-item"&gt;'
  . '&lt;div class="setting-title"&gt;$title&lt;/div&gt;'
  . '&lt;div class="chmenu"&gt;$menu&lt;/div&gt;&lt;/div&gt;';


$_CH["position"]="hlist"; // - горизонтальная ссылка (может быть несколько на одном горизонтальном уровне)
$_CH["menu"] = [
  [
    "type" => "inline|nocancel",
    "title" => "1080p",
    "default" => 1,
    "logo_30x30" => "http://example.com/icons/hd.png",
    "playlist_url" => "http://example.com/?q=1080",
    "class" => "pill active",
    "template" => '&lt;div&gt;&lt;img src=$logo_30x30&gt; $title&lt;/div&gt;'
  ],
  ["title" => "720p", "playlist_url" => "http://example.com/?q=720", "class" => "pill"],
  ["title" => "480p", "playlist_url" => "http://example.com/?q=480", "class" => "pill"]
];


$_CH["position"]="list"; // -обычный вид списка шириной в половину экрана и с description справа
$_PL["css"] = ".pill{border-radius:999px;padding:4px 14px;margin:0 4px;display:inline-block;}"
  . ".pill.active{background:#3366cc;color:#fff;}";
$_PL["channels"][] = $_CH;
print json_encode($_PL);
?&gt;</pre>


$_CH["position"]="label"; // -невысокая строка на всю ширину экрана
При <code>$menu</code> в template + <code>type:inline</code> повторный popup по OK '''не''' открывается.


$_CH["position"]="bigtile"; // - Плитка увеличенной в 2 раза высоты (128х215px)
====menu — справочник полей====


$_CH["position"]=""; // - Плитка обычного размера как в Стартовом меню (128х101px)
{| class="wikitable"
! Поле !! Тип !! Описание
|-
| <code>title</code> || string || Текст пункта (обязательно для отображения)
|-
| <code>playlist_url</code> || string || URL или команда при выборе (<code>cmd:...</code>, <code>submenu</code> и др.)
|-
| <code>stream_url</code> || string || Альтернатива <code>playlist_url</code> для видеопотока
|-
| <code>type</code> || string || '''Только menu[0]:''' опции через <code>|</code> — <code>inline</code>, <code>nocancel</code>
|-
| <code>default</code> || int/bool || <code>1</code> — пункт выделен при первом открытии меню
|-
| <code>logo_30x30</code> || string || URL иконки пункта
|-
| <code>class</code> || string || CSS-класс обёртки (по умолчанию <code>menucontext</code>)
|-
| <code>style</code> || string || Inline-стили обёртки пункта
|-
| <code>img_style</code> || string || Inline-стили иконки (если нет своего <code>template</code>)
|-
| <code>template</code> || string || Свой HTML тела пункта (как у channel)
|-
| <code>before</code> / <code>after</code> || string || HTML до / после пункта
|-
| <code>hint</code> || string || Подсказка при фокусе в popup (1 с задержка, 5 с показ)
|-
| <code>menu</code> || array || Вложенное подменю (см. [[#menu — подменю|Подменю]])
|}
 
====menu — плейсхолдеры template====
 
Используются в <code>template</code> '''пункта''' menu (не путать с <code>$menu</code> в template channel):
 
{| class="wikitable"
! Плейсхолдер !! Подставляется
|-
| <code>$title</code> || <code>title</code> пункта
|-
| <code>$description</code> || <code>description</code> пункта
|-
| <code>$logo_30x30</code> || URL иконки (для атрибута <code>src</code>)
|-
| <code>$logo</code> || Готовый тег <code>&lt;img&gt;</code>
|-
| <code>$class</code> || Значение <code>class</code> пункта
|-
| <code>$index</code> || Индекс пункта в menu (с 0)
|}
 
Плейсхолдер '''channel''': <code>$menu</code> — в <code>$_CH["template"]</code>; подставляется HTML всех пунктов inline-меню.
 
====menu — подменю====
 
Пункт с вложенным списком через <code>playlist_url":"submenu"</code> и массив <code>submenu</code>:
 
<pre>{
  "menu": [
    {
      "type": "inline",
      "title": "Ещё ▾",
      "playlist_url": "submenu",
      "submenu": [
        {"title": "Обновить", "playlist_url": "cmd:reload();"},
        {"title": "Настройки", "default": 1, "playlist_url": "http://example.com/settings"}
      ]
    }
  ]
}</pre>
 
Во встроенном <code>$menu</code> клик по такому пункту открывает подменю; для пунктов <code>submenu</code> также поддерживается <code>default</code>.
 
====menu — ответ сервера cmd без перехода====
 
Если пункт menu ведёт на URL, сервер может вернуть JSON с командой вместо новой страницы — пользователь остаётся на текущем списке, история не растёт.
 
<pre>{"cmd": "seticon([index],http://example.com/icon.svg);stop();"}</pre>
 
{| class="wikitable"
! Команда !! Назначение
|-
| <code>seticon(индекс, url)</code> || Обновить <code>logo_30x30</code> / <code>icon</code> channel; <code>[index]</code> — текущий выделенный элемент
|-
| <code>settitle(индекс, текст)</code> || Изменить заголовок channel по индексу
|-
| <code>stop()</code> || Прервать загрузку «новой страницы»; остаться на текущей (обязательно в конце цепочки cmd)
|}
 
'''Типичный сценарий''' (реакция, переключатель без перезагрузки):
 
# Пункт menu → <code>playlist_url</code> с параметром действия (например <code>&react=1</code>)
# Сервер сохраняет данные и отвечает: <code>{"cmd":"seticon([index],URL_иконки);stop();"}</code>
# Fork обновляет иконку строки; кнопка '''Назад''' сразу уходит на предыдущую страницу (без «лишнего» шага)
 
См. также глобальный тег [[#cmd_тег_.28как_глобальный_так_и_в_playlist_url.29_.2D_допустимые_команды.29|cmd]] и <code>stop();</code> в <code>$_PL["cmd"]</code>.
 
====menu — стилизация====
 
* Оформление пунктов — через <code>class</code>, <code>style</code>, <code>template</code> каждого пункта.
* Общие стили (pill, tab, badge) — <code>$_PL["css"]</code> или внешний файл <code>$_PL["link"][]</code>.
* Длинное inline-меню прокручивается по горизонтали (<code>overflow-x</code>).
 
====menu — совместимость====
 
Channel с <code>menu</code> без <code>type:inline</code> и без <code>$menu</code> в template работает как раньше: вертикальный popup по <code>playlist_url":"menu"</code>, пункт «Отмена» внизу (если нет <code>nocancel</code>).
 
=== 5.3. Навигация и оформление channel ===


===location===
===location===
Строка 115: Строка 1065:
$_CH["after"]="<nowiki><div style='color:red;'>Текст после элемента</div></nowiki>";
$_CH["after"]="<nowiki><div style='color:red;'>Текст после элемента</div></nowiki>";


===SetTimeInterval - отправка событий клиентом через интервал и вывод сообщений===
=== 5.4. Загрузка данных и расширенные ссылки ===
 
===Локальные переменные LOCAL_IP, TORRSERVE_IP, ACE_IP===
[[Файл:SetIP.png|мини]]
 
В URL портала подставляются значения из настроек ForkPlayer на устройстве пользователя.
 
'''Если не указан TORRSERVE_IP или ACE_IP''' — вместо них подставляется LOCAL_IP (устройство с RemoteFork).
 
Если не указан также LOCAL_IP — подставляется <code>127.0.0.1</code>.
 
====Примеры в magnet-ссылках====
$_CH[]=["title"=>"(magnet) '''acestream'''","playlist_url"=>"<nowiki>http://ACE_IP:6878/server/api?method=get_media_files&magnet=</nowiki>".urlencode("<nowiki>magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1</nowiki>")];
 
$_CH[]=["title"=>"(magnet) '''torrserve'''","playlist_url"=>"<nowiki>http://TORRSERVE_IP:8090/torrent/play?m3u=true&link=</nowiki>".urlencode("<nowiki>magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1</nowiki>")];
 
====Автовыбор torrserve или ace stream====
$_CH[]=["title"=>"(magnet) через ace stream или torrserve","playlist_url"=>"<nowiki>magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1</nowiki>"];
 
===SetTimeInterval — периодические запросы к серверу===
$_CH["SetTimeInterval"]=["time"=>1,"onmenu"=>"<nowiki>http://19onmenu</nowiki>","onplay"=>"<nowiki>http://19onplay</nowiki>"];
$_CH["SetTimeInterval"]=["time"=>1,"onmenu"=>"<nowiki>http://19onmenu</nowiki>","onplay"=>"<nowiki>http://19onplay</nowiki>"];


Строка 143: Строка 1112:
преобразуется в RegExp("<script>.*?js=(.*?);","i") - результат отдастся тот что в ()  
преобразуется в RegExp("<script>.*?js=(.*?);","i") - результат отдастся тот что в ()  


<channel>
<channel><title>Test </title>
  <playlist_url><![CDATA<nowiki>[http://parser.co/#POSThtml=md5hash]</nowiki>]></playlist_url>
  <​​​​​​​parser><![CDATA<nowiki>[http://www.youtube.com/watch?v=qZ3xj_UF4I8]</nowiki>]><​​​​​​​/parser>
</channel>


<title>Test </title>
==== #POST в playlist_url означает что все что после него будет передавать методом POST====


<playlist_url><![CDATA<nowiki>[http://parser.co/?page=postmd5]</nowiki>]></playlist_url>
В примере выше на адрес http://parser.co/ отправятся POST данные html=md5hash, где md5hash заменятся на полученное от parser


<​​​​​​​parser><![CDATA<nowiki>[http://www.youtube.com/watch?v=qZ3xj_UF4I8]</nowiki>]><​​​​​​​/parser>
====Допустимо в parser использовать curl запросы (доступны на андроид, частично если форк установлен в память ТВ, или через ремотeфорк)====


</channel>
====Можно перечислить порядок методов запроса парсер, например <cors>rf|android|direct|directnoheader</cors>====


Результат будет в $_POST['postmd5']
Сначала будет попытка запроса через remotefork (если включен), потом через андроид класс если Fork запущен на андроиде, потом прямой XHR запрос с попыткой установить заголовки, потом прямой XHR запрос без заголовков


Можно так преобразовывать чтоб не путаться
<channel>
foreach ($_GET as $key => $value) {
  <title>Test curl and method</title>
  <cors>rf|android|direct|directnoheader</cors>
  <playlist_url><![CDATA<nowiki>[http://parser.co/#POSThtml=md5hash]</nowiki>]></playlist_url>
  <parser><![CDATA[curl "https://mysite.com/" -H "Accept-Encoding: deflate" -H "Connection: keep-alive" -H "DNT: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"]]>
  </parser>
</channel>


    if($value=="postmd5") $_GET[$key]=$_POST['postmd5'];
Или в JSON


{
  "channels": [
    {
      "title": "Test curl and method",
      "cors": "rf|android|direct|directnoheader",
      "playlist_url": "http://parser.co/#POSThtml=md5hash",
      "parser": 'curl "https://mysite.com/" -H "Accept-Encoding: deflate" -H "Connection: keep-alive" -H "DNT: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"'
    }
  ]
  }
  }
Результат будет в $_POST['html']
Результат будет аналогично md5hash в $_GET['page']
Результат будет аналогично md5hash в $_GET['page']


====Методом POST для видеоссылок ( stream_url )====
====Методом POST для видеоссылок ( stream_url )====
 <code><channel>  </code>
<channel>
  <title>stream post</title>
  <stream_url><![CDATA<nowiki>[http://test.ru/index.php#POSTs=md5hash]</nowiki>]></stream_url>
  <parser><![CDATA<nowiki>[https://yadi.sk/d/0BpgziSQ0VWiFg]</nowiki>]></parser>
</channel>
 
Будет идти отправка POST на <nowiki>http://test.ru/index.php#POSTs=md5hash</nowiki> в $_POST["s"]
 
Ответ страницы попадет в проигрыватель в виде ссылки.
 
Тоесть <code><nowiki>http://test.ru/index.php</nowiki></code> должен дать текст ссылки на медиафайл вида <nowiki>http://.../myvideo.mp4</nowiki>
 
====Скачивание сторонней страницы по необходимости, страница <nowiki>http://parser.co/</nowiki> может дать json ответ====
{"parse":"<nowiki>http://www.youtube.com/watch?v=qZ3xj_UF4I8&gl=US&hl=en&has_verified=1&bpctr=9999999999</nowiki>"}
Скачанная страница шлется в параметре  $_POST['remoteparse']
 
== 6. Оформление и вёрстка элементов ==
 
===position при заданном $_PL["typeList"]="start";===
При <code>$_PL["typeList"]="start"</code> каждый channel может задать свой вид через <code>position</code>:
 
====$_CH["position"]=""; // - Плитка как в Стартовом меню размером 128х101px)====
 
====$_CH["position"]="html"; // - Пример элемента со своим дизайном в $_CH["template"]====
$_CH["position"]="html";
 
$_CH["br"]=0; // Не переносить навигацию на новую строку
 
$_CH["title"]="Новый вид";
 
$_CH["description"]="Описание вида";
 
$_CH["logo_30x30"]="<nowiki>http://p.lnka.ru/icons/yapfiles.png</nowiki>";
 
$_CH["template"]='<nowiki><div style="width:242px;overflow:hidden;margin:4px;text-align:center;"><img src="$logo_30x30" style="width:242px;height:171px;padding:2px 2px 0px 2px;"></nowiki><nowiki><br></nowiki>$title<nowiki><br></nowiki><nowiki><small>$description</small></nowiki><nowiki></div></nowiki>';
 
Элементы будут выстраиваться в ряд и навигация по ним будет предполагать что они в одном ряду
 
=====$_CH["br"]=====
В месте где навигация должна перейти на новую строку задайте $_CH["br"]=1; При этом в before будет добавлено значение <nowiki><br clear=both></nowiki>
 
====$_CH["position"]="fulleditline"; //- Поле ввода текста шириной почти на всю страницу====
 
====$_CH["position"]="hlist"; // - горизонтальная ссылка (может быть несколько на одном горизонтальном уровне)====
 
====$_CH["position"]="list"; // -обычный вид списка шириной в половину экрана и с description справа====
 
====$_CH["position"]="label"; // -невысокая строка на всю ширину экрана====
 
====$_CH["position"]="bigtile"; // - Плитка увеличенной в 2 раза высоты (128х215px)====


<code><title>stream post</title></code>
===description — прокрутка части большого текста===
Добавьте в описание div с id <code>scrolled</code> — прокрутка кнопками PG_UP / PG_DOWN:


<code><stream_url><![CDATA<nowiki>[http://test.ru/index.php?s=postmd5]</nowiki>]></stream_url></code>
$_CH["description"]='MY TITLE STATIC<nowiki><div id="scrolled"></nowiki>


<code><parser><![CDATA<nowiki>[https://yadi.sk/d/0BpgziSQ0VWiFg]</nowiki>]></parser></code>
PG_UP PG_DOWN


<code></channel></code>
large scrolled content


Будет идти отправка POST на <nowiki>http://test.ru/index.php?s=postmd5</nowiki> в $_POST["postmd5"]
.


Ответ страницы попадет в проигрыватель в виде ссылки.
.
 
.


Тоесть <code><nowiki>http://test.ru/index.php?s=postmd5</nowiki></code> должен дать текст ссылки на медиафайл вида <nowiki>http://.../myvideo.mp4</nowiki>
<nowiki></div></nowiki>


====Скачивание сторонней страницы по необходимости, страница <nowiki>http://parser.co/</nowiki> может дать json ответ====
MY FOOTER STATIC';
{"parse":"<nowiki>http://www.youtube.com/watch?v=qZ3xj_UF4I8&gl=US&hl=en&has_verified=1&bpctr=9999999999</nowiki>"}
Скачанная страница шлется в параметре  $_POST['remoteparse']


==Построение произвольного положения элементов на странице (c "br" вместо "coordiantion")==
=== 6.1. Произвольная вёрстка (position=html, template, br) ===
Пошаговая схема для своего дизайна плитки:


===Задаем странице вид плиткой (start)===
# <code>$_PL["typeList"]="start";</code>
$_PL["typeList"]="start";
# Задать CSS в <code>$_PL["css"]</code>
# Для каждого channel: <code>position="html"</code>, <code>template</code>, при необходимости <code>br</code>


===Прописываем свои стили===
===Прописываем свои стили===
Строка 213: Строка 1254:


===Задаем шаблон (template) каждому элементу списка===
===Задаем шаблон (template) каждому элементу списка===
$_CH["position"]="html"; // Задает возможность использовать template
<code>$_CH["position"]="html"; // Задает возможность использовать template
 
$_CH["title"]="Новый вид"; // Заголовок - подставляет в шаблоне вместо $title
$_CH["title"]="Новый вид"; // Заголовок - подставляет в шаблоне вместо $title
$_CH["logo_30x30"]="<nowiki>http://p.lnka.ru/icons/yapfiles.png</nowiki>"; // Иконка $logo_30x30
$_CH["logo_30x30"]="<nowiki>http://p.lnka.ru/icons/yapfiles.png</nowiki>"; // Иконка $logo_30x30
$_CH["description"]="Описание, если нужно"; // Описание- подставляет в шаблоне вместо $description
$_CH["description"]="Описание, если нужно"; // Описание- подставляет в шаблоне вместо $description
$_CH["template"]='<nowiki><div class="myStyle1"><img src="$logo_30x30" style="myStyle2"></nowiki><nowiki><br></nowiki>$title<nowiki></div></nowiki>';
$_CH["template"]='<nowiki><div class="myStyle1"><img src="$logo_30x30" style="myStyle2"></nowiki><nowiki><br></nowiki>$title<nowiki></div></nowiki>';


</code>


===Перенос на новую строке при html дизайне $_CH["br"]=1;===
===Перенос на новую строке при html дизайне $_CH["br"]=1;===
Строка 224: Строка 1270:
  $_CH["br"]=1;// Этот элемент разместится уже на новой строке
  $_CH["br"]=1;// Этот элемент разместится уже на новой строке


==Вывод результирующей страницы (на PHP)==
===coordination (устаревший способ навигации)===
<?php
[[Файл:Координаты.png|мини]]
 
До появления тега <code>br</code> навигацию по произвольному шаблону задавали координатами. Обязательно при <code>position=html</code> и своём <code>template</code>.
 
$_CH["coordination"]=[x,y];
 
Где <code>x</code> — позиция по горизонтали, <code>y</code> — по вертикали. Первый элемент: <code>[0,0]</code>. Элемент на новой строке: <code>[1,0]</code>.
 
=== 6.2. Горизонтальная прокрутка (nowrap) ===
Для страниц с <code>typeList=html</code> или <code>position=html</code>. Задайте <code>nowrap=1</code> только первому элементу блока (у него же <code>br=1</code>):
 
$_CH=["br"=>1,"nowrap"=1,"title"=>"Первый элемент горизонтального блока прокрутки"];
 
$_CH=["title"=>"Второй элемент горизонтального блока прокрутки"];
 
$_CH=["title"=>"Третий элемент горизонтального блока прокрутки"];
 
...
 
$_CH=["title"=>"Последний элемент горизонтального блока прокрутки"];
 
$_CH=["br"=>1,"title"=>"Новый элемент с новой строки"];
 
== 7. Формирование ответа на сервере ==
 
=== 7.1. Параметры GET от клиента ===
При открытии портала ForkPlayer добавляет к запросу идентификаторы устройства.
 
{| class="wikitable"
! Параметр !! Описание
|-
| <code>box_mac</code> || Виртуальный MAC-адрес устройства
|-
| <code>box_user</code> || Email пользователя (если выполнен вход в аккаунт Fork)
|}
 
Пример: <nowiki>http://nserv.host/?box_mac=</nowiki>'''your_mac_address'''&box_user='''your_forkplayer_tv@email'''


// Ваш код с наполнением массивов $_PL и $_CH информацией
=== 7.2. Шаблон вывода JSON (PHP) ===
<pre>&lt;?php
// $_PL — глобальные теги страницы
// $_CH — массив channel; каждый элемент — ассоциативный массив тегов


$_PL["channels"]=$_CH;
$_PL["channels"] = $_CH;


print json_encode($_PL);
header("Content-Type: application/json; charset=utf-8");
print json_encode($_PL, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?&gt;</pre>


?>
== 8. Связанные материалы ==
* [https://wiki.forkbrowser.top/wiki/FXMLbaseparser FXMLbaseparser] — PHP-класс, упрощающий генерацию FXML
* [https://wiki.forkbrowser.top/wiki/CHANGELOG CHANGELOG] — история изменений протокола
* [https://github.com/vengo634/kino.pub_forkplayerPHP/blob/master/index.php Пример портала на PHP]
* FXML CMS — визуальный редактор порталов
* Исходный код любой страницы можно просмотреть в ForkPlayer (см. скриншот выше)
<br />
<br />

Текущая версия от 12:29, 20 июня 2026

FXML — официальная спецификация языка разметки порталов для Fork browser и совместимых приложений.
Формат ответа: валидный JSON. Навигация — с пульта, без мыши и тач-экрана.

1. Область применения

FXML (Fork eXtensible Markup Language) описывает структуру страницы, которую сервер отдаёт клиенту. Клиент интерпретирует JSON и показывает список элементов, видео или произвольную вёрстку на экране телевизора.

Совместимые приложения: ForkPlayer, OTT Player, OTT-play.

Связанные документы:

2. Быстрый старт

Минимальная страница состоит из заголовка и массива элементов channels. У каждого элемента — как минимум title и ссылка (playlist_url или stream_url).

Минимальный пример (JSON):

{
  "title": "Мой портал",
  "channels": [
    {"title": "Главная", "logo_30x30": "http://example.com/icon.png", "playlist_url": "http://example.com/"},
    {"title": "Фильм", "stream_url": "http://example.com/video.m3u8"}
  ]
}

Вывод на PHP:

<?php
$_PL = ["title" => "Мой портал"];
$_CH = [
  ["title" => "Главная", "logo_30x30" => "http://example.com/icon.png", "playlist_url" => "http://example.com/"],
  ["title" => "Фильм", "stream_url" => "http://example.com/video.m3u8"]
];
$_PL["channels"] = $_CH;
header("Content-Type: application/json; charset=utf-8");
print json_encode($_PL, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>
  • playlist_url — открыть другую FXML/XML/M3U страницу
  • stream_url — запустить видеоплеер

Полный пример портала (COOLTV):

Следующий код (валидный JSON):

{"title":"COOLTV - портал нового поколения","background-image":"http://cooltv.info/img/tvcool.jpg","typeList":"start","icon":"http://cooltv.info/img/tvcool23.jpg","channels":[{"title":"Вход","logo_30x30":"http://cooltv.info/img/profle22.jpg","playlist_url":"http://cooltv.info/auth"},{"title":"Новости ","logo_30x30":"http://cooltv.info/img/rss-96.png","playlist_url":"http://cooltv.info/news"},{"title":"Поиск ","logo_30x30":"http://cooltv.info/img/icons8-search-folder-96.png","playlist_url":"http://cooltv.info/media/search.php"},{"title":"Кинозал ","logo_30x30":"http://cooltv.info/img/1icons8-film-reel-96.png","playlist_url":"http://cooltv.info/media"},{"title":"AceStream ","logo_30x30":"http://cooltv.info/img/icons8-wave-arrows-96.png","playlist_url":"http://cooltv.info/vdt/12"},{"title":"Обменник ","logo_30x30":"http://cooltv.info/img/icons8-next-100.png","playlist_url":"http://cooltv.info/media/obmen/"},{"title":"Сервисы ","logo_30x30":"http://cooltv.info/img/icons8s3-96.png","playlist_url":"http://cooltv.info/vdt/9"},{"title":"IPTV ","logo_30x30":"http://cooltv.info/img/icons8-hdtv-96.png","playlist_url":"http://cooltv.info/vdt/8"},{"title":"Мультимедиа ","logo_30x30":"http://cooltv.info/img/icons8-documentary-96.png","playlist_url":"http://cooltv.info/vdt/7"},{"title":"Чат ","logo_30x30":"http://cooltv.info/img/icons8-chat-96.png","playlist_url":"http://cooltv.info/guest"},{"title":"FAQ ","logo_30x30":"http://cooltv.info/img/icons8-info-popup-96.png","playlist_url":"http://cooltv.info/faq/"},{"title":"Копилка","logo_30x30":"http://cooltv.info/img/icons8-bad-piggies-96.png","playlist_url":"http://cooltv.info/copilka"}]}

даст такой результат (в ForkPlayer):


Просмотр исходного кода страницы в ForkPlayer

3. Термины и модель данных

Термин Описание
Документ FXML JSON-объект верхнего уровня, который сервер отдаёт клиенту
$_PL / Playlist Глобальные поля документа (заголовок страницы, стили, команды)
$_CH / Channel Один элемент списка в массиве channels[]
playlist_url Ссылка на следующую страницу (FXML, XML, M3U, команда)
stream_url Ссылка на видеопоток; при наличии у channel не используется playlist_url
typeList Режим отображения страницы: список (по умолчанию) или плитка (start)
position Вид отдельного элемента при typeList="start" (плитка, список, html и др.)

Правила:

  1. Ответ сервера — один JSON-объект с ключом channels (массив).
  2. У channel должен быть непустой title.
  3. У channel заполняется либо playlist_url, либо stream_url (если элемент не служебный).
  4. Новые теги добавляются по мере развития протокола — см. CHANGELOG.

4. Справочник глобальных тегов ($_PL)

Теги задают свойства всей страницы. В PHP соответствуют элементам массива $_PL.

Тег Назначение Раздел
title Заголовок страницы
typeList start — плитка; иначе — список [[#position при заданном $_PL["typeList"]="start";|position]]
channels Массив элементов списка §5
css CSS-стили страницы (строка) §4.1
link[] Подключение внешних CSS link
align Выравнивание плитки (left, по умолчанию — center) align
cmd Команда при загрузке страницы cmd
ping Опрос URL до появления channels ping
aside Боковое меню aside
advertising Реклама перед видео advertising
url_tvg URL телепрограммы (xmltv) url_tvg
menu Глобальная полоса меню вверху menu
cacheinfo nocache — не кешировать страницу ниже
setcookie Cookie для последующих запросов к домену ниже
info, confirm Уведомление или диалог при загрузке ниже

4.1. Оформление страницы (css, link, align)

$_PL["css"] — CSS одной строкой. При typeList="start" доступны классы .start, .list, .html и др. (см. [[#position при заданном $_PL["typeList"]="start";|position]]).

$_PL["css"]="start"; — плиточный вид + поддержка $_CH["position"].

Стили сайта по умолчанию

По умолчанию для вашей страницы задаются такие стили (вы их можете изменить задав нужные в $_PL["css"]

Смотреть CSS стили по умолчанию
-webkit-scrollbar-button:single-button {
   background-color: #bbbbbb;
   display: block;
   border-style: solid;
   height: 10px;
   width: 10px;

}

-webkit-scrollbar-button:single-button:vertical:decrement {
   border-width: 5px;
   border-color: transparent transparent #555555;

}

-webkit-scrollbar-button:single-button:vertical:increment {
   border-width: 5px;
   border-color: #555555 transparent transparent transparent;

}

-webkit-scrollbar {
   width: 10px;

}

-webkit-scrollbar-track {
   background: #ddd;

}

-webkit-scrollbar-thumb {
   background: #888;

}

-webkit-scrollbar-thumb:hover {
   background: #777;

}

.start {

   position: relative;
   border: 0px;
   text-align: center;
   display: inline-block;
   ;
   overflow: hidden;
   text-align: center;
   margin-left: 4px;
   margin-top: 1px;
   height: 10px;
   width: 10%;

}

.start .rating {

   position: absolute;
   display: none;
   text-align: left;
   height: 23px;
   width: 28px;
   padding-top: 5px;
   padding-left: 5px;
   font-size: 11px;

}

.start .icon {

   padding: 2px 2px 0px 2px;
   height: 76%;

}

.start .name {

   font-size: 17px;
   line-height: 0.9;
   position: absolute;
   bottom: 0px;
   width: 100%;
   max-height: 50%;

}

.start .title {

   background: rgba(0, 0, 0, 0.65);
   border-radius: 3px;
   width: auto;
   display: display: inline-block;
   margin: 2px;
   padding: 1px 4px 4px 4px;

}

.start .side_icon {

   position: relative;
   padding: 2px;

}

.list {

   text-align: left;
   border-radius: 4px 0px 0px 4px;
   cursor: default;
   margin: 2px 0px;
   width: 48.87708333333334%;
   height: 35px;

}

.list .contmenu {

   position: relative;
   float: right;
   display: none;

}

.list .numb {

   float: left;
   padding-top: 2px;
   font-size: 60%;
   min-width: 38px;
   text-align: center;

}

.list .icon {

   margin: 0px 2px;
   padding-right: 2px;
   float: left;

}

.list .title {

   display: inline-block;
   float: left;
   position: relative;
   white-space: nowrap;

}

.listsearch {

   background-color: #ddd;
   color: #333;
   overflow: hidden;
   border-radius: 4px;
   padding: 3px;
   margin-top: 2px;
   font-size: 85%;
   width: 90%;
   height: 68%;

}

.infolinktitle {

   height: 63%;
   font-size: 80%;
   overflow: hidden;

}

.infolink {

   padding-left: 3px;
   font-size: 11px;

}

.default .contmenu {

   display: none;

}

.selected .contmenu {

   display: block;

}

.html {

   position: relative;
   display: inline-block;
   vertical-align: top;

}

.hlink {

   position: relative;
   border: 0px solid transparent;
   margin: 0px 4px;
   border-radius: 4px;
   display: inline-block;
   overflow: hidden;

}

.hlink .title {

   overflow: hidden;
   max-width: 310px;
   font-size: 90%;
   padding: 0px 4px;
   float: left;
   height: 28px;
   text-decoration: underline;

}

.hlink .icon {

   float: left;
   height: 22px;
   padding-top: 5px;

}

.fulleditline {

   border: 1px solid transparent;
   width: 80%;
   margin: 1px 9%;
   border-radius: 4px;
   display: inline-block;
   overflow: hidden;

}

.fulleditline .title {

   overflow: hidden;
   font-size: 24px;
   height: 24px;
   margin: 3px;
   color: #2b2525;
   background-color: #eee;
   padding: 4px;

}

.fulleditline .icon {

   float: right;
   height: 22px;
   padding-top: 2px;

}

.label {

   position: relative;
   text-align: left;
   border-radius: 4px;
   margin: -2px 4%;
   font-size: 80%;
   width: 92%;
   display: inline-block;
   overflow: hidden;
   height: 26px;

}

.label .icon {

   float: left;
   height: 20px;

}

.default {

   background: none;

}

.selected {

   background: rgba(180, 180, 180, 0.7);

}

.listselected {

   color: black;

}

.site {

   height: 100%;
   width: 100%;
   overflow-y: auto;
   overflow-x: hidden;
   font-size: 27px;

}

body {

   color: rgb(238, 238, 238);
   margin: 0px;
   padding: 0px;
   height: 100%;
   width: 100%;
   overflow: hidden;
   background-color: transparent;

}

small {

   font-size: 70%;
   color: gray;

}

.buttons {

   position: fixed;
   top: 80%;
   left: 50%;
   width: 47%;
   margin: 2px;
   display: none;
   padding: 0px 3px 0px 3px;
   color: #cccccc;
   font-size: 80%;

} \#description {

   top: 0px;
   position: fixed;
   margin: 2px;
   overflow-x: hidden;
   overflow-y: auto;
   display: none;
   padding: 0px 3px 0px 3px;
   font-size: 28px;
   left: 51%;
   width: 47.2%;
   word-break: break-word;

}

Пример своего стиля (пишите чистый css, он будет преобразован в одну строку)

Если строка стилей очень большая, их лучше вынести в отдельный css файл

$_PL["link"][]=["type"=>"text/css","href"=>"http://example.com/main.css"];

#content - стиль страницы (без description)

$_PL["css"]

#rightHalf- стиль description

$_PL["css"]="#content {font-size:25px;} #rightHalf{font-size:10 px;} "; // Установим размер шрифта сайта и описания

.selected - стиль при выделении элемента

$_PL["css"]=" .selected { color: black; } "; // Делаем смену цвета выделенного элемента

.default - стиль невыделенного элемента

$_PL["css"]=" .default{ color: gray; } ";

.contmenu - визуальная кнопка контекстного меню

$_PL["css"]=" .contmenu{ display:none; } "; // Скрываем кнопку контекстного меню

.label, .start, .list, .fulleditline, .hlink, .html - стили(верхний уровень) для разных типов элементов $_CH["position"] [1]

Если не задан $_CH["position"] элемента то по умолчанию элементы отображаются на странице списком и классом .list

Если при этом задано отображение плиткой ($_PL["typeList"]="start" ) то по умолчанию элементы отображаются с классом .start

.labelselected, .startselected, .listselected, .fulleditlineselected, .hlinkselected, .htmlselected - стили(верхний уровень) для выделенных элементов

Установим цвет для выделенных элементов только .list

$_PL["css"]=" .listselected{color: red; }";

.labeldefault, .startdefault, .listdefault, .fulleditlinedefault, .hlinkdefault, .htmldefault - стили(верхний уровень) для невыделенных элементов

.list - зададим свой вид списка

$_PL["css"]=" .list{color: red; }"; // Меняем цвет

.title .icon .rating .numb вложенные в верхний уровень стили заголовка, иконки, рейтинга(для .start), порядкового номера(для .list)

$_PL["css"]=" .title{color: red; }"; // Меняем цвет заголовков

$_PL["css"]=" .numb { display:none; } "; // Скрываем отображение номера

Комбинация классов, зададим стиль иконок только для списка (.list)

$_PL["css"]=" .list .icon{background-color:white; margin:2px;}"; // Зададим белую рамку фона для иконок


$_PL["cacheinfo"]="nocache"; // Не кешировать страницу (при возврате назад страница будет грузиться заново по адресу)

$_PL["setcookie"]["name1"]="value1"; // Аналог cookie вебсайтов. Запомнит для последующих запросов страниц с этого же домена в переменной $_GET["cookie"]["name1"] значение value1

$_PL["info"]="Вам уведомление"; // Показывает alert "Вам уведомление" с кнопкой ОК (закрыть)

$_PL["confirm"]=["title"=>"Открыть вложенный CHannel?","channel"=>["playlist_url"=>"http://.."]];

4.2. Поведение страницы (cmd, ping, служебные теги)

cmd тег (как глобальный так и в playlist_url) - допустимые команды

Примеры использования cmd в глобальном теге

$_PL["cmd"]="historyback(1);" // Вернуться назад на 1 страницу

$_PL["cmd"]="info(Вам уведомление);" // Показывает alert "Вам уведомление" с кнопкой ОК (закрыть)

$_PL["cmd"]="settitle(2, Новый заголовок);" // элементу 2 дать новый title

$_PL["cmd"]="setdescription(2, Новый заголовок);" // элементу 2 дать новый description

$_PL["cmd"]="reload(2);" // Перезагрузить страницу через 2секунды

В конец cmd к командам выше можно добавить stop(); чтобы выполнить команду выше и остановить загрузку новой страницы

например $_PL["cmd"]="info(Доступ к этой странице Вам запрещен);stop();" выведет уведомление но оставить пользователя на предыдущей странице.

Примеры использования cmd в $_CH["playlist_url"]

$_CH["playlist_url"]="cmd:<Команда из списка допустимых>";

Например $_CH["playlist_url"]="reload();"; перезагрузит страницу при нажатии

Глобальный тег ping

Для выполнения действий когда они станут доступны. Например в Fork browser можно отслеживать введение кода с телефона и автоматически загружать страницу.

$_PL["ping"]["link"]="http://example.com/check.php?hash";

Если check.php отдаёт пустой ответ, Fork продолжает проверять страницу каждые 6 сек.

Если check.php отдаёт страницу с channels, загружается она, например:

$_PL["channels"]=[["location"=>1,"title"=>"Успешно вошли! Нажмите для переадресации...","playlist_url"=>"http://example.com/"]];

Глобальный тег align

Выравнивание элементов при виде плиткой ($_PL["typeList"]="start"). По умолчанию — по центру.

$_PL["align"]="left";

Глобальный тег url_tvg — своя телепрограмма

Поддерживается только формат xmltv (сжатый и нет).

$_PL["url_tvg"]="http://epg.it999.ru/edem.xml.gz";

В M3U плейлисте аналогичный параметр задаётся в начале:

#EXTM3U url-tvg="http://epg.it999.ru/edem.xml.gz"

Глобальный тег aside для вывода бокового меню

Пример бокового меню Fork

$_PL["aside"]["channels"][]=["title"=>"Home","playlist_url"=>"http://site.com/","style"=>"color:blue;","logo_30x30"=>"https://static.vecteezy.com/system/resources/thumbnails/022/013/913/small/home-icon-illustration-image-vector.jpg"];

$_PL["aside"]["channels"][]=["title"=>"Category 1","playlist_url"=>"http://site.com/cat1","logo_30x30"=>"https://static.vecteezy.com/system/resources/thumbnails/022/013/913/small/home-icon-illustration-image-vector.jpg"];

Изменить стили бокового меню по умолчанию:

$_PL["css"].=""#aside{position:absolute;z-index:1;top:0px;left:0px;height:inherit;border-radius: 0px 10px 10px 0px;padding:0px 10px 0px 2px;font-size: 27px;line-height: 27px;white-space: nowrap;overflow-x: hidden;}

#aside img{width:27px;height:27px;margin-right:8px;}

.aside_default{margin:5px 0px;padding: 4px;border-radius: 8px;border: 2px solid transparent;}

.aside_selected{margin:5px 0px;padding: 4px;border-radius: 8px;border: 2px solid #5590e7;}

.aside_hide{width:30px;background:none;overflow-y: hidden;}

.aside_show{width:auto;overflow-y: auto;background-color:rgba(0, 0, 0, 0.8);}";

Если стили свои, то лучше вынести их в отдельный css файл

Подключения css файлов стилей

$_PL["link"][]=["type"=>"text/css","href"=>"http://example.com/main.css"];

advertising для показа рекламы перед запуском видео

Видеоролик

$_PL["advertising"]=["client"=>"video",
   "title"=>"Реклама",// Надпись внизу с названием или описанием обьявления
   "skip"=>3,//Добавьте skip в секундах когда можно будет нажать кнопку пропуск
   "tag"=>"https://mlb2.adriver.ru/mf/0008908/0008908282/0/720_480.mp4"
];

VAST

XML формат рекламы. Поддерживаются перенаправления, пропуск, отправка событий

$_PL["advertising"]=["client"=>"vast",
   "title"=>"Реклама",
   "tag"=>"https://raw.githubusercontent.com/InteractiveAdvertisingBureau/VAST_Samples/master/VAST%203.0%20Samples/Inline_Companion_Tag-test.xml" 
];

В настройках форка для тестирования включите Ads: always

5. Справочник тегов channel ($_CH)

Каждый объект в channels[] — элемент списка. Ниже — теги в порядке от базовых к расширенным.

Уровень Тег Назначение
Базовый title Заголовок элемента (обязательно)
Базовый logo_30x30 URL иконки 30×30
Базовый playlist_url Переход на другую страницу
Базовый stream_url Запуск видеоплеера
Базовый description Текст справа (режим списка) или в шаблоне
Средний position, br, template Вид и вёрстка элемента
Средний menu, confirm Меню и диалоги
Средний location Переадресация (1 — заменить URL, 3 — временная)
Средний before, after HTML до/после элемента
Плеер poster, label, information Подписи в видеоплеере
Плеер subtitles, event, start_time Субтитры, события, старт с позиции
Расширенный parser, cors, iframe Загрузка и встраивание данных
Расширенный SetTimeInterval Периодические запросы к серверу

5.1. Основные теги

title

Заголовок элемента. Отображается в списке и используется как fallback для label в плеере.

playlist_url или stream_url

Адрес страницы или адрес видеопотока. Непустым может быть только один из этих тегов.

Параметры ссылок #direct и #stream_url

Добавляются в конец ссылки:

#direct — если сайт требует чистой ссылки без добавлений Fork (параметры, cookie, идентификатор):

http://pastebin.com/yuUtYu56g#direct → откроется как http://pastebin.com/yuUtYu56g

#stream_url — ссылка откроется сразу в видеоплеере.

AddFavorite и AddSearch

Команды в playlist_url для добавления портала в закладки или глобальный поиск:

$_CH[]=["logo_30x30"=>"none","title"=>"Добавить этот портал в закладки / стартовое меню","playlist_url"=>"AddFavorite(Кинопаб,https://kino.pub/images/logo.png,http://195.88.208.101/kinopub/);"];

$_CH[]=["logo_30x30"=>"none","title"=>"Добавить этот портал в Глобальный поиск","playlist_url"=>"AddSearch(Кинопаб,https://kino.pub/images/logo.png,http://195.88.208.101/kinopub/?cat=search);"];

(пример всей страницы на PHP: Кинопаб)

iframe — вставка содержимого другой страницы

На место элемента списка вставляется содержимое из ссылки. Можно использовать для поиска по нескольким плейлистам (глобальный поиск).

$_CH[]=["title"=>"Поиск Terminator в filmix.red","playlist_url"=>"https://filmix.red/fork/search?search=Terminator","iframe"=>"4","timeout"=>8];

iframe — обязательный параметр. Число — сколько ссылок из дочерней страницы показать сразу; остальное будет внизу списка. "iframe"=>"0" — показать все.

timeout — таймаут ожидания содержимого из дочерней ссылки (секунды).

Альтернативный playlist_url2, время ожидания таймаута, способы получения

Можно указать запасной источник страницы, время ожидания таймаута, способы получения

<?php
$_CH[]= [
   "title" => "Каталог",
   "playlist_url" => "http://domain1.in.net/",
   "playlist_url2" => "http://domain2.in.net/",
   "playlist_url3" => "http://domain3.in.net/",
   "cors" => "directnoheader", // Тип получения страницы(напрямую без заголовков). Если не указать то будет перебор direct, directnoheader, remotefork и потом альтернативный источник
   "timeout" => 4, // В секундах
   "error" => "http://domainstat.in.net//err.php?info=%INFO" // Адрес куда отправлять ошибку если все страницы не откроются
];


stream_url мультиссылки с разными качествами и (или) несколькими источниками, Err format json link

Для видеопотока можно задавать мультиссылки с разным качеством (можно и разные озвучки, но это будет не так явно для пользователя), выбор качества будет браться автоматически с настроек Fork(1080 или 720 и т.д.) и (или) несколькими источниками

Пример использования в PHP с json_encode В данном примере создается массив с различными ссылками на видеопотоки, и с помощью функции json_encode он преобразуется в JSON-формат для последующего использования:

<?php
// Массив с данными для разных качеств видео и двумя источниками
$stream_links = [
   "360" => [
       "title" => "360p",
       "url" => [
           "http://server.in/240.mp4",
           "http://server2.in/hls/240.mp4"
       ]
   ],
   "720" => [
       "title" => "720p",
       "url" => [
          "http://server.in/720.mp4",
          "http://server2.in/hls/720.mp4"
       ]
   ]
];
// Преобразование массива в JSON
$json_stream_url = json_encode($stream_links);
// Использование JSON в $_CH["stream_url"]
$_CH["stream_url"] = $json_stream_url;
// Вывод JSON для проверки
echo $_CH["stream_url"];
?>


Пример с несколькими источниками:

<?php
$stream_links = [
           "http://server.in/240.mp4",
           "http://server2.in/hls/240.mp4"
       ];
// Преобразование массива в JSON
$json_stream_url = json_encode($stream_links);
// Использование JSON в $_CH["stream_url"]
$_CH["stream_url"] = $json_stream_url;
// Вывод JSON для проверки
echo $_CH["stream_url"];
?>

Поддержка ссылок Яндекс.Диска

Ссылка должна быть в поле stream_url или в m3u плейлисте в формате https://yadi.sk/i/idfile

playlist — похожие видео после окончания (YouTube)

<?php $PLAYLIST=[]; $PLAYLIST[]=["title"=>"Похожее видео 1","stream_url"=>"http://www.youtube.com/watch?v=ZNLZla2xHUQ"]; $PLAYLIST[]=["title"=>"Похожее видео 2","stream_url"=>"http://www.youtube.com/watch?v=ZNLZla2xHUQ"]; $_CH[]=["title"=>"Основное видео","stream_url"=>"http://www.youtube.com/watch?v=xhFCmwrSxCU","playlist"=>$PLAYLIST]; $_PL["channels"]=$_CH; print json_encode($_PL); ?>

logo_30x30

URL иконки элемента (рекомендуемый размер 30×30 px). Используется также как fallback для poster в плеере.

5.2. Видеоплеер

Теги видеоплеера poster, label, information

Нужны чтобы по-разному выдавать информацию на сайте и в видеоплеере.

$_CH["title"]="Хало 1 сезон 1 серия"; $_CH["label"]="Хало"; $_CH["information"]="1 сезон 1 серия";

Вид экрана плеера

Плеер сначала ищет значение в специализированном теге; если он пустой — берёт из общего тега.

Картинка в видеоплеере слева внизу

$_CH["poster"]="http://example.com/videoposter.png";

если пустой — используется logo_30x30.

Название над полосой прогресса

$_CH["label"]="Хало";

если пустой — используется title.

Информация под прогрессом

$_CH["information"]="1 сезон 1 серия";

если пустой — используется description.

Тег subtitles — субтитры

$_CH["subtitles"][0]=["UA","https://tortuga.wtf/player/subtitle/18625_ua.vtt"]; $_CH["subtitles"][1]=["EN","https://tortuga.wtf/player/subtitle/18625_en.vtt"]; $_CH["subtitles"][2]=["RU","https://tortuga.wtf/player/subtitle/18625_ru.vtt"];

event — события видеоплеера

Отправка на сервер событий при старте и остановке видео:

$_CH[]=["logo_30x30"=>"hidden","title"=>"Video","stream_url"=>"http://...","event"=>["onstartvideo"=>"$siteurl/?event=onstartvideo&videoid=1","onstopvideo"=>"$siteurl/?event=onstopvideo&curTime=[curTime]&totalTime=[totalTime]&videoid=1"]];

[curTime] и [totalTime] заменяются на время остановки и общую длительность видео в секундах.

start_time — начало воспроизведения

$_CH[]=["logo_30x30"=>"hidden","title"=>"Video","stream_url"=>"http://...","start_time"=>340];

start_time — время в секундах. Предлагается на кнопке Play только если пользователь ранее не смотрел это видео; иначе нужно отметить видео непросмотренным в Меню / Отметить непросмотренным.

5.3. Интерактивные элементы

confirm - диалоговое окно с действиями при нажатии на элемент

Пример подтверждения выхода с аккаунта

$_CH[]=["title"=>"Выйти","playlist_url"=>"confirm","confirm"=>["http://host/?do=exit"],"description"=>"Выйти с аккаунта?"];

menu — контекстное меню элемента channel

Массив $_CH["menu"] задаёт контекстное меню одного элемента списка (channels[]). Каждый пункт меню — мини-channel: title, playlist_url (или stream_url) и опциональные поля оформления.

Тег Область Назначение
$_CH["menu"] Элемент списка Меню конкретного channel (качество, реакция, настройка строки)
$_PL["menu"] Страница целиком Глобальная полоса меню вверху страницы (зеркала, навигация портала)

menu — быстрый старт

Минимальное вертикальное popup-меню по OK на channel:

$menu = [];
$menu[] = ["title" => "Пункт 1", "playlist_url" => "http://example.com/1"];
$menu[] = ["title" => "Пункт 2", "playlist_url" => "http://example.com/2"];

$_CH["title"]  = "Настройки";
$_CH["playlist_url"] = "menu";   // обязательно для открытия меню
$_CH["menu"]    = $menu;

Обязательные условия:

  • у channel: "playlist_url": "menu";
  • массив menu с одним и более пунктов;
  • каждый пункт — обычный channel (URL, cmd:..., submenu и т.д.).

menu — режимы отображения

Поведение определяется комбинацией menu[0].type и наличия $menu в $_CH["template"].

Режим Условие Вид Как открыть
Popup (вертикальный) нет inline в type Список поверх страницы OK на channel с playlist_url":"menu"
Inline в строке type содержит inline + в template есть $menu Горизонтальный ряд кнопок внутри элемента Кнопки видны сразу; popup не открывается
Inline popup inline + в template нет $menu Горизонтальный popup OK на channel; навигация ←/→

Схема подключения inline в template:

  1. $_CH["position"] = "html";
  2. В template — плейсхолдер $menu
  3. Заполнить menu[]
  4. У первого пункта: "type": "inline" (и при необходимости |nocancel)

menu — опции type

Поле type задаётся только у первого пункта menu[0]. Несколько опций комбинируются через | (порядок не важен):

"type": "inline"
"type": "inline|nocancel"
"type": "nocancel"
Значение Описание
inline Горизонтальное меню: в строке ($menu в template) или горизонтальный popup
nocancel Не добавлять пункт «Отмена» в popup; закрытие — кнопкой Назад

Примечание: при inline первый пункт меню — обычная кнопка (например «1080p»), а не пустой служебный маркер.

menu — навигация

Контекст Клавиши Действие
Popup (вертикальный) ↑ / ↓ Перемещение между пунктами
Popup OK Выбор пункта
Popup Назад Закрыть меню (без перехода по истории)
Inline popup ← / → Перемещение между пунктами
Inline в строке ($menu) OK / клик Выбор пункта напрямую
Inline в строке ← / → Фокус по горизонтальному ряду (если меню в popup после OK)

Поле default — начальный фокус при первом открытии меню:

"menu": [
  {"title": "Отлично", "playlist_url": "..."},
  {"title": "Нормально", "default": 1, "playlist_url": "..."},
  {"title": "Плохо", "playlist_url": "..."}
]
  • у пункта с "default": 1 (или любое истинное значение) курсор ставится при открытии;
  • если ни у одного пункта нет default — выделяется первый (индекс 0);
  • работает в popup-меню и во вложенном submenu;
  • пункт «Отмена» в фокус по default не попадает.

Поле hint — подсказка у пункта в popup: появляется через 1 с после фокуса, скрывается через 5 с (жёлтый tooltip у элемента).

menu — встраивание $menu

Пример JSON (переключатель качества):

{
  "title": "Качество",
  "position": "html",
  "playlist_url": "menu",
  "template": "<div class='setting-item'><div class='setting-title'>$title</div><div class='chmenu'>$menu</div></div>",
  "menu": [
    {
      "type": "inline|nocancel",
      "title": "1080p",
      "logo_30x30": "http://example.com/icons/hd.png",
      "playlist_url": "http://example.com/?q=1080",
      "default": 1,
      "class": "pill active",
      "hint": "Full HD",
      "template": "<div><img src=$logo_30x30> $title</div>"
    },
    {"title": "720p", "playlist_url": "http://example.com/?q=720", "class": "pill"},
    {"title": "480p", "playlist_url": "http://example.com/?q=480", "class": "pill"}
  ]
}

Пример PHP:

<?php
$_CH["title"] = "Качество";
$_CH["position"] = "html";
$_CH["playlist_url"] = "menu";
$_CH["template"] = '<div class="setting-item">'
  . '<div class="setting-title">$title</div>'
  . '<div class="chmenu">$menu</div></div>';

$_CH["menu"] = [
  [
    "type" => "inline|nocancel",
    "title" => "1080p",
    "default" => 1,
    "logo_30x30" => "http://example.com/icons/hd.png",
    "playlist_url" => "http://example.com/?q=1080",
    "class" => "pill active",
    "template" => '<div><img src=$logo_30x30> $title</div>'
  ],
  ["title" => "720p", "playlist_url" => "http://example.com/?q=720", "class" => "pill"],
  ["title" => "480p", "playlist_url" => "http://example.com/?q=480", "class" => "pill"]
];

$_PL["css"] = ".pill{border-radius:999px;padding:4px 14px;margin:0 4px;display:inline-block;}"
  . ".pill.active{background:#3366cc;color:#fff;}";
$_PL["channels"][] = $_CH;
print json_encode($_PL);
?>

При $menu в template + type:inline повторный popup по OK не открывается.

menu — справочник полей

Поле Тип Описание
title string Текст пункта (обязательно для отображения)
playlist_url string URL или команда при выборе (cmd:..., submenu и др.)
stream_url string Альтернатива playlist_url для видеопотока
type string inline, nocancel
default int/bool 1 — пункт выделен при первом открытии меню
logo_30x30 string URL иконки пункта
class string CSS-класс обёртки (по умолчанию menucontext)
style string Inline-стили обёртки пункта
img_style string Inline-стили иконки (если нет своего template)
template string Свой HTML тела пункта (как у channel)
before / after string HTML до / после пункта
hint string Подсказка при фокусе в popup (1 с задержка, 5 с показ)
menu array Вложенное подменю (см. Подменю)

menu — плейсхолдеры template

Используются в template пункта menu (не путать с $menu в template channel):

Плейсхолдер Подставляется
$title title пункта
$description description пункта
$logo_30x30 URL иконки (для атрибута src)
$logo Готовый тег <img>
$class Значение class пункта
$index Индекс пункта в menu (с 0)

Плейсхолдер channel: $menu — в $_CH["template"]; подставляется HTML всех пунктов inline-меню.

menu — подменю

Пункт с вложенным списком через playlist_url":"submenu" и массив submenu:

{
  "menu": [
    {
      "type": "inline",
      "title": "Ещё ▾",
      "playlist_url": "submenu",
      "submenu": [
        {"title": "Обновить", "playlist_url": "cmd:reload();"},
        {"title": "Настройки", "default": 1, "playlist_url": "http://example.com/settings"}
      ]
    }
  ]
}

Во встроенном $menu клик по такому пункту открывает подменю; для пунктов submenu также поддерживается default.

menu — ответ сервера cmd без перехода

Если пункт menu ведёт на URL, сервер может вернуть JSON с командой вместо новой страницы — пользователь остаётся на текущем списке, история не растёт.

{"cmd": "seticon([index],http://example.com/icon.svg);stop();"}
Команда Назначение
seticon(индекс, url) Обновить logo_30x30 / icon channel; [index] — текущий выделенный элемент
settitle(индекс, текст) Изменить заголовок channel по индексу
stop() Прервать загрузку «новой страницы»; остаться на текущей (обязательно в конце цепочки cmd)

Типичный сценарий (реакция, переключатель без перезагрузки):

  1. Пункт menu → playlist_url с параметром действия (например &react=1)
  2. Сервер сохраняет данные и отвечает: {"cmd":"seticon([index],URL_иконки);stop();"}
  3. Fork обновляет иконку строки; кнопка Назад сразу уходит на предыдущую страницу (без «лишнего» шага)

См. также глобальный тег cmd и stop(); в $_PL["cmd"].

menu — стилизация

  • Оформление пунктов — через class, style, template каждого пункта.
  • Общие стили (pill, tab, badge) — $_PL["css"] или внешний файл $_PL["link"][].
  • Длинное inline-меню прокручивается по горизонтали (overflow-x).

menu — совместимость

Channel с menu без type:inline и без $menu в template работает как раньше: вертикальный popup по playlist_url":"menu", пункт «Отмена» внизу (если нет nocancel).

5.3. Навигация и оформление channel

location

$_CH["location"]=1; // Переход c заменой текущей ссылки окна на новую (при перезапуске форкплеера будет открываться уже с этой новой ссылки)

$_CH["location"]=3; // Переход с отображением в текущем окне (при перезапуске форкплеера будет открываться старая ссылка и опять переадресовываться, необходимо если например используются устаревающие сессии)

after и before - свой html код после и перед элементом списка

$_CH["before"]="<div style='color:red;'>Текст перед элементом</div>";

$_CH["after"]="<div style='color:red;'>Текст после элемента</div>";

5.4. Загрузка данных и расширенные ссылки

Локальные переменные LOCAL_IP, TORRSERVE_IP, ACE_IP

В URL портала подставляются значения из настроек ForkPlayer на устройстве пользователя.

Если не указан TORRSERVE_IP или ACE_IP — вместо них подставляется LOCAL_IP (устройство с RemoteFork).

Если не указан также LOCAL_IP — подставляется 127.0.0.1.

Примеры в magnet-ссылках

$_CH[]=["title"=>"(magnet) acestream","playlist_url"=>"http://ACE_IP:6878/server/api?method=get_media_files&magnet=".urlencode("magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1")];

$_CH[]=["title"=>"(magnet) torrserve","playlist_url"=>"http://TORRSERVE_IP:8090/torrent/play?m3u=true&link=".urlencode("magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1")];

Автовыбор torrserve или ace stream

$_CH[]=["title"=>"(magnet) через ace stream или torrserve","playlist_url"=>"magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1"];

SetTimeInterval — периодические запросы к серверу

$_CH["SetTimeInterval"]=["time"=>1,"onmenu"=>"http://19onmenu","onplay"=>"http://19onplay"];

time - интервал между отправкой событий в минутах

onmenu - ссылка для отправки событий если пользователь находится в списке

в ответ по ссылке onmenu http://193.30.240.2/try/ajax/send_status_log.php?action=onmenu можно давать json

{"message":"Hello user"} 

Это уведомление будет выводится вверху форкплеера

onplay- ссылка для отправки событий если пользователь смотрит видео

Можно использовать один или два сразу тега onmenu и onplay

parser - тег загрузки другой страницы перед переходом по playlist_url или stream_url

Пример в XML

<parser><![CDATA[http://www.youtube.com/watch?v=qZ3xj_UF4I8|js=|;]]></parser>

Пример в JSON FXML

$_CH["parser"]="http://www.youtube.com/watch?v=qZ3xj_UF4I8|js=|;";

результат отдастся то что между js= и ;

Регулярное выражение (по маркеру .*? )

<parser><![CDATA[http://www.youtube.com/watch?v=qZ3xj_UF4I8|<script>.*?js=|;]]></parser>

или

<​​​​​​​parser><![CDATA[http://www.youtube.com/watch?v=qZ3xj_UF4I8|js=|.*?js]]><​​​​​​​/parser>

преобразуется в RegExp("<script>.*?js=(.*?);","i") - результат отдастся тот что в ()

<channel><title>Test </title>
 <playlist_url><![CDATA[http://parser.co/#POSThtml=md5hash]]></playlist_url>
 <​​​​​​​parser><![CDATA[http://www.youtube.com/watch?v=qZ3xj_UF4I8]]><​​​​​​​/parser>
</channel>

#POST в playlist_url означает что все что после него будет передавать методом POST

В примере выше на адрес http://parser.co/ отправятся POST данные html=md5hash, где md5hash заменятся на полученное от parser

Допустимо в parser использовать curl запросы (доступны на андроид, частично если форк установлен в память ТВ, или через ремотeфорк)

Можно перечислить порядок методов запроса парсер, например <cors>rf|android|direct|directnoheader</cors>

Сначала будет попытка запроса через remotefork (если включен), потом через андроид класс если Fork запущен на андроиде, потом прямой XHR запрос с попыткой установить заголовки, потом прямой XHR запрос без заголовков

<channel>
 <title>Test curl and method</title>
 <cors>rf|android|direct|directnoheader</cors>
 <playlist_url><![CDATA[http://parser.co/#POSThtml=md5hash]]></playlist_url>
 <parser><![CDATA[curl "https://mysite.com/" -H "Accept-Encoding: deflate" -H "Connection: keep-alive" -H "DNT: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"]]>
 </parser>
</channel>

Или в JSON

{
 "channels": [
   {
     "title": "Test curl and method",
     "cors": "rf|android|direct|directnoheader",
     "playlist_url": "http://parser.co/#POSThtml=md5hash",
     "parser": 'curl "https://mysite.com/" -H "Accept-Encoding: deflate" -H "Connection: keep-alive" -H "DNT: 1" -H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"'
   }
 ]
}

Результат будет в $_POST['html']

Результат будет аналогично md5hash в $_GET['page']

Методом POST для видеоссылок ( stream_url )

<channel> 
 <title>stream post</title>
 <stream_url><![CDATA[http://test.ru/index.php#POSTs=md5hash]]></stream_url>
 <parser><![CDATA[https://yadi.sk/d/0BpgziSQ0VWiFg]]></parser>
</channel>

Будет идти отправка POST на http://test.ru/index.php#POSTs=md5hash в $_POST["s"]

Ответ страницы попадет в проигрыватель в виде ссылки.

Тоесть http://test.ru/index.php должен дать текст ссылки на медиафайл вида http://.../myvideo.mp4

Скачивание сторонней страницы по необходимости, страница http://parser.co/ может дать json ответ

{"parse":"http://www.youtube.com/watch?v=qZ3xj_UF4I8&gl=US&hl=en&has_verified=1&bpctr=9999999999"}

Скачанная страница шлется в параметре $_POST['remoteparse']

6. Оформление и вёрстка элементов

position при заданном $_PL["typeList"]="start";

При $_PL["typeList"]="start" каждый channel может задать свой вид через position:

$_CH["position"]=""; // - Плитка как в Стартовом меню размером 128х101px)

$_CH["position"]="html"; // - Пример элемента со своим дизайном в $_CH["template"]

$_CH["position"]="html";

$_CH["br"]=0; // Не переносить навигацию на новую строку

$_CH["title"]="Новый вид";

$_CH["description"]="Описание вида";

$_CH["logo_30x30"]="http://p.lnka.ru/icons/yapfiles.png";

$_CH["template"]='<div style="width:242px;overflow:hidden;margin:4px;text-align:center;"><img src="$logo_30x30" style="width:242px;height:171px;padding:2px 2px 0px 2px;"><br>$title<br><small>$description</small></div>';

Элементы будут выстраиваться в ряд и навигация по ним будет предполагать что они в одном ряду

$_CH["br"]

В месте где навигация должна перейти на новую строку задайте $_CH["br"]=1; При этом в before будет добавлено значение <br clear=both>

$_CH["position"]="fulleditline"; //- Поле ввода текста шириной почти на всю страницу

$_CH["position"]="hlist"; // - горизонтальная ссылка (может быть несколько на одном горизонтальном уровне)

$_CH["position"]="list"; // -обычный вид списка шириной в половину экрана и с description справа

$_CH["position"]="label"; // -невысокая строка на всю ширину экрана

$_CH["position"]="bigtile"; // - Плитка увеличенной в 2 раза высоты (128х215px)

description — прокрутка части большого текста

Добавьте в описание div с id scrolled — прокрутка кнопками PG_UP / PG_DOWN:

$_CH["description"]='MY TITLE STATIC<div id="scrolled">

PG_UP PG_DOWN

large scrolled content

.

.

.

</div>

MY FOOTER STATIC';

6.1. Произвольная вёрстка (position=html, template, br)

Пошаговая схема для своего дизайна плитки:

  1. $_PL["typeList"]="start";
  2. Задать CSS в $_PL["css"]
  3. Для каждого channel: position="html", template, при необходимости br

Прописываем свои стили

$_PL["css"]="

.myStyle1{

width:242px;

overflow:hidden;

margin:4px;

text-align:center;

}

.myStyle2{ width:242px; height:171px; padding:2px 2px 0px 2px; }

";

Задаем шаблон (template) каждому элементу списка

$_CH["position"]="html"; // Задает возможность использовать template

$_CH["title"]="Новый вид"; // Заголовок - подставляет в шаблоне вместо $title

$_CH["logo_30x30"]="http://p.lnka.ru/icons/yapfiles.png"; // Иконка $logo_30x30

$_CH["description"]="Описание, если нужно"; // Описание- подставляет в шаблоне вместо $description

$_CH["template"]='<div class="myStyle1"><img src="$logo_30x30" style="myStyle2"><br>$title</div>';

Перенос на новую строке при html дизайне $_CH["br"]=1;

Для правильной навигации по странице нужно знать когда у вас там новая строка. Для этого в элементе с которого нужно чтоб начиналась новая строка присваиваем

$_CH["br"]=1;// Этот элемент разместится уже на новой строке

coordination (устаревший способ навигации)

До появления тега br навигацию по произвольному шаблону задавали координатами. Обязательно при position=html и своём template.

$_CH["coordination"]=[x,y];

Где x — позиция по горизонтали, y — по вертикали. Первый элемент: [0,0]. Элемент на новой строке: [1,0].

6.2. Горизонтальная прокрутка (nowrap)

Для страниц с typeList=html или position=html. Задайте nowrap=1 только первому элементу блока (у него же br=1):

$_CH=["br"=>1,"nowrap"=1,"title"=>"Первый элемент горизонтального блока прокрутки"];

$_CH=["title"=>"Второй элемент горизонтального блока прокрутки"];

$_CH=["title"=>"Третий элемент горизонтального блока прокрутки"];

...

$_CH=["title"=>"Последний элемент горизонтального блока прокрутки"];

$_CH=["br"=>1,"title"=>"Новый элемент с новой строки"];

7. Формирование ответа на сервере

7.1. Параметры GET от клиента

При открытии портала ForkPlayer добавляет к запросу идентификаторы устройства.

Параметр Описание
box_mac Виртуальный MAC-адрес устройства
box_user Email пользователя (если выполнен вход в аккаунт Fork)

Пример: http://nserv.host/?box_mac=your_mac_address&box_user=your_forkplayer_tv@email

7.2. Шаблон вывода JSON (PHP)

<?php
// $_PL — глобальные теги страницы
// $_CH — массив channel; каждый элемент — ассоциативный массив тегов

$_PL["channels"] = $_CH;

header("Content-Type: application/json; charset=utf-8");
print json_encode($_PL, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
?>

8. Связанные материалы

  • FXMLbaseparser — PHP-класс, упрощающий генерацию FXML
  • CHANGELOG — история изменений протокола
  • Пример портала на PHP
  • FXML CMS — визуальный редактор порталов
  • Исходный код любой страницы можно просмотреть в ForkPlayer (см. скриншот выше)