<?xml version="1.0" encoding="utf-8"?>
<HmsMediaItem>
  <MediaType>3</MediaType>
  <ClassID>51</ClassID>
  <ItemID>b65318d7-8a9a-425f-92b9-9feba609ef39</ItemID>
  <ItemPath>https://www.youtube.com</ItemPath>
  <ParentID>234DF17B-418C-4FDC-9DFE-CD0C586D2E76</ParentID>
  <Properties>
    <Property>
      <ID>4</ID>
      <Value>Youtube v4.4</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>1</Value>
    </Property>
    <Property>
      <ID>512</ID>
      <Value>1</Value>
    </Property>
    <Property>
      <ID>530</ID>
      <Value>#define mpiTokensInfo    591 // Пароль, где хранится информация о токенах (при сохранении hdf эти данные в файле не сохраняются)
#define mpiAuthorization 594 // Авторизация пользователя: 1 - включена, 2 - выключена, 0 - наследуется
THmsScriptMediaItem PodcastItem = FolderItem; string MediaResourceLink = &apos;&apos;;
///////////////////////////////////////////////////////////////////////////////
//               Г Л О Б А Л Ь Н Ы Е   П Е Р Е М Е Н Н Ы Е                   //
int    gnTotalItems  = 0;   // Глобальный счетчик созданных ссылок 
string gsUrlBase     = &apos;https://www.youtube.com&apos;;
string gsHeaders     = &apos;Accept-Encoding: gzip, deflate\r\n&apos;;
string gsPodcastName = &apos;HMS Youtube v4.4&apos;; // Заголовок видео-сообщения при ошибках
string gsAPIKey      = &apos;&apos;;
string gsClientId    = &apos;&apos;;
string gsClientSecret= &apos;&apos;;
string gsLanguage    = &apos;ru-RU&apos;;
string gsRegion      = &apos;RU&apos;;
TDateTime gStart     = Now;
Variant gREQUEST; // Для хранения созданного OLE объекта WinHttp.WinHttpRequest.5.1
THmsScriptMediaItem gRoot =  GetRoot(); // Корневой элемент подкаста, содержащий информацию о токенах 
TStringList gACCESS = LoadAccessInfo(); // Создание объекта хранения информации о токенах

///////////////////////////////////////////////////////////////////////////////
//                             Ф У Н К Ц И И                                 //

///////////////////////////////////////////////////////////////////////////////
// Поиск корневой папки подкаста, где мы будем хранить в mpiAccessToken
THmsScriptMediaItem GetRoot() {
  gRoot = PodcastItem; // Начиная с текущего элемента, ищется создержащий срипт или информацию о токенах
  while ((Trim(gRoot[mpiTokensInfo])==&apos;&apos;) &amp;&amp; (Trim(gRoot[550])==&apos;&apos;) &amp;&amp; (gRoot[532]!=&apos;1&apos;) &amp;&amp; (gRoot.ItemParent!=nil)) gRoot=gRoot.ItemParent; 
  return gRoot;
}

///////////////////////////////////////////////////////////////////////////////
// Загрузка данных о токенах доступа
TStringList LoadAccessInfo() {
  gACCESS = TStringList.Create();
  if (Pos(&apos;=&apos;, Trim(gRoot[mpiTokensInfo]))&gt;0)
    gACCESS.Text = gRoot[mpiTokensInfo];
  else
    gACCESS.Text = mpPodcastAuthorizationPassword;
  return gACCESS;
}

///////////////////////////////////////////////////////////////////////////////
// Сохранение данных о токенах доступа
void SaveAccessInfo() {
  gRoot[mpiTokensInfo   ] = gACCESS.Text;
  gRoot[mpiAuthorization] = &apos;1&apos;; // Включение авторизации пользователя, чтобы данные сохранялись после перезагрузки
}

///////////////////////////////////////////////////////////////////////////////
// Запрос к API
string APIRequest(string sObject, string sParam) {
  string sData, sLink, sLang, sErr, sHeaderName, sHeaderValue; int i;
  sLang = Lowercase(gsRegion);
  if (sObject==&apos;search&apos;) {
    gsAPIKey  = &apos;AIzaSyCPxJ1HKZznL9WEFay70tLjyzDrry0DiEw&apos;;
    gsHeaders = &apos;&apos;;
  }
  sLink = &apos;https://www.googleapis.com/youtube/v3/&apos;+sObject+&apos;?key=&apos;+gsAPIKey+&apos;&amp;maxResults=50&amp;gl=&apos;+sLang+&apos;&amp;regionCode=&apos;+sLang+&apos;&amp;hl=&apos;+sLang+&apos;&amp;&apos;+sParam;
  // И всё этот бред с CreateOleObject для того, чтобы была возможность получить тело ответа при коде ответа отличных от 200 для анализа ошибок
  try {
    if (VarType(gREQUEST)!=9) gREQUEST = CreateOleObject(&apos;WinHttp.WinHttpRequest.5.1&apos;); // Если ранее не создавался объект, то создаём
    gREQUEST.open(&apos;GET&apos;, sLink, false);
    for (i=1; i &lt;= WordCount(gsHeaders, &apos;\r\n&apos;) ; i++) {
      if (!HmsRegExMatch2(&apos;^(.*?):(.*)&apos;, ExtractWord(i, gsHeaders, &apos;\r\n&apos;), sHeaderName, sHeaderValue)) continue;
      gREQUEST.SetRequestHeader(sHeaderName, sHeaderValue);
    }
    gREQUEST.send();
    sData = gREQUEST.ResponseText;
    if (gREQUEST.Status!=200) {
      HmsRegExMatch(&apos;"message"\\s*:\\s*"(.*?)"&apos;, sData, sErr);
      HmsLogMessage(2, "Ошибка API: "+HmsHtmlToText(HmsJsonDecode(sErr)));
    }
  } except {
    sData = HmsDownloadUrl(sLink, gsHeaders, true);
    if (sData==&apos;&apos;) HmsLogMessage(2, "Ошибка API: запрос вернул пустой результат. "+sLink);
  }
  return sData;
}

///////////////////////////////////////////////////////////////////////////////
// Показ информации, сохранённой в ссылке
void ShowInfo() {
  string sTitle, sCateg, sInfo, sDescr, sImg, prefix=&apos;youtubev3&apos;;
  TStrings INFO = TStringList.Create();
  INFO.Text = PodcastItem[1001001];
  sTitle = INFO.Values[&apos;Title&apos; ];
  sCateg = INFO.Values[&apos;Categ&apos; ];
  sInfo  = INFO.Values[&apos;Info&apos;  ];
  sDescr = INFO.Values[&apos;Descr&apos; ];
  sImg   = INFO.Values[&apos;Poster&apos;];
  INFO.Free();
  MediaResourceLink = GenerateVideoInfo(prefix, sTitle, sInfo, sCateg, sDescr, sImg);
}

///////////////////////////////////////////////////////////////////////////////
// Генерирование картинок и строки для ffmpeg
char GenerateVideoInfo(char prefix, char sTitle, char sInfo=&apos;&apos;, char sCateg=&apos;&apos;, char sDescr=&apos;&apos;, char sImg=&apos;&apos;, int mode=0, char sDirID=&apos;&apos;) {
  char sHtml, sPost, sVal, sCol, sLink, sData, sPos, sImagesDir;
  int i, n, nSeconds, nSecDel, nMaxImages, nMaxTextLength, nPage=0; double nH, nW; 
  char sFileImage, sCmd, sColor, sTime; TRegExpr reChannel, reCast;
  int xMargin=15, yMargin=15;
  
  nSeconds = 0; nMaxImages = 4; nMaxTextLength = 0;
  if (HmsRegExMatch(&apos;--xmargin=(\\d+)&apos;, mpPodcastParameters, sVal)) xMargin=StrToInt(sVal);
  if (HmsRegExMatch(&apos;--ymargin=(\\d+)&apos;, mpPodcastParameters, sVal)) yMargin=StrToInt(sVal);
  if (Pos(&apos;--lq&apos;, mpPodcastParameters)&gt;0) {
    nH = cfgTranscodingScreenHeight / 2;
    nW = cfgTranscodingScreenWidth  / 2;
  } else if (HmsRegExMatch2(&apos;--pr=(\\d+)x(\\d+)&apos;, mpPodcastParameters, sVal, sPos)) {
    nH = StrToInt(sPos);
    nW = StrToInt(sVal);
  } else {
    nH = cfgTranscodingScreenHeight;
    nW = cfgTranscodingScreenWidth;
  }
  if (sDirID==&apos;&apos;) sDirID = &apos;info_&apos;+PodcastItem.ItemID;
  sImagesDir = IncludeTrailingBackslash(ExtractShortPathName(HmsTempDirectory))+Trim(prefix);
  sImagesDir = IncludeTrailingBackslash(sImagesDir)+sDirID;
  ForceDirectories(sImagesDir);
  sFileImage = IncludeTrailingBackslash(sImagesDir)+&apos;info_&apos;;
  sPost = &apos;&apos;;                       
  TStrings INFO = TStringList.Create();
  INFO.Values[&apos;prfx&apos; ] = prefix;
  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;xpic&apos; ] = &apos;15&apos;;
  INFO.Values[&apos;ypic&apos; ] = &apos;15&apos;;
  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;fz&apos;   ] = &apos;3&apos;;
  INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/22));
  INFO.Values[&apos;fztitle&apos;] = IntToStr(Round(nH/18));
  INFO.Values[&apos;fzinfo&apos; ] = IntToStr(Round(nH/25));
  if (mode==1) {
    INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/16));
    INFO.Values[&apos;fztitle&apos;] = IntToStr(Round(nH/10));
    INFO.Values[&apos;fzinfo&apos; ] = IntToStr(Round(nH/14));
    INFO.Values[&apos;xm&apos;] = IntToStr(Round(nH/14));
  } else if ((mode==2) || (mode==3)) {
    INFO.Values[&apos;ct&apos;    ] = &apos;000&apos;;
    INFO.Values[&apos;ctitle&apos;] = &apos;822&apos;;
    INFO.Values[&apos;cinfo&apos; ] = &apos;223&apos;;
    INFO.Values[&apos;ccateg&apos;] = &apos;255&apos;;
    INFO.Values[&apos;fz&apos;] = &apos;6&apos;;
    INFO.Values[&apos;ns&apos;] = &apos;1&apos;;
    INFO.Values[&apos;bg&apos;] = &apos;./backgrounds/white.png&apos;;
    INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nH/3));
    if (mode==2) INFO.Values[&apos;wpic&apos;] = IntToStr(Round(nH/2));
  }
  if (sImg  !=&apos;&apos;) {
    INFO.Values[&apos;urlpic&apos;] = sImg;
    INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nH/2));
  }
  n = 0; TStrings FILELIST = TStringList.Create();
  try { 
    HmsGetFileList(sImagesDir, FILELIST, &apos;*.jpg&apos;);
    n = FILELIST.Count;
  } finally { FILELIST.Free(); }
  if (n&lt;4) {
    // Получаем ссылки на сформированные картинки, пока в параметрах встречаем lastpos=N
    sPos = &apos;0&apos;; nPage = 0;
    do {
      sColor = &apos;&apos;; sTime = &apos;&apos;; nPage++; 
      if (nPage&gt;1) {
        sData = Copy(sDescr, 1, StrToInt(sPos)); // Блок, успевший попасть на экран
        // Определяем последний использующийся цвет 
        HmsRegExMatch(&apos;.*(\\d{2}:\\d{2}\\s*?-\\s*?\\d{2}:\\d{2})&apos;, sData, sTime, 1, PCRE_SINGLELINE);
        sTime += &apos; ... &apos;;
      }
      sDescr = sColor+sTime+Trim(Copy(sDescr, StrToInt(sPos), 999999)); 
      INFO.Values[&apos;descr&apos;] = sDescr; 
      sPost = &apos;&apos;;
      for (n=0; n&lt;INFO.Count; n++) sPost += &apos;&amp;&apos;+Trim(INFO.Names[n])+&apos;=&apos;+HmsHttpEncode(INFO.Values[INFO.Names[n]]);
      sLink = HmsSendRequestEx(&apos;wonky.lostcut.net&apos;, &apos;/videopreview.php&apos;, &apos;POST&apos;, 
      &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 80, 0, &apos;&apos;, true);
      if (LeftCopy(sLink, 4)!=&apos;http&apos;) {HmsLogMessage(2, &apos;Ошибка получения картинки видеоинформации&apos;); return;}
      HmsDownloadURLToFile(sLink, sFileImage);
      for (n=0; n&lt;5; n++) { nSeconds++; CopyFile(sFileImage, sFileImage+Format(&apos;%.3d.jpg&apos;, [nSeconds]), false); }
      if (nPage&gt;=nMaxImages) break;
    } while (HmsRegExMatch2(&apos;^(.*?)\\?lastpos=(\\d+)&apos;, sLink, sLink, sPos));  
  }
  INFO.Free();
  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;; }
  sLink = Format(&apos;%s -f image2 -r 1 -i "%s" -c:v libx264 -r 30 -pix_fmt yuv420p &apos;, [sFileMP3, sFileImage+&apos;%03d.jpg&apos;]);
  PodcastItem[mpiTimeLength] = HmsTimeFormat(nSeconds)+&apos;.000&apos;;
  return sLink;
}

///////////////////////////////////////////////////////////////////////////////
// Создание информационной ссылки
void InfoItem(string sMsg) {
  THmsScriptMediaItem Item = HmsCreateMediaItem(&apos;Info&apos;+Str(PodcastItem.ChildCount), PodcastItem.ItemID);
  Item[mpiTitle     ] = sMsg;
  Item[mpiThumbnail ] = &apos;http://wonky.lostcut.net/vids/info.jpg&apos;;
  Item[mpiCreateDate] = IncTime(gStart, 0, -gnTotalItems, 0, 0); gnTotalItems++;
  HmsLogMessage(2, sMsg);
}

///////////////////////////////////////////////////////////////////////////////
// Запрос авторизации для нового устройства
void RequestNewCode() {
  string sPost, sData, sRet; TJsonObject JSON = TJsonObject.Create();
  try {
    sPost = &apos;scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube&amp;client_id=&apos;+gsClientId;
    sData = HmsSendRequestEx(&apos;accounts.google.com&apos;, &apos;/o/oauth2/device/code&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
    JSON.LoadFromString(sData);
    gACCESS.Values[&apos;code&apos;            ] = JSON.S[&apos;device_code&apos;];
    gACCESS.Values[&apos;verification_url&apos;] = JSON.S[&apos;verification_url&apos;];
    gACCESS.Values[&apos;user_code&apos;       ] = JSON.S[&apos;user_code&apos;];
    gACCESS.Values[&apos;expires_date&apos;    ] = &apos;&apos;;
    SaveAccessInfo();
    InfoItem(&apos;Перейдите по адресу &apos;+JSON.S[&apos;verification_url&apos;]);
    InfoItem(&apos;Введите код: &apos;       +JSON.S[&apos;user_code&apos;       ]);
    InfoItem(&apos;Потом обновите раздел.&apos;);
  } finally { JSON.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Авторизация
bool Login(bool bSilent=false) {
  TJsonObject JSON; string sData, sPost, sRet; bool bResult=false;
  JSON = TJsonObject.Create();
  try {
    if (TimeStamp1970ToDateTime(StrToIntDef(gACCESS.Values[&apos;expires_date&apos;], 0), false)&gt;Now) { bResult=true; return true; }
    if (gACCESS.Values[&apos;code&apos;]!=&apos;&apos;) {
      if (bSilent) return false;
      sPost = &apos;client_secret=&apos;+gsClientSecret+&apos;&amp;code=&apos;+gACCESS.Values[&apos;code&apos;]+&apos;&amp;client_id=&apos;+gsClientId+&apos;&amp;grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fdevice%2F1.0&apos;;
      sData = HmsSendRequestEx(&apos;www.googleapis.com&apos;, &apos;/oauth2/v4/token&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
      JSON.LoadFromString(sData);
      if (JSON.S[&apos;access_token&apos;]!=&apos;&apos;) {
        gACCESS.Values[&apos;access_token&apos; ] = JSON.S[&apos;access_token&apos; ];
        gACCESS.Values[&apos;refresh_token&apos;] = JSON.S[&apos;refresh_token&apos;];
        gACCESS.Values[&apos;expires_date&apos; ] = Str(DateTimeToTimeStamp1970(Now, false)+JSON.I[&apos;expires_in&apos;]);
        gACCESS.Values[&apos;code&apos;] = &apos;&apos;;
        SaveAccessInfo();
      } else if (Pos(&apos;428 Precondition Required&apos;, sRet)&gt;0) {
        if (!bSilent) {
          InfoItem(&apos;Ожидание подтверждения авторизации&apos;);
          InfoItem(&apos;По адресу &apos;+gACCESS.Values[&apos;verification_url&apos;]);
          InfoItem(&apos;Ввести код: &apos;+gACCESS.Values[&apos;user_code&apos;]);
        }
        return false;
      }
    }
    if (gACCESS.Values[&apos;access_token&apos;]==&apos;&apos;) { if (!bSilent) RequestNewCode(); return false; }
    // Проверка работоспособности токена
    sData = HmsDownloadURL(&apos;https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=&apos;+gACCESS.Values[&apos;access_token&apos;]);
    JSON.LoadFromString(sData);
    if (!JSON.B[&apos;azp&apos;]) {
      sPost = &apos;client_id=&apos;+gsClientId+&apos;&amp;client_secret=&apos;+gsClientSecret+&apos;&amp;refresh_token=&apos;+gACCESS.Values[&apos;refresh_token&apos;]+&apos;&amp;grant_type=refresh_token&apos;;
      sData = HmsSendRequestEx(&apos;www.googleapis.com&apos;, &apos;/oauth2/v4/token&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
      JSON.LoadFromString(sData);
      gACCESS.Values[&apos;access_token&apos;] = JSON.S[&apos;access_token&apos;];
    }
    gACCESS.Values[&apos;expires_date&apos;] = Str(DateTimeToTimeStamp1970(Now, false)+JSON.I[&apos;expires_in&apos;]);
    SaveAccessInfo();
    bResult = (gACCESS.Values[&apos;access_token&apos;]!=&apos;&apos;);
  } finally { 
    JSON.Free; 
    if (bResult) {
      gsHeaders += &apos;Authorization: Bearer &apos;+gACCESS.Values[&apos;access_token&apos;]+&apos;\r\n&apos;;
    } 
  }
  return bResult;
}

///////////////////////////////////////////////////////////////////////////////
// Получение ссылки на Youtube
bool GetLink_Youtube33(string sLink) {
  string sData, sVideoID=&apos;&apos;, sAudio=&apos;&apos;, sSubtitlesLanguage=&apos;ru&apos;,
  sSubtitlesUrl, sFile, sVal, sMsg, sConfig, sHeaders, hlsUrl, subsUrl, jsUrl, 
  streamMap, algorithm, sType, itag, sig, alg, s, sp; TStringList QLIST;
  int i, n, w, num, height, priority, minPriority = 90, selHeight, maxHeight = 1080, audioQual;
  TJsonObject JSON, PLAYER_RESPONSE, VIDEO; TJsonArray FORMATS, SUBS; TRegExpr RegEx;
  
  sHeaders = &apos;Referer: &apos;+sLink+&apos;\r\n&apos;+
             &apos;User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36\r\n&apos;+
             &apos;Origin: https://www.youtube.com\r\n&apos;;
  
  if (HmsRegExMatch(&apos;--maxheight=(\\d+)&apos;, mpPodcastParameters, sVal)) maxHeight = StrToInt(sVal);
  HmsRegExMatch(&apos;--sublanguage=(\\w{2})&apos;, mpPodcastParameters, sSubtitlesLanguage);
  bool bSubtitles = Pos(&apos;--subtitles&apos; , mpPodcastParameters)&gt;0;  
  bool bAdaptive  = Pos(&apos;--adaptive&apos;  , mpPodcastParameters)&gt;0;  
  bool bQualLog   = Pos(&apos;--qualitylog&apos;, mpPodcastParameters)&gt;0;
  
  if (!HmsRegExMatch(&apos;[\\?&amp;]v=([^&amp;]+)&apos;       , sLink, sVideoID))
  if (!HmsRegExMatch(&apos;youtu.be/([^&amp;]+)&apos;      , sLink, sVideoID))
       HmsRegExMatch(&apos;/(?:embed|v)/([^\\?]+)&apos;, sLink, sVideoID);
  
  if (sVideoID==&apos;&apos;) return;
  
  sLink = gsUrlBase+&apos;/watch?v=&apos;+sVideoID+&apos;&amp;hl=ru&apos;;
  sData = HmsDownloadURL(sLink, sHeaders, true);
  sData = HmsRemoveLineBreaks(sData);
  
  if (!HmsRegExMatch(&apos;player.config\\s*=\\s*({.*?});&apos;, sData, sConfig, 1, PCRE_SINGLELINE)) {
    HmsLogMessage(2 , mpTitle+&apos;: No player.config data in loaded page.&apos;); 
    return; 
  }
  
  JSON = TJsonObject.Create();
  PLAYER_RESPONSE = TJsonObject.Create();
  QLIST = TStringList.Create();
  try {
    JSON.LoadFromString(sConfig);
    PLAYER_RESPONSE.LoadFromString(JSON.S[&apos;args\\player_response&apos;]);
    
    // Если есть субтитры и в дополнительных параметрах указано их показывать - загружаем 
    if (bSubtitles &amp;&amp; PLAYER_RESPONSE.B[&apos;captions\\playerCaptionsTracklistRenderer\\captionTracks&apos;]) {
      string sTime1, sTime2, engSubs; float nStart, nDur;
      SUBS = PLAYER_RESPONSE.O[&apos;captions\\playerCaptionsTracklistRenderer\\captionTracks&apos;].AsArray;
      for (i=0; i &lt; SUBS.Length; i++) {
        if (SUBS[i].S[&apos;languageCode&apos;]==sSubtitlesLanguage) subsUrl = SUBS[i].S[&apos;baseUrl&apos;];
        if (SUBS[i].S[&apos;languageCode&apos;]==&apos;en&apos;              ) engSubs = SUBS[i].S[&apos;baseUrl&apos;];
      }
      if ((subsUrl==&apos;&apos;) &amp;&amp; (engSubs!=&apos;&apos;)) subsUrl = engSubs+&apos;&amp;tlang=&apos;+sSubtitlesLanguage;
      if (subsUrl!=&apos;&apos;) {
        sData = HmsDownloadURL(subsUrl+&apos;&amp;fmt=srv3&apos;, sHeaders, true);
        sMsg  = &apos;&apos;; i = 0;
        RegEx = TRegExpr.Create(&apos;(&lt;(text|p).*?&lt;/(text|p)&gt;)&apos;, PCRE_SINGLELINE); // Convert to srt format
        try {
          if (RegEx.Search(sData)) do {
            if      (HmsRegExMatch(&apos;start="([\\d\\.]+)&apos;, RegEx.Match, sVal)) nStart = StrToFloat(ReplaceStr(sVal, &apos;.&apos;, &apos;,&apos;))*1000;
            else if (HmsRegExMatch(&apos;t="(\\d+)&apos;         , RegEx.Match, sVal)) nStart = StrToFloat(sVal);
            if      (HmsRegExMatch(&apos;dur="([\\d\\.]+)&apos;  , RegEx.Match, sVal)) nDur   = StrToFloat(ReplaceStr(sVal, &apos;.&apos;, &apos;,&apos;))*1000;
            else if (HmsRegExMatch(&apos;d="(\\d+)&apos;         , RegEx.Match, sVal)) nDur   = StrToFloat(sVal);
            sTime1 = HmsTimeFormat(Int(nStart/1000))+&apos;,&apos;+RightCopy(Str(nStart), 3);
            sTime2 = HmsTimeFormat(Int((nStart+nDur)/1000))+&apos;,&apos;+RightCopy(Str(nStart+nDur), 3);
            sMsg += Format("%d\n%s --&gt; %s\n%s\n\n", [i, sTime1, sTime2, HmsHtmlToText(HmsHtmlToText(RegEx.Match(0), 65001))]);
            i++;
          } while (RegEx.SearchAgain());
        } finally { RegEx.Free(); }
        sFile = HmsSubtitlesDirectory+&apos;\\Youtube\\&apos;+PodcastItem.ItemID+&apos;.&apos;+sSubtitlesLanguage+&apos;.srt&apos;;
        HmsStringToFile(sMsg, sFile);
        if (Trim(PodcastItem[mpiSubtitleLanguage])!=&apos;&apos;) bAdaptive = false;
        PodcastItem[mpiSubtitleLanguage] = sFile;
      }
    }
    
    hlsUrl = PLAYER_RESPONSE.S[&apos;streamingData\\hlsManifestUrl&apos;];
    jsUrl  = JSON.S[&apos;assets\\js&apos;];
    
    if (hlsUrl!=&apos;&apos;) {
      MediaResourceLink = &apos; &apos;+hlsUrl;
      bAdaptive = false;
      sData = HmsDownloadUrl(hlsUrl, sHeaders, true);
      RegEx = TRegExpr.Create(&apos;BANDWIDTH=(\\d+).*?RESOLUTION=(\\d+)x(\\d+).*?(http[^#]*)&apos;, PCRE_SINGLELINE);
      try {
        if (RegEx.Search(sData)) do {
          sLink = &apos;&apos; + RegEx.Match(4);
          height = StrToIntDef(RegEx.Match(3), 0);
          if (mpPodcastMediaFormats!=&apos;&apos;) {
            priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
            if ((priority&gt;=0) &amp;&amp; (priority&gt;minPriority)) {
              MediaResourceLink = sLink; minPriority = priority;
            }
          } else if ((height &gt; selHeight) &amp;&amp; (height &lt;= maxHeight)) {
            MediaResourceLink = sLink; selHeight = height;
          }
          QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        } while (RegEx.SearchAgain());
      } finally { RegEx.Free(); }
      
    } else if (PLAYER_RESPONSE.B[&apos;streamingData\\formats&apos;]) {
      FORMATS = PLAYER_RESPONSE.O[&apos;streamingData\\formats&apos;].AsArray;
      if (FORMATS[0].B[&apos;signatureCipher&apos;])
        algorithm = HmsDownloadURL(&apos;https://hms.lostcut.net/yt/getalgo.php?jsurl=&apos;+HmsHttpEncode(jsUrl));
      for(i=0; i&lt;FORMATS.Length; i++) {
        VIDEO = FORMATS[i];
        if (VIDEO.B[&apos;signatureCipher&apos;]) {
          sLink = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;url&apos;, &apos;&apos;, &apos;&amp;&apos;));
          sig   = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;s&apos;  , &apos;&apos;, &apos;&amp;&apos;));
          sp    = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;sp&apos; , &apos;&apos;, &apos;&amp;&apos;));
          if (sig!=&apos;&apos;) {
            for (w=1; w&lt;=WordCount(algorithm, &apos; &apos;); w++) {
              alg = ExtractWord(w, algorithm, &apos; &apos;);
              if (Length(alg)&lt;1) continue;
              if (Length(alg)&gt;1) TryStrToInt(Copy(alg, 2, 4), num);
              if (alg[1]==&apos;r&apos;) {s=&apos;&apos;; for(n=Length(sig); n&gt;0; n--) s+=sig[n]; sig = s;   } // Reverse
              if (alg[1]==&apos;s&apos;) {sig = Copy(sig, num+1, Length(sig));                     } // Clone
              if (alg[1]==&apos;w&apos;) {n = (num-Trunc(num/Length(sig)))+1; Swap(sig[1], sig[n]);} // Swap
            }
          }
          sLink += &apos;&amp;&apos;+sp+&apos;=&apos;+sig;
        } else {
          sLink = VIDEO.S[&apos;url&apos;];
        }
        height = VIDEO.I[&apos;height&apos;];
        if (height&gt;0) QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        if (mpPodcastMediaFormats!=&apos;&apos;) {
          priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
          if ((priority&gt;=0) || (priority&lt;minPriority)) {
            MediaResourceLink = sLink; minPriority = priority; selHeight = height;
          }
        } else if ((height&gt;selHeight) &amp;&amp; (height&lt;= maxHeight)) {
          MediaResourceLink = sLink; selHeight = height;
          
        } else if ((height&gt;=selHeight) &amp;&amp; (height&lt;= maxHeight) &amp;&amp; (VIDEO.I[&apos;itag&apos;] in ([18,22,37,38,82,83,84,85]))) {
          // Если высота такая же, но формат MP4 - то выбираем именно его (делаем приоритет MP4)
          MediaResourceLink = sLink; selHeight = height;
        }
      }
    } 
    if (bAdaptive || (MediaResourceLink==&apos;&apos;) &amp;&amp; PLAYER_RESPONSE.B[&apos;streamingData\\adaptiveFormats&apos;]) {
      FORMATS = PLAYER_RESPONSE.O[&apos;streamingData\\adaptiveFormats&apos;].AsArray;
      if (FORMATS[0].B[&apos;cipher&apos;] &amp;&amp; (algorithm==&apos;&apos;))
        algorithm = HmsDownloadURL(&apos;https://hms.lostcut.net/yt/getalgo.php?jsurl=&apos;+HmsHttpEncode(jsUrl));
      for(i=0; i&lt;FORMATS.Length; i++) {
        VIDEO = FORMATS[i];
        if (VIDEO.B[&apos;cipher&apos;]) {
          sLink = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;url&apos;, &apos;&apos;, &apos;&amp;&apos;));
          sig   = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;s&apos;  , &apos;&apos;, &apos;&amp;&apos;));
          sp    = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;sp&apos; , &apos;&apos;, &apos;&amp;&apos;));
          if (sig!=&apos;&apos;) {
            for (w=1; w&lt;=WordCount(algorithm, &apos; &apos;); w++) {
              alg = ExtractWord(w, algorithm, &apos; &apos;);
              if (Length(alg)&lt;1) continue;
              if (Length(alg)&gt;1) TryStrToInt(Copy(alg, 2, 4), num);
              if (alg[1]==&apos;r&apos;) {s=&apos;&apos;; for(n=Length(sig); n&gt;0; n--) s+=sig[n]; sig = s;   } // Reverse
              if (alg[1]==&apos;s&apos;) {sig = Copy(sig, num+1, Length(sig));                     } // Clone
              if (alg[1]==&apos;w&apos;) {n = (num-Trunc(num/Length(sig)))+1; Swap(sig[1], sig[n]);} // Swap
            }
          }
          sLink += &apos;&amp;&apos;+sp+&apos;=&apos;+sig;
        } else {
          sLink = VIDEO.S[&apos;url&apos;];
        }
        if (VIDEO.B[&apos;audioSampleRate&apos;] &amp;&amp; (audioQual &lt; VIDEO.I[&apos;bitrate&apos;])) {
          sAudio = sLink; audioQual = VIDEO.I[&apos;bitrate&apos;]; continue; 
        }
        height = VIDEO.I[&apos;height&apos;];
        if (height&gt;0) QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        if (mpPodcastMediaFormats!=&apos;&apos;) {
          priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
          if ((priority&gt;=0) || (priority&lt;minPriority)) {
            MediaResourceLink = sLink; minPriority = priority; selHeight = height;
          }
        } else if ((height&gt;selHeight) &amp;&amp; (height&lt;= maxHeight)) {
          MediaResourceLink = sLink; selHeight = height;
        }
      }
      sVal = ""; if (Trim(mpTimeStart)!="") sVal = " -ss "+mpTimeStart;
      if (bAdaptive &amp;&amp; (sAudio!=&apos;&apos;)) {
        if (Pos(&apos;--downloadaudio&apos;, mpPodcastParameters)&gt;0) {
          sFile = HmsTempDirectory+&apos;\\&apos;+PodcastItem.ItemID+&apos;.webm&apos;;
          if (!FileExists(sFile))
            HmsDownloadURLToFile(sAudio, sFile, sHeaders, true);
        } else sFile = sAudio;
        MediaResourceLink = &apos;-hide_banner -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 -fflags +genpts -i "&apos;+Trim(MediaResourceLink)+&apos;"&apos;+sVal+&apos; -i "&apos;+Trim(sFile)+&apos;"&apos;;
      }
    }
    // Если еще не установлена реальная длительность видео - устанавливаем
    if ((Trim(mpTimeLength)==&apos;&apos;) || (RightCopy(mpTimeLength, 6)==&apos;00.000&apos;) &amp;&amp; (hlsUrl==&apos;&apos;)) {
      PodcastItem[mpiTimeLength] = HmsTimeFormat(PLAYER_RESPONSE.I[&apos;videoDetails\\lengthSeconds&apos;]);
    }
    if (bQualLog) {
      QLIST.Sort();
      sMsg = &apos;Доступное качество: &apos;;
      for (i = 0; i &lt; QLIST.Count; i++) {
        if (i&gt;0) sMsg += &apos;, &apos;;
        sMsg += IntToStr(StrToInt(QLIST.Names[i])); // Обрезаем лидирующие нули
      }
      sMsg += &apos;. Выбрано: &apos; + Str(selHeight);
      HmsLogMessage(1, mpTitle+&apos;. &apos;+sMsg);
    }
  } finally { JSON.Free; PLAYER_RESPONSE.Free; QLIST.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Показ видео сообщения
bool VideoMessage(string sMsg) {
  string sFileMP3 = HmsTempDirectory+&apos;\\sa.mp3&apos;;
  string sFileImg = HmsTempDirectory+&apos;\\youtubemsg_&apos;;
  sMsg = HmsHtmlToText(HmsJsonDecode(sMsg));
  int nH = cfgTranscodingScreenHeight;
  int nW = cfgTranscodingScreenWidth;
  HmsLogMessage(2, mpTitle+&apos;: &apos;+sMsg);
  string sLink = Format(&apos;http://wonky.lostcut.net/videomessage.php?h=%d&amp;w=%d&amp;captsize=%d&amp;fontsize=%d&amp;caption=%s&amp;msg=%s&apos;, [nH, nW, Round(nH/8), Round(nH/17), gsPodcastName, HmsHttpEncode(sMsg)]);
  HmsDownloadURLToFile(sLink, sFileImg);
  for (int i=1; i&lt;=7; i++) CopyFile(sFileImg, sFileImg+Format(&apos;%.3d.jpg&apos;, [i]), false);
  try {
    if (!FileExists(sFileMP3)) HmsDownloadURLToFile(&apos;http://wonky.lostcut.net/mp3/sa.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 -r 30 -pix_fmt yuv420p &apos;, [sFileMP3, sFileImg+&apos;%03d.jpg&apos;]);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылки-ошибки
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;;
  HmsLogMessage(2, sMsg);
}

///////////////////////////////////////////////////////////////////////////////
// Создание видео-ссылки
THmsScriptMediaItem CreateVideo(string sID, string sTitle, string sImg, string sTime, string sDate, string sComment=&apos;&apos;) {
  if (LeftCopy(sID, 4)!=&apos;http&apos;) sID = gsUrlBase+&apos;/watch?v=&apos;+Trim(sID); 
  THmsScriptMediaItem Item = HmsCreateMediaItem(sID, PodcastItem.ItemID);
  Item[mpiTitle      ] = sTitle;
  Item[mpiThumbnail  ] = sImg;
  Item[mpiTimeLength ] = sTime;
  Item[mpiCreateDate ] = sDate; 
  Item[mpiComment    ] = sComment;
  gnTotalItems++; 
  return Item; 
}

///////////////////////////////////////////////////////////////////////////////
// Создание папки или обновляемого подкаста
THmsScriptMediaItem CreateFolder(string sName, string sLink, string sImg=&apos;&apos;) {
  THmsScriptMediaItem Item = PodcastItem.AddFolder(sLink); // Создаём папку с указанной ссылкой
  Item[mpiTitle      ] = sName; // Присваиваем наименование
  Item[mpiThumbnail  ] = sImg;  // Картинка папки
  Item[mpiCreateDate ] = IncTime(gStart, 0, -gnTotalItems, 0, 0); gnTotalItems++;
  return Item;
}

///////////////////////////////////////////////////////////////////////////////
// Перевод некоторых английских фраз из информации о плейлистах
string Translate(string sText) {
  string sVal, sResult=sText; TStrings DICT = TStringList.Create();
  DICT.Values[&apos;Best of YouTube&apos;] = &apos;Лучшее на YouTube&apos;;
  DICT.Values[&apos;Paid channels&apos;  ] = &apos;Платные каналы&apos;;
  DICT.Values[&apos;Top YouTube Collections&apos;] = &apos;Лучшие коллекции YouTube&apos;;
  DICT.Values[&apos;Popular Artists&apos;] = &apos;Популярные исполнители&apos;;
  if (Trim(DICT.Values[sText])!=&apos;&apos;) sResult = DICT.Values[sText];
  DICT.Free();
  if (HmsRegExMatch(&apos;(Popular Artist Mixes)&apos;, sResult, sVal)) sResult = ReplaceStr(sResult, sVal, &apos;Миксы популярных исполнителей&apos;);
  return sResult;
}

///////////////////////////////////////////////////////////////////////////////
// Получение безопасного валидного наименования
string SafeName(string sName) {
  sName = ReplaceStr(sName, &apos;/&apos; , &apos;-&apos;);
  sName = ReplaceStr(sName, &apos;\\&apos;, &apos;-&apos;);
  sName = Translate(sName);   
  return sName;  
}

///////////////////////////////////////////////////////////////////////////////
// Получение длительности для HMS из формата youtube
string ConvertTime(string sTime) {
  string sVal; int nSeconds = 0;
  if (HmsRegExMatch(&apos;(\\d+)H&apos;, sTime, sVal)) nSeconds += StrToInt(sVal)*3600;  
  if (HmsRegExMatch(&apos;(\\d+)M&apos;, sTime, sVal)) nSeconds += StrToInt(sVal)*60;  
  if (HmsRegExMatch(&apos;(\\d+)S&apos;, sTime, sVal)) nSeconds += StrToInt(sVal);
  if (nSeconds==0) nSeconds = 600;
  return HmsTimeFormat(nSeconds)+&apos;.000&apos;;
}

///////////////////////////////////////////////////////////////////////////////
// Получение даты создания для HMS из формата youtube
string ConvertDate(string sDate) {
  string sY, sM, sD, sTime;
  HmsRegExMatch3(&apos;(\\d{4}).(\\d{2}).(\\d{2})&apos;, sDate, sY, sM, sD);  
  HmsRegExMatch (&apos;T(\\d{2}:\\d{2}:\\d{2})&apos;   , sDate, sTime     );  
  return Format(&apos;%s.%s.%s %s&apos;, [sD, sM, sY, sTime]);
}

///////////////////////////////////////////////////////////////////////////////
// Универсальная функция создания элементов видео (или сбора информации)
string CreateItems(string sPath, string sObject, string sParam, string sFiltr=&apos;&apos;) {
  string sLink, sData, sName, sID, sImg, sCh, sVal, sResult=&apos;&apos;, sTime;
  int i, nLevel; TJsonObject JSON, ITEM; TJsonArray ITEMS;
  
  if (HmsRegExMatch(&apos;pageToken=([\\w-_]+)&apos;, mpFilePath, sVal)) sParam += &apos;&amp;pageToken=&apos;+sVal; 
  if (HmsRegExMatch(&apos;&amp;level=(\\d+)&apos;       , mpFilePath, sVal)) nLevel = StrToInt(sVal); 
  
  sData = APIRequest(sObject, sParam);
  
  JSON = TJsonObject.Create();  
  try {
    JSON.LoadFromString(sData);
    sID = JSON.S(&apos;nextPageToken&apos;);
    if ((sID!=&apos;&apos;) &amp;&amp; (nLevel&lt;3)) {
      if (sObject==&apos;search&apos;) mpFilePath = &apos;-search=&apos;+sParam;  
      if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
      if (HmsRegExMatch(&apos;(&amp;level=\\d+)&apos;        , mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
      sCh = &apos;?&apos;; if (Pos(&apos;?&apos;, mpFilePath)&gt;0) sCh = &apos;&amp;&apos;;
      CreateFolder(&apos;Следующая страница&apos;, mpFilePath+sCh+&apos;pageToken=&apos;+sID+&apos;&amp;level=&apos;+Str(nLevel+1));
    }
    
    ITEMS = JSON.A(&apos;items&apos;);
    if (ITEMS != nil) {
      for (i=0; i&lt;ITEMS.Length; i++) {
        ITEM = ITEMS[i];
        
        // Хоть playlistItems и возвращает список видео, но там нет длительности
        // Поэтому собираем ID видео, чтобы потом вызвать videos со списком этих ID 
        if (sObject==&apos;playlistItems&apos;) { 
          if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
          sResult += ITEM.S(&apos;contentDetails\\videoId&apos;);
          continue;
          
        } else if (sObject==&apos;activities&apos;) {
          if (sFiltr!=&apos;&apos;) if (ITEM.S[sFiltr]==&apos;&apos;) continue;   
          if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
          sID = ITEM.S(&apos;contentDetails\\recommendation\\resourceId\\videoId&apos;);
          if (sID==&apos;&apos;) sID = ITEM.S(&apos;contentDetails\\playlistItem\\resourceId\\videoId&apos;);
          if (sID==&apos;&apos;) sID = ITEM.S(&apos;contentDetails\\upload\\videoId&apos;);
          if (sID!=&apos;&apos;) sResult += sID;
          continue;
          
        } else if (sObject==&apos;search&apos;) {
          sVal = ITEM.S(&apos;id\\kind&apos;);
          if (sVal==&apos;youtube#playlist&apos;) {
            sID   = ITEM.S(&apos;id\\playlistId&apos;);
            sPath = gsUrlBase+&apos;/playlist?list=&apos;;
          } else if (sVal==&apos;youtube#channel&apos;) {
            sID   = ITEM.S(&apos;id\\channelId&apos;);
            sPath = gsUrlBase+&apos;/channel/&apos;;
          } else {
            sID   = ITEM.S(&apos;id\\videoId&apos;);
            sPath = &apos;video&apos;;
            if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
            if (sID!=&apos;&apos;) sResult += sID;
            continue;
          }
          
        } else if (sObject==&apos;videoCategories&apos;) {
          if (!ITEM.B(&apos;snippet\\assignable&apos;)) continue;
          sID   = ITEM.S(&apos;id&apos;);
          if (sID==&apos;1&apos;) sID = &apos;30&apos;;
          
        } else if (sObject==&apos;subscriptions&apos;) {
          sID   = ITEM.S(&apos;snippet\\resourceId\\channelId&apos;);
          
        } else {
          sID   = ITEM.S(&apos;id&apos;);
        }
        if (sID==&apos;&apos;) continue;
        if (LeftCopy(sPath, 6)==&apos;getids&apos;) { if (sResult!=&apos;&apos;) sResult+=&apos;,&apos;;  sResult += sID; continue; }
        sName = ITEM.S(&apos;snippet\\localized\\title&apos;);  
        if (sName==&apos;&apos;) sName = ITEM.S(&apos;snippet\\title&apos;);  
        sImg  = ITEM.S(&apos;snippet\\thumbnails\\medium\\url&apos;);
        sName = SafeName(HmsUtf8Decode(sName));
        sVal  = ITEM.S(&apos;contentDetails\\itemCount&apos;); if (sVal==&apos;0&apos;) continue;
        if (sVal!=&apos;&apos;) sName += &apos; [&apos;+sVal+&apos;]&apos;;
        
        if (Pos(sPath, &apos;%s&apos;)&gt;0) sLink = Format(sPath, [sID]);
        else                    sLink = sPath + sID;
        
        if (sPath==&apos;video&apos; ) {
          if (ITEM.S(&apos;snippet\\liveBroadcastContent&apos;)==&apos;live&apos;) sTime = &apos;04:00:00.000&apos;; 
          else sTime = ConvertTime(ITEM.S(&apos;contentDetails\\duration&apos;));
          if (Pos(&apos;DE&apos;, ITEM.S(&apos;contentDetails\\regionRestriction\\blocked&apos;))&gt;0) sID += &apos;&amp;notde=1&apos;;
        } 
        
        if (sPath==&apos;video&apos; ) CreateVideo(sID, sName, sImg, sTime, ConvertDate(ITEM.S(&apos;snippet\\publishedAt&apos;)), HmsUtf8Decode(ITEM.S(&apos;snippet\\description&apos;)));
        else                 CreateFolder(sName, sLink, sImg);
      }
    } 
    
  } finally { JSON.Free(); } 
  return sResult;
}

///////////////////////////////////////////////////////////////////////////////
// Создание каналов конкретной категории
void CreateChannels(string sID) {
  Login(true);
  CreateItems(gsUrlBase+&apos;/channel/&apos;, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;categoryId=&apos;+sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка категорий назначенных самим youtube автоматически
void CreateGuideCategories() {
  Login(true);
  CreateItems(gsUrlBase+&apos;/channels/&apos;, &apos;guideCategories&apos;, &apos;part=snippet&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка категорий
void CreateVideoCategories() {
  Login(true);
  CreateItems(&apos;-category=&apos;, &apos;videoCategories&apos;, &apos;part=snippet&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео по выбранной категории
void CreateVideosByCategory(string sID) {
  Login(true);
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;chart=mostPopular&amp;videoCategoryId=&apos;+sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного плейлиста
void CreatePlaylistVideos(string sID) {
  string sIDs, sVal;
  Login(true);
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;playlistItems&apos;, &apos;part=contentDetails&amp;fields=nextPageToken,items(contentDetails%2FvideoId)&amp;playlistId=&apos;+sID);
  
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
  
}

///////////////////////////////////////////////////////////////////////////////
// Создание подкастов каналов в родительской папке
void CreateMySubscriptions() {
  if (!Login()) return;
  TJsonObject SUBS = TJsonObject.Create();
  try {
    SUBS.LoadFromString(APIRequest(&apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;));
    if (SUBS["items"]==nil) return;
    for (int n=0; n &lt; SUBS["items"].AsArray.Length; n++) {
      TJsonObject OBJECT = SUBS["items"].AsArray[n];
      string ch = OBJECT.S[&apos;snippet\\resourceId\\channelId&apos;]; if (ch==&apos;&apos;) continue;
      THmsScriptMediaItem Item = PodcastItem.AddFolder(gsUrlBase+&apos;/channel/&apos;+ch);
      Item[mpiTitle     ] = HmsUtf8Decode(OBJECT.S[&apos;snippet\\title&apos;]);
      Item[mpiComment   ] = HmsUtf8Decode(OBJECT.S[&apos;snippet\\description&apos;]);
      Item[mpiThumbnail ] = OBJECT.S[&apos;snippet\\thumbnails\\medium\\url&apos;];
      Item[mpiCreateDate] = ConvertDate(OBJECT.S[&apos;snippet\\publishedAt&apos;]); gnTotalItems++;
    }
  } finally { SUBS.Free; }    
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка подписчиков
void CreateMySubscribers() {
  if (!Login()) return;
  CreateItems(gsUrlBase+&apos;/channel/&apos;, &apos;subscriptions&apos;, &apos;part=snippet,contentDetails&amp;mySubscribers=true&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео по ключевому слову названия плейлиста ("WL", "likes"...)  
void CreateMyPlaylist(string sKey) {
  string sData, sID;
  
  if (!Login()) return;
  
  sData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;mine=true&apos;);
  
  if (HmsRegExMatch(&apos;"&apos;+sKey+&apos;":\\s*?"(.*?)"&apos;, sData, sID)) CreatePlaylistVideos(sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео "Рекомендации"  
void CreateMyRecommendations() {
  string sIDs, sVal;
  
  if (!Login()) return;
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;activities&apos;, &apos;part=contentDetails&amp;home=true&apos;);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Получение списка ID через запятую по переданной json строке
string GetIDs(string sData, string sPath) {
  string sID, sIDs;
  TJsonObject JSON = TJsonObject.Create();
  int nCount = 0;
  try {
    JSON.LoadFromString(sData);
    if (JSON["items"].AsArray!=nil) {
      for (int i=0; i &lt; JSON["items"].AsArray.Length; i++) {
        sID = JSON["items"].AsArray[i].S[sPath]; if (sID==&apos;&apos;) continue;
        if (sIDs==&apos;&apos;) sIDs = sID; else sIDs += &apos;,&apos;+sID;
        nCount++; if (nCount &gt;= 50) break;
      }          
    }
  } finally { JSON.Free; }
  return sIDS;
}

///////////////////////////////////////////////////////////////////////////////
// Список новых видео в подписках (НОВЫЙ СПОСОБ)
void NewVideos() {
  string sID, sIDs, sData, sName, sTime, sDesc, sChan; TJsonArray VIDEOS;
  if (!Login()) return;
  TJsonObject SUBS = TJsonObject.Create();
  TJsonObject JSON = TJsonObject.Create();
  TStringList LIST = TStringList.Create();
  int nCount = 0;
  try {
    sData = APIRequest(&apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;);
    SUBS.LoadFromString(sData);
    if (SUBS["items"] != nil) {
      for (int n=0; n &lt; SUBS["items"].AsArray.Length; n++) {
        sChan = SUBS["items"].AsArray[n].S[&apos;snippet\\resourceId\\channelId&apos;]; if (sChan==&apos;&apos;) continue;
        sData = APIRequest(&apos;activities&apos;, &apos;part=contentDetails&amp;channelId=&apos;+sChan);
        sIDs  = GetIDs(sData, &apos;contentDetails\\upload\\videoId&apos;);
        sData = APIRequest(&apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
        JSON.LoadFromString(sData);
        if (JSON["items"]==nil) continue;
        for (int i=0; i &lt; JSON["items"].AsArray.Length; i++) {
          sID   = JSON["items"].AsArray[i].S[&apos;id&apos;];
          sData = JSON["items"].AsArray[i].S[&apos;snippet\\publishedAt&apos;];
          sTime = JSON["items"].AsArray[i].S[&apos;contentDetails\\duration&apos;];
          sName = HmsUtf8Decode(JSON["items"].AsArray[i].S[&apos;snippet\\title&apos;]);
          sDesc = HmsUtf8Decode(JSON["items"].AsArray[i].S[&apos;snippet\\description&apos;]);
          LIST.Add(sData+&apos;=&apos;+sID+&apos;|&apos;+sName+&apos;|&apos;+sTime+&apos;|&apos;+sDesc);
        }
      }
      CreateVideosFromList(LIST);
    }
  } finally { SUBS.Free; JSON.Free; LIST.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Создание видео из переданного списка TStringList
void CreateVideosFromList(TStringList LIST) {
  string sID, sLink, sName, sTime, sDate, sDesc, sImg;
  if (LIST.Count==0) return;
  LIST.Sort();
  for (int i=LIST.Count-1; i&gt;=0; i--) {
    HmsRegExMatch2(&apos;^(.*?)=(.*?)\\|&apos;        , LIST[i], sDate, sID);
    HmsRegExMatch3(&apos;\\|(.*?)\\|(.*?)\\|(.*)&apos;, LIST[i], sName, sTime, sDesc);
    sLink = gsUrlBase+&apos;/watch?v=&apos;+sID;
    THmsScriptMediaItem Item = HmsCreateMediaItem(sLink, PodcastItem.ItemID);
    Item[mpiTitle     ] = sName;
    Item[mpiComment   ] = sDesc;
    Item[mpiThumbnail ] = &apos;https://i.ytimg.com/vi/&apos;+sID+&apos;/mqdefault.jpg&apos;;
    Item[mpiTimeLength] = ConvertTime(sTime);
    Item[mpiCreateDate] = ConvertDate(sDate); gnTotalItems++;
  }
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка новых видео в подписках
void CreateMyNewVideos() {
  string sIDs, sDate, sID, sLink, sData, sPlaylistIDs, sName, sImg, sTime=&apos;10&apos;, sVal;
  int i, n, nCount, nMaxPages; TJsonObject JSON, VIDEO; TJsonArray VIDEOS; TStringList LIST;
  
  if (!Login()) return;
  
  sPlaylistIDs = CreateItems(&apos;getids&apos;, &apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;);
  
  LIST = TStringList.Create();
  JSON = TJsonObject.Create();
  
  for (i=1; i&lt;=WordCount(sPlaylistIDs, &apos;,&apos;); i++) {
    sID   = ExtractWord(i, sPlaylistIDs, &apos;,&apos;); // ИД плейлиста, на который мы подписаны
    sData = APIRequest(&apos;search&apos;, &apos;part=snippet&amp;order=date&amp;channelId=&apos;+sID);
    JSON.LoadFromString(sData);
    VIDEOS = JSON["items"].AsArray;
    if (VIDEOS!=nil) {
      for (n=0; n &lt; VIDEOS.Length; n++) {
        VIDEO = VIDEOS[n];
        if (VIDEO.S[&apos;id\\kind&apos;]!=&apos;youtube#video&apos;) continue;
        sID   = VIDEO.S[&apos;id\\videoId&apos;];
        sDate = VIDEO.S[&apos;snippet\\publishedAt&apos;];
        sName = VIDEO.S[&apos;snippet\\title&apos;];
        LIST.Add(sDate+&apos;;&apos;+sID+&apos;;&apos;+HmsUtf8Decode(sName));
      }
    }
  }
  LIST.Sort();
  nMaxPages = 1;
  if (HmsRegExMatch(&apos;--pages=(\\d+)&apos;, mpPodcastParameters, sVal)) nMaxPages = StrToInt(sVal);
  i=LIST.Count-1;
  for (n=0; n&lt;nMaxPages; n++) {
    nCount=0; sIDs=&apos;&apos;;
    while (i&gt;=0) {
     
      HmsRegExMatch3(&apos;(.*?);(.*?);(.*)&apos;, LIST[i], sDate, sID, sName);
      if (sIDs!=&apos;&apos;) sIDs += &apos;,&apos;; sIDs += sID;
      i--; nCount++;
      if (nCount &gt;= 50) break;
    }
    CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
  }
  LIST.Free(); JSON.Free();
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного канала
void CreateChannelVideos(string sChannelID) {
  Login(true);
  SearchVideosByParam(&apos;&amp;q=&amp;part=snippet&amp;type=video&amp;order=date&amp;channelId=&apos;+sChannelID);
}

///////////////////////////////////////////////////////////////////////////////
// Поиск видео с выбранными параметрами
void SearchVideosByParam(string sParam) {
  string sIDs, sVal; 
  
  Login(true);
  
  sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, sParam);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного региона
void CreateVideosByRegion(string sRegion) {
  string sVal, sParam; 
  Login(true);
  
  gsRegion = sRegion;
  sParam = &apos;&amp;q=&amp;part=snippet&amp;type=video&amp;order=rating&amp;regionCode=&apos;+sRegion+&apos;&amp;relevanceLanguage=&apos;+sRegion;
  if (HmsRegExMatch(&apos;category=([\\w-_]+)&apos;, mpFilePath, sVal)) sParam += &apos;&amp;videoCategoryId=&apos; + sVal; 
  
  SearchVideosByParam(sParam);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка последних обновлений
void CreateMyActivities() {
  string sIDs, sVal;
  
  if (!Login()) return;
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;activities&apos;, &apos;part=contentDetails&amp;mine=true&apos;);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео конкретного канала (по его id)
void CreateChannelPlaylists(string sChannelID) {
  Login(true);
  CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;channelId=&apos;+sChannelID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание секций конкретного канала ("Загруженные", "Плейлисты" и проч.)
void CreateChannelSections(string sChannelID, bool bForUser=false) {
  string sLink, sData, sIDs=&apos;&apos;, sType, sID, sName, sChannelData, sVal;
  int i, nCnt, nMax; TJsonObject JSON, ITEM; TJsonArray ITEMS; TStrings CHANNELS;
  Login(true);
  if (bForUser)  
    sChannelData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;forUsername=&apos;+sChannelID);
  else
    sChannelData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sChannelID);
  HmsRegExMatch(&apos;"id":\\s*?"(.*?)"&apos;, sChannelData, sChannelID);
  if ((Pos(&apos;--translate&apos;, mpPodcastParameters)&gt;0) &amp;&amp; HmsRegExMatch(&apos;"country"\\s*:\\s*"(.*?)"&apos;, sChannelData, sVal) &amp;&amp; (sVal!=gsRegion)) {
    if ((Pos(&apos;--subtitles&apos;, mpPodcastParameters)&lt;1)) PodcastItem[mpiPodcastParameters] += &apos; --subtitles&apos;;
  }
  sData = APIRequest(&apos;channelSections&apos;, &apos;part=snippet,contentDetails&amp;fields=items(id,snippet/type,snippet/title,snippet/localized/title,contentDetails)&amp;channelId=&apos;+sChannelID);
  JSON = TJsonObject.Create();
  CHANNELS = TStringList.Create();
  try {
    JSON.LoadFromString(sData);
    ITEMS = JSON.A(&apos;items&apos;);
    if (ITEMS != nil) {
      for (i=0; i&lt;ITEMS.Length; i++) {
        ITEM = ITEMS[i];
        sType = ITEM.S(&apos;snippet\\type&apos;);  
        if (sType==&apos;singlePlaylist&apos;) {
          sID   = ITEM.S(&apos;contentDetails\\playlists[0]&apos;);
          CHANNELS.Values[sID] = &apos;1&apos;;

        } else if ((sType==&apos;multiplePlaylists&apos;)||(sType==&apos;multipleChannels&apos;)) {
          sID   = ITEM.S(&apos;id&apos;);
          sName = ITEM.S(&apos;snippet\\localized\\title&apos;);
          if (sName==&apos;&apos;) sName = ITEM.S(&apos;snippet\\title&apos;);
          sName = SafeName(HmsUtf8Decode(sName));
          CreateFolder(sName, &apos;-channelSection=&apos;+sID);

        } 
      }
    }

  } finally { JSON.Free(); }

  // Create single playlists
  nCnt = 0; nMax = 4; 
  for (i=0; i&lt;CHANNELS.Count; i++) {
    sID = CHANNELS.Names[i];
    nCnt++; if (nCnt&gt;=50) {
      CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
      nCnt = 1; sIDs = sID; nMax --; if (nMax&lt;=0) break;
    }
    if (sIDs!=&apos;&apos;) sIDs += &apos;,&apos;; sIDs += sID;
  }
  if (sIDs!=&apos;&apos;) CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);

  CHANNELS.Free();

  CreateFolder(&apos;Плейлисты&apos;, &apos;-channelPlaylists=&apos;+sChannelID);

  sLink = gsUrlBase+&apos;/playlist?list=&apos;;
  if (HmsRegExMatch(&apos;"uploads":\\s*?"(.*?)"&apos;  , sChannelData, sID)) CreateFolder(&apos;Загруженные видео&apos;, sLink+sID);
  if (HmsRegExMatch(&apos;"likes":\\s*?"(.*?)"&apos;    , sChannelData, sID)) CreateFolder(&apos;Понравившиеся&apos;    , sLink+sID);
  if (HmsRegExMatch(&apos;"favorites":\\s*?"(.*?)"&apos;, sChannelData, sID)) CreateFolder(&apos;Любимые&apos;          , sLink+sID);

}

///////////////////////////////////////////////////////////////////////////////
// Создание видео конкретной секции канала
void CreateByChannelSection(string sSectionID) {
  string sLink, sData, sIDs=&apos;&apos;, sType, sID, sName;
  int i; TJsonObject JSON, ITEM; TJsonArray ITEMS;
  Login(true);
  sData = APIRequest(&apos;channelSections&apos;, &apos;part=contentDetails&amp;id=&apos;+sSectionID);

  JSON = TJsonObject.Create();
  try {
    JSON.LoadFromString(sData);
    sLink = gsUrlBase+&apos;/channel/&apos;;
    sIDs  = JSON.S(&apos;items[0]\\contentDetails\\channels&apos;);
    HmsRegExMatch(&apos;\\[(.*?)\\]&apos;, sIDs, sIDs); 
    sIDs  = ReplaceStr(sIDs, &apos;"&apos;, &apos;&apos;);
    if (WordCount(sIDs, &apos;,&apos;)&gt;50) {
      sID = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
      CreateItems(sLink, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sID);
      sIDs = Copy(sIDs, WordPosition(51, sIDs, &apos;,&apos;), 99999);
      if (WordCount(sIDs, &apos;,&apos;)&gt;50) sIDs = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
    }     
    if (sIDs!=&apos;&apos;) CreateItems(sLink, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);     

    sLink = gsUrlBase+&apos;/playlist?list=&apos;;
    sIDs  = JSON.S(&apos;items[0]\\contentDetails\\playlists&apos;);
    HmsRegExMatch(&apos;\\[(.*?)\\]&apos;, sIDs, sIDs); 
    sIDs  = ReplaceStr(sIDs, &apos;"&apos;, &apos;&apos;);
    if (WordCount(sIDs, &apos;,&apos;)&gt;50) {
      sID = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
      CreateItems(sLink, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sID);
      sIDs = Copy(sIDs, WordPosition(51, sIDs, &apos;,&apos;), 99999);
      if (WordCount(sIDs, &apos;,&apos;)&gt;50) sIDs = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
    }     
    if (sIDs!=&apos;&apos;) CreateItems(sLink, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
    
  } finally { JSON.Free(); } 

}

///////////////////////////////////////////////////////////////////////////////
// Создание списка своих собственных плейлистов
void CreateMyPlaylists() {
  string sData, sLink, sID;
  
  if (!Login()) return;

  CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;mine=true&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Поиск видео по названию
void SearchVideos(string sName, bool bContinue=false) {
  string sVal=&apos;&apos;, sParam = &apos;&apos;, sIDs, sPodcastParams, sKey, sPossibleKeys; int i, nCnt;
  if (!bContinue) {
    sPodcastParams = mpFilePath + &apos; &apos; + PodcastItem.ItemParent[mpiFilePath] + &apos; &apos; + mpPodcastParameters;
    sPossibleKeys  = &apos;channelId,channelType,eventType,location,locationRadius,maxResults,order,publishedAfter,publishedBefore,regionCode,relevanceLanguage,safeSearch,topicId,type,videoCaption,videoCategoryId,videoDefinition,videoDimension,videoDuration,videoEmbeddable,videoLicense,videoSyndicated,videoType&apos;;
    for (i=1; i&lt;=WordCount(sPossibleKeys, &apos;,&apos;); i++) {
      sKey = ExtractWord(i, sPossibleKeys, &apos;,&apos;);
      if (HmsRegExMatch(&apos;-&apos;+sKey+&apos;=([\\w_\\-\\.,:]+)&apos;, sPodcastParams, sVal)) sParam += &apos;&amp;&apos;+sKey+&apos;=&apos; +sVal; 
    }
    if (PodcastItem.ItemParent[mpiComment]==&apos;+&apos;) sName = PodcastItem.ItemParent[mpiTitle] + &apos; &apos; + Trim(sName);
    if (ProgramVersion&gt;&apos;3.0&apos;) sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, &apos;part=snippet&amp;q=&apos;+HmsHttpEncode(sName)+sParam);
    else                      sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, &apos;part=snippet&amp;q=&apos;+HmsHttpEncode(HmsUtf8Encode(sName))+sParam); 

  } else {
    if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, sName, sVal)) sName = ReplaceStr(sName, sVal, &apos;&apos;); 
    sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, sName);
  
  }
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 

  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание структуры подкаста - папок меню
void CreateMyFolders() {
  THmsScriptMediaItem Item;
  CreateFolder(&apos;Новые видео в подписках&apos;, &apos;-newVideos&apos;);
  CreateFolder(&apos;Мои подписки&apos;     , gsUrlBase+&apos;/feed/subscriptions&apos;);
  CreateFolder(&apos;Плейлисты&apos;        , &apos;-playlists&apos;);
//CreateFolder(&apos;Посмотреть позже&apos; , gsUrlBase+&apos;/playlist?list=WL&apos;);
  CreateFolder(&apos;Загруженные видео&apos;, &apos;-uploads&apos;);
  CreateFolder(&apos;Понравившиеся&apos;    , &apos;-likes&apos;);
  CreateFolder(&apos;Просмотренные&apos;    , gsUrlBase+&apos;/feed/history&apos;);
//CreateFolder(&apos;Рекомендации&apos;     , &apos;-recommend&apos;);
//CreateFolder(&apos;Мои подписчики&apos;   , &apos;-mySubscribers&apos;);
}

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
// ----------------------------------------------------------------------------
{
  string s;
  HmsRegExMatch(&apos;--language=(\\w+)&apos;     , mpPodcastParameters, gsLanguage); 
  HmsRegExMatch(&apos;--regionCode=([\\w-]+)&apos;, mpPodcastParameters, gsRegion  ); 
  // Проверка на упоротых, которые пытаются запустить "Обновление подкастов" для всего подкаста разом
  if (InteractiveMode &amp;&amp; (HmsCurrentMediaTreeItem.ItemClassName==&apos;TVideoPodcastsFolderItem&apos;)) {
    HmsCurrentMediaTreeItem.DeleteChildItems(); // Дабы обновления всех подкастов не запустилось - удаляем их.
    ShowMessage(&apos;Обновление всех разделов разом запрещено!\nДля восстановления структуры\nзапустите "Создать ленты подкастов".&apos;);
    return;
  } 

  HmsRegExMatch(&apos;APIKey="(.*?)"&apos;      , mpPodcastParameters, gsAPIKey      );
  HmsRegExMatch(&apos;ClientId="(.*?)"&apos;    , mpPodcastParameters, gsClientId    );
  HmsRegExMatch(&apos;ClientSecret="(.*?)"&apos;, mpPodcastParameters, gsClientSecret);
  
  if (PodcastItem.IsFolder) { 
    PodcastItem.DeleteChildItems(); // Удаление существующих ссылок

    if ((gsAPIKey==&apos;&apos;) || (gsClientId==&apos;&apos;) || (gsClientSecret==&apos;&apos;)) {
      s = &apos;Не все параметры подкаста установлены (--APIKey, --ClientId или --ClientSecret)&apos;;
      HmsLogMessage(2, s);
      ErrorItem(s);
      return;
    }
    
    // Анализ текущей ссылки
         if (HmsRegExMatch(&apos;-newVideos&apos;         , mpFilePath, s)) CreateMyNewVideos(); //NewVideos();
    else if (HmsRegExMatch(&apos;/feed/subscriptions&apos;, mpFilePath, s)) CreateMySubscriptions();
    else if (HmsRegExMatch(&apos;-MyChannel&apos;         , mpFilePath, s)) CreateMyFolders();
    else if (HmsRegExMatch(&apos;-playlists&apos;         , mpFilePath, s)) CreateMyPlaylists();
    else if (HmsRegExMatch(&apos;/playlist\\?list=WL&apos;, mpFilePath, s)) CreateMyPlaylist(&apos;watchLater&apos;);
    else if (HmsRegExMatch(&apos;-likes&apos;             , mpFilePath, s)) CreateMyPlaylist(&apos;likes&apos;);
    else if (HmsRegExMatch(&apos;-favorites&apos;         , mpFilePath, s)) CreateMyPlaylist(&apos;favorites&apos;);
    else if (HmsRegExMatch(&apos;-uploads&apos;           , mpFilePath, s)) CreateMyPlaylist(&apos;uploads&apos;);
    else if (HmsRegExMatch(&apos;/feed/history&apos;      , mpFilePath, s)) CreateMyPlaylist(&apos;watchHistory&apos;);
    else if (HmsRegExMatch(&apos;/feed/music&apos;        , mpFilePath, s)) CreateMyPlaylist(&apos;music&apos;);
    else if (HmsRegExMatch(&apos;-mySubscribers&apos;     , mpFilePath, s)) CreateMySubscribers();
    else if (HmsRegExMatch(&apos;-recommend&apos;         , mpFilePath, s)) CreateMyRecommendations();
    else if (HmsRegExMatch(&apos;-activities&apos;        , mpFilePath, s)) CreateMyActivities();

    else if (HmsRegExMatch(&apos;-videosByRegion=([\\w-_]+)&apos;, mpFilePath, s)) CreateVideosByRegion(s);
    else if (HmsRegExMatch(&apos;youtube.com/channels$&apos; , mpFilePath, s)) CreateGuideCategories();
    else if (HmsRegExMatch(&apos;-videoCategories&apos;      , mpFilePath, s)) CreateVideoCategories();
    else if (HmsRegExMatch(&apos;-category=(.*)&apos;        , mpFilePath, s)) CreateVideosByCategory(s);
    else if (HmsRegExMatch(&apos;-channelSection=(.*)&apos;  , mpFilePath, s)) CreateByChannelSection(s);
    else if (HmsRegExMatch(&apos;-channelPlaylists=(.*)&apos;, mpFilePath, s)) CreateChannelPlaylists(s);
    else if (HmsRegExMatch(&apos;/channels/([\\w-_]+)&apos;  , mpFilePath, s)) CreateChannels(s);
    else if (HmsRegExMatch(&apos;/channel/([\\w-_]+)/videos&apos;, mpFilePath, s)) CreateChannelVideos(s);
    else if (HmsRegExMatch(&apos;/channel/([\\w-_]+)&apos;  , mpFilePath, s)) CreateChannelSections(s);
    else if (HmsRegExMatch(&apos;/user/([\\w-_]+)&apos;     , mpFilePath, s)) CreateChannelSections(s, true);
    else if (HmsRegExMatch(&apos;\\?list=([\\w-_]+)&apos;   , mpFilePath, s)) CreatePlaylistVideos(s);

    else if (HmsRegExMatch(&apos;-search="(.*?)"&apos;      , mpFilePath, s)) SearchVideos(s);       // Просто поиск значения
    else if (HmsRegExMatch(&apos;-search=(.*)&apos;         , mpFilePath, s)) SearchVideos(s, true); // Продожение поиска (техническая ссылка, создаётся сама)
    else if (!HmsRegExMatch(&apos;^http&apos;, mpFilePath, s)) SearchVideos(mpTitle);                    // Иначе, если в ссылке не http адрес - поиск названия
    else ErrorItem(&apos;Не умею обрабатывать ссылку :(&apos;);  

    HmsLogMessage(1, mpTitle+&apos;: Создано ссылок - &apos;+IntToStr(gnTotalItems));
  } else {
    // Устанавливаем значение goRoot как корневой элемент подкаста
    if (LeftCopy(mpFilePath, 4)==&apos;Info&apos;) { ShowInfo(); return; }

    if (HmsRegExMatch(&apos;youtu.?be&apos;, mpFilePath, s)) GetLink_Youtube33(mpFilePath); 
    else MediaResourceLink = mpFilePath; 
  }
}</Value>
    </Property>
    <Property>
      <ID>531</ID>
      <Value>C++Script</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>b65318d7-8a9a-425f-92b9-9feba609ef39</Value>
    </Property>
    <Property>
      <ID>93</ID>
      <Value>42160,1342670833</Value>
    </Property>
    <Property>
      <ID>55</ID>
      <Value>http://cdn.bgr.com/2012/04/youtube.jpg</Value>
    </Property>
    <Property>
      <ID>550</ID>
      <Value>#define mpiTokensInfo    591 // Пароль, где хранится информация о токенах (при сохранении hdf эти данные в файле не сохраняются)
#define mpiAuthorization 594 // Авторизация пользователя: 1 - включена, 2 - выключена, 0 - наследуется
//THmsScriptMediaItem PodcastItem = FolderItem; string MediaResourceLink = &apos;&apos;;
///////////////////////////////////////////////////////////////////////////////
//               Г Л О Б А Л Ь Н Ы Е   П Е Р Е М Е Н Н Ы Е                   //
int    gnTotalItems  = 0;   // Глобальный счетчик созданных ссылок 
string gsUrlBase     = &apos;https://www.youtube.com&apos;;
string gsHeaders     = &apos;Accept-Encoding: gzip, deflate\r\n&apos;;
string gsPodcastName = &apos;HMS Youtube v4.4&apos;; // Заголовок видео-сообщения при ошибках
string gsAPIKey      = &apos;&apos;;
string gsClientId    = &apos;&apos;;
string gsClientSecret= &apos;&apos;;
string gsLanguage    = &apos;ru-RU&apos;;
string gsRegion      = &apos;RU&apos;;
TDateTime gStart     = Now;
Variant gREQUEST; // Для хранения созданного OLE объекта WinHttp.WinHttpRequest.5.1
THmsScriptMediaItem gRoot =  GetRoot(); // Корневой элемент подкаста, содержащий информацию о токенах 
TStringList gACCESS = LoadAccessInfo(); // Создание объекта хранения информации о токенах

///////////////////////////////////////////////////////////////////////////////
//                             Ф У Н К Ц И И                                 //

///////////////////////////////////////////////////////////////////////////////
// Поиск корневой папки подкаста, где мы будем хранить в mpiAccessToken
THmsScriptMediaItem GetRoot() {
  gRoot = PodcastItem; // Начиная с текущего элемента, ищется создержащий срипт или информацию о токенах
  while ((Trim(gRoot[mpiTokensInfo])==&apos;&apos;) &amp;&amp; (Trim(gRoot[550])==&apos;&apos;) &amp;&amp; (gRoot[532]!=&apos;1&apos;) &amp;&amp; (gRoot.ItemParent!=nil)) gRoot=gRoot.ItemParent; 
  return gRoot;
}

///////////////////////////////////////////////////////////////////////////////
// Загрузка данных о токенах доступа
TStringList LoadAccessInfo() {
  gACCESS = TStringList.Create();
  if (Pos(&apos;=&apos;, Trim(gRoot[mpiTokensInfo]))&gt;0)
    gACCESS.Text = gRoot[mpiTokensInfo];
  else
    gACCESS.Text = mpPodcastAuthorizationPassword;
  return gACCESS;
}

///////////////////////////////////////////////////////////////////////////////
// Сохранение данных о токенах доступа
void SaveAccessInfo() {
  gRoot[mpiTokensInfo   ] = gACCESS.Text;
  gRoot[mpiAuthorization] = &apos;1&apos;; // Включение авторизации пользователя, чтобы данные сохранялись после перезагрузки
}

///////////////////////////////////////////////////////////////////////////////
// Запрос к API
string APIRequest(string sObject, string sParam) {
  string sData, sLink, sLang, sErr, sHeaderName, sHeaderValue; int i;
  sLang = Lowercase(gsRegion);
  if (sObject==&apos;search&apos;) {
    gsAPIKey  = &apos;AIzaSyCPxJ1HKZznL9WEFay70tLjyzDrry0DiEw&apos;;
    gsHeaders = &apos;&apos;;
  }
  sLink = &apos;https://www.googleapis.com/youtube/v3/&apos;+sObject+&apos;?key=&apos;+gsAPIKey+&apos;&amp;maxResults=50&amp;gl=&apos;+sLang+&apos;&amp;regionCode=&apos;+sLang+&apos;&amp;hl=&apos;+sLang+&apos;&amp;&apos;+sParam;
  // И всё этот бред с CreateOleObject для того, чтобы была возможность получить тело ответа при коде ответа отличных от 200 для анализа ошибок
  try {
    if (VarType(gREQUEST)!=9) gREQUEST = CreateOleObject(&apos;WinHttp.WinHttpRequest.5.1&apos;); // Если ранее не создавался объект, то создаём
    gREQUEST.open(&apos;GET&apos;, sLink, false);
    for (i=1; i &lt;= WordCount(gsHeaders, &apos;\r\n&apos;) ; i++) {
      if (!HmsRegExMatch2(&apos;^(.*?):(.*)&apos;, ExtractWord(i, gsHeaders, &apos;\r\n&apos;), sHeaderName, sHeaderValue)) continue;
      gREQUEST.SetRequestHeader(sHeaderName, sHeaderValue);
    }
    gREQUEST.send();
    sData = gREQUEST.ResponseText;
    if (gREQUEST.Status!=200) {
      HmsRegExMatch(&apos;"message"\\s*:\\s*"(.*?)"&apos;, sData, sErr);
      HmsLogMessage(2, "Ошибка API: "+HmsHtmlToText(HmsJsonDecode(sErr)));
    }
  } except {
    sData = HmsDownloadUrl(sLink, gsHeaders, true);
    if (sData==&apos;&apos;) HmsLogMessage(2, "Ошибка API: запрос вернул пустой результат. "+sLink);
  }
  return sData;
}

///////////////////////////////////////////////////////////////////////////////
// Показ информации, сохранённой в ссылке
void ShowInfo() {
  string sTitle, sCateg, sInfo, sDescr, sImg, prefix=&apos;youtubev3&apos;;
  TStrings INFO = TStringList.Create();
  INFO.Text = PodcastItem[1001001];
  sTitle = INFO.Values[&apos;Title&apos; ];
  sCateg = INFO.Values[&apos;Categ&apos; ];
  sInfo  = INFO.Values[&apos;Info&apos;  ];
  sDescr = INFO.Values[&apos;Descr&apos; ];
  sImg   = INFO.Values[&apos;Poster&apos;];
  INFO.Free();
  MediaResourceLink = GenerateVideoInfo(prefix, sTitle, sInfo, sCateg, sDescr, sImg);
}

///////////////////////////////////////////////////////////////////////////////
// Генерирование картинок и строки для ffmpeg
char GenerateVideoInfo(char prefix, char sTitle, char sInfo=&apos;&apos;, char sCateg=&apos;&apos;, char sDescr=&apos;&apos;, char sImg=&apos;&apos;, int mode=0, char sDirID=&apos;&apos;) {
  char sHtml, sPost, sVal, sCol, sLink, sData, sPos, sImagesDir;
  int i, n, nSeconds, nSecDel, nMaxImages, nMaxTextLength, nPage=0; double nH, nW; 
  char sFileImage, sCmd, sColor, sTime; TRegExpr reChannel, reCast;
  int xMargin=15, yMargin=15;
  
  nSeconds = 0; nMaxImages = 4; nMaxTextLength = 0;
  if (HmsRegExMatch(&apos;--xmargin=(\\d+)&apos;, mpPodcastParameters, sVal)) xMargin=StrToInt(sVal);
  if (HmsRegExMatch(&apos;--ymargin=(\\d+)&apos;, mpPodcastParameters, sVal)) yMargin=StrToInt(sVal);
  if (Pos(&apos;--lq&apos;, mpPodcastParameters)&gt;0) {
    nH = cfgTranscodingScreenHeight / 2;
    nW = cfgTranscodingScreenWidth  / 2;
  } else if (HmsRegExMatch2(&apos;--pr=(\\d+)x(\\d+)&apos;, mpPodcastParameters, sVal, sPos)) {
    nH = StrToInt(sPos);
    nW = StrToInt(sVal);
  } else {
    nH = cfgTranscodingScreenHeight;
    nW = cfgTranscodingScreenWidth;
  }
  if (sDirID==&apos;&apos;) sDirID = &apos;info_&apos;+PodcastItem.ItemID;
  sImagesDir = IncludeTrailingBackslash(ExtractShortPathName(HmsTempDirectory))+Trim(prefix);
  sImagesDir = IncludeTrailingBackslash(sImagesDir)+sDirID;
  ForceDirectories(sImagesDir);
  sFileImage = IncludeTrailingBackslash(sImagesDir)+&apos;info_&apos;;
  sPost = &apos;&apos;;                       
  TStrings INFO = TStringList.Create();
  INFO.Values[&apos;prfx&apos; ] = prefix;
  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;xpic&apos; ] = &apos;15&apos;;
  INFO.Values[&apos;ypic&apos; ] = &apos;15&apos;;
  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;fz&apos;   ] = &apos;3&apos;;
  INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/22));
  INFO.Values[&apos;fztitle&apos;] = IntToStr(Round(nH/18));
  INFO.Values[&apos;fzinfo&apos; ] = IntToStr(Round(nH/25));
  if (mode==1) {
    INFO.Values[&apos;fzdescr&apos;] = IntToStr(Round(nH/16));
    INFO.Values[&apos;fztitle&apos;] = IntToStr(Round(nH/10));
    INFO.Values[&apos;fzinfo&apos; ] = IntToStr(Round(nH/14));
    INFO.Values[&apos;xm&apos;] = IntToStr(Round(nH/14));
  } else if ((mode==2) || (mode==3)) {
    INFO.Values[&apos;ct&apos;    ] = &apos;000&apos;;
    INFO.Values[&apos;ctitle&apos;] = &apos;822&apos;;
    INFO.Values[&apos;cinfo&apos; ] = &apos;223&apos;;
    INFO.Values[&apos;ccateg&apos;] = &apos;255&apos;;
    INFO.Values[&apos;fz&apos;] = &apos;6&apos;;
    INFO.Values[&apos;ns&apos;] = &apos;1&apos;;
    INFO.Values[&apos;bg&apos;] = &apos;./backgrounds/white.png&apos;;
    INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nH/3));
    if (mode==2) INFO.Values[&apos;wpic&apos;] = IntToStr(Round(nH/2));
  }
  if (sImg  !=&apos;&apos;) {
    INFO.Values[&apos;urlpic&apos;] = sImg;
    INFO.Values[&apos;wpic&apos;  ] = IntToStr(Round(nH/2));
  }
  n = 0; TStrings FILELIST = TStringList.Create();
  try { 
    HmsGetFileList(sImagesDir, FILELIST, &apos;*.jpg&apos;);
    n = FILELIST.Count;
  } finally { FILELIST.Free(); }
  if (n&lt;4) {
    // Получаем ссылки на сформированные картинки, пока в параметрах встречаем lastpos=N
    sPos = &apos;0&apos;; nPage = 0;
    do {
      sColor = &apos;&apos;; sTime = &apos;&apos;; nPage++; 
      if (nPage&gt;1) {
        sData = Copy(sDescr, 1, StrToInt(sPos)); // Блок, успевший попасть на экран
        // Определяем последний использующийся цвет 
        HmsRegExMatch(&apos;.*(\\d{2}:\\d{2}\\s*?-\\s*?\\d{2}:\\d{2})&apos;, sData, sTime, 1, PCRE_SINGLELINE);
        sTime += &apos; ... &apos;;
      }
      sDescr = sColor+sTime+Trim(Copy(sDescr, StrToInt(sPos), 999999)); 
      INFO.Values[&apos;descr&apos;] = sDescr; 
      sPost = &apos;&apos;;
      for (n=0; n&lt;INFO.Count; n++) sPost += &apos;&amp;&apos;+Trim(INFO.Names[n])+&apos;=&apos;+HmsHttpEncode(INFO.Values[INFO.Names[n]]);
      sLink = HmsSendRequestEx(&apos;wonky.lostcut.net&apos;, &apos;/videopreview.php&apos;, &apos;POST&apos;, 
      &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 80, 0, &apos;&apos;, true);
      if (LeftCopy(sLink, 4)!=&apos;http&apos;) {HmsLogMessage(2, &apos;Ошибка получения картинки видеоинформации&apos;); return;}
      HmsDownloadURLToFile(sLink, sFileImage);
      for (n=0; n&lt;5; n++) { nSeconds++; CopyFile(sFileImage, sFileImage+Format(&apos;%.3d.jpg&apos;, [nSeconds]), false); }
      if (nPage&gt;=nMaxImages) break;
    } while (HmsRegExMatch2(&apos;^(.*?)\\?lastpos=(\\d+)&apos;, sLink, sLink, sPos));  
  }
  INFO.Free();
  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;; }
  sLink = Format(&apos;%s -f image2 -r 1 -i "%s" -c:v libx264 -r 30 -pix_fmt yuv420p &apos;, [sFileMP3, sFileImage+&apos;%03d.jpg&apos;]);
  PodcastItem[mpiTimeLength] = HmsTimeFormat(nSeconds)+&apos;.000&apos;;
  return sLink;
}

///////////////////////////////////////////////////////////////////////////////
// Создание информационной ссылки
void InfoItem(string sMsg) {
  THmsScriptMediaItem Item = HmsCreateMediaItem(&apos;Info&apos;+Str(PodcastItem.ChildCount), PodcastItem.ItemID);
  Item[mpiTitle     ] = sMsg;
  Item[mpiThumbnail ] = &apos;http://wonky.lostcut.net/vids/info.jpg&apos;;
  Item[mpiCreateDate] = IncTime(gStart, 0, -gnTotalItems, 0, 0); gnTotalItems++;
  HmsLogMessage(2, sMsg);
}

///////////////////////////////////////////////////////////////////////////////
// Запрос авторизации для нового устройства
void RequestNewCode() {
  string sPost, sData, sRet; TJsonObject JSON = TJsonObject.Create();
  try {
    sPost = &apos;scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube&amp;client_id=&apos;+gsClientId;
    sData = HmsSendRequestEx(&apos;accounts.google.com&apos;, &apos;/o/oauth2/device/code&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
    JSON.LoadFromString(sData);
    gACCESS.Values[&apos;code&apos;            ] = JSON.S[&apos;device_code&apos;];
    gACCESS.Values[&apos;verification_url&apos;] = JSON.S[&apos;verification_url&apos;];
    gACCESS.Values[&apos;user_code&apos;       ] = JSON.S[&apos;user_code&apos;];
    gACCESS.Values[&apos;expires_date&apos;    ] = &apos;&apos;;
    SaveAccessInfo();
    InfoItem(&apos;Перейдите по адресу &apos;+JSON.S[&apos;verification_url&apos;]);
    InfoItem(&apos;Введите код: &apos;       +JSON.S[&apos;user_code&apos;       ]);
    InfoItem(&apos;Потом обновите раздел.&apos;);
  } finally { JSON.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Авторизация
bool Login(bool bSilent=false) {
  TJsonObject JSON; string sData, sPost, sRet; bool bResult=false;
  JSON = TJsonObject.Create();
  try {
    if (TimeStamp1970ToDateTime(StrToIntDef(gACCESS.Values[&apos;expires_date&apos;], 0), false)&gt;Now) { bResult=true; return true; }
    if (gACCESS.Values[&apos;code&apos;]!=&apos;&apos;) {
      if (bSilent) return false;
      sPost = &apos;client_secret=&apos;+gsClientSecret+&apos;&amp;code=&apos;+gACCESS.Values[&apos;code&apos;]+&apos;&amp;client_id=&apos;+gsClientId+&apos;&amp;grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fdevice%2F1.0&apos;;
      sData = HmsSendRequestEx(&apos;www.googleapis.com&apos;, &apos;/oauth2/v4/token&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
      JSON.LoadFromString(sData);
      if (JSON.S[&apos;access_token&apos;]!=&apos;&apos;) {
        gACCESS.Values[&apos;access_token&apos; ] = JSON.S[&apos;access_token&apos; ];
        gACCESS.Values[&apos;refresh_token&apos;] = JSON.S[&apos;refresh_token&apos;];
        gACCESS.Values[&apos;expires_date&apos; ] = Str(DateTimeToTimeStamp1970(Now, false)+JSON.I[&apos;expires_in&apos;]);
        gACCESS.Values[&apos;code&apos;] = &apos;&apos;;
        SaveAccessInfo();
      } else if (Pos(&apos;428 Precondition Required&apos;, sRet)&gt;0) {
        if (!bSilent) {
          InfoItem(&apos;Ожидание подтверждения авторизации&apos;);
          InfoItem(&apos;По адресу &apos;+gACCESS.Values[&apos;verification_url&apos;]);
          InfoItem(&apos;Ввести код: &apos;+gACCESS.Values[&apos;user_code&apos;]);
        }
        return false;
      }
    }
    if (gACCESS.Values[&apos;access_token&apos;]==&apos;&apos;) { if (!bSilent) RequestNewCode(); return false; }
    // Проверка работоспособности токена
    sData = HmsDownloadURL(&apos;https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=&apos;+gACCESS.Values[&apos;access_token&apos;]);
    JSON.LoadFromString(sData);
    if (!JSON.B[&apos;azp&apos;]) {
      sPost = &apos;client_id=&apos;+gsClientId+&apos;&amp;client_secret=&apos;+gsClientSecret+&apos;&amp;refresh_token=&apos;+gACCESS.Values[&apos;refresh_token&apos;]+&apos;&amp;grant_type=refresh_token&apos;;
      sData = HmsSendRequestEx(&apos;www.googleapis.com&apos;, &apos;/oauth2/v4/token&apos;, &apos;POST&apos;, &apos;application/x-www-form-urlencoded&apos;, &apos;&apos;, sPost, 443, 0, sRet, true);
      JSON.LoadFromString(sData);
      gACCESS.Values[&apos;access_token&apos;] = JSON.S[&apos;access_token&apos;];
    }
    gACCESS.Values[&apos;expires_date&apos;] = Str(DateTimeToTimeStamp1970(Now, false)+JSON.I[&apos;expires_in&apos;]);
    SaveAccessInfo();
    bResult = (gACCESS.Values[&apos;access_token&apos;]!=&apos;&apos;);
  } finally { 
    JSON.Free; 
    if (bResult) {
      gsHeaders += &apos;Authorization: Bearer &apos;+gACCESS.Values[&apos;access_token&apos;]+&apos;\r\n&apos;;
    } 
  }
  return bResult;
}

///////////////////////////////////////////////////////////////////////////////
// Получение ссылки на Youtube
bool GetLink_Youtube33(string sLink) {
  string sData, sVideoID=&apos;&apos;, sAudio=&apos;&apos;, sSubtitlesLanguage=&apos;ru&apos;,
  sSubtitlesUrl, sFile, sVal, sMsg, sConfig, sHeaders, hlsUrl, subsUrl, jsUrl, 
  streamMap, algorithm, sType, itag, sig, alg, s, sp; TStringList QLIST;
  int i, n, w, num, height, priority, minPriority = 90, selHeight, maxHeight = 1080, audioQual;
  TJsonObject JSON, PLAYER_RESPONSE, VIDEO; TJsonArray FORMATS, SUBS; TRegExpr RegEx;
  
  sHeaders = &apos;Referer: &apos;+sLink+&apos;\r\n&apos;+
             &apos;User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36\r\n&apos;+
             &apos;Origin: https://www.youtube.com\r\n&apos;;
  
  if (HmsRegExMatch(&apos;--maxheight=(\\d+)&apos;, mpPodcastParameters, sVal)) maxHeight = StrToInt(sVal);
  HmsRegExMatch(&apos;--sublanguage=(\\w{2})&apos;, mpPodcastParameters, sSubtitlesLanguage);
  bool bSubtitles = Pos(&apos;--subtitles&apos; , mpPodcastParameters)&gt;0;  
  bool bAdaptive  = Pos(&apos;--adaptive&apos;  , mpPodcastParameters)&gt;0;  
  bool bQualLog   = Pos(&apos;--qualitylog&apos;, mpPodcastParameters)&gt;0;
  
  if (!HmsRegExMatch(&apos;[\\?&amp;]v=([^&amp;]+)&apos;       , sLink, sVideoID))
  if (!HmsRegExMatch(&apos;youtu.be/([^&amp;]+)&apos;      , sLink, sVideoID))
       HmsRegExMatch(&apos;/(?:embed|v)/([^\\?]+)&apos;, sLink, sVideoID);
  
  if (sVideoID==&apos;&apos;) return;
  
  sLink = gsUrlBase+&apos;/watch?v=&apos;+sVideoID+&apos;&amp;hl=ru&apos;;
  sData = HmsDownloadURL(sLink, sHeaders, true);
  sData = HmsRemoveLineBreaks(sData);
  
  if (!HmsRegExMatch(&apos;player.config\\s*=\\s*({.*?});&apos;, sData, sConfig, 1, PCRE_SINGLELINE)) {
    HmsLogMessage(2 , mpTitle+&apos;: No player.config data in loaded page.&apos;); 
    return; 
  }
  
  JSON = TJsonObject.Create();
  PLAYER_RESPONSE = TJsonObject.Create();
  QLIST = TStringList.Create();
  try {
    JSON.LoadFromString(sConfig);
    PLAYER_RESPONSE.LoadFromString(JSON.S[&apos;args\\player_response&apos;]);
    
    // Если есть субтитры и в дополнительных параметрах указано их показывать - загружаем 
    if (bSubtitles &amp;&amp; PLAYER_RESPONSE.B[&apos;captions\\playerCaptionsTracklistRenderer\\captionTracks&apos;]) {
      string sTime1, sTime2, engSubs; float nStart, nDur;
      SUBS = PLAYER_RESPONSE.O[&apos;captions\\playerCaptionsTracklistRenderer\\captionTracks&apos;].AsArray;
      for (i=0; i &lt; SUBS.Length; i++) {
        if (SUBS[i].S[&apos;languageCode&apos;]==sSubtitlesLanguage) subsUrl = SUBS[i].S[&apos;baseUrl&apos;];
        if (SUBS[i].S[&apos;languageCode&apos;]==&apos;en&apos;              ) engSubs = SUBS[i].S[&apos;baseUrl&apos;];
      }
      if ((subsUrl==&apos;&apos;) &amp;&amp; (engSubs!=&apos;&apos;)) subsUrl = engSubs+&apos;&amp;tlang=&apos;+sSubtitlesLanguage;
      if (subsUrl!=&apos;&apos;) {
        sData = HmsDownloadURL(subsUrl+&apos;&amp;fmt=srv3&apos;, sHeaders, true);
        sMsg  = &apos;&apos;; i = 0;
        RegEx = TRegExpr.Create(&apos;(&lt;(text|p).*?&lt;/(text|p)&gt;)&apos;, PCRE_SINGLELINE); // Convert to srt format
        try {
          if (RegEx.Search(sData)) do {
            if      (HmsRegExMatch(&apos;start="([\\d\\.]+)&apos;, RegEx.Match, sVal)) nStart = StrToFloat(ReplaceStr(sVal, &apos;.&apos;, &apos;,&apos;))*1000;
            else if (HmsRegExMatch(&apos;t="(\\d+)&apos;         , RegEx.Match, sVal)) nStart = StrToFloat(sVal);
            if      (HmsRegExMatch(&apos;dur="([\\d\\.]+)&apos;  , RegEx.Match, sVal)) nDur   = StrToFloat(ReplaceStr(sVal, &apos;.&apos;, &apos;,&apos;))*1000;
            else if (HmsRegExMatch(&apos;d="(\\d+)&apos;         , RegEx.Match, sVal)) nDur   = StrToFloat(sVal);
            sTime1 = HmsTimeFormat(Int(nStart/1000))+&apos;,&apos;+RightCopy(Str(nStart), 3);
            sTime2 = HmsTimeFormat(Int((nStart+nDur)/1000))+&apos;,&apos;+RightCopy(Str(nStart+nDur), 3);
            sMsg += Format("%d\n%s --&gt; %s\n%s\n\n", [i, sTime1, sTime2, HmsHtmlToText(HmsHtmlToText(RegEx.Match(0), 65001))]);
            i++;
          } while (RegEx.SearchAgain());
        } finally { RegEx.Free(); }
        sFile = HmsSubtitlesDirectory+&apos;\\Youtube\\&apos;+PodcastItem.ItemID+&apos;.&apos;+sSubtitlesLanguage+&apos;.srt&apos;;
        HmsStringToFile(sMsg, sFile);
        if (Trim(PodcastItem[mpiSubtitleLanguage])!=&apos;&apos;) bAdaptive = false;
        PodcastItem[mpiSubtitleLanguage] = sFile;
      }
    }
    
    hlsUrl = PLAYER_RESPONSE.S[&apos;streamingData\\hlsManifestUrl&apos;];
    jsUrl  = JSON.S[&apos;assets\\js&apos;];
    
    if (hlsUrl!=&apos;&apos;) {
      MediaResourceLink = &apos; &apos;+hlsUrl;
      bAdaptive = false;
      sData = HmsDownloadUrl(hlsUrl, sHeaders, true);
      RegEx = TRegExpr.Create(&apos;BANDWIDTH=(\\d+).*?RESOLUTION=(\\d+)x(\\d+).*?(http[^#]*)&apos;, PCRE_SINGLELINE);
      try {
        if (RegEx.Search(sData)) do {
          sLink = &apos;&apos; + RegEx.Match(4);
          height = StrToIntDef(RegEx.Match(3), 0);
          if (mpPodcastMediaFormats!=&apos;&apos;) {
            priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
            if ((priority&gt;=0) &amp;&amp; (priority&gt;minPriority)) {
              MediaResourceLink = sLink; minPriority = priority;
            }
          } else if ((height &gt; selHeight) &amp;&amp; (height &lt;= maxHeight)) {
            MediaResourceLink = sLink; selHeight = height;
          }
          QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        } while (RegEx.SearchAgain());
      } finally { RegEx.Free(); }
      
    } else if (PLAYER_RESPONSE.B[&apos;streamingData\\formats&apos;]) {
      FORMATS = PLAYER_RESPONSE.O[&apos;streamingData\\formats&apos;].AsArray;
      if (FORMATS[0].B[&apos;signatureCipher&apos;])
        algorithm = HmsDownloadURL(&apos;https://hms.lostcut.net/yt/getalgo.php?jsurl=&apos;+HmsHttpEncode(jsUrl));
      for(i=0; i&lt;FORMATS.Length; i++) {
        VIDEO = FORMATS[i];
        if (VIDEO.B[&apos;signatureCipher&apos;]) {
          sLink = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;url&apos;, &apos;&apos;, &apos;&amp;&apos;));
          sig   = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;s&apos;  , &apos;&apos;, &apos;&amp;&apos;));
          sp    = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;signatureCipher&apos;], &apos;sp&apos; , &apos;&apos;, &apos;&amp;&apos;));
          if (sig!=&apos;&apos;) {
            for (w=1; w&lt;=WordCount(algorithm, &apos; &apos;); w++) {
              alg = ExtractWord(w, algorithm, &apos; &apos;);
              if (Length(alg)&lt;1) continue;
              if (Length(alg)&gt;1) TryStrToInt(Copy(alg, 2, 4), num);
              if (alg[1]==&apos;r&apos;) {s=&apos;&apos;; for(n=Length(sig); n&gt;0; n--) s+=sig[n]; sig = s;   } // Reverse
              if (alg[1]==&apos;s&apos;) {sig = Copy(sig, num+1, Length(sig));                     } // Clone
              if (alg[1]==&apos;w&apos;) {n = (num-Trunc(num/Length(sig)))+1; Swap(sig[1], sig[n]);} // Swap
            }
          }
          sLink += &apos;&amp;&apos;+sp+&apos;=&apos;+sig;
        } else {
          sLink = VIDEO.S[&apos;url&apos;];
        }
        height = VIDEO.I[&apos;height&apos;];
        if (height&gt;0) QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        if (mpPodcastMediaFormats!=&apos;&apos;) {
          priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
          if ((priority&gt;=0) || (priority&lt;minPriority)) {
            MediaResourceLink = sLink; minPriority = priority; selHeight = height;
          }
        } else if ((height&gt;selHeight) &amp;&amp; (height&lt;= maxHeight)) {
          MediaResourceLink = sLink; selHeight = height;
          
        } else if ((height&gt;=selHeight) &amp;&amp; (height&lt;= maxHeight) &amp;&amp; (VIDEO.I[&apos;itag&apos;] in ([18,22,37,38,82,83,84,85]))) {
          // Если высота такая же, но формат MP4 - то выбираем именно его (делаем приоритет MP4)
          MediaResourceLink = sLink; selHeight = height;
        }
      }
    } 
    if (bAdaptive || (MediaResourceLink==&apos;&apos;) &amp;&amp; PLAYER_RESPONSE.B[&apos;streamingData\\adaptiveFormats&apos;]) {
      FORMATS = PLAYER_RESPONSE.O[&apos;streamingData\\adaptiveFormats&apos;].AsArray;
      if (FORMATS[0].B[&apos;cipher&apos;] &amp;&amp; (algorithm==&apos;&apos;))
        algorithm = HmsDownloadURL(&apos;https://hms.lostcut.net/yt/getalgo.php?jsurl=&apos;+HmsHttpEncode(jsUrl));
      for(i=0; i&lt;FORMATS.Length; i++) {
        VIDEO = FORMATS[i];
        if (VIDEO.B[&apos;cipher&apos;]) {
          sLink = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;url&apos;, &apos;&apos;, &apos;&amp;&apos;));
          sig   = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;s&apos;  , &apos;&apos;, &apos;&amp;&apos;));
          sp    = HmsHttpDecode(ExtractParam(VIDEO.S[&apos;cipher&apos;], &apos;sp&apos; , &apos;&apos;, &apos;&amp;&apos;));
          if (sig!=&apos;&apos;) {
            for (w=1; w&lt;=WordCount(algorithm, &apos; &apos;); w++) {
              alg = ExtractWord(w, algorithm, &apos; &apos;);
              if (Length(alg)&lt;1) continue;
              if (Length(alg)&gt;1) TryStrToInt(Copy(alg, 2, 4), num);
              if (alg[1]==&apos;r&apos;) {s=&apos;&apos;; for(n=Length(sig); n&gt;0; n--) s+=sig[n]; sig = s;   } // Reverse
              if (alg[1]==&apos;s&apos;) {sig = Copy(sig, num+1, Length(sig));                     } // Clone
              if (alg[1]==&apos;w&apos;) {n = (num-Trunc(num/Length(sig)))+1; Swap(sig[1], sig[n]);} // Swap
            }
          }
          sLink += &apos;&amp;&apos;+sp+&apos;=&apos;+sig;
        } else {
          sLink = VIDEO.S[&apos;url&apos;];
        }
        if (VIDEO.B[&apos;audioSampleRate&apos;] &amp;&amp; (audioQual &lt; VIDEO.I[&apos;bitrate&apos;])) {
          sAudio = sLink; audioQual = VIDEO.I[&apos;bitrate&apos;]; continue; 
        }
        height = VIDEO.I[&apos;height&apos;];
        if (height&gt;0) QLIST.Values[Format(&apos;%.4d&apos;, [height])] = sLink;
        if (mpPodcastMediaFormats!=&apos;&apos;) {
          priority = HmsMediaFormatPriority(height, mpPodcastMediaFormats);
          if ((priority&gt;=0) || (priority&lt;minPriority)) {
            MediaResourceLink = sLink; minPriority = priority; selHeight = height;
          }
        } else if ((height&gt;selHeight) &amp;&amp; (height&lt;= maxHeight)) {
          MediaResourceLink = sLink; selHeight = height;
        }
      }
      sVal = ""; if (Trim(mpTimeStart)!="") sVal = " -ss "+mpTimeStart;
      if (bAdaptive &amp;&amp; (sAudio!=&apos;&apos;)) {
        if (Pos(&apos;--downloadaudio&apos;, mpPodcastParameters)&gt;0) {
          sFile = HmsTempDirectory+&apos;\\&apos;+PodcastItem.ItemID+&apos;.webm&apos;;
          if (!FileExists(sFile))
            HmsDownloadURLToFile(sAudio, sFile, sHeaders, true);
        } else sFile = sAudio;
        MediaResourceLink = &apos;-hide_banner -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 -fflags +genpts -i "&apos;+Trim(MediaResourceLink)+&apos;"&apos;+sVal+&apos; -i "&apos;+Trim(sFile)+&apos;"&apos;;
      }
    }
    // Если еще не установлена реальная длительность видео - устанавливаем
    if ((Trim(mpTimeLength)==&apos;&apos;) || (RightCopy(mpTimeLength, 6)==&apos;00.000&apos;) &amp;&amp; (hlsUrl==&apos;&apos;)) {
      PodcastItem[mpiTimeLength] = HmsTimeFormat(PLAYER_RESPONSE.I[&apos;videoDetails\\lengthSeconds&apos;]);
    }
    if (bQualLog) {
      QLIST.Sort();
      sMsg = &apos;Доступное качество: &apos;;
      for (i = 0; i &lt; QLIST.Count; i++) {
        if (i&gt;0) sMsg += &apos;, &apos;;
        sMsg += IntToStr(StrToInt(QLIST.Names[i])); // Обрезаем лидирующие нули
      }
      sMsg += &apos;. Выбрано: &apos; + Str(selHeight);
      HmsLogMessage(1, mpTitle+&apos;. &apos;+sMsg);
    }
  } finally { JSON.Free; PLAYER_RESPONSE.Free; QLIST.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Показ видео сообщения
bool VideoMessage(string sMsg) {
  string sFileMP3 = HmsTempDirectory+&apos;\\sa.mp3&apos;;
  string sFileImg = HmsTempDirectory+&apos;\\youtubemsg_&apos;;
  sMsg = HmsHtmlToText(HmsJsonDecode(sMsg));
  int nH = cfgTranscodingScreenHeight;
  int nW = cfgTranscodingScreenWidth;
  HmsLogMessage(2, mpTitle+&apos;: &apos;+sMsg);
  string sLink = Format(&apos;http://wonky.lostcut.net/videomessage.php?h=%d&amp;w=%d&amp;captsize=%d&amp;fontsize=%d&amp;caption=%s&amp;msg=%s&apos;, [nH, nW, Round(nH/8), Round(nH/17), gsPodcastName, HmsHttpEncode(sMsg)]);
  HmsDownloadURLToFile(sLink, sFileImg);
  for (int i=1; i&lt;=7; i++) CopyFile(sFileImg, sFileImg+Format(&apos;%.3d.jpg&apos;, [i]), false);
  try {
    if (!FileExists(sFileMP3)) HmsDownloadURLToFile(&apos;http://wonky.lostcut.net/mp3/sa.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 -r 30 -pix_fmt yuv420p &apos;, [sFileMP3, sFileImg+&apos;%03d.jpg&apos;]);
  return false;
}

///////////////////////////////////////////////////////////////////////////////
// Создание ссылки-ошибки
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;;
  HmsLogMessage(2, sMsg);
}

///////////////////////////////////////////////////////////////////////////////
// Создание видео-ссылки
THmsScriptMediaItem CreateVideo(string sID, string sTitle, string sImg, string sTime, string sDate, string sComment=&apos;&apos;) {
  if (LeftCopy(sID, 4)!=&apos;http&apos;) sID = gsUrlBase+&apos;/watch?v=&apos;+Trim(sID); 
  THmsScriptMediaItem Item = HmsCreateMediaItem(sID, PodcastItem.ItemID);
  Item[mpiTitle      ] = sTitle;
  Item[mpiThumbnail  ] = sImg;
  Item[mpiTimeLength ] = sTime;
  Item[mpiCreateDate ] = sDate; 
  Item[mpiComment    ] = sComment;
  gnTotalItems++; 
  return Item; 
}

///////////////////////////////////////////////////////////////////////////////
// Создание папки или обновляемого подкаста
THmsScriptMediaItem CreateFolder(string sName, string sLink, string sImg=&apos;&apos;) {
  THmsScriptMediaItem Item = PodcastItem.AddFolder(sLink); // Создаём папку с указанной ссылкой
  Item[mpiTitle      ] = sName; // Присваиваем наименование
  Item[mpiThumbnail  ] = sImg;  // Картинка папки
  Item[mpiCreateDate ] = IncTime(gStart, 0, -gnTotalItems, 0, 0); gnTotalItems++;
  return Item;
}

///////////////////////////////////////////////////////////////////////////////
// Перевод некоторых английских фраз из информации о плейлистах
string Translate(string sText) {
  string sVal, sResult=sText; TStrings DICT = TStringList.Create();
  DICT.Values[&apos;Best of YouTube&apos;] = &apos;Лучшее на YouTube&apos;;
  DICT.Values[&apos;Paid channels&apos;  ] = &apos;Платные каналы&apos;;
  DICT.Values[&apos;Top YouTube Collections&apos;] = &apos;Лучшие коллекции YouTube&apos;;
  DICT.Values[&apos;Popular Artists&apos;] = &apos;Популярные исполнители&apos;;
  if (Trim(DICT.Values[sText])!=&apos;&apos;) sResult = DICT.Values[sText];
  DICT.Free();
  if (HmsRegExMatch(&apos;(Popular Artist Mixes)&apos;, sResult, sVal)) sResult = ReplaceStr(sResult, sVal, &apos;Миксы популярных исполнителей&apos;);
  return sResult;
}

///////////////////////////////////////////////////////////////////////////////
// Получение безопасного валидного наименования
string SafeName(string sName) {
  sName = ReplaceStr(sName, &apos;/&apos; , &apos;-&apos;);
  sName = ReplaceStr(sName, &apos;\\&apos;, &apos;-&apos;);
  sName = Translate(sName);   
  return sName;  
}

///////////////////////////////////////////////////////////////////////////////
// Получение длительности для HMS из формата youtube
string ConvertTime(string sTime) {
  string sVal; int nSeconds = 0;
  if (HmsRegExMatch(&apos;(\\d+)H&apos;, sTime, sVal)) nSeconds += StrToInt(sVal)*3600;  
  if (HmsRegExMatch(&apos;(\\d+)M&apos;, sTime, sVal)) nSeconds += StrToInt(sVal)*60;  
  if (HmsRegExMatch(&apos;(\\d+)S&apos;, sTime, sVal)) nSeconds += StrToInt(sVal);
  if (nSeconds==0) nSeconds = 600;
  return HmsTimeFormat(nSeconds)+&apos;.000&apos;;
}

///////////////////////////////////////////////////////////////////////////////
// Получение даты создания для HMS из формата youtube
string ConvertDate(string sDate) {
  string sY, sM, sD, sTime;
  HmsRegExMatch3(&apos;(\\d{4}).(\\d{2}).(\\d{2})&apos;, sDate, sY, sM, sD);  
  HmsRegExMatch (&apos;T(\\d{2}:\\d{2}:\\d{2})&apos;   , sDate, sTime     );  
  return Format(&apos;%s.%s.%s %s&apos;, [sD, sM, sY, sTime]);
}

///////////////////////////////////////////////////////////////////////////////
// Универсальная функция создания элементов видео (или сбора информации)
string CreateItems(string sPath, string sObject, string sParam, string sFiltr=&apos;&apos;) {
  string sLink, sData, sName, sID, sImg, sCh, sVal, sResult=&apos;&apos;, sTime;
  int i, nLevel; TJsonObject JSON, ITEM; TJsonArray ITEMS;
  
  if (HmsRegExMatch(&apos;pageToken=([\\w-_]+)&apos;, mpFilePath, sVal)) sParam += &apos;&amp;pageToken=&apos;+sVal; 
  if (HmsRegExMatch(&apos;&amp;level=(\\d+)&apos;       , mpFilePath, sVal)) nLevel = StrToInt(sVal); 
  
  sData = APIRequest(sObject, sParam);
  
  JSON = TJsonObject.Create();  
  try {
    JSON.LoadFromString(sData);
    sID = JSON.S(&apos;nextPageToken&apos;);
    if ((sID!=&apos;&apos;) &amp;&amp; (nLevel&lt;3)) {
      if (sObject==&apos;search&apos;) mpFilePath = &apos;-search=&apos;+sParam;  
      if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
      if (HmsRegExMatch(&apos;(&amp;level=\\d+)&apos;        , mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
      sCh = &apos;?&apos;; if (Pos(&apos;?&apos;, mpFilePath)&gt;0) sCh = &apos;&amp;&apos;;
      CreateFolder(&apos;Следующая страница&apos;, mpFilePath+sCh+&apos;pageToken=&apos;+sID+&apos;&amp;level=&apos;+Str(nLevel+1));
    }
    
    ITEMS = JSON.A(&apos;items&apos;);
    if (ITEMS != nil) {
      for (i=0; i&lt;ITEMS.Length; i++) {
        ITEM = ITEMS[i];
        
        // Хоть playlistItems и возвращает список видео, но там нет длительности
        // Поэтому собираем ID видео, чтобы потом вызвать videos со списком этих ID 
        if (sObject==&apos;playlistItems&apos;) { 
          if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
          sResult += ITEM.S(&apos;contentDetails\\videoId&apos;);
          continue;
          
        } else if (sObject==&apos;activities&apos;) {
          if (sFiltr!=&apos;&apos;) if (ITEM.S[sFiltr]==&apos;&apos;) continue;   
          if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
          sID = ITEM.S(&apos;contentDetails\\recommendation\\resourceId\\videoId&apos;);
          if (sID==&apos;&apos;) sID = ITEM.S(&apos;contentDetails\\playlistItem\\resourceId\\videoId&apos;);
          if (sID==&apos;&apos;) sID = ITEM.S(&apos;contentDetails\\upload\\videoId&apos;);
          if (sID!=&apos;&apos;) sResult += sID;
          continue;
          
        } else if (sObject==&apos;search&apos;) {
          sVal = ITEM.S(&apos;id\\kind&apos;);
          if (sVal==&apos;youtube#playlist&apos;) {
            sID   = ITEM.S(&apos;id\\playlistId&apos;);
            sPath = gsUrlBase+&apos;/playlist?list=&apos;;
          } else if (sVal==&apos;youtube#channel&apos;) {
            sID   = ITEM.S(&apos;id\\channelId&apos;);
            sPath = gsUrlBase+&apos;/channel/&apos;;
          } else {
            sID   = ITEM.S(&apos;id\\videoId&apos;);
            sPath = &apos;video&apos;;
            if (sResult!=&apos;&apos;) sResult +=  &apos;,&apos;;
            if (sID!=&apos;&apos;) sResult += sID;
            continue;
          }
          
        } else if (sObject==&apos;videoCategories&apos;) {
          if (!ITEM.B(&apos;snippet\\assignable&apos;)) continue;
          sID   = ITEM.S(&apos;id&apos;);
          if (sID==&apos;1&apos;) sID = &apos;30&apos;;
          
        } else if (sObject==&apos;subscriptions&apos;) {
          sID   = ITEM.S(&apos;snippet\\resourceId\\channelId&apos;);
          
        } else {
          sID   = ITEM.S(&apos;id&apos;);
        }
        if (sID==&apos;&apos;) continue;
        if (LeftCopy(sPath, 6)==&apos;getids&apos;) { if (sResult!=&apos;&apos;) sResult+=&apos;,&apos;;  sResult += sID; continue; }
        sName = ITEM.S(&apos;snippet\\localized\\title&apos;);  
        if (sName==&apos;&apos;) sName = ITEM.S(&apos;snippet\\title&apos;);  
        sImg  = ITEM.S(&apos;snippet\\thumbnails\\medium\\url&apos;);
        sName = SafeName(HmsUtf8Decode(sName));
        sVal  = ITEM.S(&apos;contentDetails\\itemCount&apos;); if (sVal==&apos;0&apos;) continue;
        if (sVal!=&apos;&apos;) sName += &apos; [&apos;+sVal+&apos;]&apos;;
        
        if (Pos(sPath, &apos;%s&apos;)&gt;0) sLink = Format(sPath, [sID]);
        else                    sLink = sPath + sID;
        
        if (sPath==&apos;video&apos; ) {
          if (ITEM.S(&apos;snippet\\liveBroadcastContent&apos;)==&apos;live&apos;) sTime = &apos;04:00:00.000&apos;; 
          else sTime = ConvertTime(ITEM.S(&apos;contentDetails\\duration&apos;));
          if (Pos(&apos;DE&apos;, ITEM.S(&apos;contentDetails\\regionRestriction\\blocked&apos;))&gt;0) sID += &apos;&amp;notde=1&apos;;
        } 
        
        if (sPath==&apos;video&apos; ) CreateVideo(sID, sName, sImg, sTime, ConvertDate(ITEM.S(&apos;snippet\\publishedAt&apos;)), HmsUtf8Decode(ITEM.S(&apos;snippet\\description&apos;)));
        else                 CreateFolder(sName, sLink, sImg);
      }
    } 
    
  } finally { JSON.Free(); } 
  return sResult;
}

///////////////////////////////////////////////////////////////////////////////
// Создание каналов конкретной категории
void CreateChannels(string sID) {
  Login(true);
  CreateItems(gsUrlBase+&apos;/channel/&apos;, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;categoryId=&apos;+sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка категорий назначенных самим youtube автоматически
void CreateGuideCategories() {
  Login(true);
  CreateItems(gsUrlBase+&apos;/channels/&apos;, &apos;guideCategories&apos;, &apos;part=snippet&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка категорий
void CreateVideoCategories() {
  Login(true);
  CreateItems(&apos;-category=&apos;, &apos;videoCategories&apos;, &apos;part=snippet&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео по выбранной категории
void CreateVideosByCategory(string sID) {
  Login(true);
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;chart=mostPopular&amp;videoCategoryId=&apos;+sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного плейлиста
void CreatePlaylistVideos(string sID) {
  string sIDs, sVal;
  Login(true);
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;playlistItems&apos;, &apos;part=contentDetails&amp;fields=nextPageToken,items(contentDetails%2FvideoId)&amp;playlistId=&apos;+sID);
  
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
  
}

///////////////////////////////////////////////////////////////////////////////
// Создание подкастов каналов в родительской папке
void CreateMySubscriptions() {
  if (!Login()) return;
  TJsonObject SUBS = TJsonObject.Create();
  try {
    SUBS.LoadFromString(APIRequest(&apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;));
    if (SUBS["items"]==nil) return;
    for (int n=0; n &lt; SUBS["items"].AsArray.Length; n++) {
      TJsonObject OBJECT = SUBS["items"].AsArray[n];
      string ch = OBJECT.S[&apos;snippet\\resourceId\\channelId&apos;]; if (ch==&apos;&apos;) continue;
      THmsScriptMediaItem Item = PodcastItem.AddFolder(gsUrlBase+&apos;/channel/&apos;+ch);
      Item[mpiTitle     ] = HmsUtf8Decode(OBJECT.S[&apos;snippet\\title&apos;]);
      Item[mpiComment   ] = HmsUtf8Decode(OBJECT.S[&apos;snippet\\description&apos;]);
      Item[mpiThumbnail ] = OBJECT.S[&apos;snippet\\thumbnails\\medium\\url&apos;];
      Item[mpiCreateDate] = ConvertDate(OBJECT.S[&apos;snippet\\publishedAt&apos;]); gnTotalItems++;
    }
  } finally { SUBS.Free; }    
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка подписчиков
void CreateMySubscribers() {
  if (!Login()) return;
  CreateItems(gsUrlBase+&apos;/channel/&apos;, &apos;subscriptions&apos;, &apos;part=snippet,contentDetails&amp;mySubscribers=true&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео по ключевому слову названия плейлиста ("WL", "likes"...)  
void CreateMyPlaylist(string sKey) {
  string sData, sID;
  
  if (!Login()) return;
  
  sData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;mine=true&apos;);
  
  if (HmsRegExMatch(&apos;"&apos;+sKey+&apos;":\\s*?"(.*?)"&apos;, sData, sID)) CreatePlaylistVideos(sID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео "Рекомендации"  
void CreateMyRecommendations() {
  string sIDs, sVal;
  
  if (!Login()) return;
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;activities&apos;, &apos;part=contentDetails&amp;home=true&apos;);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Получение списка ID через запятую по переданной json строке
string GetIDs(string sData, string sPath) {
  string sID, sIDs;
  TJsonObject JSON = TJsonObject.Create();
  int nCount = 0;
  try {
    JSON.LoadFromString(sData);
    if (JSON["items"].AsArray!=nil) {
      for (int i=0; i &lt; JSON["items"].AsArray.Length; i++) {
        sID = JSON["items"].AsArray[i].S[sPath]; if (sID==&apos;&apos;) continue;
        if (sIDs==&apos;&apos;) sIDs = sID; else sIDs += &apos;,&apos;+sID;
        nCount++; if (nCount &gt;= 50) break;
      }          
    }
  } finally { JSON.Free; }
  return sIDS;
}

///////////////////////////////////////////////////////////////////////////////
// Список новых видео в подписках (НОВЫЙ СПОСОБ)
void NewVideos() {
  string sID, sIDs, sData, sName, sTime, sDesc, sChan; TJsonArray VIDEOS;
  if (!Login()) return;
  TJsonObject SUBS = TJsonObject.Create();
  TJsonObject JSON = TJsonObject.Create();
  TStringList LIST = TStringList.Create();
  int nCount = 0;
  try {
    sData = APIRequest(&apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;);
    SUBS.LoadFromString(sData);
    if (SUBS["items"] != nil) {
      for (int n=0; n &lt; SUBS["items"].AsArray.Length; n++) {
        sChan = SUBS["items"].AsArray[n].S[&apos;snippet\\resourceId\\channelId&apos;]; if (sChan==&apos;&apos;) continue;
        sData = APIRequest(&apos;activities&apos;, &apos;part=contentDetails&amp;channelId=&apos;+sChan);
        sIDs  = GetIDs(sData, &apos;contentDetails\\upload\\videoId&apos;);
        sData = APIRequest(&apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
        JSON.LoadFromString(sData);
        if (JSON["items"]==nil) continue;
        for (int i=0; i &lt; JSON["items"].AsArray.Length; i++) {
          sID   = JSON["items"].AsArray[i].S[&apos;id&apos;];
          sData = JSON["items"].AsArray[i].S[&apos;snippet\\publishedAt&apos;];
          sTime = JSON["items"].AsArray[i].S[&apos;contentDetails\\duration&apos;];
          sName = HmsUtf8Decode(JSON["items"].AsArray[i].S[&apos;snippet\\title&apos;]);
          sDesc = HmsUtf8Decode(JSON["items"].AsArray[i].S[&apos;snippet\\description&apos;]);
          LIST.Add(sData+&apos;=&apos;+sID+&apos;|&apos;+sName+&apos;|&apos;+sTime+&apos;|&apos;+sDesc);
        }
      }
      CreateVideosFromList(LIST);
    }
  } finally { SUBS.Free; JSON.Free; LIST.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Создание видео из переданного списка TStringList
void CreateVideosFromList(TStringList LIST) {
  string sID, sLink, sName, sTime, sDate, sDesc, sImg;
  if (LIST.Count==0) return;
  LIST.Sort();
  for (int i=LIST.Count-1; i&gt;=0; i--) {
    HmsRegExMatch2(&apos;^(.*?)=(.*?)\\|&apos;        , LIST[i], sDate, sID);
    HmsRegExMatch3(&apos;\\|(.*?)\\|(.*?)\\|(.*)&apos;, LIST[i], sName, sTime, sDesc);
    sLink = gsUrlBase+&apos;/watch?v=&apos;+sID;
    THmsScriptMediaItem Item = HmsCreateMediaItem(sLink, PodcastItem.ItemID);
    Item[mpiTitle     ] = sName;
    Item[mpiComment   ] = sDesc;
    Item[mpiThumbnail ] = &apos;https://i.ytimg.com/vi/&apos;+sID+&apos;/mqdefault.jpg&apos;;
    Item[mpiTimeLength] = ConvertTime(sTime);
    Item[mpiCreateDate] = ConvertDate(sDate); gnTotalItems++;
  }
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка новых видео в подписках
void CreateMyNewVideos() {
  string sIDs, sDate, sID, sLink, sData, sPlaylistIDs, sName, sImg, sTime=&apos;10&apos;, sVal;
  int i, n, nCount, nMaxPages; TJsonObject JSON, VIDEO; TJsonArray VIDEOS; TStringList LIST;
  
  if (!Login()) return;
  
  sPlaylistIDs = CreateItems(&apos;getids&apos;, &apos;subscriptions&apos;, &apos;part=snippet&amp;mine=true&apos;);
  
  LIST = TStringList.Create();
  JSON = TJsonObject.Create();
  
  for (i=1; i&lt;=WordCount(sPlaylistIDs, &apos;,&apos;); i++) {
    sID   = ExtractWord(i, sPlaylistIDs, &apos;,&apos;); // ИД плейлиста, на который мы подписаны
    sData = APIRequest(&apos;search&apos;, &apos;part=snippet&amp;order=date&amp;channelId=&apos;+sID);
    JSON.LoadFromString(sData);
    VIDEOS = JSON["items"].AsArray;
    if (VIDEOS!=nil) {
      for (n=0; n &lt; VIDEOS.Length; n++) {
        VIDEO = VIDEOS[n];
        if (VIDEO.S[&apos;id\\kind&apos;]!=&apos;youtube#video&apos;) continue;
        sID   = VIDEO.S[&apos;id\\videoId&apos;];
        sDate = VIDEO.S[&apos;snippet\\publishedAt&apos;];
        sName = VIDEO.S[&apos;snippet\\title&apos;];
        LIST.Add(sDate+&apos;;&apos;+sID+&apos;;&apos;+HmsUtf8Decode(sName));
      }
    }
  }
  LIST.Sort();
  nMaxPages = 1;
  if (HmsRegExMatch(&apos;--pages=(\\d+)&apos;, mpPodcastParameters, sVal)) nMaxPages = StrToInt(sVal);
  i=LIST.Count-1;
  for (n=0; n&lt;nMaxPages; n++) {
    nCount=0; sIDs=&apos;&apos;;
    while (i&gt;=0) {
     
      HmsRegExMatch3(&apos;(.*?);(.*?);(.*)&apos;, LIST[i], sDate, sID, sName);
      if (sIDs!=&apos;&apos;) sIDs += &apos;,&apos;; sIDs += sID;
      i--; nCount++;
      if (nCount &gt;= 50) break;
    }
    CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
  }
  LIST.Free(); JSON.Free();
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного канала
void CreateChannelVideos(string sChannelID) {
  Login(true);
  SearchVideosByParam(&apos;&amp;q=&amp;part=snippet&amp;type=video&amp;order=date&amp;channelId=&apos;+sChannelID);
}

///////////////////////////////////////////////////////////////////////////////
// Поиск видео с выбранными параметрами
void SearchVideosByParam(string sParam) {
  string sIDs, sVal; 
  
  Login(true);
  
  sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, sParam);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео выбранного региона
void CreateVideosByRegion(string sRegion) {
  string sVal, sParam; 
  Login(true);
  
  gsRegion = sRegion;
  sParam = &apos;&amp;q=&amp;part=snippet&amp;type=video&amp;order=rating&amp;regionCode=&apos;+sRegion+&apos;&amp;relevanceLanguage=&apos;+sRegion;
  if (HmsRegExMatch(&apos;category=([\\w-_]+)&apos;, mpFilePath, sVal)) sParam += &apos;&amp;videoCategoryId=&apos; + sVal; 
  
  SearchVideosByParam(sParam);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка последних обновлений
void CreateMyActivities() {
  string sIDs, sVal;
  
  if (!Login()) return;
  
  sIDs = CreateItems(&apos;getids&apos;, &apos;activities&apos;, &apos;part=contentDetails&amp;mine=true&apos;);
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 
  
  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание списка видео конкретного канала (по его id)
void CreateChannelPlaylists(string sChannelID) {
  Login(true);
  CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;channelId=&apos;+sChannelID);
}

///////////////////////////////////////////////////////////////////////////////
// Создание секций конкретного канала ("Загруженные", "Плейлисты" и проч.)
void CreateChannelSections(string sChannelID, bool bForUser=false) {
  string sLink, sData, sIDs=&apos;&apos;, sType, sID, sName, sChannelData, sVal;
  int i, nCnt, nMax; TJsonObject JSON, ITEM; TJsonArray ITEMS; TStrings CHANNELS;
  Login(true);
  if (bForUser)  
    sChannelData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;forUsername=&apos;+sChannelID);
  else
    sChannelData = APIRequest(&apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sChannelID);
  HmsRegExMatch(&apos;"id":\\s*?"(.*?)"&apos;, sChannelData, sChannelID);
  if ((Pos(&apos;--translate&apos;, mpPodcastParameters)&gt;0) &amp;&amp; HmsRegExMatch(&apos;"country"\\s*:\\s*"(.*?)"&apos;, sChannelData, sVal) &amp;&amp; (sVal!=gsRegion)) {
    if ((Pos(&apos;--subtitles&apos;, mpPodcastParameters)&lt;1)) PodcastItem[mpiPodcastParameters] += &apos; --subtitles&apos;;
  }
  sData = APIRequest(&apos;channelSections&apos;, &apos;part=snippet,contentDetails&amp;fields=items(id,snippet/type,snippet/title,snippet/localized/title,contentDetails)&amp;channelId=&apos;+sChannelID);
  JSON = TJsonObject.Create();
  CHANNELS = TStringList.Create();
  try {
    JSON.LoadFromString(sData);
    ITEMS = JSON.A(&apos;items&apos;);
    if (ITEMS != nil) {
      for (i=0; i&lt;ITEMS.Length; i++) {
        ITEM = ITEMS[i];
        sType = ITEM.S(&apos;snippet\\type&apos;);  
        if (sType==&apos;singlePlaylist&apos;) {
          sID   = ITEM.S(&apos;contentDetails\\playlists[0]&apos;);
          CHANNELS.Values[sID] = &apos;1&apos;;

        } else if ((sType==&apos;multiplePlaylists&apos;)||(sType==&apos;multipleChannels&apos;)) {
          sID   = ITEM.S(&apos;id&apos;);
          sName = ITEM.S(&apos;snippet\\localized\\title&apos;);
          if (sName==&apos;&apos;) sName = ITEM.S(&apos;snippet\\title&apos;);
          sName = SafeName(HmsUtf8Decode(sName));
          CreateFolder(sName, &apos;-channelSection=&apos;+sID);

        } 
      }
    }

  } finally { JSON.Free(); }

  // Create single playlists
  nCnt = 0; nMax = 4; 
  for (i=0; i&lt;CHANNELS.Count; i++) {
    sID = CHANNELS.Names[i];
    nCnt++; if (nCnt&gt;=50) {
      CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
      nCnt = 1; sIDs = sID; nMax --; if (nMax&lt;=0) break;
    }
    if (sIDs!=&apos;&apos;) sIDs += &apos;,&apos;; sIDs += sID;
  }
  if (sIDs!=&apos;&apos;) CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);

  CHANNELS.Free();

  CreateFolder(&apos;Плейлисты&apos;, &apos;-channelPlaylists=&apos;+sChannelID);

  sLink = gsUrlBase+&apos;/playlist?list=&apos;;
  if (HmsRegExMatch(&apos;"uploads":\\s*?"(.*?)"&apos;  , sChannelData, sID)) CreateFolder(&apos;Загруженные видео&apos;, sLink+sID);
  if (HmsRegExMatch(&apos;"likes":\\s*?"(.*?)"&apos;    , sChannelData, sID)) CreateFolder(&apos;Понравившиеся&apos;    , sLink+sID);
  if (HmsRegExMatch(&apos;"favorites":\\s*?"(.*?)"&apos;, sChannelData, sID)) CreateFolder(&apos;Любимые&apos;          , sLink+sID);

}

///////////////////////////////////////////////////////////////////////////////
// Создание видео конкретной секции канала
void CreateByChannelSection(string sSectionID) {
  string sLink, sData, sIDs=&apos;&apos;, sType, sID, sName;
  int i; TJsonObject JSON, ITEM; TJsonArray ITEMS;
  Login(true);
  sData = APIRequest(&apos;channelSections&apos;, &apos;part=contentDetails&amp;id=&apos;+sSectionID);

  JSON = TJsonObject.Create();
  try {
    JSON.LoadFromString(sData);
    sLink = gsUrlBase+&apos;/channel/&apos;;
    sIDs  = JSON.S(&apos;items[0]\\contentDetails\\channels&apos;);
    HmsRegExMatch(&apos;\\[(.*?)\\]&apos;, sIDs, sIDs); 
    sIDs  = ReplaceStr(sIDs, &apos;"&apos;, &apos;&apos;);
    if (WordCount(sIDs, &apos;,&apos;)&gt;50) {
      sID = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
      CreateItems(sLink, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sID);
      sIDs = Copy(sIDs, WordPosition(51, sIDs, &apos;,&apos;), 99999);
      if (WordCount(sIDs, &apos;,&apos;)&gt;50) sIDs = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
    }     
    if (sIDs!=&apos;&apos;) CreateItems(sLink, &apos;channels&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);     

    sLink = gsUrlBase+&apos;/playlist?list=&apos;;
    sIDs  = JSON.S(&apos;items[0]\\contentDetails\\playlists&apos;);
    HmsRegExMatch(&apos;\\[(.*?)\\]&apos;, sIDs, sIDs); 
    sIDs  = ReplaceStr(sIDs, &apos;"&apos;, &apos;&apos;);
    if (WordCount(sIDs, &apos;,&apos;)&gt;50) {
      sID = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
      CreateItems(sLink, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sID);
      sIDs = Copy(sIDs, WordPosition(51, sIDs, &apos;,&apos;), 99999);
      if (WordCount(sIDs, &apos;,&apos;)&gt;50) sIDs = Copy(sIDs, 1, WordPosition(51, sIDs, &apos;,&apos;)-2);
    }     
    if (sIDs!=&apos;&apos;) CreateItems(sLink, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
    
  } finally { JSON.Free(); } 

}

///////////////////////////////////////////////////////////////////////////////
// Создание списка своих собственных плейлистов
void CreateMyPlaylists() {
  string sData, sLink, sID;
  
  if (!Login()) return;

  CreateItems(gsUrlBase+&apos;/playlist?list=&apos;, &apos;playlists&apos;, &apos;part=snippet,contentDetails&amp;mine=true&apos;);
}

///////////////////////////////////////////////////////////////////////////////
// Поиск видео по названию
void SearchVideos(string sName, bool bContinue=false) {
  string sVal=&apos;&apos;, sParam = &apos;&apos;, sIDs, sPodcastParams, sKey, sPossibleKeys; int i, nCnt;
  if (!bContinue) {
    sPodcastParams = mpFilePath + &apos; &apos; + PodcastItem.ItemParent[mpiFilePath] + &apos; &apos; + mpPodcastParameters;
    sPossibleKeys  = &apos;channelId,channelType,eventType,location,locationRadius,maxResults,order,publishedAfter,publishedBefore,regionCode,relevanceLanguage,safeSearch,topicId,type,videoCaption,videoCategoryId,videoDefinition,videoDimension,videoDuration,videoEmbeddable,videoLicense,videoSyndicated,videoType&apos;;
    for (i=1; i&lt;=WordCount(sPossibleKeys, &apos;,&apos;); i++) {
      sKey = ExtractWord(i, sPossibleKeys, &apos;,&apos;);
      if (HmsRegExMatch(&apos;-&apos;+sKey+&apos;=([\\w_\\-\\.,:]+)&apos;, sPodcastParams, sVal)) sParam += &apos;&amp;&apos;+sKey+&apos;=&apos; +sVal; 
    }
    if (PodcastItem.ItemParent[mpiComment]==&apos;+&apos;) sName = PodcastItem.ItemParent[mpiTitle] + &apos; &apos; + Trim(sName);
    if (ProgramVersion&gt;&apos;3.0&apos;) sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, &apos;part=snippet&amp;q=&apos;+HmsHttpEncode(sName)+sParam);
    else                      sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, &apos;part=snippet&amp;q=&apos;+HmsHttpEncode(HmsUtf8Encode(sName))+sParam); 

  } else {
    if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, sName, sVal)) sName = ReplaceStr(sName, sVal, &apos;&apos;); 
    sIDs = CreateItems(&apos;video&apos;, &apos;search&apos;, sName);
  
  }
  if (HmsRegExMatch(&apos;(.pageToken=[\\w-_]+)&apos;, mpFilePath, sVal)) mpFilePath = ReplaceStr(mpFilePath, sVal, &apos;&apos;); 

  CreateItems(&apos;video&apos;, &apos;videos&apos;, &apos;part=snippet,contentDetails&amp;id=&apos;+sIDs);
}

///////////////////////////////////////////////////////////////////////////////
// Создание структуры подкаста - папок меню
void CreateMyFolders() {
  THmsScriptMediaItem Item;
  CreateFolder(&apos;Новые видео в подписках&apos;, &apos;-newVideos&apos;);
  CreateFolder(&apos;Мои подписки&apos;     , gsUrlBase+&apos;/feed/subscriptions&apos;);
  CreateFolder(&apos;Плейлисты&apos;        , &apos;-playlists&apos;);
//CreateFolder(&apos;Посмотреть позже&apos; , gsUrlBase+&apos;/playlist?list=WL&apos;);
  CreateFolder(&apos;Загруженные видео&apos;, &apos;-uploads&apos;);
  CreateFolder(&apos;Понравившиеся&apos;    , &apos;-likes&apos;);
  CreateFolder(&apos;Просмотренные&apos;    , gsUrlBase+&apos;/feed/history&apos;);
//CreateFolder(&apos;Рекомендации&apos;     , &apos;-recommend&apos;);
//CreateFolder(&apos;Мои подписчики&apos;   , &apos;-mySubscribers&apos;);
}

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
// ----------------------------------------------------------------------------
{
  string s;
  HmsRegExMatch(&apos;--language=(\\w+)&apos;     , mpPodcastParameters, gsLanguage); 
  HmsRegExMatch(&apos;--regionCode=([\\w-]+)&apos;, mpPodcastParameters, gsRegion  ); 
  // Проверка на упоротых, которые пытаются запустить "Обновление подкастов" для всего подкаста разом
  if (InteractiveMode &amp;&amp; (HmsCurrentMediaTreeItem.ItemClassName==&apos;TVideoPodcastsFolderItem&apos;)) {
    HmsCurrentMediaTreeItem.DeleteChildItems(); // Дабы обновления всех подкастов не запустилось - удаляем их.
    ShowMessage(&apos;Обновление всех разделов разом запрещено!\nДля восстановления структуры\nзапустите "Создать ленты подкастов".&apos;);
    return;
  } 

  HmsRegExMatch(&apos;APIKey="(.*?)"&apos;      , mpPodcastParameters, gsAPIKey      );
  HmsRegExMatch(&apos;ClientId="(.*?)"&apos;    , mpPodcastParameters, gsClientId    );
  HmsRegExMatch(&apos;ClientSecret="(.*?)"&apos;, mpPodcastParameters, gsClientSecret);
  
  if (PodcastItem.IsFolder) { 
    PodcastItem.DeleteChildItems(); // Удаление существующих ссылок

    if ((gsAPIKey==&apos;&apos;) || (gsClientId==&apos;&apos;) || (gsClientSecret==&apos;&apos;)) {
      s = &apos;Не все параметры подкаста установлены (--APIKey, --ClientId или --ClientSecret)&apos;;
      HmsLogMessage(2, s);
      ErrorItem(s);
      return;
    }
    
    // Анализ текущей ссылки
         if (HmsRegExMatch(&apos;-newVideos&apos;         , mpFilePath, s)) CreateMyNewVideos(); //NewVideos();
    else if (HmsRegExMatch(&apos;/feed/subscriptions&apos;, mpFilePath, s)) CreateMySubscriptions();
    else if (HmsRegExMatch(&apos;-MyChannel&apos;         , mpFilePath, s)) CreateMyFolders();
    else if (HmsRegExMatch(&apos;-playlists&apos;         , mpFilePath, s)) CreateMyPlaylists();
    else if (HmsRegExMatch(&apos;/playlist\\?list=WL&apos;, mpFilePath, s)) CreateMyPlaylist(&apos;watchLater&apos;);
    else if (HmsRegExMatch(&apos;-likes&apos;             , mpFilePath, s)) CreateMyPlaylist(&apos;likes&apos;);
    else if (HmsRegExMatch(&apos;-favorites&apos;         , mpFilePath, s)) CreateMyPlaylist(&apos;favorites&apos;);
    else if (HmsRegExMatch(&apos;-uploads&apos;           , mpFilePath, s)) CreateMyPlaylist(&apos;uploads&apos;);
    else if (HmsRegExMatch(&apos;/feed/history&apos;      , mpFilePath, s)) CreateMyPlaylist(&apos;watchHistory&apos;);
    else if (HmsRegExMatch(&apos;/feed/music&apos;        , mpFilePath, s)) CreateMyPlaylist(&apos;music&apos;);
    else if (HmsRegExMatch(&apos;-mySubscribers&apos;     , mpFilePath, s)) CreateMySubscribers();
    else if (HmsRegExMatch(&apos;-recommend&apos;         , mpFilePath, s)) CreateMyRecommendations();
    else if (HmsRegExMatch(&apos;-activities&apos;        , mpFilePath, s)) CreateMyActivities();

    else if (HmsRegExMatch(&apos;-videosByRegion=([\\w-_]+)&apos;, mpFilePath, s)) CreateVideosByRegion(s);
    else if (HmsRegExMatch(&apos;youtube.com/channels$&apos; , mpFilePath, s)) CreateGuideCategories();
    else if (HmsRegExMatch(&apos;-videoCategories&apos;      , mpFilePath, s)) CreateVideoCategories();
    else if (HmsRegExMatch(&apos;-category=(.*)&apos;        , mpFilePath, s)) CreateVideosByCategory(s);
    else if (HmsRegExMatch(&apos;-channelSection=(.*)&apos;  , mpFilePath, s)) CreateByChannelSection(s);
    else if (HmsRegExMatch(&apos;-channelPlaylists=(.*)&apos;, mpFilePath, s)) CreateChannelPlaylists(s);
    else if (HmsRegExMatch(&apos;/channels/([\\w-_]+)&apos;  , mpFilePath, s)) CreateChannels(s);
    else if (HmsRegExMatch(&apos;/channel/([\\w-_]+)/videos&apos;, mpFilePath, s)) CreateChannelVideos(s);
    else if (HmsRegExMatch(&apos;/channel/([\\w-_]+)&apos;  , mpFilePath, s)) CreateChannelSections(s);
    else if (HmsRegExMatch(&apos;/user/([\\w-_]+)&apos;     , mpFilePath, s)) CreateChannelSections(s, true);
    else if (HmsRegExMatch(&apos;\\?list=([\\w-_]+)&apos;   , mpFilePath, s)) CreatePlaylistVideos(s);

    else if (HmsRegExMatch(&apos;-search="(.*?)"&apos;      , mpFilePath, s)) SearchVideos(s);       // Просто поиск значения
    else if (HmsRegExMatch(&apos;-search=(.*)&apos;         , mpFilePath, s)) SearchVideos(s, true); // Продожение поиска (техническая ссылка, создаётся сама)
    else if (!HmsRegExMatch(&apos;^http&apos;, mpFilePath, s)) SearchVideos(mpTitle);                    // Иначе, если в ссылке не http адрес - поиск названия
    else ErrorItem(&apos;Не умею обрабатывать ссылку :(&apos;);  

    HmsLogMessage(1, mpTitle+&apos;: Создано ссылок - &apos;+IntToStr(gnTotalItems));
  } else {
    // Устанавливаем значение goRoot как корневой элемент подкаста
    if (LeftCopy(mpFilePath, 4)==&apos;Info&apos;) { ShowInfo(); return; }

    if (HmsRegExMatch(&apos;youtu.?be&apos;, mpFilePath, s)) GetLink_Youtube33(mpFilePath); 
    else MediaResourceLink = mpFilePath; 
  }
}</Value>
    </Property>
    <Property>
      <ID>551</ID>
      <Value>C++Script</Value>
    </Property>
    <Property>
      <ID>571</ID>
      <Value>// 2020.01.27
///////////////////////  Создание структуры подкаста  /////////////////////////
#define mpiScriptAlt3 510 // Cкрипт по Alt+3

///////////////////////////////////////////////////////////////////////////////
//               Г Л О Б А Л Ь Н Ы Е   П Е Р Е М Е Н Н Ы Е                   //
THmsScriptMediaItem Podcast = GetRoot(); // Главная папка подкаста
int       gnTotalItems = 0; 
TDateTime gStart       = Now;

///////////////////////////////////////////////////////////////////////////////
//                             Ф У Н К Ц И И                                 //

///////////////////////////////////////////////////////////////////////////////
// Установка переменной Podcast: поиск родительской папки, содержащий скрипт
THmsScriptMediaItem GetRoot() {
  Podcast = FolderItem; // Начиная с текущего элемента, ищется создержащий срипт
  while ((Trim(Podcast[550])==&apos;&apos;) &amp;&amp; (Podcast[532]!=&apos;1&apos;) &amp;&amp; (Podcast.ItemParent!=nil)) 
    Podcast=Podcast.ItemParent;
  return Podcast;
}

///////////////////////////////////////////////////////////////////////////////
// Создание папки или подкаста
THmsScriptMediaItem CreateFolder(THmsScriptMediaItem Parent, string sName, string sLink, string sParams=&apos;&apos;, bool bForceFolder=false, bool bCreateStruct=false) {
  THmsScriptMediaItem Item = Parent.AddFolder(sLink, bForceFolder); // Создаём папку с указанной ссылкой
  Item[mpiTitle     ] = sName; // Присваиваем наименование
  Item[mpiCreateDate] = IncTime(gStart,0,-gnTotalItems,0,0); gnTotalItems++;
  Item[mpiPodcastParameters] = sParams;
  Item[mpiFolderSortOrder  ] = "-mpCreateDate";
  if (bCreateStruct) Item[570] = 2;
  return Item;
}

///////////////////////////////////////////////////////////////////////////////
// Функция создания динамической папки с указанным скриптом
THmsScriptMediaItem CreateDynamicItem(THmsScriptMediaItem prntItem, string sTitle, string sLink, string &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;JScript&apos;; // mpiDynamicSyntaxType
  Folder[mpiFolderSortOrder] = -mpiCreateDate;
  return Folder;
}

///////////////////////////////////////////////////////////////////////////////
// Замена в тексте загруженного скрипта значения текстовой переменной
void ReplaceVarValue(string &amp;sText, string sVarName, string sNewVal) {
  char sVal, sVal2;
  if (HmsRegExMatch2("("+sVarName+"\\s*?=.*?&apos;;)", sText, sVal, sVal2, PCRE_SINGLELINE)) {
    HmsRegExMatch(sVarName+"\\s*?=\\s*?&apos;(.*)&apos;", sVal, sVal2);
    sText = ReplaceStr(sText, sVal, ReplaceStr(sVal , sVal2, sNewVal));
  }
}

///////////////////////////////////////////////////////////////////////////////
// Создание папки ПОИСК (с загрузкой скрипта с форума homemediaserver.ru)
void CreateSearchFolder(THmsScriptMediaItem prntItem, string sTitle) {
  string sScript=&apos;&apos;, sHtml; THmsScriptMediaItem Folder;
  
  // Да да, загружаем скрипт с сайта форума HMS
  sHtml = HmsUtf8Decode(HmsDownloadURL(&apos;http://homemediaserver.ru/forum/viewtopic.php?f=15&amp;t=2793&amp;p=17395&apos;));
  HmsRegExMatch(&apos;BeginSearchJScript\\*/(.*?)/\\*EndSearchJScript&apos;, sHtml, sScript, 1, PCRE_SINGLELINE);
  sScript = HmsHtmlToText(sScript, 1251);
  sScript = ReplaceStr(sScript, #160, &apos; &apos;);
  
  // И меняем значения переменных на свои
  //ReplaceVarValue(sScript, &apos;gsSuggestUrl&apos;, gsAPIUrl+&apos;videos?q=&apos;);
  //ReplaceVarValue(sScript, &apos;gsSuggestResultCut&apos;, &apos;&apos;);
  //ReplaceVarValue(sScript, &apos;gsSuggestRegExp&apos;, &apos;"name":"(.*?)"&apos;);
  //ReplaceVarValue(sScript, &apos;gsSuggestMethod&apos; , &apos;POST&apos;);
  //sScript = ReplaceStr(sScript, &apos;gnSuggestNoUTFEnc  = 0&apos;, &apos;gnSuggestNoUTFEnc  = 1&apos;);
  
  Folder = prntItem.AddFolder(sTitle, true);
  Folder[mpiCreateDate     ] = VarToStr(IncTime(gStart,0,-gnTotalItems,0,0));
  Folder[mpiFolderSortOrder] = "-mpCreateDate";
  gnTotalItems++;
  
  CreateDynamicItem(Folder, &apos;"Набрать текст"&apos;, &apos;-SearchCommands&apos;, sScript);
  
  CreateFolder(Folder, &apos;Каналы&apos;     , &apos;-type=channel&apos; , &apos;&apos;, true);
  CreateFolder(Folder, &apos;Плейлисты&apos;  , &apos;-type=playlist&apos;, &apos;&apos;, true);
  CreateFolder(Folder, &apos;Видео&apos;      , &apos;-type=video&apos;   , &apos;&apos;, true);
  CreateFolder(Folder, &apos;Мультфильмы&apos;, &apos;-type=video&apos;   , &apos;&apos;, true);
  CreateFolder(Folder, &apos;Фильмы&apos;     , &apos;-type=video -videoType=movie&apos;  , &apos;&apos;, true);
  CreateFolder(Folder, &apos;Шоу&apos;        , &apos;-type=video -videoType=episode&apos;, &apos;&apos;, true);
}

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
// ----------------------------------------------------------------------------
{
  THmsScriptMediaItem Folder, Folder2, Item;
  
  FolderItem.DeleteChildItems(); // Удаление существующих ссылок
  
  CreateFolder(FolderItem, &apos;Новые видео в подписках&apos;, &apos;-newVideos&apos;, &apos;--subtitles --pages=4&apos;);
  
  CreateSearchFolder(FolderItem, &apos;Поиск&apos;);
  
  Folder = CreateFolder(FolderItem, &apos;Мой канал&apos;, &apos;-MyChannel&apos;, &apos;&apos;, true);
  
  CreateFolder(Folder, &apos;Новые видео в подписках&apos;, &apos;-newVideos&apos;);
  
  CreateFolder(Folder, &apos;Мои подписки&apos;, &apos;https://www.youtube.com/feed/subscriptions&apos;);
  //Folder2 = CreateFolder(Folder, &apos;Мои подписки&apos;, &apos;-MySubscriptions&apos;, &apos;&apos;, true);
  //CreateFolder(Folder2, &apos;Обновить список каналов&apos;, &apos;https://www.youtube.com/feed/subscriptions&apos;);
  
  //    Folder2 = CreateFolder(Folder, &apos;Плейлисты&apos;        , &apos;-playlists&apos;, &apos;&apos;, true);
  //    CreateFolder(Folder2, &apos;Обновить список плейлистов&apos;, &apos;-UpdateMyPlaylists&apos;);
  CreateFolder(Folder, &apos;Мои плейлисты&apos;, &apos;-playlists&apos;);

    //CreateFolder(Folder, &apos;Посмотреть позже&apos; , &apos;https://www.youtube.com/playlist?list=WL&apos;);
    CreateFolder(Folder, &apos;Загруженные видео&apos;, &apos;-uploads&apos;);
    CreateFolder(Folder, &apos;Понравившиеся&apos;    , &apos;-likes&apos;);
    //CreateFolder(Folder, &apos;Просмотренные&apos;    , &apos;http://www.youtube.com/feed/history&apos;);
    //CreateFolder(Folder, &apos;Рекомендации&apos;     , &apos;-recommend&apos;);

  //CreateFolder(FolderItem, &apos;Каталог каналов&apos;, &apos;http://www.youtube.com/channels&apos;);

  //CreateFolder(FolderItem, &apos;Категории&apos;, &apos;-videoCategories&apos;);

  //CreateFolder(FolderItem, &apos;Русский Youtube&apos;, &apos;-videosByRegion=ru&apos;, &apos;&apos;);
  //CreateFolder(FolderItem, &apos;Русская музыка Youtube&apos;, &apos;-videosByRegion=ru -category=10&apos;, &apos;&apos;);
   
  Folder = CreateFolder(FolderItem, &apos;Прямые трансляции&apos;, &apos;-search="" -type=video -eventType=live -order=viewCount -relevanceLanguage=ru&apos;); 
  Folder[mpiTranscodingProfile] = &apos;Фильмы (основной) - FFMPEG&apos;;
  
  Folder = CreateFolder(FolderItem, &apos;Музыка&apos;, &apos;Музыка&apos;, &apos;&apos;, true);
  CreateFolder(Folder, &apos;Музыка - лучшее в Аргентине&apos;, &apos;https://www.youtube.com/playlist?list=PLFgquLnL59amyKLSulnqkHkLf-GZRVmtN&apos;);
  CreateFolder(Folder, &apos;Популярная музыка - Россия&apos;, &apos;https://www.youtube.com/playlist?list=PLgMaGEI-ZiiaHUw-Swt12WOtknrO9UK8G&apos;);
  CreateFolder(Folder, &apos;Boiler Room&apos;, &apos;https://www.youtube.com/user/brtvofficial&apos;);
  CreateFolder(Folder, &apos;Новые Official Video&apos;, &apos;-search="Official Video" -type=video -videoCategoryId=10 -order=date&apos;);
  
  Folder = CreateDynamicItem(FolderItem, &apos;Настройки&apos;, &apos;-SettingsFolder&apos;, FolderItem[mpiScriptAlt3]);
  Folder[501] = &apos;C++Script&apos;; // mpiDynamicSyntaxType
}</Value>
    </Property>
    <Property>
      <ID>572</ID>
      <Value>C++Script</Value>
    </Property>
    <Property>
      <ID>215</ID>
      <Value>-mpCreateDate</Value>
    </Property>
    <Property>
      <ID>511</ID>
      <Value>Нет скрипта</Value>
    </Property>
    <Property>
      <ID>510</ID>
      <Value>///////////////////////////////////////////////////////////////////////////////
//               Г Л О Б А Л Ь Н Ы Е   П Е Р Е М Е Н Н Ы Е                   //
THmsScriptMediaItem Root = FolderItem;    // Корневая папка настроек
string    gsRootPath   = &apos;-SettingsFolder&apos;, // Значение поля путь (ссылка) корневой папки настроек
          gsKey        = &apos;&apos;,                // Ключ (определяется ниже)
          gsValue      = &apos;&apos;;                // Значение ключа (устанавливается ниже)
TDateTime gTimeStart   = Now;    // Время запуска скрипта
TStrings  SETTINGS;              // Объект TStrings для хранения описания настроек
int
  mpiAccessToken = 101315,       // Коды идентификаторов параметра медиа-ресурса
  mpiRefreshToken= 101316,       // для хранения токенов в корневой папке подкаста  
  gnTotalItems         = 0,      // Глобальный счетчик
  // Константы параметров этой динамической папки
  mpiFolderType        = 200,    // Тип папки     
  mpiDynamicScript     = 500,    // Скрипт
  mpiDynamicSyntaxType = 501,    // Язык скрипта
  mpiPreviousItemID    = 200104, // Предыдущий ItemID
  ;
///////////////////////////////////////////////////////////////////////////////
//                             Ф У Н К Ц И И                                 //

// ----------------------------------------------------- Структура настроек ---
void SettingsStructure() {
  // Указываем ключи и их наименования
  // Также указываем ключ и далее знак &apos;:&apos; и после - значение ключа (или &apos;+&apos; - добавить, &apos;-&apos; - удалить ключ)
 
  //SETTINGS.Values["--chkupdates"] = "Проверять обновления подкаста";
  //SETTINGS.Values["--chkupdates:-"] = &apos;Не проверять&apos;;   
  //SETTINGS.Values["--chkupdates:+"] = &apos;Проверять&apos;;   

  SETTINGS.Values["--maxheight"] = "Максимальная высота кадра (качество)";
  SETTINGS.Values["--maxheight:240" ] = &apos;240&apos;;   
  SETTINGS.Values["--maxheight:320" ] = &apos;320&apos;;   
  SETTINGS.Values["--maxheight:480" ] = &apos;480&apos;;   
  SETTINGS.Values["--maxheight:640" ] = &apos;640&apos;;   
  SETTINGS.Values["--maxheight:1080"] = &apos;1080&apos;;   
  SETTINGS.Values["--maxheight:2048"] = &apos;2048&apos;;   
  SETTINGS.Values["--maxheight:4096"] = &apos;4096&apos;;   
  SETTINGS.Values["--maxheight:4320"] = &apos;4320&apos;;   
     
  SETTINGS.Values["--subtitles"] = "Показ субтитров";
  SETTINGS.Values["--subtitles:-"] = &apos;Не показывать субтитры&apos;;   
  SETTINGS.Values["--subtitles:+"] = &apos;Показывать субтитры&apos;;   

  SETTINGS.Values["--adaptive"] = "Адаптивные медиа-потоки (adaptive)";
  SETTINGS.Values["--adaptive:-"] = &apos;Выключить adaptive&apos;;   
  SETTINGS.Values["--adaptive:+"] = &apos;Включить adaptive&apos;;   

  SETTINGS.Values["--sublanguage"] = "Язык субтитров";
  SETTINGS.Values["--sublanguage:ru"] = &apos;Русский&apos;;   
  SETTINGS.Values["--sublanguage:en"] = &apos;Английский&apos;;   
  SETTINGS.Values["--sublanguage:hy"] = &apos;Армянский&apos;;   
  SETTINGS.Values["--sublanguage:be"] = &apos;Белорусский&apos;;   
  SETTINGS.Values["--sublanguage:kk"] = &apos;Казахский&apos;;   
  SETTINGS.Values["--sublanguage:lv"] = &apos;Латышский&apos;;   
  SETTINGS.Values["--sublanguage:pl"] = &apos;Польский&apos;;   
  SETTINGS.Values["--sublanguage:uk"] = &apos;Украинский&apos;;   
  SETTINGS.Values["--sublanguage:fr"] = &apos;Французский&apos;;   
  SETTINGS.Values["--sublanguage:cs"] = &apos;Чешский&apos;;   
  SETTINGS.Values["--sublanguage:et"] = &apos;Эстонский&apos;;

//SETTINGS.Values["--alert"] = "Вывод окна сообщения авторизации";
//SETTINGS.Values["--alert:-"] = &apos;Не выводить окно&apos;;
//SETTINGS.Values["--alert:+"] = &apos;Выводить окно&apos;;
}

// ----------------------------------------------------------------------------
void ClearAllTokens(THmsScriptMediaItem Folder) {
  THmsScriptMediaItem Item;
  Folder[mpiAccessToken ] = &apos;&apos;;
  Folder[mpiRefreshToken] = &apos;&apos;;
  for (int i=0; i&lt;Folder.ChildCount; i++) {
    Item = Folder.ChildItems[i]; if (!Item.IsFolder) continue;
    Item[mpiAccessToken ] = &apos;&apos;;
    Item[mpiRefreshToken] = &apos;&apos;;
    if (Item.HasChildItems) ClearAllTokens(Item);
  } 
}

// ---------------------------------- Проверка текущего состояния настройки ---
bool CheckKeyState(string sVal) {
  string PARAMS = Root.ItemParent[mpiPodcastParameters]+&apos; &apos;; // Строка установленных параметров подкаста
  bool   bExist = (Pos(gsKey+&apos; &apos;, PARAMS)&gt;0);      // Проверяем, указан ли уже ключ
  if      (sVal==&apos;-&apos;) return !bExist;              // Если проверяемое значение &apos;-&apos; - ключ не должен быть указан       
  else if (sVal==&apos;+&apos;) return bExist;               // Если проверяемое значение &apos;+&apos; - указан ли ключ
  else return (Pos(gsKey+&apos;=&apos;+sVal+&apos; &apos;, PARAMS)&gt;0); // Иначе проверяем, установлено ли в параметрах заданное значение
}
 
// ---------------------------------------------- Создание ссылки-сообщения ---
void ShowMessageLink(string sMsg) {
  THmsScriptMediaItem Item = HmsCreateMediaItem(sMsg, FolderItem.ItemID);
  Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/ok.png&apos;;
}

// ---------- Функция создания динамической папки с унаследованным скриптом ---
void CreateItem(string sTitle, string sLink) {
  THmsScriptMediaItem Folder = FolderItem.AddFolder(sLink, true);
  Folder[mpiTitle     ] = sTitle;
  Folder[mpiCreateDate] = VarToStr(IncTime(gTimeStart,0,-gnTotalItems,0,0)); gnTotalItems++;
  Folder.CopyProperties(FolderItem, [mpiFolderType, mpiDynamicScript, mpiDynamicSyntaxType, mpiFolderSortOrder]);
}

///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------- Создание списка настроек ---
void CreateMainMenu() {
  for (int i=0; i&lt;SETTINGS.Count; i++) { // Обходим в цикле все настройки
    string sKey = SETTINGS.Names[i];     // Получаем ключ настроек
    if (Pos(&apos;:&apos;, sKey)&gt;0) continue;      // Если там есть &apos;:&apos;, то это вариант настройки - пропускаем
    CreateItem(SETTINGS.Values[sKey], &apos;-showValues=&apos;+sKey); // Создаём пункт настройки
  }
//CreateItem(&apos;Удалить токены для смены кода доступа&apos;, &apos;-clearTokens&apos;);
}
// ------------------------------------ Создание списка вариантов настройки ---
void CreateValuesList() {
  int i; string sVal, sKey, sName, sState; // При входе в процедуру в gsKey уже сам ключ
  for (i=0; i&lt;SETTINGS.Count; i++) { // Обходим в цикле все настройки
    sKey   = SETTINGS.Names[i];      // Получаем ключ настроек
    sName  = SETTINGS.Values[sKey];  // Наименование варианта настройки
    if (!HmsRegExMatch(&apos;^&apos;+gsKey+&apos;:(.*)&apos;, sKey, sVal)) continue; // Если не получили значение нашего ключа, пропускаем
    if (CheckKeyState(sVal)) sState=&apos;[v]&apos;; else sState=&apos;[ ]&apos;;    // Проверка: установлено ли данное значение, ставим пометку
    CreateItem(sState+&apos; &apos;+sName, &apos;-key=&apos;+gsKey+&apos; -value=&apos;+sVal); // Создаём пункт варианта настройки   
  }
}
// ------------------------ Включение/Отключение настройки или его значения ---
void ApplyKeyValue() {
  // При входе в процедуру уже в gsKey - ключ, в gsValue - его значение (может быть &apos;+&apos; или &apos;-&apos;, это установить или удалить параметр)
  bool bExist; string sOldVal=gsKey, sNewVal, PARAMS;        // По-умолчанию в sOldVal сам ключ
 
  PARAMS = Root.ItemParent[mpiPodcastParameters]+&apos; &apos;;      // Строка установленных параметров подкаста
  HmsRegExMatch(&apos;(&apos;+gsKey+&apos;=.*?)\\s&apos;, PARAMS, sOldVal);      // Вылавливаем в sOldVal установленное значение
  bExist = (Pos(sOldVal, PARAMS)&gt;0);                         // Устанавливаем флаг присутсвия ключа в параметрах
 
  if      (gsValue==&apos;-&apos;) sNewVal = &apos;&apos;;                       // Замена на пустое значение = удалению
  else if (gsValue==&apos;+&apos;) sNewVal = gsKey;                    // Просто устанавливаем ключ
  else                   sNewVal = gsKey+&apos;=&apos;+gsValue;        // Устанавливаем ключ с новым значением
  if (bExist) PARAMS = ReplaceStr(PARAMS, sOldVal, sNewVal); // Если ключ уже присутствует - заменяем
  else        PARAMS += sNewVal;                             // Иначе просто добавляем
  ShowMessageLink(&apos;ВЫБРАНО: &apos;+Copy(mpTitle, 5, 99));         // Пропускаем &apos;[ ] &apos; в mpTitle и сообщаем о выбранном варианте
  Root.ItemParent[mpiPodcastParameters] = Trim(ReplaceStr(PARAMS, &apos;  &apos;, &apos; &apos;)); // Сохраняем параметры подкаста
}

// ----------------------------------------------------------------------------
// Проверка значения ссылки текущей папки и извлечение группировок регулярного выражения в gsKey и gsValue
bool CheckPath(string sPattern) { return HmsRegExMatch2(sPattern, mpFilePath, gsKey, gsValue); }

///////////////////////////////////////////////////////////////////////////////
//                    Г Л А В Н А Я   П Р О Ц Е Д У Р А                      //
// ----------------------------------------------------------------------------
{
  // Поиск корневой динамической папки (ибо этот скрипт может выполнятся и в подпапках)
  while ((Root[mpiFilePath]!=gsRootPath) &amp;&amp; (Root.ItemParent!=nil)) Root = Root.ItemParent;
  if (Root[mpiFilePath]!=gsRootPath) { ShowMessageLink(&apos;Не найдена папка настроек с путём &apos;+gsRootPath); return; }
 
  // Если это повторный вызов, смены папки не произошло - ничего не делаем
  if ((FolderItem.ItemID==Root[mpiPreviousItemID]) &amp;&amp; !DebugMode &amp;&amp; (FolderItem[mpiFilePath]!=gsRootPath)) return;
  FolderItem.DeleteChildItems(); Root[mpiPreviousItemID] = FolderItem.ItemID;
  SETTINGS = TStringList.Create();
  try {
    SettingsStructure();
    if      (CheckPath(gsRootPath))               CreateMainMenu();   // Если это корень - создаём список настроек
    else if (CheckPath(&apos;-showValues=(.*)&apos;))       CreateValuesList(); // Зашли в настройку - показываем список вариантов значений
    else if (CheckPath(&apos;-key=(.*?) -value=(.*)&apos;)) ApplyKeyValue();    // Зашли в вариант значения настройки - применяем этот вариант
    else if (CheckPath(&apos;-clearTokens&apos;)) {
      ClearAllTokens(Root.ItemParent); // &apos;Удалить токены для смены кода доступа&apos;
      ShowMessageLink(&apos;Токены очищены!&apos;);
    }  

  } finally { SETTINGS.Free(); }
  HmsIncSystemUpdateID(); // Говорим устройству об обновлении содержания
}</Value>
    </Property>
  </Properties>
  <ChildItems>
    <Item>
      <ClassID>53</ClassID>
      <ItemID>c3f67e87aa15ae314e32cb7beac8cb09</ItemID>
      <ItemPath>-newVideos</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9389074884</Value>
        </Property>
        <Property>
          <ID>527</ID>
          <Value>--subtitles --pages=4</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201157</Value>
        </Property>
        <Property>
          <ID>525</ID>
          <Value>44131,9830998032</Value>
        </Property>
      </Properties>
    </Item>
    <Item>
      <ClassID>51</ClassID>
      <ItemID>01fa37188cfa11d74fb14e3d31d87c11</ItemID>
      <ItemPath>Поиск</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9382175926</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201157</Value>
        </Property>
      </Properties>
      <ChildItems>
        <Item>
          <ClassID>32</ClassID>
          <ItemID>3add83ac4a34b90ab195fa4950187302</ItemID>
          <ItemPath>-SearchCommands</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9382175926</Value>
            </Property>
            <Property>
              <ID>200</ID>
              <Value>5</Value>
            </Property>
            <Property>
              <ID>500</ID>
              <Value>mpiPreviousItemID  = 200104;
mpiDoNothing       = 201100;
Root               = GetRoot();
gsTextSearch       = HmsGetUserSearchText();
gsMsg              = &apos;&apos;;
gsSuggestUrl       = &apos;http://www.google.ru/complete/search?sclient=psy-ab&amp;q=&apos;;
gsSuggestResultCut = &apos;&apos;;
gsSuggestRegExp    = &apos;\\["(.*?)",&apos;;
gsSuggestMethod    = &apos;GET&apos;;
gnSuggestNoUTFEnc  = 0;
///////////////////////////////////////////////////////////////////////////////
function GetRoot() {
  folder = FolderItem;
  while ((folder[mpiFilePath]!=&apos;-SearchCommands&apos;) &amp;&amp; (folder.ItemParent!=nil)) folder=folder.ItemParent;
  return folder;
}
///////////////////////////////////////////////////////////////////////////////
function CreateLink(folder, title, img) {
  Item = HmsCreateMediaItem(folder.ItemID, folder.ItemID);
  Item[mpiTitle    ] = title;
  Item[mpiThumbnail] = img;
}
///////////////////////////////////////////////////////////////////////////////
function CreateFolder(title, link) {
  if (Trim(title)==&apos;&apos;) title = &apos;Пробел&apos;;
  folder = FolderItem.AddFolder(link, true);
  folder[mpiTitle] = title;
  folder.CopyProperties(FolderItem, [200, 500, 501]); // Копируем тип папки, скрипт, язык срипта
}
///////////////////////////////////////////////////////////////////////////////
function AddPodcastSearch(itemID) {
  if (Trim(gsTextSearch)==&apos;&apos;) { gsMsg = &apos;Текст поиска не набран! Добавлять нечего.&apos;; return; }
  if (LowerCase(gsTextSearch)==gsTextSearch) gsTextSearch = NameCase(gsTextSearch);
  if (Trim(itemID)!=&apos;&apos;) Folder = Root.ItemParent.FindItemByProperty(mpiItemID, itemID);
  else                  Folder = Root.ItemParent;
  Item = Folder.AddFolder(gsTextSearch, false);
  Item[mpiFilePath ] = Format(&apos;search="%s"&apos;, [gsTextSearch]);
  Item[mpiTitle    ] = gsTextSearch;
  HmsDatabaseAutoSave(false);
  gsMsg = Format(&apos;Подкаст "%s" добавлен в "%s"&apos;, [gsTextSearch, Folder[mpiTitle]]);
}
///////////////////////////////////////////////////////////////////////////////
function CreateSearchCommands() {
  CreateFolder(&apos;#&apos;,   &apos;-Chars=!:&apos;);
  CreateFolder(&apos;A-Z&apos;, &apos;-Chars=AZ&apos;);
  CreateFolder(&apos;А-Я&apos;, &apos;-Chars=АЯ&apos;);
  CreateFolder(&apos;Очистить текст поиска&apos;, &apos;-Clear&apos;);
  CreateFolder(&apos;Добавить в папку &apos;+Root.ItemParent[mpiTitle], &apos;-Save=&apos;);
  for(i=0; i&lt;Root.ItemParent.ChildCount; i++) {
    Item = Root.ItemParent.ChildItems[i];
    if (Item.ItemClassID==51) CreateFolder(&apos;Добавить в папку &apos;+Item[mpiTitle], &apos;-Save=&apos;+Item[mpiItemID]);
  }
  CreateFolder(&apos;Очистить историю поиска в папке &apos;+Root.ItemParent[mpiTitle], &apos;-ClearHistory=&apos;);
  for(i=0; i&lt;Root.ItemParent.ChildCount; i++) {
    Item = Root.ItemParent.ChildItems[i];
    if (Item.ItemClassID==51) CreateFolder(&apos;Очистить историю поиска в папке &apos;+Item[mpiTitle], &apos;-ClearHistory=&apos;+Item[mpiItemID]);
  }
}
///////////////////////////////////////////////////////////////////////////////
function ClearSearchHistory(itemID) {
  if (Trim(itemID)!=&apos;&apos;) Folder = Root.ItemParent.FindItemByProperty(mpiItemID, itemID);
  else                  Folder = Root.ItemParent;
  for(i=0; i&lt;Folder.ChildCount; i++) {
    Item = Folder.ChildItems[i]; // Ищем все элементы, у которых значение mpiFilePath начиначется с &apos;search&apos;
    if (LeftCopy(Item[mpiFilePath], 6)==&apos;search&apos;) { Item.Delete(); i--; }
  }
  gsMsg = &apos;История поиска в папке "&apos;+Folder[mpiTitle]+&apos;" очищена&apos;;
}
///////////////////////////////////////////////////////////////////////////////
function CreateCharFolders(chars) {
  ch1 = Copy(chars, 1, 1); ch2 = Copy(chars, 2, 1);
  CreateFolder(&apos;Удалить последний символ&apos;, &apos;-Char=Delete&apos;); // Вначале - команда удаления символа
  CreateFolder(&apos; &apos;, &apos;-Char= &apos;); // Пробел
  for (i=ord(ch1); i&lt;=ord(ch2); i++) CreateFolder(Chr(i), &apos;-Char=&apos;+Chr(i));
}
///////////////////////////////////////////////////////////////////////////////
function LoadSuggestions() {
  // Suggestions ------ Блок работы с подсказками -------
  if ((LeftCopy(gsTextSearch, 1)==&apos; &apos;) || (Trim(gsSuggestUrl)==&apos;&apos;) || (Length(gsTextSearch)&lt;2)) return;
 
  nPort = 80; if (LeftCopy(gsSuggestUrl, 5)=="https") nPort = 443;
  text  = gsTextSearch; if (gnSuggestNoUTFEnc==0) text = HmsUtf8Encode(text); // Если не указано не кодировать в UTF - кодируем
  text  = HmsPercentEncode(text);
  // Если есть ключ &lt;TEXT&gt; в запросе - заменяем его на значение набранного текста, иначе просто добавляем в конец
  if (Pos(&apos;&lt;TEXT&gt;&apos;, gsSuggestUrl)&gt;0) gsSuggestUrl = ReplaceStr(gsSuggestUrl, &apos;&lt;TEXT&gt;&apos;, text);
  else                               gsSuggestUrl += text;
  HmsRegExMatch3(&apos;(https?://(.*?))(/.*)&apos;, gsSuggestUrl, urlBase, server, page);
  if (gsSuggestMethod==&apos;POST&apos;) HmsRegExMatch2(&apos;^(.*?)\\?(.*)&apos;, page, page, post);
  headers = urlBase+&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;+urlBase+&apos;/\r\n&apos;+
            &apos;Accept: application/json, text/javascript, */*; q=0.01\r\n&apos;;    // Для включения возможности gzip в запросах
  text = HmsSendRequestEx(server, page, gsSuggestMethod, &apos;application/x-www-form-urlencoded; Charset=UTF-8&apos;, headers, post, nPort, 0x10, retHeaders, true);
  text = HmsUtf8Decode(text);
  if (gsSuggestResultCut!=&apos;&apos;) HmsRegExMatch(gsSuggestResultCut, text, text);// Если есть выражение обрезки - обрезаем
  text              = HmsJsonDecode(text); TRegExpr t = TRegExpr.Create(&apos;(&lt;[^&gt;]+&gt;)&apos;);    // Избавляемся от тегов в середине слов подсказки
  TRegExpr reSearch = TRegExpr.Create(gsSuggestRegExp, PCRE_SINGLELINE);
  if (reSearch.Search(text)) do {
    s = reSearch.Match; gnTotalItems=0;
    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 (Pos(LowerCase(gsTextSearch), LowerCase(s))&lt;1) continue;
    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(ReplaceStr(ReplaceStr(sCh, &apos;\\&apos;, &apos;&apos;), &apos;,&apos;, &apos;&apos;), &apos;:&apos;, &apos;&apos;);
        CreateFolder(&apos;Вариант: &apos;+sCh, &apos;-SetText=&apos;+sCh);
      }
    } gnTotalItems++;
    // Создаём папку с предложением варианта (подсказку)
    if (LowerCase(s)!=LowerCase(gsTextSearch)) CreateFolder(&apos;Вариант: &apos;+s, &apos;-SetText=&apos;+s);
    if (gnTotalItems&gt;100) break; // Ограничиваем количество создаваемых элементов = 100
  } while (reSearch.SearchAgain());
}
///////////////////////////////////////////////////////////////////////////////
{
  // Если это повторный вызов, смены папки не произошло - ничего не делаем
  if ((FolderItem.ItemID==Root[mpiPreviousItemID]) &amp;&amp; !DebugMode &amp;&amp; (FolderItem[mpiFilePath]!=&apos;-SearchCommands&apos;)) return;
  FolderItem.DeleteChildItems(); Root[mpiPreviousItemID] = FolderItem.ItemID;
  if (Root[mpiDoNothing]==&apos;1&apos;) Root[mpiDoNothing] = &apos;&apos;;  // Флаг "Ничего не делать" - например, при возврате в команду набирания буквы из подпапки варианта
  else if (mpFilePath==&apos;-SearchCommands&apos;) CreateSearchCommands();
  else if (mpFilePath==&apos;-Clear&apos;         ) gsTextSearch = &apos;&apos;;
  else if (mpFilePath==&apos;-Char=Delete&apos;   ) gsTextSearch = LeftCopy(gsTextSearch, Length(gsTextSearch)-1); // Удаление последнего символа
  else if (HmsRegExMatch(&apos;-Save=(.*)&apos;        , mpFilePath, chars)) AddPodcastSearch  (chars);
  else if (HmsRegExMatch(&apos;-ClearHistory=(.*)&apos;, mpFilePath, chars)) ClearSearchHistory(chars);
  else if (HmsRegExMatch(&apos;-Char=(.+)&apos;   , mpFilePath, chars)) { if (chars==&apos;&apos;) chars =&apos; &apos;; gsTextSearch += chars; }
  else if (HmsRegExMatch(&apos;-Chars=(.+)&apos;  , mpFilePath, chars)) CreateCharFolders(chars);
  else if (HmsRegExMatch(&apos;-SetText=(.+)&apos;, mpFilePath, chars)) { gsTextSearch = chars; Root[mpiDoNothing]=&apos;1&apos;; } // включаем флаг не выполнять команду при возврате из этой папки
 
  HmsSetUserSearchText(gsTextSearch);
  if (gsMsg!=&apos;&apos;) CreateLink(FolderItem, gsMsg, &apos;http://wonky.lostcut.net/icons/ok.png&apos;);
  else           CreateLink(FolderItem, &apos;Текст поиска: &apos;+gsTextSearch, &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;);
  CreateLink(Root, &apos;Текст поиска: &apos;+gsTextSearch, &apos;http://wonky.lostcut.net/icons/search-icon1.jpg&apos;);
 
  if (HmsRegExMatch(&apos;-(Char)=&apos;, mpFilePath, &apos;&apos;)) LoadSuggestions();
 
  HmsIncSystemUpdateID(); // Говорим устройству об обновлении содержания
}</Value>
            </Property>
            <Property>
              <ID>501</ID>
              <Value>JScript</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-35</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
            <Property>
              <ID>200104</ID>
              <Value>fbcf01f870e0415c04d349145b5f5828</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>51</ClassID>
          <ItemID>fba19edb9bcb3950235ddab9934e57e0</ItemID>
          <ItemPath>-type=channel</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9375185995</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
          </Properties>
          <ChildItems></ChildItems>
        </Item>
        <Item>
          <ClassID>51</ClassID>
          <ItemID>6932f1eeab1d1c0774d989db66644070</ItemID>
          <ItemPath>-type=playlist</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9368241551</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
          </Properties>
          <ChildItems></ChildItems>
        </Item>
        <Item>
          <ClassID>51</ClassID>
          <ItemID>3712a0d2ffe2b1408d8931c7a01aea35</ItemID>
          <ItemPath>-type=video</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9354352662</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
          </Properties>
          <ChildItems></ChildItems>
        </Item>
        <Item>
          <ClassID>51</ClassID>
          <ItemID>6f66bade291e964d76288d8bbc4c9576</ItemID>
          <ItemPath>-type=video -videoType=movie</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9347408218</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
          </Properties>
          <ChildItems></ChildItems>
        </Item>
        <Item>
          <ClassID>51</ClassID>
          <ItemID>9c98763f2815c6b7741901f9d8902417</ItemID>
          <ItemPath>-type=video -videoType=episode</ItemPath>
          <ParentID>01fa37188cfa11d74fb14e3d31d87c11</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>43862,9340463773</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201157</Value>
            </Property>
          </Properties>
          <ChildItems></ChildItems>
        </Item>
      </ChildItems>
    </Item>
    <Item>
      <ClassID>51</ClassID>
      <ItemID>2cf7fb56b648f64b80acf8e87eb77744</ItemID>
      <ItemPath>-MyChannel</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9333519329</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201273</Value>
        </Property>
      </Properties>
      <ChildItems>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>a9a2f5b4c7fd7753d712e04deba25a5e</ItemID>
          <ItemPath>-newVideos</ItemPath>
          <ParentID>2cf7fb56b648f64b80acf8e87eb77744</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>43862,9326574884</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
            <Property>
              <ID>525</ID>
              <Value>44009,7970188426</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>a10ef0cbb9ef5923c7367f98cab2620c</ItemID>
          <ItemPath>https://www.youtube.com/feed/subscriptions</ItemPath>
          <ParentID>2cf7fb56b648f64b80acf8e87eb77744</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>43862,931963044</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
            <Property>
              <ID>525</ID>
              <Value>44131,9833118981</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>1627de2c22f8b74a59d214c03764da66</ItemID>
          <ItemPath>-playlists</ItemPath>
          <ParentID>2cf7fb56b648f64b80acf8e87eb77744</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>43862,9312685995</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>d0a13ca18b252520c63a280648f3c7df</ItemID>
          <ItemPath>-uploads</ItemPath>
          <ParentID>2cf7fb56b648f64b80acf8e87eb77744</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>43862,9305741551</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>c5ba2c6b1675584fa744199f1a98605c</ItemID>
          <ItemPath>-likes</ItemPath>
          <ParentID>2cf7fb56b648f64b80acf8e87eb77744</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>43862,9298797106</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
            <Property>
              <ID>525</ID>
              <Value>43906,926959456</Value>
            </Property>
          </Properties>
        </Item>
      </ChildItems>
    </Item>
    <Item>
      <ClassID>53</ClassID>
      <ItemID>2ceff415550aa0cd3747752595a5fa8a</ItemID>
      <ItemPath>-search="" -type=video -eventType=live -order=viewCount -relevanceLanguage=ru</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9291852662</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>50</ID>
          <Value>Фильмы (основной) - FFMPEG</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201273</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>522</ID>
          <Value>0</Value>
        </Property>
        <Property>
          <ID>245</ID>
          <Value>2ceff415550aa0cd3747752595a5fa8a</Value>
        </Property>
        <Property>
          <ID>525</ID>
          <Value>44131,9597086921</Value>
        </Property>
      </Properties>
      <HmsTranscodingConfig>
        <TranscoderList>
          <Transcoder>
            <Name>FFMPEG</Name>
            <Path>Transcoders\ffmpeg.exe</Path>
            <HomePage>http://www.ffmpeg.org/</HomePage>
            <Download>http://www.ffmpeg.org/</Download>
          </Transcoder>
        </TranscoderList>
        <TranscodingProfileList>
          <Profile>
            <TranscoderName>FFMPEG</TranscoderName>
            <TranscodingCondition></TranscodingCondition>
            <TranscodingConditionSyntaxType>Нет скрипта</TranscodingConditionSyntaxType>
            <TranscodingComment></TranscodingComment>
            <TranscodingFolder>Транскодирование с перекодированием видео</TranscodingFolder>
            <TranscodingMediaType>3</TranscodingMediaType>
            <TranscodingMimeType></TranscodingMimeType>
            <TranscodingMimeTypeScript>begin
  if SameText(cfgTranscodingFileFormat, &apos;MPEG (DVD)&apos;) then
    FileExt := &apos;mpg&apos;
  else if Pos(&apos;MPEGTS&apos;, cfgTranscodingFileFormat) &gt; 0 then
    FileExt := &apos;ts&apos;
  else if Pos(&apos;ASF&apos;, cfgTranscodingFileFormat) &gt; 0 then
    FileExt := &apos;wmv&apos;
  else if SameText(cfgTranscodingFileFormat, &apos;MP4&apos;) then
    FileExt := &apos;mp4&apos;
  else if SameText(cfgTranscodingFileFormat, &apos;MPEG1&apos;) then
    FileExt := &apos;mpeg&apos;
  else
    FileExt := &apos;&apos;;    
  if FileExt &lt;&gt; &apos;&apos; then
    MimeType := HmsGetMimeType(FileExt)    
  else            
    MimeType := &apos;&apos;
end.</TranscodingMimeTypeScript>
            <TranscodingMimeTypeSyntaxType>PascalScript</TranscodingMimeTypeSyntaxType>
            <TranscodingMode>0</TranscodingMode>
            <TranscodingParams>{
  TranscodingParams = HmsTranscodingInputParams + HmsTranscodingVideoParams + HmsTranscodingMapParams(mpAudioStreamNo);
  
  If (HmsRegExMatch(&apos;(-vstreamid \\d+)&apos;, TranscodingParams, gsUserVariable1))
  TranscodingParams = ReplaceStr(TranscodingParams, gsUserVariable1, &apos;&apos;);
  
  If (HmsRegExMatch(&apos;(-astreamid \\d+)&apos;, TranscodingParams, gsUserVariable1))
  TranscodingParams = ReplaceStr(TranscodingParams, gsUserVariable1, &apos;&apos;);
}
</TranscodingParams>
            <TranscodingParamsSyntaxType>JScript</TranscodingParamsSyntaxType>
            <TranscodingProfile>Фильмы (основной) - FFMPEG</TranscodingProfile>
            <TranscodingProfileActive>-1</TranscodingProfileActive>
            <TranscodingProfilePriority>4</TranscodingProfilePriority>
            <TranscodingProfileUuid>b15ffc44-3c1b-4f7d-9c19-fbd75ab5b042</TranscodingProfileUuid>
          </Profile>
        </TranscodingProfileList>
      </HmsTranscodingConfig>
    </Item>
    <Item>
      <ClassID>51</ClassID>
      <ItemID>38ab1d14429c099ff644c137f59287fd</ItemID>
      <ItemPath>Музыка</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9284908218</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201273</Value>
        </Property>
      </Properties>
      <ChildItems>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>5709a22b5fa60fddc609feada7390802</ItemID>
          <ItemPath>https://www.youtube.com/playlist?list=PLFgquLnL59amyKLSulnqkHkLf-GZRVmtN</ItemPath>
          <ParentID>38ab1d14429c099ff644c137f59287fd</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>43862,9277963773</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>f04522c18ce5a18c313858426dd065da</ItemID>
          <ItemPath>https://www.youtube.com/playlist?list=PLgMaGEI-ZiiaHUw-Swt12WOtknrO9UK8G</ItemPath>
          <ParentID>38ab1d14429c099ff644c137f59287fd</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>43862,9271019329</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>22ced0d85c4a266423969b5450067135</ItemID>
          <ItemPath>https://www.youtube.com/user/brtvofficial</ItemPath>
          <ParentID>38ab1d14429c099ff644c137f59287fd</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>Boiler Room</Value>
            </Property>
            <Property>
              <ID>35</ID>
              <Value>43862,9264074884</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
        <Item>
          <ClassID>53</ClassID>
          <ItemID>dada9df18d7b10f3291bf10aecbbfb88</ItemID>
          <ItemPath>-search="Official Video" -type=video -videoCategoryId=10 -order=date</ItemPath>
          <ParentID>38ab1d14429c099ff644c137f59287fd</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>Новые Official Video</Value>
            </Property>
            <Property>
              <ID>35</ID>
              <Value>43862,925713044</Value>
            </Property>
            <Property>
              <ID>215</ID>
              <Value>-mpCreateDate</Value>
            </Property>
            <Property>
              <ID>93</ID>
              <Value>43862,9389201273</Value>
            </Property>
          </Properties>
        </Item>
      </ChildItems>
    </Item>
    <Item>
      <ClassID>32</ClassID>
      <ItemID>a87bec1bafc9b3a26e07c374e16b29bb</ItemID>
      <ItemPath>-SettingsFolder</ItemPath>
      <ParentID>b65318d7-8a9a-425f-92b9-9feba609ef39</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>43862,9347453704</Value>
        </Property>
        <Property>
          <ID>200</ID>
          <Value>5</Value>
        </Property>
        <Property>
          <ID>500</ID>
          <Value>///////////////////////////////////////////////////////////////////////////////
//               Г Л О Б А Л Ь Н Ы Е   П Е Р Е М Е Н Н Ы Е                   //
THmsScriptMediaItem Root = FolderItem;    // Корневая папка настроек
string    gsRootPath   = &apos;-SettingsFolder&apos;, // Значение поля путь (ссылка) корневой папки настроек
          gsKey        = &apos;&apos;,                // Ключ (определяется ниже)
          gsValue      = &apos;&apos;;                // Значение ключа (устанавливается ниже)
TDateTime gTimeStart   = Now;    // Время запуска скрипта
TStrings  SETTINGS;              // Объект TStrings для хранения описания настроек
int
  mpiAccessToken = 101315,       // Коды идентификаторов параметра медиа-ресурса
  mpiRefreshToken= 101316,       // для хранения токенов в корневой папке подкаста  
  gnTotalItems         = 0,      // Глобальный счетчик
  // Константы параметров этой динамической папки
  mpiFolderType        = 200,    // Тип папки     
  mpiDynamicScript     = 500,    // Скрипт
  mpiDynamicSyntaxType = 501,    // Язык скрипта
  mpiPreviousItemID    = 200104, // Предыдущий ItemID
  ;
///////////////////////////////////////////////////////////////////////////////
//                             Ф У Н К Ц И И                                 //

// ----------------------------------------------------- Структура настроек ---
void SettingsStructure() {
  // Указываем ключи и их наименования
  // Также указываем ключ и далее знак &apos;:&apos; и после - значение ключа (или &apos;+&apos; - добавить, &apos;-&apos; - удалить ключ)
 
  //SETTINGS.Values["--chkupdates"] = "Проверять обновления подкаста";
  //SETTINGS.Values["--chkupdates:-"] = &apos;Не проверять&apos;;   
  //SETTINGS.Values["--chkupdates:+"] = &apos;Проверять&apos;;   

  SETTINGS.Values["--maxheight"] = "Максимальная высота кадра (качество)";
  SETTINGS.Values["--maxheight:240" ] = &apos;240&apos;;   
  SETTINGS.Values["--maxheight:320" ] = &apos;320&apos;;   
  SETTINGS.Values["--maxheight:480" ] = &apos;480&apos;;   
  SETTINGS.Values["--maxheight:640" ] = &apos;640&apos;;   
  SETTINGS.Values["--maxheight:1080"] = &apos;1080&apos;;   
  SETTINGS.Values["--maxheight:2048"] = &apos;2048&apos;;   
  SETTINGS.Values["--maxheight:4096"] = &apos;4096&apos;;   
  SETTINGS.Values["--maxheight:4320"] = &apos;4320&apos;;   
     
  SETTINGS.Values["--subtitles"] = "Показ субтитров";
  SETTINGS.Values["--subtitles:-"] = &apos;Не показывать субтитры&apos;;   
  SETTINGS.Values["--subtitles:+"] = &apos;Показывать субтитры&apos;;   

  SETTINGS.Values["--adaptive"] = "Адаптивные медиа-потоки (adaptive)";
  SETTINGS.Values["--adaptive:-"] = &apos;Выключить adaptive&apos;;   
  SETTINGS.Values["--adaptive:+"] = &apos;Включить adaptive&apos;;   

  SETTINGS.Values["--sublanguage"] = "Язык субтитров";
  SETTINGS.Values["--sublanguage:ru"] = &apos;Русский&apos;;   
  SETTINGS.Values["--sublanguage:en"] = &apos;Английский&apos;;   
  SETTINGS.Values["--sublanguage:hy"] = &apos;Армянский&apos;;   
  SETTINGS.Values["--sublanguage:be"] = &apos;Белорусский&apos;;   
  SETTINGS.Values["--sublanguage:kk"] = &apos;Казахский&apos;;   
  SETTINGS.Values["--sublanguage:lv"] = &apos;Латышский&apos;;   
  SETTINGS.Values["--sublanguage:pl"] = &apos;Польский&apos;;   
  SETTINGS.Values["--sublanguage:uk"] = &apos;Украинский&apos;;   
  SETTINGS.Values["--sublanguage:fr"] = &apos;Французский&apos;;   
  SETTINGS.Values["--sublanguage:cs"] = &apos;Чешский&apos;;   
  SETTINGS.Values["--sublanguage:et"] = &apos;Эстонский&apos;;

//SETTINGS.Values["--alert"] = "Вывод окна сообщения авторизации";
//SETTINGS.Values["--alert:-"] = &apos;Не выводить окно&apos;;
//SETTINGS.Values["--alert:+"] = &apos;Выводить окно&apos;;
}

// ----------------------------------------------------------------------------
void ClearAllTokens(THmsScriptMediaItem Folder) {
  THmsScriptMediaItem Item;
  Folder[mpiAccessToken ] = &apos;&apos;;
  Folder[mpiRefreshToken] = &apos;&apos;;
  for (int i=0; i&lt;Folder.ChildCount; i++) {
    Item = Folder.ChildItems[i]; if (!Item.IsFolder) continue;
    Item[mpiAccessToken ] = &apos;&apos;;
    Item[mpiRefreshToken] = &apos;&apos;;
    if (Item.HasChildItems) ClearAllTokens(Item);
  } 
}

// ---------------------------------- Проверка текущего состояния настройки ---
bool CheckKeyState(string sVal) {
  string PARAMS = Root.ItemParent[mpiPodcastParameters]+&apos; &apos;; // Строка установленных параметров подкаста
  bool   bExist = (Pos(gsKey+&apos; &apos;, PARAMS)&gt;0);      // Проверяем, указан ли уже ключ
  if      (sVal==&apos;-&apos;) return !bExist;              // Если проверяемое значение &apos;-&apos; - ключ не должен быть указан       
  else if (sVal==&apos;+&apos;) return bExist;               // Если проверяемое значение &apos;+&apos; - указан ли ключ
  else return (Pos(gsKey+&apos;=&apos;+sVal+&apos; &apos;, PARAMS)&gt;0); // Иначе проверяем, установлено ли в параметрах заданное значение
}
 
// ---------------------------------------------- Создание ссылки-сообщения ---
void ShowMessageLink(string sMsg) {
  THmsScriptMediaItem Item = HmsCreateMediaItem(sMsg, FolderItem.ItemID);
  Item[mpiThumbnail] = &apos;http://wonky.lostcut.net/icons/ok.png&apos;;
}

// ---------- Функция создания динамической папки с унаследованным скриптом ---
void CreateItem(string sTitle, string sLink) {
  THmsScriptMediaItem Folder = FolderItem.AddFolder(sLink, true);
  Folder[mpiTitle     ] = sTitle;
  Folder[mpiCreateDate] = VarToStr(IncTime(gTimeStart,0,-gnTotalItems,0,0)); gnTotalItems++;
  Folder.CopyProperties(FolderItem, [mpiFolderType, mpiDynamicScript, mpiDynamicSyntaxType, mpiFolderSortOrder]);
}

///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------- Создание списка настроек ---
void CreateMainMenu() {
  for (int i=0; i&lt;SETTINGS.Count; i++) { // Обходим в цикле все настройки
    string sKey = SETTINGS.Names[i];     // Получаем ключ настроек
    if (Pos(&apos;:&apos;, sKey)&gt;0) continue;      // Если там есть &apos;:&apos;, то это вариант настройки - пропускаем
    CreateItem(SETTINGS.Values[sKey], &apos;-showValues=&apos;+sKey); // Создаём пункт настройки
  }
//CreateItem(&apos;Удалить токены для смены кода доступа&apos;, &apos;-clearTokens&apos;);
}
// ------------------------------------ Создание списка вариантов настройки ---
void CreateValuesList() {
  int i; string sVal, sKey, sName, sState; // При входе в процедуру в gsKey уже сам ключ
  for (i=0; i&lt;SETTINGS.Count; i++) { // Обходим в цикле все настройки
    sKey   = SETTINGS.Names[i];      // Получаем ключ настроек
    sName  = SETTINGS.Values[sKey];  // Наименование варианта настройки
    if (!HmsRegExMatch(&apos;^&apos;+gsKey+&apos;:(.*)&apos;, sKey, sVal)) continue; // Если не получили значение нашего ключа, пропускаем
    if (CheckKeyState(sVal)) sState=&apos;[v]&apos;; else sState=&apos;[ ]&apos;;    // Проверка: установлено ли данное значение, ставим пометку
    CreateItem(sState+&apos; &apos;+sName, &apos;-key=&apos;+gsKey+&apos; -value=&apos;+sVal); // Создаём пункт варианта настройки   
  }
}
// ------------------------ Включение/Отключение настройки или его значения ---
void ApplyKeyValue() {
  // При входе в процедуру уже в gsKey - ключ, в gsValue - его значение (может быть &apos;+&apos; или &apos;-&apos;, это установить или удалить параметр)
  bool bExist; string sOldVal=gsKey, sNewVal, PARAMS;        // По-умолчанию в sOldVal сам ключ
 
  PARAMS = Root.ItemParent[mpiPodcastParameters]+&apos; &apos;;      // Строка установленных параметров подкаста
  HmsRegExMatch(&apos;(&apos;+gsKey+&apos;=.*?)\\s&apos;, PARAMS, sOldVal);      // Вылавливаем в sOldVal установленное значение
  bExist = (Pos(sOldVal, PARAMS)&gt;0);                         // Устанавливаем флаг присутсвия ключа в параметрах
 
  if      (gsValue==&apos;-&apos;) sNewVal = &apos;&apos;;                       // Замена на пустое значение = удалению
  else if (gsValue==&apos;+&apos;) sNewVal = gsKey;                    // Просто устанавливаем ключ
  else                   sNewVal = gsKey+&apos;=&apos;+gsValue;        // Устанавливаем ключ с новым значением
  if (bExist) PARAMS = ReplaceStr(PARAMS, sOldVal, sNewVal); // Если ключ уже присутствует - заменяем
  else        PARAMS += sNewVal;                             // Иначе просто добавляем
  ShowMessageLink(&apos;ВЫБРАНО: &apos;+Copy(mpTitle, 5, 99));         // Пропускаем &apos;[ ] &apos; в mpTitle и сообщаем о выбранном варианте
  Root.ItemParent[mpiPodcastParameters] = Trim(ReplaceStr(PARAMS, &apos;  &apos;, &apos; &apos;)); // Сохраняем параметры подкаста
}

// ----------------------------------------------------------------------------
// Проверка значения ссылки текущей папки и извлечение группировок регулярного выражения в gsKey и gsValue
bool CheckPath(string sPattern) { return HmsRegExMatch2(sPattern, mpFilePath, gsKey, gsValue); }

///////////////////////////////////////////////////////////////////////////////
//                    Г Л А В Н А Я   П Р О Ц Е Д У Р А                      //
// ----------------------------------------------------------------------------
{
  // Поиск корневой динамической папки (ибо этот скрипт может выполнятся и в подпапках)
  while ((Root[mpiFilePath]!=gsRootPath) &amp;&amp; (Root.ItemParent!=nil)) Root = Root.ItemParent;
  if (Root[mpiFilePath]!=gsRootPath) { ShowMessageLink(&apos;Не найдена папка настроек с путём &apos;+gsRootPath); return; }
 
  // Если это повторный вызов, смены папки не произошло - ничего не делаем
  if ((FolderItem.ItemID==Root[mpiPreviousItemID]) &amp;&amp; !DebugMode &amp;&amp; (FolderItem[mpiFilePath]!=gsRootPath)) return;
  FolderItem.DeleteChildItems(); Root[mpiPreviousItemID] = FolderItem.ItemID;
  SETTINGS = TStringList.Create();
  try {
    SettingsStructure();
    if      (CheckPath(gsRootPath))               CreateMainMenu();   // Если это корень - создаём список настроек
    else if (CheckPath(&apos;-showValues=(.*)&apos;))       CreateValuesList(); // Зашли в настройку - показываем список вариантов значений
    else if (CheckPath(&apos;-key=(.*?) -value=(.*)&apos;)) ApplyKeyValue();    // Зашли в вариант значения настройки - применяем этот вариант
    else if (CheckPath(&apos;-clearTokens&apos;)) {
      ClearAllTokens(Root.ItemParent); // &apos;Удалить токены для смены кода доступа&apos;
      ShowMessageLink(&apos;Токены очищены!&apos;);
    }  

  } finally { SETTINGS.Free(); }
  HmsIncSystemUpdateID(); // Говорим устройству об обновлении содержания
}</Value>
        </Property>
        <Property>
          <ID>501</ID>
          <Value>C++Script</Value>
        </Property>
        <Property>
          <ID>215</ID>
          <Value>-mpCreateDate</Value>
        </Property>
        <Property>
          <ID>93</ID>
          <Value>43862,9389201273</Value>
        </Property>
        <Property>
          <ID>200104</ID>
          <Value>c107bbfaf1aa594f1bad2f164057fecb</Value>
        </Property>
        <Property>
          <ID>245</ID>
          <Value>a87bec1bafc9b3a26e07c374e16b29bb</Value>
        </Property>
      </Properties>
    </Item>
  </ChildItems>
</HmsMediaItem>
