1

Тема: Работа с torrent файлами

По просьбам телезрителей и как обещано опишу на примере как работать с торрент ресурсами и самими торрент-файлами.

На самом деле всё очень просто и всё можно описать в одном предложении: скачиваем torrent-файл и указываем в создаваемой ссылке на видео такую строку "torrent:<Путь до скачанного torrent файла>?index=<Номер видео файла в файле torrent>". Где <Номер видео файла в файле torrent> - можно не указывать, если в раздаче всего-лишь один файл.

На этом всё. До новых встреч.

Правда, просившие люди всё разъяснить, вряд ли будут довольны таким изложением и ждут что я напишу целую книгу про это. Поэтому я всё-таки разберу это дело на примере сайта и напишем целый подкаст. За пример возьму предложенный михаилом сайт toreents.club.

Для начала немного теории. Я буду краток, чтобы информация была предельно простой, не вдаваясь в некоторые подробности и сложности, которые могут быть. Так, мне кажется, будет лучше для понимания принципа. А усложнить кодеры и без меня смогут при написании скриптов. Правда большая часть текста будет посвящена общим вопросам написания подкастов.

Итак.

Общая информация о торрент-файлах

В самих торрент файлах содержится информация о том, сколько и какие файлы присутствуют в раздаче. Их имя и размер. Собственно и всё. Конечно также и адреса, откуда идёт раздача. Торрент файлы могут быть двух типов:

  • Однофайловый - там содержится информация об одном файле, его имя и размер.
  • Многофайловый - содержится информация о количестве файлов, их расположении в подпапках, их имена и размер.

Создание подкаста

Теперь приступим к написанию самого подкаста для работы с этими файлами.

Для начала добавим папку с названием "toreents.club", в которой будут подкасты разделов, например "Каталог фильмов" со ссылкой "http://toreents.club/katalog-torrent-films/" и "Зарубежные сериалы" со ссылкой "http://toreents.club/serialy/zarubezhnye-serialy/".
Добавим в созданную папку эти подкасты и зададим им вышеуказанные значения.
Или если вы такой же лентяй как и я, то можно написать простейший скрипт, который создаст их за нас.

Скрипт создания подкаст-лент (Alt+1)

Для этого нажав правой клавишей мышки на корневой папке подкаста выберем "Изменить название / Настройки F2". Внизу выбираем вкладку "Скрипты" и выставляем следующие значения:
"Скрипт создания подкаст лент" - "Включён" и нажимаем кнопку редактирования скрипта.

Там в редакторе выставляем язык "JScript" и вставляем простейший скрипт:

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
{
  FolderItem.DeleteChildItems();
  
  base = 'http://toreents.club/';

  FolderItem.AddFolder(base+'katalog-torrent-films/'              )[mpiTitle] = 'Каталог фильмов';
  FolderItem.AddFolder(base+'katalog-torrent-films/russkie-fylmy/')[mpiTitle] = 'Русские фильмы';
  
  FolderItem.AddFolder(base+'serialy/russkie-serialy/'    )[mpiTitle] = 'Русские сериалы';
  FolderItem.AddFolder(base+'serialy/zarubezhnye-serialy/')[mpiTitle] = 'Зарубежные сериалы';
}

После нажатия ОК и сохранения этого всего дела можем нажав правой клавишей мышки на корневой папке выбрать пункт "Создать ленты подкастов", после чего указанные разделы будут созданы автоматически.
Доработать подобный скрипт по своему усмотрению всё также не трудно.

Скрипт обновления раздела подкаста (Alt+2)

Теперь переходим к созданию скрипта, который непосредственно будет создавать в существующих разделах списки фильмов или сериалов. На самом деле вот то, что я сейчас рассказываю вообще к работе с torrent-файлами отношения никакого не имеет, это просто создание обычного подкаста, который ищет информацию на сайте и создаёт список найденных видео или папок, как в нашем случае, с наименованиями фильмов или сериалов.
Но деваться некуда, ведь подкаст нужно сделать, да так, чтобы он работал. Поэтому создадим простой скрипт, который при обновлении раздела будет искать на сайте информацию о фильмах и создавать нам этот список в подкасте.

Именно этот этап очень сильно зависит от того, что за сайт мы обрабатываем, какой он структуры и прочее. Поэтому как и что делать тут сильно зависит от того, в каком виде и как именно информация представлена на сайте.
Именно в нашем этом конкретном случае на сайте есть список фильмов, при заходе на страницу фильма там выложены разные торрент-файлы этого фильма или сериала. А в сериалах сразу выложены торрент-файлы всего сезона или нескольких серий.
Поэтому в нашем случае логичнее всего сделать следующее:

  • При обновлении подкаста раздела - будут созданы папки с именем фильма/сериала.
  • При заходе в такую папку - будут созданы папки с именем найденных торрент-файлов на странице фильма.
  • А уже при заходе в папку торрент-файла - будут созданы ссылки на видео, которые уже непосредственно будут воспроизводиться.

Итак, создадим скрипт обновления раздела, который создаст список фильмов.
Для этого идём в всё туда же - встаём на корневую папку подкаста и нажимаем F2, на вкладке "Скрипты" ставим "Режим чтения списка ресурсов" в значение "Специальный (скрипт)", в разделе "Скрипт чтения списка ресурсов (Alt+2)" выставляем "Включен" и нажимаем кнопку редактирования скрипта.
Там выставляем язык "C++Script" и можем сразу набрать шаблон с главной процедурой и объявлением единственной переменной gsUrlBase, которая будет равна "http://toreents.club".
А в главной процедуре пишем пару строк:
Стандартная FolderItem.DeleteChildItems() - для удаления ранее созданных элементов в подкасте раздела и вызов функции LoadAndParse(), которую мы напишем для загрузки и обработки данных:

string gsUrlBase = 'http://toreents.club'; // База для относительных ссылок

///////////////////////////////////////////////////////////////////////////////
//                    Г Л А В Н А Я    П Р О Ц Е Д У Р А                     //
{
  FolderItem.DeleteChildItems(); // Удаляем созданные ранее элементы в текущей папке
  LoadAndParse();                // Запускаем загрузку страниц и создание папок видео
}

Перед главной процедурой добавляем функцию LoadAndParse() и пока просто объявляем переменные в ней:

///////////////////////////////////////////////////////////////////////////////
// Загрузка страниц и парсинг
void LoadAndParse() {
  string sHtml, sLink, sName, sImg, sYear; int i, nPages; TRegExpr RegEx; // Объявление переменных

}

Если мы полазим по сайту, то обнаружим, что переходя на следующие страницы с фильмами, окончание ссылки будет выглядеть вот так: /page/2/ и проч. Это значит, что мы можем в цикле загрузить несколько страниц, чтобы в них уже искать имена и ссылки на выложенные фильмы.

Можно написать вот такую функцию цикла загрузки страниц раздела:

  sHtml  = ''; // Общий текст загруженных страниц сайта
  nPages = 2 ; // Количество загружаемых страниц

  // Загружаем первые сколько-то страниц (указано в nPages)
  for (i=1; i<=nPages; i++) {
    HmsSetProgress(Trunc(i*100/nPages));          // Устанавливаем позицию прогресса загрузки
    HmsShowProgress(Format('%s: Страница %d из %d', [mpTitle, i, nPages])); // Показываем окно прогресса выполнения
    sLink = mpFilePath+'page/'+IntToStr(i)+'/';   // Формируем ссылку для загрузки, включающую номер страницы
    sHtml+= HmsUtf8Decode(HmsDownloadUrl(sLink)); // Загружаем страницу
    if (HmsCancelPressed()) break;                // Если в окне прогресса нажали "Отмена" - прерываем цикл
  }
  HmsHideProgress();                              // Убираем окно прогресса с экрана

Т.е. мы задаём количество страниц, которые хотим загрузить и в цикле в нашей ссылке раздела добавляем строку page/2/, page/3/ и т.д.
Заодно показывая прогресс загрузки с указанием какую из скольки страниц мы загрузили.
За это отвечают функции HmsSetProgress() (устанавливает позицию полосы прогресса), HmsShowProgress() (отображение самого прогресса и его заголовка), а после цикла скрываем прогресс функцией HmsHideProgress(). В итоге в переменной sHtml у нас будет содержаться весь html текст страниц, которые мы загрузили.

И вот в этом html мы будем искать, как обычно - по регулярным выражениям, блоки с информацией о фильме. Посмотрев исходники страниц сайта, можно убедиться, что блоки с информацией заключены между словами "shortstory" и "bsep". Поэтому создаём объект типа TRegExpr для поиска таких блоков. А внутри найденных блоков уже ищем информацию об имени, ссылке на страницу фильма и изображении.

Вот такой код получился для поиска фильмов на странице:

  RegEx = TRegExpr.Create('shortstory(.*?)bsep', PCRE_SINGLELINE);
  try {
    if (RegEx.Search(sHtml)) do {            // Если нашли совпадение, запускаем цикл
      sLink=''; sName=''; sImg=''; sYear=''; // Очищаем переменные от предыдущих значений
      // Получаем значения в переменные по регулярным выражениям
      HmsRegExMatch('<a[^>]+href="(.*?)"'  , RegEx.Match, sLink); // Ссылка
      HmsRegExMatch('(<a[^>]+href=.*?</a>)', RegEx.Match, sName); // Наименование
      HmsRegExMatch('<img[^>]+src="(.*?)"' , RegEx.Match, sImg ); // Картинка
      HmsRegExMatch('(\\d{4})\\)'          , sName      , sYear); // Год

      if (sLink=='') continue;          // Если нет ссылки, значит что-то не так

      sLink = HmsExpandLink(sLink, gsUrlBase);             // Делаем ссылку полной, если она таковой не является
      if (sImg!='') sImg = HmsExpandLink(sImg, gsUrlBase); // Если есть ссылка на картинку, делаем ссылку полной
      sName = HmsHtmlToText(sName);                        // Преобразуем html в простой текст
      HmsRegExMatch('(.*?)/' , sName, sName);              // Обрезаем слишком длинные названия (на англ. языке)

      // Если в названии нет года, добавляем год выхода
      if ((sYear!='') && (Pos(sYear, sName)<1)) sName += ' ('+sYear+')';

      THmsScriptMediaItem Item = FolderItem.AddFolder(sLink); // Создаём папку с указанной ссылкой
      Item[mpiTitle     ] = sName; // Присваиваем наименование
      Item[mpiThumbnail ] = sImg;  // Картинка

    } while (RegEx.SearchAgain);        // Повторяем цикл, если найдено следующее совпадение

  } finally { RegEx.Free; }             // Что бы ни случилось, освобождаем объект из памяти

Всё, скрипт для поиска фильмов создания папок для них готов.

+ Итоговый скрипт обновления раздела подкаста Alt+2

string gsUrlBase = 'http://toreents.club'; // База для относительных ссылок

///////////////////////////////////////////////////////////////////////////////
// Загрузка страниц и парсинг
void LoadAndParse() {
  string sHtml, sLink, sName, sImg, sYear; int i, nPages; TRegExpr RegEx;
  
  sHtml  = ''; // Текст загруженных страниц сайта
  nPages = 2 ; // Количество загружаемых страниц

  // Загружаем первые сколько-то страниц (указано в nPages)
  for (i=1; i<=nPages; i++) {
    HmsSetProgress(Trunc(i*100/nPages));          // Устанавливаем позицию прогресса загрузки
    HmsShowProgress(Format('%s: Страница %d из %d', [mpTitle, i, nPages])); // Показываем окно прогресса выполнения
    sLink = mpFilePath+'page/'+IntToStr(i)+'/';   // Формируем ссылку для загрузки, включающую номер страницы
    sHtml+= HmsUtf8Decode(HmsDownloadUrl(sLink)); // Загружаем страницу
    if (HmsCancelPressed()) break;                // Если в окне прогресса нажали "Отмена" - прерываем цикл
  }
  HmsHideProgress();                              // Убираем окно прогресса с экрана

  RegEx = TRegExpr.Create('shortstory(.*?)bsep', PCRE_SINGLELINE);
  try {
    if (RegEx.Search(sHtml)) do {            // Если нашли совпадение, запускаем цикл
      sLink=''; sName=''; sImg=''; sYear=''; // Очищаем переменные от предыдущих значений
      // Получаем значения в переменные по регулярным выражениям
      HmsRegExMatch('<a[^>]+href="(.*?)"'  , RegEx.Match, sLink); // Ссылка
      HmsRegExMatch('(<a[^>]+href=.*?</a>)', RegEx.Match, sName); // Наименование
      HmsRegExMatch('<img[^>]+src="(.*?)"' , RegEx.Match, sImg ); // Картинка
      HmsRegExMatch('(\\d{4})\\)'          , sName      , sYear); // Год

      if (sLink=='') continue;          // Если нет ссылки, значит что-то не так

      sLink = HmsExpandLink(sLink, gsUrlBase);             // Делаем ссылку полной, если она таковой не является
      if (sImg!='') sImg = HmsExpandLink(sImg, gsUrlBase); // Если есть ссылка на картинку, делаем ссылку полной
      sName = HmsHtmlToText(sName);                        // Преобразуем html в простой текст
      HmsRegExMatch('(.*?)/' , sName, sName);              // Обрезаем слишком длинные названия (на англ. языке)

      // Если в названии нет года, добавляем год выхода
      if ((sYear!='') && (Pos(sYear, sName)<1)) sName += ' ('+sYear+')';

      THmsScriptMediaItem Item = FolderItem.AddFolder(sLink); // Создаём папку с указанной ссылкой
      Item[mpiTitle     ] = sName; // Присваиваем наименование
      Item[mpiThumbnail ] = sImg;  // Картинка

    } while (RegEx.SearchAgain);        // Повторяем цикл, если найдено следующее совпадение

  } finally { RegEx.Free; }             // Что бы ни случилось, освобождаем объект из памяти
}

///////////////////////////////////////////////////////////////////////////////
//                    Г Л А В Н А Я    П Р О Ц Е Д У Р А                     //
{
  FolderItem.DeleteChildItems(); // Удаляем созданные ранее элементы в текущей папке
  LoadAndParse();                // Запускаем загрузку страниц и создание папок видео
}

Сохраняем это всё и теперь вызывать этот скрипт для редактирования можно выделив конкретный раздел и нажав Alt+2.

Ура, если обновить раздел "Каталог фильмов" у нас будут созданы папки с именами фильмов.

Скрипт получения ссылки на ресурс (Alt+4)

Теперь нужно написать скрипт, который будет срабатывать при заходе в такую папку фильма, а также который будет срабатывать при запуске ссылки видео на просмотр. Включается он тут: встаём на корневую папку подкаста и нажимаем F2, на вкладке "Скрипты" раздел "Скрипт получения ссылки на ресурс (Alt+4)" ставим значение "Включен" и нажимаем кнопку редактирования скрипта. Выбираем язык "C++Script" и зададим для начала всего-лишь пустой шаблон вида:

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
{

  // Проверяем, при каком событии было вызвано выполнение скрипта (не вход ли в папку это?)
  if (PodcastItem.IsFolder) {
    
    PodcastItem.DeleteChildItems(); // Удаляем в текущей папке все элементы
    
  } else {

    MediaResourceLink = mpFilePath; // Это просто запустили ссылку видео на просмотр

  }

}

В этом скрипте, как обычно, нужно проверять, сработал ли он при событии захода в пустую подпапку подкаста или это запустили фильм на просмотр. Переменная PodcastItem содержит значение элемента базы данных программы - либо это папка, либо это запущенная ссылка. Поэтому проверив у этого элемента свойство IsFolder мы можем определить, является ли текущий элемент папкой. Если является - удаляем на всякий случай все в ней элементы (полезно при отладке) и потом нужно будет вызвать функцию, в которой мы загрузим страницу фильма, поищем там торрент файлы и создадим папки с их именем и ссылкой на скачивание этих файлов.

Для этого создадим такую функцию:

///////////////////////////////////////////////////////////////////////////////
// Поиск торрент-файлов на странице фильма и создание папок для них
void CreateTorrentFolders() {
  string sLink;
  string sHtml = HmsUtf8Decode(HmsDownloadURL(mpFilePath));

  // Поиск торрент-файлов на странице фильма / сериала
  TRegExpr RegEx = TRegExpr.Create('"attachment">(.*?)</div>', PCRE_SINGLELINE);
  if (RegEx.Search(sHtml)) do {
    HmsRegExMatch('<a[^>]+href="(.*?)"', RegEx.Match, sLink);
    THmsScriptMediaItem Item = PodcastItem.AddFolder(sLink);
    Item[mpiTitle] = HmsHtmlToText(RegEx.Match);
  } while (RegEx.SearchAgain());

}

Т.е. мы командой sHtml = HmsUtf8Decode(HmsDownloadURL(mpFilePath)) загружаем страницу фильма.
Потом создаём объект типа TRegExpr для поиска блоков между "attachment"> и </div>. Там как раз попадается ссылка на скачивание торрент-файлов.
В каждом таком найденном блоке мы получаем ссылку href из html тега <a>. И командой
THmsScriptMediaItem Item = PodcastItem.AddFolder(sLink);
мы создаём папку торрент файла, где в ссылке будет указано ссылка на скачивание вида "http://toreents.club/engine/download.php?id=<Номер>".

Т.к. при заходе в пустую папку это может быть как фильм, так и папка торрент файла такого вида, то нам нужно также проверять, в какую именно вида папку мы зашли и вызывать разные для этого функции. Поэтому главную процедуру можно сделать такого вида:

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
{

  // Проверяем, при каком событии было вызвано выполнение скрипта (не вход ли в папку это?)
  if (PodcastItem.IsFolder) {
    
    PodcastItem.DeleteChildItems(); // Удаляем в текущей папке все элементы
    
    if (Pos('download.php', mpFilePath)>0) 
      CreateFilesFromTorrentFile(); // Скачиваем сам torrent файл и создаём ссылки на видео из него
    else                                   
      CreateTorrentFolders();       // Ищем ссылки на torrent файлы и создаём папки с их наименованиями

  } else {

    MediaResourceLink = mpFilePath; // Это просто запустили ссылку видео на просмотр

  }

}

Где функция CreateTorrentFolders() у нас уже написана, а функцию CreateFilesFromTorrentFile() мы пока сделаем пустой, но напишем потом там самое интересное - обработку torrent-файла.

///////////////////////////////////////////////////////////////////////////////
// Создание ссылок видео из torrent-файла
void CreateFilesFromTorrentFile() {
}

Теперь логика главной процедуры такая:

  • Если зашли в папку - проверяем, содержит ли ссылка этой папки слово "download.php",
  • если содержит, то это папка со ссылкой на конкретный torrent-файл,
  • а если не содержит - то это считаем папка фильма/сериала, где мы вызываем CreateTorrentFolders для загрузки страницы фильма и поиска там ссылок на скачивание торрент-файлов.

Ну а теперь нам осталось написать функцию, которая при заходе в папку со ссылкой на скачивание торрент-файла - скачивала бы его, анализировала и создавала бы ссылки на видео, которые этот торрент-файл раздаёт. Т.е. мы подошли наконец-то к сути нашего урока - работы с торрент файлами и каким видом ссылки могут быть у подобных видео.

Для начала нам нужно скачать сам торрент-файл.
Сделать это удобнее всего командой HmsDownloadUrlToFile(), где в параметрах указывается ссылка на торрент и сам файл на диске, который им станет после скачивания. Также, кстати, в этой команде передаются http заголовки, которые помогут нам не обломаться со скачиванием.

Поэтому для начала объявляем переменные и присваиваем переменной sHeaders значение с "Referer":
sHeaders = 'Referer: '+PodcastItem.ItemParent[mpiFilePath]+'\r\n';
После этого будет указан Referer со значением ссылки самого фильма.

Также задаём имя, в который будет скачан торрент-файл:
sFile = HmsTranscodingTempDirectory+mpTitle;

Как не трудно догадаться HmsTranscodingTempDirectory() - это функция получения директории для хранения временных файлов программы.

После этого переменная sFile может содержать что-то вроде "C:\Users\body\AppData\Local\Home Media Server\HMS\Temp\kinosvit.net.city.of.gold.2018.p.dvdrip.14oomb.avi.torrent 81 чел."

Т.к. название в данном случае не совсем хорошее для файла (лишнее в конце), то сделаем обрезку ненужного с помощью всё той же регулярки:
HmsRegExMatch('^(.*?.torrent)', sFile, sFile);

После этого у нас в переменной sFile будет уже что-то вроде:
"C:\Users\body\AppData\Local\Home Media Server\HMS\Temp\kinosvit.net.city.of.gold.2018.p.dvdrip.14oomb.avi.torrent"
Что более подходит для имени файла.

Теперь можно его скачать:
HmsDownloadUrlToFile(mpFilePath, sFile, sHeaders);

Если всё удачно, у нас появится скачанный торрент-файл. Это очень важный момент и переменная sFile нам ещё пригодится для формировании ссылки видео далее.

Работа с torrent-файлами

Ну а теперь непосредственная работа со скачанным торрент файлом. Для работы с ним в программе существуют два класса: TTorrentFile и TTorrentSubFile. Второй нужен только для того, чтобы обработать вложенные файлы, если в скачанном torrent-файле будет их несколько.

Работа с ними имеет такой вид.
Создаём объект:
TORRENT = TTorrentFile.Create();

Загружаем в него скачанный на диск торрент-файл:
TORRENT.LoadFromFile(sFile);

У класса TTorrentFile (наша переменная TORRENT) есть несколько свойств:

  • TORRENT.MultiFile - истина, если это многофайловый торрент.
  • TORRENT.Count - количество файлов в торренте (если однофайловый - всегда 1)
  • TORRENT.Length - длина раздаваемого файла и TORRENT.Name - имя раздаваемого файла.
    Последние два свойства установлены, только если это однофайловый торрент.

Если это многофайловый, то работа с ним должна вестись следующим способом. Нужно перебрать все файлы в нём через TORRENT.Files[n], где n - номер файла.

    if (TORRENT.MultiFile) {
      // Перебираем все файлы
      for (int i=0; i < TORRENT.Count; i++) {
        SUBTORRENT = TORRENT.Files[i];
        ... ... работаем с объектом SUBTORRENT
      }
    }

У объекта SUBTORRENT всего несколько свойств:

  • SUBTORRENT.Length - длина раздаваемого файла
  • SUBTORRENT.Name - имя раздаваемого файла
  • SUBTORRENT.Path - путь (папки) в подкаталогах, если таковая структура имеется (например, если раздача идёт с подкаталогами).

В нашем случае нужно создать ссылки на все видео файлы, раздаваемые скачанным торрентом.

И теперь ещё один ключевой момент. Программа HMS поддерживает работу с торрент-файлами из коробки. Для того, чтобы программа могла на лету скачивать раздачу и отправлять на телек, ссылка должна начинаться на "torrent:" где далее идёт ссылка на скачанный локальный торрент файл. Также можно добавить знак вопроса и строку "index=" с указанием индекса файла, если мы хотим посмотреть не первый в списке, а конкретный в многофайловом торрент-файле.

Поэтому код для создания ссылок на видео будет таким:

  TTorrentFile TORRENT = TTorrentFile.Create();  
  try {
    TORRENT.LoadFromFile(sFile);
    // Проверяем, в этом торренте несколько файлов?
    if (TORRENT.MultiFile) {
      // Перебираем все файлы
      for (int i=0; i < TORRENT.Count; i++) {

        SUBTORRENT = TORRENT.Files[i];

        // Проверяем тип файла по расширению
        if (HmsFileMediaType(SUBTORRENT.Name)==mtVideo) {

          // Создаём ссылку с указанием локального файла и его индексом
          sLink = Format('torrent:%s?index=%d', [sFile, i]);

          // Создаём элемент видео в программе
          Item = HmsCreateMediaItem(sLink, PodcastItem.ItemID);
          Item[mpiTitle   ] = SUBTORRENT.Name;
          Item[mpiFileSize] = SUBTORRENT.Length;

        }
      }

    } else {
      // В торренте раздаётся единственный файл
      Item = HmsCreateMediaItem('torrent:'+sFile, PodcastItem.ItemID);
      Item[mpiTitle   ] = TORRENT.Name;
      Item[mpiFileSize] = TORRENT.Length;

    }
  } finally { TORRENT.Free; }

Т.к. в раздачах могут присутствовать не только видео файлы но и любые другие, то следует проверять тип раздаваемых файлов по расширению. Это делает функция HmsFileMediaType(), где она должна в нашем случае возвращать тип mtVideo.

Вот и всё. Т.е. в нашей последней функции мы по-сути делаем следующее:

  • Загружаем файл по ссылке на локальный диск во временную папку;
  • Потом создаём объект типа TTorrentFile, загружаем в него скачанный torrent-файл;
  • Проверяем, не является ли он многофайловым и если является, перебираем все файлы в нём, создавая ссылки вида "torrent:<Путь до локального файла>?index=<Номер файла>";
  • Если тип торрент-файла - однофайловый, то просто создаём видео ссылку "torrent:<Путь до локального файла>".

+ Вот итоговый скрипт получения ссылки на медиа-ресурс по Alt+4

///////////////////////////////////////////////////////////////////////////////
// Создание ссылок видео из torrent-файла
void CreateFilesFromTorrentFile() {
  string sFile, sLink, sName, sHeaders;
  TTorrentFile TORRENT; TTorrentSubFile SUBTORRENT; THmsScriptMediaItem Item;
  sHeaders = 'Referer: '+PodcastItem.ItemParent[mpiFilePath]+'\r\n';
  
  HmsRegExMatch('^(.*?.torrent)', mpTitle, mpTitle); // В данном случае очищаем название, оставляя только реальное имя файла торрента

  sFile = HmsTranscodingTempDirectory+mpTitle;

  if (!FileExists(sFile) || (Length(HmsStringFromFile(sFile))==0))
    HmsDownloadUrlToFile(mpFilePath, sFile, sHeaders);
  
  TORRENT = TTorrentFile.Create();  
  try {
    TORRENT.LoadFromFile(sFile);
    // Проверяем, в этом торренте несколько файлов?
    if (TORRENT.MultiFile) {
      // Перебираем все файлы
      for (int i=0; i < TORRENT.Count; i++) {
        SUBTORRENT = TORRENT.Files[i];
        // Проверяем тип файла по расширению
        if (HmsFileMediaType(SUBTORRENT.Name)==mtVideo) {
          sLink = Format('torrent:%s?index=%d', [sFile, i]);
          Item = HmsCreateMediaItem(sLink, PodcastItem.ItemID);
          Item[mpiTitle   ] = SUBTORRENT.Name;
          Item[mpiFileSize] = SUBTORRENT.Length;
        }
      }
    } else {
      // В торренте раздаётся единственный файл
      Item = HmsCreateMediaItem('torrent:'+sFile, PodcastItem.ItemID);
      Item[mpiTitle   ] = TORRENT.Name;
      Item[mpiFileSize] = TORRENT.Length;
    }
  } finally { TORRENT.Free; }
}

///////////////////////////////////////////////////////////////////////////////
// Поиск торрент-файлов на странице фильма и создание папок для них
void CreateTorrentFolders() {
  string sLink;
  string sHtml = HmsUtf8Decode(HmsDownloadURL(mpFilePath));
  // Поиск торрент-файлов на странице фильма / сериала
  TRegExpr RegEx = TRegExpr.Create('"attachment">(.*?)</div>', PCRE_SINGLELINE);
  if (RegEx.Search(sHtml)) do {
    HmsRegExMatch('<a[^>]+href="(.*?)"', RegEx.Match, sLink);
    THmsScriptMediaItem Item = PodcastItem.AddFolder(sLink);
    Item[mpiTitle] = HmsHtmlToText(RegEx.Match);
  } while (RegEx.SearchAgain());
}

///////////////////////////////////////////////////////////////////////////////
//                     Г Л А В Н А Я   П Р О Ц Е Д У Р А                     //
{

  // Проверяем, при каком событии было вызвано выполнение скрипта (не вход ли в папку это?)
  if (PodcastItem.IsFolder) {
    
    PodcastItem.DeleteChildItems(); // Удаляем в текущей папке все элементы
    
    if (Pos('download.php', mpFilePath)>0) 
      CreateFilesFromTorrentFile(); // Скачиваем сам torrent файл и создаём ссылки на видео из него
    else                                   
      CreateTorrentFolders();       // Ищем ссылки на torrent файлы и создаём папки с их наименованиями

  } else {

    MediaResourceLink = mpFilePath; // Это просто запустили ссылку видео на просмотр

  }

}

Для кучи прикладываю получившийся подкаст. Его хорошо бы довести до ума сделать нормальным. Но это уже пусть кто-нибудь сделает сам.

P.S.: Пока занимался написанием подкаста и отладкой - доработал и выложил новую версию HMSEditor 2.0.3.6 - сделал так, что при отладке и наведении мыши на объект подсказка сразу показывала все доступные свойства объекта. Удобно смотреть объекты TORRENT и SUBTORRENT при отладке.

Прикреплённые файлы сообщения

torrents.club.zip 4.23 kb, скачивалось 396 раз, начиная с 2019.11.11

Sony Bravia KDL-32CX523
Спасибо сказали: Spell, lidars, михаил, satelitas, Oompaloomp, Hunter, Dr.Dru7

2

Re: Работа с torrent файлами

Спасибо за статью! :)

"Хорошо написанная программа — это программа, написанная 2 раза" :-X
Спасибо сказали: Hunter, Dr.Dru2