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

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




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


https://github.com/vengo634/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>.


и FXML CMS
{| 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> || Уведомление или диалог при загрузке || ниже
|}


<br />
=== 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["typeList"]="start"; // Плиточный + другой(с использованием $_CH["position"] ) виды страницы


====Стили сайта по умолчанию====
====Стили сайта по умолчанию====
Строка 33: Строка 140:
<div class="mw-collapsible-content">
<div class="mw-collapsible-content">


.start {
::-webkit-scrollbar-button:single-button {
    background-color: #bbbbbb;
    display: block;
    border-style: solid;
    height: 10px;
    width: 10px;
}


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


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


text-align: center;
::-webkit-scrollbar {
    width: 10px;
}


display: inline-block;
::-webkit-scrollbar-track {
    background: #ddd;
}


overflow: hidden;
::-webkit-scrollbar-thumb {
    background: #888;
}


text-align: center;
::-webkit-scrollbar-thumb:hover {
 
    background: #777;
margin-left: 4px;
}
 
margin-top: 1px;
 
height: 101px;
 
width: 10%;


.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 {
.start .rating {
 
    position: absolute;
position: absolute;
    display: none;
 
    text-align: left;
display: none;
    height: 23px;
 
    width: 28px;
text-align: left;
    padding-top: 5px;
 
    padding-left: 5px;
height: 23px;
    font-size: 11px;
 
width: 28px;
 
padding-top: 5px;
 
padding-left: 5px;
 
font-size: 11px;
 
}
}


.start .icon {
.start .icon {
 
    padding: 2px 2px 0px 2px;
padding: 2px 2px 0px 2px;
    height: 76%;
 
height: 76%;
 
}
}


.start .name {
.start .name {
 
    font-size: 17px;
font-size: 17px;
    line-height: 0.9;
 
    position: absolute;
line-height: 0.9;
    bottom: 0px;
 
    width: 100%;
position: absolute;
    max-height: 50%;
 
bottom: 0px;
 
width: 100%;
 
max-height: 50%;
 
}
}


.start .title {
.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;
}


background: rgba(0, 0, 0, 0.65);
.start .side_icon {
    position: relative;
    padding: 2px;
}


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


width: auto;
.list .contmenu {
    position: relative;
    float: right;
    display: none;
}


display: inline-block;
.list .numb {
    float: left;
    padding-top: 2px;
    font-size: 60%;
    min-width: 38px;
    text-align: center;
}


margin: 2px;
.list .icon {
    margin: 0px 2px;
    padding-right: 2px;
    float: left;
}


padding: 1px 4px 4px 4px;
.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;
}


.start .side_icon {
.infolink {
    padding-left: 3px;
    font-size: 11px;
}


position: relative;
.default .contmenu {
    display: none;
}


padding: 2px;
.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;
}


.list {
.hlink .title {
    overflow: hidden;
    max-width: 310px;
    font-size: 90%;
    padding: 0px 4px;
    float: left;
    height: 28px;
    text-decoration: underline;
}


text-align: left;
.hlink .icon {
    float: left;
    height: 22px;
    padding-top: 5px;
}


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


cursor: default;
.fulleditline .title {
    overflow: hidden;
    font-size: 24px;
    height: 24px;
    margin: 3px;
    color: #2b2525;
    background-color: #eee;
    padding: 4px;
}


margin: 2px 0px;
.fulleditline .icon {
    float: right;
    height: 22px;
    padding-top: 2px;
}


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


height: 35px;
.label .icon {
    float: left;
    height: 20px;
}


.default {
    background: none;
}
}


.selected {
    background: rgba(180, 180, 180, 0.7);
}


.list .contmenu {
.listselected {
    color: black;
}


position: relative;
.site {
    height: 100%;
    width: 100%;
    overflow-y: auto;
    overflow-x: hidden;
    font-size: 27px;
}


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


display: none;
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>


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


float: left;
===Если строка стилей очень большая, их лучше вынести в отдельный css файл===
$_PL["link"][]=["type"=>"text/css","href"=>"http://example.com/main.css"];


padding-top: 2px;
====#content - стиль страницы (без description)====
$_PL["css"]


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


min-width: 38px;
====.selected - стиль при выделении элемента====
$_PL["css"]=" .selected {  color: black; } "; // Делаем смену цвета выделенного элемента


text-align: center;
====.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


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


display: none;
=====Установим цвет для выделенных элементов только .list=====
$_PL["css"]=" .listselected{color: red; }";


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


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


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


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


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




.html {


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


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


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


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


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


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


position: relative;
====Примеры использования '''cmd''' в глобальном теге====
$_PL["cmd"]="historyback(1);"  // Вернуться назад на 1 страницу


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


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


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


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


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


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


====Примеры использования '''cmd''' в $_CH["playlist_url"]====
$_CH["playlist_url"]="cmd:<Команда из списка допустимых>";


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


overflow: hidden;
===Глобальный тег ping===
Для выполнения действий когда они станут доступны. Например в Fork browser можно отслеживать введение кода с телефона и автоматически загружать страницу.


max-width: 310px;
$_PL["ping"]["link"]="<nowiki>http://example.com/check.php?hash</nowiki>";


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


padding: 0px 4px;
Если check.php отдаёт страницу с <code>channels</code>, загружается она, например:


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


height: 28px;
===Глобальный тег align===
Выравнивание элементов при виде плиткой (<code>$_PL["typeList"]="start"</code>). По умолчанию — по центру.


text-decoration: underline;
$_PL["align"]="left";


}
===Глобальный тег url_tvg — своя телепрограмма===
Поддерживается только формат xmltv (сжатый и нет).


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


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


float: left;
<nowiki>#</nowiki>EXTM3U url-tvg="<nowiki>http://epg.it999.ru/edem.xml.gz</nowiki>"


height: 22px;
===Глобальный тег 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"];


padding-top: 5px;
$_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;}


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


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


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


margin: 1px 9%;
.aside_hide{width:30px;background:none;overflow-y: hidden;}


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


display: inline-block;
Если стили свои, то лучше вынести их в отдельный css файл


overflow: hidden;
===Подключения 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"
];


.fulleditline .title {
====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


overflow: hidden;
== 5. Справочник тегов channel ($_CH) ==
Каждый объект в <code>channels[]</code> — элемент списка. Ниже — теги в порядке от базовых к расширенным.


font-size: 24px;
{| 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> || Периодические запросы к серверу
|}


height: 24px;
=== 5.1. Основные теги ===


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


color: #2b2525;
===playlist_url или stream_url===
Адрес страницы или адрес видеопотока. Непустым может быть только один из этих тегов.


background-color: #eee;
====Параметры ссылок #direct и #stream_url====
Добавляются в конец ссылки:


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


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


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


.fulleditline .icon {
====AddFavorite и AddSearch====
Команды в <code>playlist_url</code> для добавления портала в закладки или глобальный поиск:


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


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


padding-top: 2px;
(пример всей страницы на 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];


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


position: relative;
<code>timeout</code> — таймаут ожидания содержимого из дочерней ссылки (секунды).


text-align: left;
====Альтернативный playlist_url2, время ожидания таймаута, способы получения====
Можно указать запасной источник страницы, время ожидания таймаута, способы получения


border-radius: 4px;
<?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" // Адрес куда отправлять ошибку если все страницы не откроются
];


margin: -2px 4%;


font-size: 80%;
====stream_url мультиссылки с разными качествами и (или) несколькими источниками, Err format json link====
Для видеопотока можно задавать мультиссылки с разным качеством (можно и разные озвучки, но это будет не так явно для пользователя), выбор качества будет браться автоматически с настроек Fork(1080 или 720 и т.д.) и (или) несколькими источниками


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


display: inline-block;
<?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"];
?>


overflow: hidden;


height: 26px;
Пример с несколькими источниками:


}
<?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>


.label .icon {
====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);
?>


float: left;
===logo_30x30===
URL иконки элемента (рекомендуемый размер 30×30 px). Используется также как fallback для <code>poster</code> в плеере.


height: 20px;
=== 5.2. Видеоплеер ===


}
===Теги видеоплеера poster, label, information===
Нужны чтобы по-разному выдавать информацию на сайте и в видеоплеере.


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


.default {
[[Файл:Halo.png|мини|Вид экрана плеера]]


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


}
====Картинка в видеоплеере слева внизу====
$_CH["poster"]="<nowiki>http://example.com/videoposter.png</nowiki>";


если пустой — используется <code>logo_30x30</code>.


.selected {
====Название над полосой прогресса====
$_CH["label"]="Хало";


background: rgba(180, 180, 180, 0.7);
если пустой — используется <code>title</code>.


}
====Информация под прогрессом====
$_CH["information"]="1 сезон 1 серия";


если пустой — используется <code>description</code>.


.listselected {
===Тег 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>"];


color: black;
===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> заменяются на время остановки и общую длительность видео в секундах.


.rightHalf {
===start_time — начало воспроизведения===
$_CH[]=["logo_30x30"=>"hidden","title"=>"Video","stream_url"=>"<nowiki>http://...</nowiki>","start_time"=>340];


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


height: 95%;
=== 5.3. Интерактивные элементы ===


overflow-x: hidden;
===confirm - диалоговое окно с действиями при нажатии на элемент===
Пример подтверждения выхода с аккаунта


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


display: none;
===menu — контекстное меню элемента channel===


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


left: 51%;
{| class="wikitable"
! Тег !! Область !! Назначение
|-
| <code>$_CH["menu"]</code> || Элемент списка || Меню '''конкретного''' channel (качество, реакция, настройка строки)
|-
| <code>$_PL["menu"]</code> || Страница целиком || Глобальная полоса меню '''вверху''' страницы (зеркала, навигация портала)
|}


width: 47.5%;
<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>


word-break: break-word;
====menu — быстрый старт====


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


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


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


===Пример своего стиля (пишите чистый css, он будет преобразован в одну строку)===
'''Обязательные условия:'''
* у channel: <code>"playlist_url": "menu"</code>;
* массив <code>menu</code> с одним и более пунктов;
* каждый пункт — обычный channel (URL, <code>cmd:...</code>, <code>submenu</code> и т.д.).


====#content - стиль страницы (без description)====
====menu — режимы отображения====
$_PL["css"]="#content {font-size:25px;}"; // Установим размер шрифта


====#rightHalf- стиль description====
Поведение определяется комбинацией '''<code>menu[0].type</code>''' и наличия '''<code>$menu</code>''' в <code>$_CH["template"]</code>.
$_PL["css"]="#content {font-size:25px;}  #rightHalf{font-size:27 px;} "; // Установим размер шрифта сайта и описания


====.selected - стиль при выделении элемента====
{| class="wikitable"
$_PL["css"]=" .selected {  color: black; } "; // Делаем смену цвета выделенного элемента
! Режим !! Условие !! Вид !! Как открыть
|-
| '''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; навигация ←/
|}


====.default - стиль невыделенного элемента====
'''Схема подключения inline в template:'''
$_PL["css"]=" .default{  color: gray; } ";


====.contmenu - визуальная кнопка контекстного меню====
# <code>$_CH["position"] = "html";</code>
$_PL["css"]=" .contmenu{ display:none; } "// Скрываем кнопку контекстного меню
# В <code>template</code> — плейсхолдер <code>$menu</code>
# Заполнить <code>menu[]</code>
# У первого пункта: <code>"type": "inline"</code> (и при необходимости <code>|nocancel</code>)


====.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]====
====menu — опции type====
Если не задан $_CH["position"] элемента то по умолчанию элементы отображаются на странице списком и классом .list


Если при этом задано отображение плиткой ($_PL["typeList"]="start" ) то по умолчанию элементы отображаются с классом .start
Поле <code>type</code> задаётся '''только у первого пункта''' <code>menu[0]</code>. Несколько опций комбинируются через <code>|</code> (порядок не важен):


====.labelselected, .startselected, .listselected, .fulleditlineselected, .hlinkselected, .htmlselected - стили(верхний уровень) для выделенных элементов====
<pre>"type": "inline"
"type": "inline|nocancel"
"type": "nocancel"</pre>


=====Установим цвет для выделенных элементов только .list=====
{| class="wikitable"
$_PL["css"]=" .listselected{color: red; }";
! Значение !! Описание
|-
| <code>inline</code> || Горизонтальное меню: в строке (<code>$menu</code> в template) или горизонтальный popup
|-
| <code>nocancel</code> || Не добавлять пункт «Отмена» в popup; закрытие — кнопкой '''Назад'''
|}


====.labeldefault, .startdefault, .listdefault, .fulleditlinedefault, .hlinkdefault, .htmldefault - стили(верхний уровень) для невыделенных элементов====
'''Примечание:''' при <code>inline</code> первый пункт меню — обычная кнопка (например «1080p»), а не пустой служебный маркер.


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


====.title .icon .rating .numb вложенные в верхний уровень стили заголовка, иконки, рейтинга(для .start), порядкового номера(для .list)====
{| class="wikitable"
$_PL["css"]=" .title{color: red; }"; // Меняем цвет заголовков
! Контекст !! Клавиши !! Действие
|-
| Popup (вертикальный) || ↑ / ↓ || Перемещение между пунктами
|-
| Popup || OK || Выбор пункта
|-
| Popup || Назад || Закрыть меню (без перехода по истории)
|-
| Inline popup || ← / → || Перемещение между пунктами
|-
| Inline в строке (<code>$menu</code>) || OK / клик || Выбор пункта напрямую
|-
| Inline в строке || ← / → || Фокус по горизонтальному ряду (если меню в popup после OK)
|}


$_PL["css"]=" .numb { display:none; } "; // Скрываем отображение номера
'''Поле <code>default</code>''' — начальный фокус при первом открытии меню:


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


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


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


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


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


===cmd тег (как глобальный так и в playlist_url) - допустимые команды===
<pre>&lt;?php
$_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;';


====Примеры использования '''cmd''' в глобальном теге====
$_CH["menu"] = [
$_PL["cmd"]="historyback(1);" // Вернуться назад на 1 страницу
  [
    "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"]
];


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


$_PL["cmd"]="settitle(2, Новый заголовок);"  // элементу 2 дать новый title
При <code>$menu</code> в template + <code>type:inline</code> повторный popup по OK '''не''' открывается.


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


$_PL["cmd"]="reload(2);"  // Перезагрузить страницу через 2секунды
{| 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 — подменю|Подменю]])
|}


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


например $_PL["cmd"]="info(Доступ к этой странице Вам запрещен);stop();" выведет уведомление но оставить пользователя на предыдущей странице.
Используются в <code>template</code> '''пункта''' menu (не путать с <code>$menu</code> в template channel):


====Примеры использования '''cmd''' в $_CH["playlist_url"]====
{| class="wikitable"
$_CH["playlist_url"]="cmd:<Команда из списка допустимых>";
! Плейсхолдер !! Подставляется
|-
| <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)
|}


Например $_CH["playlist_url"]="reload();"; перезагрузит страницу при нажатии
Плейсхолдер '''channel''': <code>$menu</code> — в <code>$_CH["template"]</code>; подставляется HTML всех пунктов inline-меню.


==Channel теги==
====menu — подменю====


===title===
Пункт с вложенным списком через <code>playlist_url":"submenu"</code> и массив <code>submenu</code>:
Заголовок


===playlist_url или stream_url===
<pre>{
Адрес страницы или адрес видеопотока (непустым может быть только один из этих тегов)
  "menu": [
    {
      "type": "inline",
      "title": "Ещё ▾",
      "playlist_url": "submenu",
      "submenu": [
        {"title": "Обновить", "playlist_url": "cmd:reload();"},
        {"title": "Настройки", "default": 1, "playlist_url": "http://example.com/settings"}
      ]
    }
  ]
}</pre>


===logo_30x30===
Во встроенном <code>$menu</code> клик по такому пункту открывает подменю; для пунктов <code>submenu</code> также поддерживается <code>default</code>.
Адрес иконки


===confirm - диалоговое окно с действиями при нажатии на элемент===
====menu — ответ сервера cmd без перехода====
Пример подтверждения выхода с аккаунта


$_CH[]=["title"=>"Выйти","playlist_url"=>"confirm","confirm"=>["http://host/?do<nowiki>=exit"],"description"=>"Выйти с аккаунта?"];</nowiki>
Если пункт menu ведёт на URL, сервер может вернуть JSON с командой вместо новой страницы — пользователь остаётся на текущем списке, история не растёт.


===menu - контекстное меню элемента===
<pre>{"cmd": "seticon([index],http://example.com/icon.svg);stop();"}</pre>
$menu=[];


$menu[0]=["title"=>"Контекстное меню1","playlist_url"=>"http..."]; // Аналог обычного $_CH
{| 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[0]=["title"=>"Контекстное меню2","playlist_url"=>"http..."]; //  Аналог обычного $_CH
'''Типичный сценарий''' (реакция, переключатель без перезагрузки):


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


===position при заданном $_PL["typeList"]="start";===
См. также глобальный тег [[#cmd_тег_.28как_глобальный_так_и_в_playlist_url.29_.2D_допустимые_команды.29|cmd]] и <code>stop();</code> в <code>$_PL["cmd"]</code>.


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


====$_CH["position"]="html"; // - Пример элемента со своим дизайном в $_CH["template"]====
* Оформление пунктов — через <code>class</code>, <code>style</code>, <code>template</code> каждого пункта.
$_CH["position"]="html";
* Общие стили (pill, tab, badge) — <code>$_PL["css"]</code> или внешний файл <code>$_PL["link"][]</code>.
* Длинное inline-меню прокручивается по горизонтали (<code>overflow-x</code>).


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


$_CH["title"]="Новый вид";
Channel с <code>menu</code> без <code>type:inline</code> и без <code>$menu</code> в template работает как раньше: вертикальный popup по <code>playlist_url":"menu"</code>, пункт «Отмена» внизу (если нет <code>nocancel</code>).


$_CH["description"]="Описание вида";
=== 5.3. Навигация и оформление channel ===


$_CH["logo_30x30"]="<nowiki>http://p.lnka.ru/icons/yapfiles.png</nowiki>";
===location===
$_CH["location"]=1; // Переход c заменой текущей ссылки окна на новую (при перезапуске форкплеера будет открываться уже с этой новой ссылки)


$_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["location"]=3; // Переход с отображением в текущем окне (при перезапуске форкплеера будет открываться старая ссылка и опять переадресовываться, необходимо если например используются устаревающие сессии)


Элементы будут выстраиваться в ряд и навигация по ним будет предполагать что они в одном ряду
===after и before - свой html код после и перед элементом списка===
$_CH["before"]="<nowiki><div style='color:red;'>Текст перед элементом</div></nowiki>";


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


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


====$_CH["position"]="hlist"; // - горизонтальная ссылка (может быть несколько на одном горизонтальном уровне)====
===Локальные переменные LOCAL_IP, TORRSERVE_IP, ACE_IP===
[[Файл:SetIP.png|мини]]


====$_CH["position"]="list"; // -обычный вид списка шириной в половину экрана и с description справа====
В URL портала подставляются значения из настроек ForkPlayer на устройстве пользователя.


====$_CH["position"]="label"; // -невысокая строка на всю ширину экрана====
'''Если не указан TORRSERVE_IP или ACE_IP''' — вместо них подставляется LOCAL_IP (устройство с RemoteFork).


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


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


===after и before - свой html код после и перед элементом списка===
$_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>")];
$_CH["before"]="<nowiki><div style='color:red;'>Текст перед элементом</div></nowiki>";


$_CH["after"]="<nowiki><div style='color:red;'>Текст после элемента</div></nowiki>";
====Автовыбор torrserve или ace stream====
$_CH[]=["title"=>"(magnet) через ace stream или torrserve","playlist_url"=>"<nowiki>magnet:?xt=urn:btih:642a36ec9dcb2c5ba7b08835bd04ae8738281bb1</nowiki>"];


===SetTimeInterval - отправка событий клиентом через интервал и вывод сообщений===
===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>"];


Строка 537: Строка 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>


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


<code><stream_url><![CDATA<nowiki>[http://test.ru/index.php?s=postmd5]</nowiki>]></stream_url></code>
Ответ страницы попадет в проигрыватель в виде ссылки.


<code><parser><![CDATA<nowiki>[https://yadi.sk/d/0BpgziSQ0VWiFg]</nowiki>]></parser></code>
Тоесть <code><nowiki>http://test.ru/index.php</nowiki></code> должен дать текст ссылки на медиафайл вида <nowiki>http://.../myvideo.mp4</nowiki>


<code></channel></code>  
====Скачивание сторонней страницы по необходимости, страница <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']


Будет идти отправка POST на <nowiki>http://test.ru/index.php?s=postmd5</nowiki> в $_POST["postmd5"]
== 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)====
 
===description — прокрутка части большого текста===
Добавьте в описание div с id <code>scrolled</code> — прокрутка кнопками PG_UP / PG_DOWN:
 
$_CH["description"]='MY TITLE STATIC<nowiki><div id="scrolled"></nowiki>
 
PG_UP PG_DOWN
 
large scrolled content
 
.
 
.
 
.


Тоесть <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>


===Прописываем свои стили===
===Прописываем свои стили===
Строка 623: Строка 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 (см. скриншот выше)