<?xml version="1.0" encoding="utf-8"?>
<HmsMediaItem>
  <MediaType>3</MediaType>
  <ClassID>51</ClassID>
  <ItemID>349a3c53-c37c-4d65-ae41-b8eebd7ebc9f</ItemID>
  <ItemPath>https://serialage.tv</ItemPath>
  <ParentID>234DF17B-418C-4FDC-9DFE-CD0C586D2E76</ParentID>
  <Properties>
    <Property>
      <ID>4</ID>
      <Value>SerialAge.tv (С поиском)</Value>
    </Property>
    <Property>
      <ID>515</ID>
      <Value>1</Value>
    </Property>
    <Property>
      <ID>700</ID>
      <Value>0</Value>
    </Property>
    <Property>
      <ID>701</ID>
      <Value>-1</Value>
    </Property>
    <Property>
      <ID>702</ID>
      <Value>-1</Value>
    </Property>
    <Property>
      <ID>517</ID>
      <Value>578-720,722-1080,482-576,402-480,322-400,202-320,0-200</Value>
    </Property>
    <Property>
      <ID>518</ID>
      <Value>0</Value>
    </Property>
    <Property>
      <ID>512</ID>
      <Value>0</Value>
    </Property>
    <Property>
      <ID>532</ID>
      <Value>1</Value>
    </Property>
    <Property>
      <ID>553</ID>
      <Value>1</Value>
    </Property>
    <Property>
      <ID>522</ID>
      <Value>0</Value>
    </Property>
    <Property>
      <ID>570</ID>
      <Value>1</Value>
    </Property>
    <Property>
      <ID>245</ID>
      <Value>349a3c53-c37c-4d65-ae41-b8eebd7ebc9f</Value>
    </Property>
    <Property>
      <ID>93</ID>
      <Value>43194,959372662</Value>
    </Property>
    <Property>
      <ID>530</ID>
      <Value>string    gsUrlBase    = &apos;https://serialage.tv&apos;; // База для относительных ссылок
int       gnTotalItems = 0;                 // Счётчик созданных элементов
TDateTime gStart       = Now;               // Время начала запуска скрипта
int gnMaxPages=10, gnMaxInGroup=100; bool gbYearInTitle=false; char gsGroupMode=&apos;&apos;;

// Регулярные выражения для поиска на странице блоков с информацией о видео
string
  gsPatternBlock  = &apos;&lt;tr&gt;(.*?)&lt;/tr&gt;&apos;                   , // Искомые блоки  
  gsCutPage       = &apos;&lt;fromCut&gt;(.*?)&lt;toCut&gt;&apos;            , // Обрезка загруженной страницы
  gsPatternTitle  = &apos;(&lt;a.*?&lt;/a&gt;)&apos;                      , // Название
  gsPatternLink   = &apos;&lt;a[^&gt;]+href="(.*?)"&apos;              , // Ссылка  
  gsPatternImg    = &apos;&lt;img[^&gt;]+src="(.*?)"&apos;             , // Картинка
  gsPatternYear   = &apos;shortinfo.*?&gt;(\\d{4})&lt;&apos;           , // Год
  gsPatternAudio  = &apos;&apos;                                 , // Озвучка / Перевод
  gsPatternPages  = &apos;.*/page/\\d+/"&gt;(\\d+)&apos;            , // Регулярное выражение для поиска максимального номера страницы для дозагрузки
  gsPagesParams   = &apos;page/&lt;PN&gt;/&apos;                       , // Параметр с номером страницы, который добавляется к ссылке
  ;

///////////////////////////////////////////////////////////////////////////////
// Создание папки или подкаста
THmsScriptMediaItem CreateFolder(THmsScriptMediaItem ParentFolder, string sName, string sLink, string sImg=&apos;&apos;) {
  THmsScriptMediaItem Item = ParentFolder.AddFolder(sLink); // Создаём папку с указанной ссылкой
  Item[mpiTitle     ] = sName; // Присваиваем наименование
  Item[mpiThumbnail ] = sImg;  // Картинка
  Item[mpiCreateDate] = DateTimeToStr(IncTime(gStart, 0, -gnTotalItems, 0, 0)); // Для обратной сортировки по дате создания

  gnTotalItems++;             // Увеличиваем счетчик созданных элементов
  return Item;                // Возвращаем созданный объект
}

///////////////////////////////////////////////////////////////////////////////
// Получение имени группировки по имени видео (первая буква, "A..Z" или "#")
string GetGroupName(string sName) {
  string sGrp = &apos;#&apos;;
  if (HmsRegExMatch(&apos;([A-ZА-Я0-9])&apos;, sName, sGrp, 1, PCRE_CASELESS)) sGrp = UpperCase(sGrp);
  if (HmsRegExMatch(&apos;[A-Z]&apos;, sGrp, sGrp)) sGrp = &apos;A..Z&apos;;
  if (HmsRegExMatch(&apos;[0-9]&apos;, sGrp, sGrp)) sGrp = &apos;#&apos;;
  return sGrp;
}

///////////////////////////////////////////////////////////////////////////////
// Загрузка страниц и парсинг 
void LoadAndParse() {
  string sHtml, sData, sName, sLink, sImg, sYear, sPage, sVal, sPost, sServ; // Объявляем переменные
  THmsScriptMediaItem Item, Folder = FolderItem; TRegExpr RegEx;
  int i, n, nPages=0, iCnt=0, nGrp=0; char sGrp=""; bool bGroup=false;

  if (HmsRegExMatch(&apos;--maxingroup=(\\d+)&apos;, mpPodcastParameters, sVal)) gnMaxInGroup = StrToInt(sVal);
  if (HmsRegExMatch(&apos;--maxpages=(\\d+)&apos;  , mpPodcastParameters, sVal)) gnMaxPages   = StrToInt(sVal);
  HmsRegExMatch(&apos;--group=(\\w+)&apos;, mpPodcastParameters, gsGroupMode);
  gbYearInTitle = (Pos(&apos;--yearintitle&apos;, mpPodcastParameters)&gt;0); 

  if (LeftCopy(mpFilePath, 4) != "http") {
    // Если нет ссылки - делаем поиск названия
    sLink = &apos;https://serialage.tv/search?query=&apos;+HmsPercentEncode(HmsUtf8Encode(mpTitle));
    sHtml = HmsDownloadURL(sLink, &apos;Referer: &apos;+gsUrlBase, true);
    
  } else {
    // Иначе просто, загружаем страницу по ссылке
    sHtml = HmsDownloadURL(mpFilePath, &apos;Referer: &apos;+gsUrlBase, true);
    
  }
  sHtml = HmsUtf8Decode(sHtml);       // Декодируем страницу из UTF-8
  sHtml = HmsRemoveLineBreaks(sHtml); // Удаляем переносы строк, для облегчения работы с регулярными выражениями

  // Если указан шаблон поиска максимального номера страницы - применяем
  if ((gsPatternPages!=&apos;&apos;) &amp;&amp; HmsRegExMatch(gsPatternPages, sHtml, sVal)) nPages = StrToIntDef(sVal, 0);

  // Вырезаем только нужный участок текста HTML, где будем искать блоки.
  // Вместо &lt;fromCut&gt; и &lt;toCut&gt; вставляем начало и конец участка HTML, между которыми
  // будем искать блоки текста с сылкой, наименованием и проч.
  HmsRegExMatch(gsCutPage, sHtml, sHtml); // ищем в sHtml, результат кладём обратно в sHtml

  // =========================================================================
  // Дозагрузка страниц
  if ((gnMaxPages!=0) &amp;&amp; (nPages&gt;gnMaxPages)) nPages = gnMaxPages;
  for (i=2; i&lt;=nPages; i++) {
    HmsSetProgress(Trunc(i*100/nPages));
    HmsShowProgress(Format(&apos;%s: Загрузка страницы %d из %d&apos;, [mpTitle, i, nPages]));
    sLink = mpFilePath + ReplaceStr(gsPagesParams, &apos;&lt;PN&gt;&apos;, IntToStr(i));
    sPage = HmsDownloadURL(sLink, &apos;Referer: &apos;+gsUrlBase, true);
    sPage = HmsUtf8Decode(sPage);
    if (gsCutPage!=&apos;&apos;) HmsRegExMatch(gsCutPage, sPage, sPage);
    sHtml += sPage;
    if (HmsCancelPressed) break;
  }
  HmsHideProgress();                                                                                                   
  // =========================================================================
      
  // Создаём объект для поиска блоков текста по регулярному выражению,
  // в которых есть информация: ссылка, наименование, ссылка на картинку и проч.
  // Обычно, определяем начало и конец блока и вставляем их вместо &lt;section&gt; и &lt;/section&gt;
  RegEx = TRegExpr.Create(gsPatternBlock, PCRE_SINGLELINE);
  try {
    // Определяем, если блоков в загруженном более чем gnMaxInGroup, включаем группировку
    i=0; if (RegEx.Search(sHtml)) do i++; while (RegEx.SearchAgain());
    bGroup = (i &gt; gnMaxInGroup);
    // Главный цикл поиска блоков
    if (RegEx.Search(sHtml)) do {
      sLink=&apos;&apos;; sImg=&apos;&apos;; sYear=&apos;&apos;; sName=&apos;&apos;;
      HmsRegExMatch(gsPatternTitle, RegEx.Match, sName);
      HmsRegExMatch(gsPatternLink , RegEx.Match, sLink);
      HmsRegExMatch(gsPatternImg  , RegEx.Match, sImg );
      HmsRegExMatch(gsPatternYear , RegEx.Match, sYear);
      if (Trim(sLink)=="") continue;
    
      sName = ReplaceStr(HmsHtmlToText(sName), "/", "-");
      sLink = HmsExpandLink(sLink, gsUrlBase);

      if (sImg!=&apos;&apos;) sImg = HmsExpandLink(sImg, gsUrlBase);

      // Если в ссылках встречаются русские символы - делаем их безопасными
      if (HmsRegExMatch(&apos;^.*?([а-яА-Я].*)&apos;, sImg, sVal)) sImg = ReplaceStr(sImg, sVal, HmsPercentEncode(HmsUtf8Encode(sVal)));

      // Если указано добавлять год вназвание и в названии его нет, добавляем
      if (gbYearInTitle &amp;&amp; (sYear!=&apos;&apos;) &amp;&amp; (Pos(sYear, sName)&lt;1)) sName += &apos; (&apos;+sYear+&apos;)&apos;;

      // Контроль группировки (создаём папку с именем группы)
      if (gsGroupMode==&apos;alph&apos;) {
        Folder = FolderItem.AddFolder(GetGroupName(sName)); 
        Folder[mpiFolderSortOrder] = "mpTitle";
      } else if (gsGroupMode==&apos;year&apos;) {
        Folder = FolderItem.AddFolder(sYear); 
        Folder[mpiFolderSortOrder] = "mpTitle";
        Folder[mpiYear           ] = sYear;
      } else if (bGroup) {
        iCnt++; if (iCnt&gt;=gnMaxInGroup) { nGrp++; iCnt=0; }
        Folder = FolderItem.AddFolder(Format(&apos;%.2d&apos;, [nGrp])); 
      }

      CreateFolder(Folder, sName, sLink, sImg); // Создание ссылки (папки с фильмом)

    } while (RegEx.SearchAgain);
  } finally { RegEx.Free(); }
  if      (gsGroupMode==&apos;alph&apos;) FolderItem.Sort(&apos;mpTitle&apos;);
  else if (gsGroupMode==&apos;year&apos;) FolderItem.Sort(&apos;-mpYear&apos;);

  HmsLogMessage(1, mpTitle+&apos;: создано элементов - &apos;+IntToStr(gnTotalItems));
}

///////////////////////////////////////////////////////////////////////////////
//                    Г Л А В Н А Я    П Р О Ц Е Д У Р А                     //
{
  FolderItem.DeleteChildItems(); // Удаляем созданные ранее элементы в текущей папке
  LoadAndParse();                // Запускаем загрузку страниц и создание папок видео
}
</Value>
    </Property>
    <Property>
      <ID>531</ID>
      <Value>C++Script</Value>
    </Property>
    <Property>
      <ID>550</ID>
      <Value>string gsUrlBase = "https://serialage.tv";
TDateTime gStart = Now; // Время старта скрипта
int gnTotalItems = 0;   // Счётчик созданных ссылок
int       mpiCountry   = 10012; // Идентификаторы для хранения дополнительной
int       mpiTranslate = 10013; // информации в свойствах подкаста
int       mpiQuality   = 10014;
int       mpiSiteRating   = 10015;
int       mpiVideoMessage = 1001001;
string    gsTime = "01:00:00.000"; // Длительность по-умолчанию для всех видео
string    gsPodcastName   = "serialage.tv";
string    gsPreviewPrefix = &apos;serialage&apos;; // Префикс кеша информационных картинок на сервере wonky.lostcut.net

///////////////////////////////////////////////////////////////////////////////
// Создание папки или подкаста
THmsScriptMediaItem CreateFolder(THmsScriptMediaItem ParentFolder, string sName, string sLink, string sImg=&apos;&apos;) {
  THmsScriptMediaItem Item = ParentFolder.AddFolder(sLink); // Создаём папку с указанной ссылкой
  Item[mpiTitle     ] = sName; // Присваиваем наименование
  Item[mpiThumbnail ] = sImg;  // Картинка
  Item[mpiCreateDate] = DateTimeToStr(IncTime(gStart, 0, -gnTotalItems, 0, 0)); // Для обратной сортировки по дате создания
  gnTotalItems++;              // Увеличиваем счетчик созданных элементов
  return Item;                 // Возвращаем созданный объект
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылки на видео
THmsScriptMediaItem CreateMediaItem(THmsScriptMediaItem Folder, string sTitle, string sLink, string sImg="", string sGrp="") {
  THmsScriptMediaItem Item = HmsCreateMediaItem(sLink, Folder.ItemID, sGrp);
  Item[mpiTitle     ] = sTitle;
  Item[mpiThumbnail ] = sImg;
  Item[mpiCreateDate] = VarToStr(IncTime(gStart,0,-gnTotalItems,0,0));
  Item[mpiTimeLength] = gsTime;
  gnTotalItems++;
  return Item;
}

///////////////////////////////////////////////////////////////////////////////
// Вывод видео сообщения с заданным текстом
void ShowVideoMessage(string sMsg, string sTitle=&apos;&apos;, int nErr=0, string sDescr=&apos;&apos;) {
  if (nErr==1) sMsg = &apos;&lt;c:#e22&gt;&apos;+Trim(sMsg);
  TStrings INFO = TStringList.Create();
  INFO.Values[&apos;Title&apos; ] = sTitle;
  INFO.Values[&apos;Info&apos;  ] = sMsg;
  INFO.Values[&apos;Descr&apos; ] = sDescr;
  PodcastItem[mpiVideoMessage] = INFO.Text;
  INFO.Free();
  VideoPreview();
}

///////////////////////////////////////////////////////////////////////////////
// Создание информационной ссылки
void CreateInfoItem(string sName, string sVal) {
  THmsScriptMediaItem Item; sVal = Trim(sVal);
  if (sVal=="") return;
  Item = HmsCreateMediaItem(&apos;Info&apos;+IntToStr(PodcastItem.ChildCount), PodcastItem.ItemID);
  Item[mpiTitle     ] = sName+&apos;: &apos;+sVal;
  Item[mpiThumbnail ] = &apos;http://wonky.lostcut.net/vids/info.jpg&apos;;
  Item[mpiTimeLength] = 9;
  Item[mpiCreateDate] = VarToStr(IncTime(gStart,0,-gnTotalItems,0,0));
  gnTotalItems++;
}

///////////////////////////////////////////////////////////////////////////////
// Формирование видео с картинкой с информацией о фильме
bool VideoPreview() {
  string sVal, sFileImage, sPoster, sTitle, sDescr, sCateg, sInfo, sLink, sData;
  int xMargin=7, yMargin=10, nSeconds=10, n; string sCacheDir;
  float nH=cfgTranscodingScreenHeight, nW=cfgTranscodingScreenWidth;
  // Проверяем и, если указаны в параметрах подкаста, выставляем значения смещения от краёв
  if (HmsRegExMatch(&apos;--xmargin=(\\d+)&apos;, mpPodcastParameters, sVal)) xMargin=StrToInt(sVal);
  if (HmsRegExMatch(&apos;--ymargin=(\\d+)&apos;, mpPodcastParameters, sVal)) yMargin=StrToInt(sVal);
  sCacheDir = IncludeTrailingBackslash(HmsTempDirectory);
  
  if (Trim(PodcastItem[mpiVideoMessage])==&apos;&apos;) return; // Если нет инфы - выходим быстро!
    TStrings INFO = TStringList.Create();       // Создаём объект TStrings
  INFO.Text  = PodcastItem[1001001];          // И загружаем туда информацию
  sPoster = INFO.Values[&apos;Poster&apos;];            // Постер
  sTitle  = INFO.Values[&apos;Title&apos; ];            // Самая верхняя надпись - Название
  sCateg  = INFO.Values[&apos;Genre&apos; ];            // Жанр
  sInfo   = INFO.Values[&apos;Info&apos;  ];            // Блок информации
  sDescr  = INFO.Values[&apos;Descr&apos; ];            // Описание
  if (sTitle==&apos;&apos;) sTitle = &apos; &apos;;
  ForceDirectories(sCacheDir);
  sFileImage = ExtractShortPathName(sCacheDir)+&apos;videopreview_&apos;; // Файл-заготовка для сохранения картинки
  sDescr = Copy(sDescr, 1, 3000); // Если блок описания получился слишком большой - обрезаем
  
  INFO.Text = ""; // Очищаем объект TStrings для формирования параметров запроса
  INFO.Values[&apos;prfx&apos; ] = gsPreviewPrefix;  // Префикс кеша сформированных картинок на сервере
  INFO.Values[&apos;title&apos;] = sTitle;           // Блок - Название
  INFO.Values[&apos;info&apos; ] = sInfo;            // Блок - Информация
  INFO.Values[&apos;categ&apos;] = sCateg;           // Блок - Жанр/категории
  INFO.Values[&apos;descr&apos;] = sDescr;           // Блок - Описание фильма
  INFO.Values[&apos;mlinfo&apos;] = &apos;20&apos;;            // Максимальное число срок блока Info
  INFO.Values[&apos;w&apos; ] = IntToStr(Round(nW)); // Ширина кадра
  INFO.Values[&apos;h&apos; ] = IntToStr(Round(nH)); // Высота кадра
  INFO.Values[&apos;xm&apos;] = IntToStr(xMargin);   // Отступ от краёв слева/справа
  INFO.Values[&apos;ym&apos;] = IntToStr(yMargin);   // Отступ от краёв сверху/снизу
  INFO.Values[&apos;bg&apos;] = &apos;http://www.pageresource.com/wallpapers/wallpaper/noir-blue-dark_3512158.jpg&apos;; // Катринка фона (кэшируется на сервере)
  INFO.Values[&apos;fx&apos;] = &apos;3&apos;; // Номер эффекта для фона: 0-нет, 1-Blur, 2-more Blur, 3-motion Blur, 4-radial Blur
  INFO.Values[&apos;fztitle&apos;] = IntToStr(Round(nH/14)); // Размер шрифта блока названия (тут относительно высоты кадра)
  INFO.Values[&apos;fzinfo&apos; ] = IntToStr(Round(nH/22)); // Размер шрифта блока информации
  INFO.Values[&apos;fzcateg&apos;] = IntToStr(Round(nH/26)); // Размер шрифта блока жанра/категории
  INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/18)); // Размер шрифта блока описания
  // Если текста описания больше чем нужно - немного уменьшаем шрифт блока
  if (Length(sDescr)&gt;890) INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/20));
  // Если есть постер, задаём его параметры отображения (где, каким размером)
  if (sPoster!=&apos;&apos;) {
    INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nW/4)); // Ширина постера (1/4 ширины кадра)
    INFO.Values[&apos;xpic&apos;  ] = &apos;10&apos;;    // x-координата постера
    INFO.Values[&apos;ypic&apos;  ] = &apos;10&apos;;    // y-координата постера
    if (mpFilePath==&apos;InfoUpdate&apos;) {
      INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nW/6)); // Ширина постера (1/6 ширины кадра)
      INFO.Values[&apos;xpic&apos;  ] = IntToStr(Round(nW/2 - nW/12)); // центрируем
    }
    INFO.Values[&apos;urlpic&apos;] = sPoster; // Адрес изображения постера
  }
  sData = &apos;&apos;;  // Из установленных параметров формируем строку POST запроса
  for (n=0; n&lt;INFO.Count; n++) sData += &apos;&amp;&apos;+Trim(INFO.Names[n])+&apos;=&apos;+HmsHttpEncode(INFO.Values[INFO.Names[n]]);
  INFO.Free(); // Освобождаем объект из памяти, теперь он нам не нужен
  // Делаем POST запрос не сервер формирования картинки с информацией
  sLink = HmsSendRequestEx(&apos;wonky.lostcut.net&apos;, &apos;/videopreview.php?p=&apos;+gsPreviewPrefix, &apos;POST&apos;,
  &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sData, 80, 0, &apos;&apos;, true);
  // В ответе должна быть ссылка на сформированную картинку
  if (LeftCopy(sLink, 4)!=&apos;http&apos;) {HmsLogMessage(2, &apos;Ошибка получения файла информации videopreview.&apos;); return;}
  // Сохраняем сформированную картинку с информацией в файл на диске
  HmsDownloadURLToFile(sLink, sFileImage);
  // Копируем и нумеруем файл картики столько раз, сколько секунд мы будем её показывать
  for (n=1; n&lt;=nSeconds; n++) CopyFile(sFileImage, sFileImage+Format(&apos;%.3d.jpg&apos;, [n]), false);
  // Для некоторых телевизоров (Samsung) видео без звука отказывается проигрывать, поэтому скачиваем звук тишины
  char sFileMP3 = ExtractShortPathName(HmsTempDirectory)+&apos;\\silent.mp3&apos;;
  try {
    if (!FileExists(sFileMP3)) HmsDownloadURLToFile(&apos;http://wonky.lostcut.net/mp3/silent.mp3&apos;, sFileMP3);
    sFileMP3 = &apos;-i "&apos;+sFileMP3+&apos;"&apos;;
  } except { sFileMP3=&apos;&apos;; }
  // Формируем из файлов пронумерованных картинок и звукового команду для формирования видео
  MediaResourceLink = Format(&apos;%s -f image2 -r 1 -i "%s" -c:v libx264 -pix_fmt yuv420p &apos;, [sFileMP3, sFileImage+&apos;%03d.jpg&apos;]);
}

///////////////////////////////////////////////////////////////////////////////
// Вывод вместо видео заданного сообщения
bool VideoMessage(char sCaption, char sMessage, int nTime=30) {
  char sFileImage = HmsTempDirectory+&apos;\\videomessage.jpg&apos;; char sCmd;
  sCaption = HmsHttpEncode(ReplaceStr(sCaption, &apos;\n&apos;, &apos;|&apos;));
  sMessage = HmsHttpEncode(ReplaceStr(sMessage, &apos;\n&apos;, &apos;|&apos;));
  HmsDownloadURLToFile(&apos;http://wonky.lostcut.net/videomessage.php?testpic=1&amp;caption=&apos;+sCaption+&apos;&amp;msg=&apos;+sMessage, sFileImage);
  char sFileMP3 = HmsTempDirectory+&apos;\\silent.mp3&apos;;
  try {
    if (!FileExists(sFileMP3)) HmsDownloadURLToFile(&apos;http://wonky.lostcut.net/mp3/silent.mp3&apos;, sFileMP3);
    sFileMP3 = &apos;-i "&apos;+sFileMP3+&apos;"&apos;;
  } except {
    sFileMP3 = &apos;&apos;;
  }
  sCmd = Format(&apos;%s -loop 1 -f image2 -i "%s" -t %d -r 25 &apos;, [ExtractShortPathName(sFileMP3), ExtractShortPathName(sFileImage), 7]);
  MediaResourceLink = sCmd;
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылки-ошибки
void ErrorItem(string sMsg) {
  THmsScriptMediaItem Item = HmsCreateMediaItem(&apos;Err&apos;, PodcastItem.ItemID);
  Item[mpiTitle     ] = sMsg;
  Item[mpiThumbnail ] = &apos;http://wonky.lostcut.net/icons/symbol-error.png&apos;;
}

///////////////////////////////////////////////////////////////////////////////
// Вывод видео сообщения с информацией о фильме
void ShowVideoInfo() {
  string sInfo; THmsScriptMediaItem Parent = PodcastItem.ItemParent;
  
  sInfo = &apos;&apos;;
  if (Trim(Parent[mpiCountry  ])!=&apos;&apos;) sInfo += &apos;Страна: &apos;  +Parent[mpiCountry  ]+"|";
  if (Trim(Parent[mpiTranslate])!=&apos;&apos;) sInfo += &apos;Перевод: &apos; +Parent[mpiTranslate]+"|";
  if (Trim(Parent[mpiQuality  ])!=&apos;&apos;) sInfo += &apos;Качество: &apos;+Parent[mpiQuality  ]+"|";
  if (Trim(Parent[mpiDirector ])!=&apos;&apos;) sInfo += &apos;Режиссер: &apos;+Parent[mpiDirector ]+"|";
  if (Trim(Parent[mpiActor    ])!=&apos;&apos;) sInfo += &apos;В ролях: &apos; +Parent[mpiActor    ]+"|";
  sInfo = Copy(sInfo, 1, Length(sInfo)-1); // Обрезаем последний символ "|"
  TStrings INFO = TStringList.Create();
  INFO.Values[&apos;Poster&apos;] = Parent[mpiThumbnail];
  INFO.Values[&apos;Title&apos; ] = Parent[mpiTitle];
  INFO.Values[&apos;Genre&apos; ] = Parent[mpiGenre];
  INFO.Values[&apos;Info&apos;  ] = sInfo;
  INFO.Values[&apos;Descr&apos; ] = ReplaceStr(Parent[mpiComment], "\n", "|");
  PodcastItem[mpiVideoMessage] = INFO.Text;
  INFO.Free();
  VideoPreview();
}

///////////////////////////////////////////////////////////////////////////////
// Создание папок сезонов
void CreateSeasons(string sLink) {
  string sHtml, sName, sID; TRegExpr RE; int n;
  
  // Загружаем страницу сериала
  sHtml = HmsUtf8Decode(HmsDownloadURL(sLink, "Referer: "+gsUrlBase));
  RE    = TRegExpr.Create(&apos;&lt;a[^&gt;]+data-id="(\\d+)".*?&lt;/a&gt;&apos;, PCRE_SINGLELINE);
  try {
    // Подсчитываем количество сезонов
    n=0; if (RE.Search(sHtml)) do { n++; sID=RE.Match(1); HmsRegExMatch("(\\d+)", HmsHtmlToText(RE.Match(0)), sName); } while (RE.SearchAgain);
    // Если всего один сезон и номер сезона 1, то не создаём папку сезона, а запускаем сразу создание серий
    if ((n==1) &amp;&amp; (sName=="1")) { CreateSeries(gsUrlBase+"/playlists/"+sID+".json"); return; }
    // Цикл поиска сезонов
    if (RE.Search(sHtml)) {
      do {
        sID   = RE.Match(1);                     // Получаем ID сериала (сезона)
        sName = HmsHtmlToText(RE.Match(0));      // Номер сезона (название)
        sLink = gsUrlBase+"/playlists/"+sID+".json";
        CreateFolder(PodcastItem, sName, sLink); // Создаём папку сезона
      } while (RE.SearchAgain);
    } else {
      sName = "Не удалось найти data-id на странице фильма.";
      ErrorItem(sName); // Создание информационной ссылки с сообщением об ошибке
      HmsLogMessage(2, mpTitle+": "+sName); // Вывод сообщения в журнал программы
    }
  } finally { RE.Free; }
}  

///////////////////////////////////////////////////////////////////////////////
// Создание ссылок на серии с фильтрацией по конкретному pid (родительский ID)
// Первый запуск функции без указания sParentID, т.е. с pid=""
void CreateSeriesByPID(TJsonArray LIST, string sParentID="", string sGrp="") {
  string sName, sLink, sID, sVal; int i; TJsonObject VIDEO;
  // Цикл обхода всех серий с проверкой, соответствует ли pid серии указанному в sParentID
  for (i = 0; i &lt; LIST.Length; i++) {
    VIDEO = LIST[i]; if (VIDEO.S["pid"]!=sParentID) continue; // Пропускаем все видео, где pid не совпадаем с переданным в параметрах sParentID 
    sName = VIDEO.S["title"];
    sLink = VIDEO.S["file" ];
    sID   = VIDEO.S["id"   ];
    sLink = HmsExpandLink(sLink, gsUrlBase);
    // Форматируем номер серии в два знака
    if (HmsRegExMatch("ерия\\s*(\\d+)", sName, sVal)) sName = Format("%.2d серия", [StrToInt(sVal)]);
    // Если ссылки нет - значит это папка, запускаем рекурсивно создание серий для нашей группы с нашим id
    if (sLink=="") CreateSeriesByPID(LIST, sID, sGrp+"\\"+sName);
    else           CreateMediaItem(PodcastItem, sName, sLink, mpThumbnail, sGrp); // Создаём ссылку на видео
  }
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылок на серии
void CreateSeries(string sLink) {
  // Загружаем JSON данные конкретного сезона сериала со списком серий
  string sData = HmsUtf8Decode(HmsDownloadURL(sLink, "Referer: "+gsUrlBase));
  TJsonObject JSON = TJsonObject.Create(); // Создаём объект для работы с JSON
  try {
    JSON.LoadFromString(sData);            // Загружаем JSON строку в объект
    if (JSON.AsArray == nil) return;       // Если массив значений пустой - выходим
    CreateSeriesByPID(JSON.AsArray);       // Запускаем создание серий из массива JSON
  } finally { JSON.Free; } // Что бы ни случилось, освобождаем созданный объект из памяти
}

///////////////////////////////////////////////////////////////////////////////
string NormVal(string sVal) {
  string sName = HmsHtmlToText(sVal);
  sName = ReplaceStr(sName, &apos;|&apos;, &apos;&apos;);
  return Trim(sName);
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылок видео
void CreateLinks() {
  string sHtml, sData, sLink, sName, sTime, sImg, sVal, sSeason, sID;
  THmsScriptMediaItem Item; TRegExpr RegEx;
  
  //sSeason = &apos;&apos;;
  //HmsRegExMatch(&apos;season=(\\d+)&apos;, mpFilePath, sSeason);
  //
  sHtml = HmsDownloadURL(mpFilePath, &apos;Referer: &apos;+mpFilePath, true);
  sHtml = HmsUtf8Decode(sHtml);
  sHtml = HmsRemoveLineBreaks(sHtml);
  
  // =========================================================================
  // Сбор информации о фильме
  if (HmsRegExMatch(&apos;&lt;td&gt;Год выхода&lt;/td&gt;&lt;td itemprop="copyrightYear"&gt;(.*?)&lt;/td&gt;&apos;, sHtml, sVal)) PodcastItem[mpiYear] = sVal;
  if (HmsRegExMatch(&apos;duration="(\\d+)"&apos;, sHtml, sVal)) gsTime = HmsTimeFormat(StrToInt(sVal)+120)+&apos;.000&apos;;
  if (HmsRegExMatch(&apos;&lt;td&gt;Жанр&lt;/td&gt;(.+?)&lt;/td&gt;&apos;    , sHtml, sVal)) PodcastItem[mpiGenre    ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;span itemprop="ratingValue" class="serial-rating--value"&gt;(.+?)&lt;/span&gt;&apos;    , sHtml, sVal)) PodcastItem[mpiRating    ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Страна&lt;/td&gt;(.+?)&lt;/td&gt;&apos;  , sHtml, sVal)) PodcastItem[mpiCountry  ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Статус&lt;/td&gt;(.+?)&lt;/td&gt;&apos; , sHtml, sVal)) PodcastItem[mpiTranslate] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Автор&lt;/td&gt;(.+?)&lt;/td&gt;&apos;, sHtml, sVal)) PodcastItem[mpiQuality  ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Режисер&lt;/td&gt;(.+?)&lt;/td&gt;&apos;, sHtml, sVal)) PodcastItem[mpiDirector ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Режисер&lt;/td&gt;(.+?)&lt;/td&gt;&apos;, sHtml, sVal)) PodcastItem[mpiDirector ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Актеры&lt;/td&gt;(.+?)&lt;/td&gt;&apos; , sHtml, sVal)) PodcastItem[mpiActor    ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;div id="serial-descriptions" itemprop="description"&gt;(.+?)&lt;/div&gt;&apos; , sHtml, sVal)) PodcastItem[mpiMood    ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Возрастная категория&lt;/td&gt;(.+?)&lt;/td&gt;&apos;    , sHtml, sVal)) PodcastItem[mpiAlbum ] = NormVal(sVal);
  if (HmsRegExMatch(&apos;(&lt;div id="serial-descriptions" itemprop="description"&gt;.*?)&lt;/div&gt;&apos;, sHtml, sVal)) PodcastItem[mpiComment] = NormVal(sVal);
  if (HmsRegExMatch(&apos;&lt;td&gt;Название:&lt;/td&gt;(.+?)&lt;/td&gt;&apos;, sHtml, sVal)) mpTitle = NormVal(sVal);
  HmsRegExMatch(&apos;itemprop="thumbnail" content="(.*?)"&apos;, sHtml, mpThumbnail);
  PodcastItem[mpiTimeLength] = gsTime;
  PodcastItem[mpiThumbnail ] = mpThumbnail;
  HmsRegExMatch(&apos;^(.*?)/&apos;, mpTitle, mpTitle);
  // =========================================================================

// Если установлен ключ отображения информационных ссылок - добавляем их
if (Pos(&apos;--addinfoitems&apos;, mpPodcastParameters)&gt;0) {
  if (Trim(PodcastItem[mpiYear     ])!=&apos;&apos;) CreateInfoItem(&apos;Год выхода&apos;          , PodcastItem[mpiYear     ]);
  if (Trim(PodcastItem[mpiGenre    ])!=&apos;&apos;) CreateInfoItem(&apos;Жанр&apos;                , PodcastItem[mpiGenre    ]);
  if (Trim(PodcastItem[mpiCountry  ])!=&apos;&apos;) CreateInfoItem(&apos;Страна&apos;              , PodcastItem[mpiCountry  ]);
  if (Trim(PodcastItem[mpiTranslate])!=&apos;&apos;) CreateInfoItem(&apos;Статус&apos;              , PodcastItem[mpiTranslate]);
  if (Trim(PodcastItem[mpiAlbum    ])!=&apos;&apos;) CreateInfoItem(&apos;Возрастная категория&apos;, PodcastItem[mpiAlbum    ]);
  if (Trim(PodcastItem[mpiQuality  ])!=&apos;&apos;) CreateInfoItem(&apos;Автор&apos;               , PodcastItem[mpiQuality  ]);
  if (Trim(PodcastItem[mpiDirector ])!=&apos;&apos;) CreateInfoItem(&apos;Режиссер&apos;            , PodcastItem[mpiDirector ]);
  if (Trim(PodcastItem[mpiMood     ])!=&apos;&apos;) CreateInfoItem(&apos;Описание&apos;            , PodcastItem[mpiMood     ]);
  if (Trim(PodcastItem[mpiRating   ])!=&apos;&apos;) CreateInfoItem(&apos;Рейтинг&apos;             , PodcastItem[mpiRating   ]);
}

}

///////////////////////////////////////////////////////////////////////////////
// Получение ссылки на медиаресурс в переменную MediaResourceLink
void GetLink() {
  if      (LeftCopy(mpFilePath, 4)==&apos;Info&apos;) ShowVideoInfo();
  else if (LeftCopy(mpFilePath, 3)==&apos;Err&apos; ) VideoMessage(gsPodcastName, mpTitle);  
  else MediaResourceLink = mpFilePath;
}

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
{
  // Проверяем, заход ли это в папку или запуск фильма на просмотр
  if (PodcastItem.IsFolder) {
    
    PodcastItem.DeleteChildItems();
       
    CreateLinks(); // Это зашли в папку подкаста (фильма) - создаём ссылки
    
    // Если это папка - проверяем ссылку, если там ссылка на json - это папка сезона
    if (ExtractFileExt(mpFilePath)==".json") CreateSeries (mpFilePath);
    else                                     CreateSeasons(mpFilePath); // Иначе считаем что это ссылка на сериал на сайте
  } else {
    // Если это просто запустили на просмотр видео, то просто передаём ссылку на файл
    MediaResourceLink = mpFilePath;
  }
}</Value>
    </Property>
    <Property>
      <ID>551</ID>
      <Value>C++Script</Value>
    </Property>
    <Property>
      <ID>527</ID>
      <Value>--addinfoitems</Value>
    </Property>
    <Property>
      <ID>50</ID>
      <Value>Фильмы (основной)_480x270</Value>
    </Property>
    <Property>
      <ID>571</ID>
      <Value>char gsUrlBase="https://serialage.tv"; int gnTotalItems=0; TDateTime gTimeStart=Now;

///////////////////////  Создание структуры подкаста  ///////////////////////// 

///////////////////////////////////////////////////////////////////////////////
// Функция создания динамической папки с указанным скриптом
THmsScriptMediaItem CreateDynamicItem(THmsScriptMediaItem prntItem, char sTitle, char sLink, char &amp;sScript=&apos;&apos;) {                                
  THmsScriptMediaItem Folder = prntItem.AddFolder(sLink, false, 32);
  Folder[mpiTitle     ] = sTitle;
  Folder[mpiCreateDate] = VarToStr(IncTime(Now,0,-prntItem.ChildCount,0,0));
  Folder[200] = 5;           // mpiFolderType
  Folder[500] = sScript;     // mpiDynamicScript
  Folder[501] = &apos;C++Script&apos;; // mpiDynamicSyntaxType
  Folder[mpiFolderSortOrder] = -mpiCreateDate;
  return Folder;
}

///////////////////////////////////////////////////////////////////////////////
// Замена в тексте загруженного скрипта значения текстовой переменной
void ReplaceVarValue(char &amp;sText, char sVarName, char sNewVal) {
  char sVal, sVal2;
  if (HmsRegExMatch2("("+sVarName+"\\s*?=.*?&apos;;)", sText, sVal, sVal2)) {
     HmsRegExMatch(sVarName+"\\s*?=\\s*?&apos;(.*)&apos;", sVal, sVal2); 
     sText = ReplaceStr(sText, sVal, ReplaceStr(sVal , sVal2, sNewVal));
  }
}

///////////////////////////////////////////////////////////////////////////////
// Создание папки ПОИСК (с загрузкой скрипта с форума homemediaserver.ru)
void CreateSearchFolder(THmsScriptMediaItem prntItem, char sTitle) {
  char sScript=&apos;&apos;, sLink, sHtml, sRE, sVal; THmsScriptMediaItem Folder;
  
  // Да да, загружаем скрипт с сайта форума HMS
  sHtml = HmsUtf8Decode(HmsDownloadURL(&apos;http://homemediaserver.ru/forum/viewtopic.php?f=15&amp;t=2793&amp;p=17395#p17395&apos;, &apos;&apos;, true));
  HmsRegExMatch(&apos;BeginDynamicSearchScript\\*/(.*?)/\\*EndDynamicSearchScript&apos;, sHtml, sScript, 1, PCRE_SINGLELINE);
  sScript = HmsHtmlToText(sScript, 1251);
  sScript = ReplaceStr(sScript, #160, &apos; &apos;);

  // И меняем значения переменных на свои
  ReplaceVarValue(sScript, &apos;gsSuggestQuery&apos;  , &apos;https://serialage.tv/search?query=&apos;);
  ReplaceVarValue(sScript, &apos;gsSuggestRegExpr&apos;, &apos;&lt;input[^&gt;]+placeholder="(.*?)"&apos;);
  ReplaceVarValue(sScript, &apos;gsSuggestMethod&apos; , &apos;GET&apos;);
  sScript = ReplaceStr(sScript, &apos;gnSuggestNoUTFEnc = 0&apos;, &apos;gnSuggestNoUTFEnc = 1&apos;);
  
  Folder = prntItem.AddFolder(sTitle, true);
  Folder[mpiCreateDate     ] = VarToStr(IncTime(gTimeStart,0,-gnTotalItems,0,0));
  Folder[mpiFolderSortOrder] = "-mpCreateDate";
  gnTotalItems++;
  
  CreateDynamicItem(Folder, &apos;"Набрать текст"&apos;, &apos;-SearchCommands&apos;, sScript);
}

///////////////////////////////////////////////////////////////////////////////
// Создание подкаста или папки
THmsScriptMediaItem CreateItem(THmsScriptMediaItem Parent, char sTitle=&apos;&apos;, char sLink=&apos;&apos;) {
  THmsScriptMediaItem Item; bool bForceFolder = false;

  if (sLink==&apos;&apos;) { sLink = sTitle; bForceFolder = true; }
  else             sLink = HmsExpandLink(sLink, gsUrlBase);
  
  Item = Parent.AddFolder(sLink, bForceFolder);
  Item[mpiTitle     ] = sTitle;
  Item[mpiCreateDate] = VarToStr(IncTime(gTimeStart,0,-gnTotalItems,0,0));
  Item[mpiFolderSortOrder] = -mpiCreateDate;
  gnTotalItems++;
  return Item;
}

///////////////////////////////////////////////////////////////////////////////
// ---------------------  M A I N  P R O C E D U R E  -------------------------
{
  THmsScriptMediaItem Folder, Item;

  FolderItem.DeleteChildItems();

  CreateSearchFolder (FolderItem, &apos;00. Поиск&apos;);
  Folder = CreateItem(FolderItem, &apos;01. Все сериалы&apos;, &apos;/&apos;);
  Folder[mpiPodcastParameters] = &apos;--group=alph&apos;;
  
  HmsLogMessage(1, mpTitle+&apos;: Создано ссылок - &apos;+IntToStr(gnTotalItems));
}</Value>
    </Property>
    <Property>
      <ID>572</ID>
      <Value>C++Script</Value>
    </Property>
  </Properties>
  <ChildItems>
    <Item>
      <ClassID>51</ClassID>
      <ItemID>5b1cf3a13b01f462811c0642059f74e8</ItemID>
      <ItemPath>00. Поиск</ItemPath>
      <ParentID>349a3c53-c37c-4d65-ae41-b8eebd7ebc9f</ParentID>
      <Properties>
        <Property>
          <ID>515</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>512</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>532</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>700</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>553</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>42</ID>
          <Value>3</Value>
        </Property>
        <Property>
          <ID>35</ID>
          <Value>43197,7245138889</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43197,7245287731</Value>
        </Property>
      </Properties>
      <ChildItems>
        <Item>
          <ClassID>32</ClassID>
          <ItemID>066816d957444a71bf4f97ab1ed8bb6a</ItemID>
          <ItemPath>-SearchCommands</ItemPath>
          <ParentID>5b1cf3a13b01f462811c0642059f74e8</ParentID>
          <Properties>
            <Property>
              <ID>515</ID>
              <Value>2</Value>
            </Property>
            <Property>
              <ID>512</ID>
              <Value>2</Value>
            </Property>
            <Property>
              <ID>532</ID>
              <Value>2</Value>
            </Property>
            <Property>
              <ID>700</ID>
              <Value>2</Value>
            </Property>
            <Property>
              <ID>553</ID>
              <Value>2</Value>
            </Property>
            <Property>
              <ID>42</ID>
              <Value>3</Value>
            </Property>
            <Property>
              <ID>4</ID>
              <Value>"Набрать текст"</Value>
            </Property>
            <Property>
              <ID>35</ID>
              <Value>43197,7238310185</Value>
            </Property>
            <Property>
              <ID>200</ID>
              <Value>5</Value>
            </Property>
            <Property>
              <ID>500</ID>
              <Value>// Var 21.12.2014
int mpiFolderType        = 200;
int mpiDynamicScript     = 500;
int mpiDynamicSyntaxType = 501;
int mpiPreviousItemID    = 200104;
int mpiDoNothing         = 201100;
char gsSpace           = &apos;Пробел&apos;;
char gsDelLastChar     = &apos;Удалить последний символ&apos;;
char gsClearSearch     = &apos;Очистить текст поиска&apos;;
char gsClearHystoryIn  = &apos;Очистить историю поиска в папке "%s"&apos;;
char gsMsgClearHistory = &apos;История поиска в папке "%s" очищена&apos;;
char gsMsgTextSaved    = &apos;Подкаст "%s" добавлен в "%s"&apos;;
char gsMsgNothingSaved = &apos;Текст поиска не набран! Добавлять нечего.&apos;;
char gsMsgSearchText   = &apos;Текст поиска: %s&apos;;
char gsAddSearchTo     = &apos;Добавить в папку "%s"&apos;;
char gsSuggestQuery    = &apos;https://serialage.tv/search?query=&apos;;
char gsSuggestResultCut= &apos;&apos;;
char gsSuggestRegExpr  = &apos;&lt;input[^&gt;]+placeholder="(.*?)"&apos;;
char gsSuggestMethod   = &apos;GET&apos;;
int  gnSuggestNoUTFEnc = 1;
char gsSuggestMessage  = &apos;Вариант: &apos;;
int gnTotalItems=0; TDateTime gTimeStart = Now; char gsTextSearch, gsMsg=&apos;&apos;;
THmsScriptMediaItem goRootItem, goCmdItem;
// ------------------------------------------ Создание папок команд поиска ----
void CreateSearchCommands() {
  int i; char sText; THmsScriptMediaItem Item;
  CreateDynamicItem(FolderItem, &apos;#&apos;,   &apos;-SearchCreateChars=From:32To:58&apos;);
  CreateDynamicItem(FolderItem, &apos;A-Z&apos;, &apos;-SearchCreateChars=From:65To:91&apos;);
  CreateDynamicItem(FolderItem, &apos;А-Я&apos;, &apos;-SearchCreateChars=From:192To:224&apos;);
  CreateDynamicItem(FolderItem, gsClearSearch, &apos;-SearchCmd=ClearSearchText&apos;);
  // Создание команды добавления в корневую папку поиска
  CreateDynamicItem(FolderItem, Format(gsAddSearchTo, [goRootItem[mpiTitle]]), &apos;-SearchCmd=SaveSearchText&apos;);
  // Создание команд добавления в папки, находящиеся в корневой, если поле "Файл" (mpiFilePath) у них пустое
  for (i=0; i&lt;goRootItem.ChildCount; i++) {
    Item = goRootItem.ChildItems[i];
    if (Item.ItemClassID!=51) continue;
    CreateDynamicItem(FolderItem, Format(gsAddSearchTo, [Item[mpiTitle]]), &apos;-SaveSearchTextTo=&apos;+Item.ItemID);
  }
  // Создание команды очистки истории в корневой папке поиска
  CreateDynamicItem(FolderItem, Format(gsClearHystoryIn, [goRootItem[mpiTitle]]), &apos;-SearchCmd=ClearSearchHistory&apos;);
  // Создание команд очистки истории в папках, которые наодятся в корневой
  for (i=0; i&lt;goRootItem.ChildCount; i++) {
    Item = goRootItem.ChildItems[i];
    if (Item.ItemClassID!=51) continue;
    CreateDynamicItem(FolderItem, Format(gsClearHystoryIn, [Item[mpiTitle]]), &apos;-ClearSearchHistoryIn=&apos;+Item.ItemID);
  }
  // Создание ссылки, информирующей о текущем набранном тексте поиска
  sText = Format(gsMsgSearchText, [gsTextSearch]);
  Item = HmsCreateMediaItem(sText, goCmdItem.ItemID);
  Item[mpiTitle    ] = sText;
  Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;;
}
// ---------------------------- Добавление подкаста с именем текста поиска ----
void AddPodcastSearch(THmsScriptMediaItem prntItem) {
  THmsScriptMediaItem Item;
  if (Trim(gsTextSearch)==&apos;&apos;) {gsMsg = gsMsgNothingSaved; return;}
  if (LowerCase(gsTextSearch)==gsTextSearch) gsTextSearch = NameCase(gsTextSearch);
  Item = prntItem.AddFolder(gsTextSearch, false);
  Item[mpiFilePath ] = Format(&apos;search="%s"&apos;, [gsTextSearch]);
  Item[mpiTitle    ] = gsTextSearch;
  HmsDatabaseAutoSave(false);
  gsMsg = Format(gsMsgTextSaved, [gsTextSearch, prntItem[mpiTitle]]);
  //gsTextSearch = &apos;&apos;;
}
// --------- Функция создания динамической папки с унаследованным скриптом ----
void CreateDynamicItem(THmsScriptMediaItem prntItem, char sTitle, char sLink) {
char s; THmsScriptMediaItem Folder;
  if (Trim(sTitle)==&apos;&apos;) sTitle = gsSpace;
  Folder = prntItem.AddFolder(sLink, true);
  Folder[mpiTitle            ] = sTitle;
  Folder[mpiCreateDate       ] = VarToStr(IncTime(gTimeStart,0,-gnTotalItems,0,0)); gnTotalItems++;
  Folder[mpiFolderType       ] = prntItem[mpiFolderType];
  Folder[mpiDynamicScript    ] = prntItem[mpiDynamicScript];
  Folder[mpiDynamicSyntaxType] = prntItem[mpiDynamicSyntaxType];
  Folder[mpiFolderSortOrder  ] = prntItem[mpiFolderSortOrder];
}
// ----------------------------  MAIN PROCEDURE  ------------------------------
{
  int i, nCnt; char sCh, sCh1, sCh2, sText; THmsScriptMediaItem Item;
  char s, sUrlServer = &apos;&apos;, sRequestPage = &apos;/&apos;, sPostData = &apos;&apos;, sHeaders = &apos;&apos;;
  if (LeftCopy(mpFilePath, 1) != &apos;-&apos;) return; // Если это не команда - то и не обрабатываем
  // Поиск корневого каталога поиска (папки, которая содержит динамическую папку с путем "-SearchCommands")
  goRootItem = FolderItem; goCmdItem = FolderItem;
  while ((goRootItem.ItemParent != HmsDatabaseRootItem) &amp;&amp; (goRootItem.ItemParent != nil)) {
    if (goRootItem[mpiFilePath] == &apos;-SearchCommands&apos;) {goCmdItem=goRootItem; goRootItem=goCmdItem.ItemParent; break;}
    goRootItem = goRootItem.ItemParent;
  }
  // Если это повторный вызов, смены папки не произошло - ничего не делаем
  if ((FolderItem.ItemID==goRootItem[mpiPreviousItemID]) &amp;&amp; !DebugMode &amp;&amp; (FolderItem!=goCmdItem)) return;
  FolderItem.DeleteChildItems(); goRootItem[mpiPreviousItemID] = FolderItem.ItemID;
  gsTextSearch = HmsGetUserSearchText(); // Получаем текущее значение текста поиска
  // ---- Проверяем команды - по значению в mpFilePath ----
  if (goRootItem[mpiDoNothing]==&apos;1&apos;) {   // Флаг "Ничего не делать" - например, при возврате
    goRootItem[mpiDoNothing] = &apos;&apos;;       //   в команду набирания буквы из подпапки варианта
  } else if (HmsRegExMatch(&apos;-SearchCommands&apos;, mpFilePath, sCh1)) {              // Создание корневых команд поиска
    CreateSearchCommands(); return;
  } else if (HmsRegExMatch(&apos;-SearchChar=(\\d+)&apos;, mpFilePath, sCh1)) {           // Добавление буквы (символа) к набираемому тексту поиска
    gsTextSearch += Chr(StrToInt(sCh1));
  } else if (HmsRegExMatch(&apos;-SetSearchText=(.*)&apos;, mpFilePath, gsTextSearch)) {  // Назначить текст поиска значением варианта подсказки
    goRootItem[mpiDoNothing] = &apos;1&apos;; gsSuggestQuery = &apos;&apos;; // включаем флаг не выполнять команду при возврате из этой папки
  } else if (HmsRegExMatch(&apos;-SaveSearchTextTo=(.*)&apos;, mpFilePath, sCh1)) {       // Добавление в папку Х. Поиск этой папки по ItemID.
    Item = goRootItem; // Ищем папку с ItemID равному идентификатору, переданному в mpFilePath
    for (i=0; i&lt;goRootItem.ChildCount; i++) {
      if (goRootItem.ChildItems[i].ItemID==sCh1) {Item = goRootItem.ChildItems[i]; break;}
    }
    AddPodcastSearch(Item); // и добавляем в найденную папку подкаст текстом поиска
  } else if (HmsRegExMatch(&apos;-ClearSearchHistoryIn=(.*)&apos;, mpFilePath, sCh1)) {   // Очистка истории (добавленных ранее) поисковых значений
    Item = goRootItem; // Ищем папку с ItemID равному идентификатору, переданному в mpFilePath
    for (i=0; i&lt;goRootItem.ChildCount; i++) {
      if (goRootItem.ChildItems[i].ItemID==sCh1) {Item = goRootItem.ChildItems[i]; break;}
    }
    for(i=0; i&lt;Item.ChildCount; i++) { // Удаляем все элементы с начинающимся словом &apos;search&apos; в поле mpiFilePath
      if (LeftCopy(Item.ChildItems[i].Properties[mpiFilePath], 6)==&apos;search&apos;) {Item.ChildItems[i].Delete();i--;}
    }
    gsMsg = Format(gsMsgClearHistory, [Item[mpiTitle]]); // вывод сообщения, что история очищена
  } else if (HmsRegExMatch(&apos;-SearchCmd=(\\w+)&apos;,   mpFilePath, sCh1)) {
    if      (sCh1==&apos;DeleteLastChar&apos; ) gsTextSearch = LeftCopy(gsTextSearch, Length(gsTextSearch)-1); // Удаление последнего символа
    else if (sCh1==&apos;SaveSearchText&apos; ) AddPodcastSearch(goRootItem);             // Добавить текст поиска в корневую папку
    else if (sCh1==&apos;ClearSearchText&apos;) gsTextSearch = &apos;&apos;;                        // Очистка текста поиска
    else if (sCh1==&apos;ClearSearchHistory&apos;) {                                      // Очистка истории (добавленных значений поиска)
      for(i=0; i&lt;goRootItem.ChildCount; i++) {
        Item = goRootItem.ChildItems[i]; // Ищем все элементы, у которых значение mpiFilePath начиначется с &apos;search&apos;
        if (LeftCopy(Item[mpiFilePath], 6)==&apos;search&apos;) {goRootItem.ChildItems[i].Delete();i--;}
      }
      gsMsg = Format(gsMsgClearHistory, [goRootItem[mpiTitle]]);
    }
  } else if (HmsRegExMatch2(&apos;-SearchCreateChars=From:(\\d+)To:(\\d+)&apos;, mpFilePath, sCh1, sCh2)) { // Создание списка букв/символов
    CreateDynamicItem(FolderItem, gsDelLastChar, &apos;-SearchCmd=DeleteLastChar Suggestions&apos;); // В начало - команда удаления символа
    CreateDynamicItem(FolderItem, &apos; &apos;, &apos;-SearchChar=32 Suggestions&apos;); // Пробел
    for (i= StrToInt(sCh1); i&lt;StrToInt(sCh2); i++) CreateDynamicItem(FolderItem, Chr(i), &apos;-SearchChar=&apos;+IntToStr(i)+&apos; Suggestions&apos;);
    // слово "Suggestions" добавлено к значению mpiFilePath тек команд, в которых возможно использование подсказок
  }
  else return; // Если неизвестная нам команда - просто выходим
  HmsSetUserSearchText(gsTextSearch); // Устанавливаем значение текста поиска
  Item = HmsCreateMediaItem(&apos;-command=none&apos;, FolderItem.ItemID);
  if (gsMsg==&apos;&apos;) { // Если сообщения небыло - просто выводим ссылку с текущим значением набранного текста
    Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;;
    Item[mpiFilePath ] = Format(gsMsgSearchText, [gsTextSearch]);
  } else {         // Выводим сообщение
    Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/ok.png&apos;;
    Item[mpiFilePath ] = gsMsg;
  }
  Item = HmsCreateMediaItem(&apos;-command=none&apos;, FolderItem.ItemParent.ItemID);     // В родительской папке (выше) обновляем информацию о текущем значении текста
  Item[mpiFilePath]  = Format(gsMsgSearchText, [gsTextSearch]);
  Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;;
  Item = HmsCreateMediaItem(&apos;-command=none&apos;, goCmdItem.ItemID);                 // Также в папке, содержащую главные команды поиска
  Item[mpiFilePath]  = Format(gsMsgSearchText, [gsTextSearch]);
  Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;;
  // Suggestions ------ Блок работы с подсказками -------
  if ((gsSuggestQuery!=&apos;&apos;) &amp;&amp; (Pos(&apos;Suggestions&apos;, mpFilePath)&gt;0) &amp;&amp; (Length(gsTextSearch)&gt;1) &amp;&amp; !HmsRegExMatch("^\\s", gsTextSearch, "")) {
    int nPort = 80; if (LeftCopy(gsSuggestQuery, 5)=="https") nPort = 443;
    sText = gsTextSearch; if (gnSuggestNoUTFEnc==0) sText = HmsUtf8Encode(sText); // Если не указано не кодировать в UTF - кодируем
    sText = HmsHttpEncode(sText); string sUrlBase;
    // Если есть ключ &lt;TEXT&gt; в запросе - заменяем его на значение набранного текста, иначе просто добавляем в конец
    if (Pos(&apos;&lt;TEXT&gt;&apos;, gsSuggestQuery)&gt;0) gsSuggestQuery = ReplaceStr(gsSuggestQuery, &apos;&lt;TEXT&gt;&apos;, sText);
    else gsSuggestQuery = gsSuggestQuery + sText;
    HmsRegExMatch3(&apos;(https?://(.*?))(/.*)&apos;, gsSuggestQuery, sUrlBase, sUrlServer, sRequestPage);
    if (gsSuggestMethod==&apos;POST&apos;) HmsRegExMatch2(&apos;^(.*?)\\?(.*)&apos;, sRequestPage, sRequestPage, sPostData);
    sHeaders = sUrlBase+&apos;/\r\n&apos;+
               &apos;Accept-Encoding: gzip, deflate\r\n&apos;+
               &apos;User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0\r\n&apos;+
               &apos;Connection: Keep-Alive\r\n&apos;+
               &apos;Origin: &apos;+sUrlBase+&apos;/\r\n&apos;+
               &apos;Accept: application/json, text/javascript, */*; q=0.01\r\n&apos;;    // Для включения возможности gzip в запросах
    sText = HmsSendRequestEx(sUrlServer, sRequestPage, gsSuggestMethod, &apos;application/x-www-form-urlencoded; Charset=UTF-8&apos;, sHeaders, sPostData, nPort, 0x10, &apos;&apos;, true);
    sText = HmsUtf8Decode(sText);
    if (gsSuggestResultCut!=&apos;&apos;) HmsRegExMatch(gsSuggestResultCut, sText, sText);// Если есть выражение обрезки - обрезаем
    sText = HmsJsonDecode(sText); TRegExpr t = TRegExpr.Create(&apos;(&lt;[^&gt;]+&gt;)&apos;);    // Избавляемся от тегов в середине слов подсказки
    TRegExpr reSearch = TRegExpr.Create(gsSuggestRegExpr, PCRE_SINGLELINE);
    if (reSearch.Search(sText)) do {
      s = reSearch.Match;
      if (t.Search(s)) do s=ReplaceStr(s, t.Match, &apos;&apos;); while (t.SearchAgain());// (функция HmsHtmlToText не подходит т.к. ставит пробел в середине слова)
      if (HmsRegExMatch(&apos;^(.*?)[/\\(\\|]&apos;, s, sCh)) {                           // Обрезаем подсказку до знаков /, ( или |
        if (Pos(LowerCase(gsTextSearch), LowerCase(sCh))&gt;0) s = sCh;            // Если после этого в подсказке встречается набранный текст - то так и оставляем
      }
      if (LowerCase(s)==s) s = NameCase(s);                                     // Если подсказки - все маленькие буквы, делаем NameCase
      // Если в подсказке больше одного слова - дополнительно создаём сначала подсказки из слов, которые содержат набранный текст (выделяем слова отдельно)
      if (WordCount(s, &apos; &apos;)&gt;1) {
        nCnt = WordCount(s, &apos; &apos;);
        for (i=1; i&lt;=nCnt; i++) {
          sCh=ExtractWord(i, s, &apos; &apos;); if (Trim(sCh)==&apos;&apos;) continue;
          if (Pos(LowerCase(gsTextSearch), LowerCase(sCh))&lt;1) continue;
          if (LowerCase(gsTextSearch)==LowerCase(sCh)) continue;
          sCh = ReplaceStr(sCh, &apos;:&apos;, &apos;&apos;); sCh=ReplaceStr(sCh, &apos;\\&apos;, &apos;&apos;);
          CreateDynamicItem(FolderItem, gsSuggestMessage+sCh, &apos;-SetSearchText=&apos;+sCh);
        }
      }
      // Создаём папку с предложением варианта (подсказку)
      if (LowerCase(s)!=LowerCase(gsTextSearch)) CreateDynamicItem(FolderItem, gsSuggestMessage+s, &apos;-SetSearchText=&apos;+s);
      if (gnTotalItems&gt;100) break; // Ограничиваем количество создаваемых элементов = 100
    } while (reSearch.SearchAgain());
  }
  HmsIncSystemUpdateID(); // Говорим устройству об обновлении содержания
}</Value>
            </Property>
            <Property>
              <ID>501</ID>
              <Value>C++Script</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-35</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43197,7245287731</Value>
            </Property>
          </Properties>
        </Item>
      </ChildItems>
    </Item>
    <Item>
      <ClassID>53</ClassID>
      <ItemID>b255a83526c7115b5e459c94d3e44146</ItemID>
      <ItemPath>https://serialage.tv/</ItemPath>
      <ParentID>349a3c53-c37c-4d65-ae41-b8eebd7ebc9f</ParentID>
      <Properties>
        <Property>
          <ID>515</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>512</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>532</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>700</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>553</ID>
          <Value>2</Value>
        </Property>
        <Property>
          <ID>42</ID>
          <Value>3</Value>
        </Property>
        <Property>
          <ID>4</ID>
          <Value>01. Все сериалы</Value>
        </Property>
        <Property>
          <ID>35</ID>
          <Value>43197,7238194444</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-35</Value>
        </Property>
        <Property>
          <ID>527</ID>
          <Value>--group=alph</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43197,7245287731</Value>
        </Property>
        <Property>
          <ID>525</ID>
          <Value>43197,7246184491</Value>
        </Property>
      </Properties>
    </Item>
  </ChildItems>
</HmsMediaItem>
