Тема: Написать скрипт, который бы загружал плейлист m3u8
Была просьба сделать скрипт, который бы загружал плейлист m3u8, создавать ссылки из него и мог бы играть HLS поток из него.
Проще всего сделать это - сделать подкаст.
Т.к. у меня мечта, чтобы тут ещё люди появились, которые хотят научится делать подкасты или умеют, то я решил не просто сделать, а показать и объяснить как можно его создать.
Была цель: создать ссылки из указанного плейлиста, которые содержат ссылки на поток HLS и чтобы Домашний медиа-сервер мог их проиграть.
Ок.
Давайте создадим новый подкаст в программе:
В поле заголовок вписываем название, которое будет у нас отображаться, а в поле "Ссылка" впишем путь до нашего плейлиста.
Сразу запланируем, чтобы сделать возможность указывать как локальные папки (типа D:\playlist.m3u8), так и ссылки на плейлисты из интернета (адрес начинается на http://).
Нажав внизу вкладку "Скрипты", проставим:
1. "Режим чтения списка ресурсов" в значение "Специальный (скрипт)";
2. "Скрипт чтения списка ресурсов (Alt + 2)" в значение "Включен";
3. "Скрипт получения ссылки на ресурс (Alt + 4)" тоже в значение "Включен";
и нажмём ОК.
У нас добавиться базу новый подкаст. На нём мы нажмём опять правой клавишей мышки и выберем "Изменить название", тем самым мы откроем окно свойств подкаста.
Нажмём кнопку рядом с "Включен" в разделе "Скрипт чтения списка ресурсов (Alt + 2)" и попадём в окно редактирования скриптов.
Внизу выберем язык синтаксиса, который мы будем использовать. Ставьте такой, который вам по душе, но тут я буду показывать примеры на C++Script и PascalScript.
сразу зададим главную процедуру:
///////////////////////////////////////////////////////////////////////////////
// Г Л А В Н А Я П Р О Ц Е Д У Р А //
// ----------------------------------------------------------------------------
{
}
///////////////////////////////////////////////////////////////////////////////
// Г Л А В Н А Я П Р О Ц Е Д У Р А //
// ----------------------------------------------------------------------------
Begin
End.
В ней напишем первую команду, которая будет очищать содержимое подкаста (ранее созданные ссылки):
FolderItem.DeleteChildItems(); // Удаляем созданные ранее ссылки
FolderItem - это встроенная переменная, которая содержит объект текущей папки (или подкаста) базы данных программы - т.е. нашего текущего подкаста.
Список доступных переменных можно посмотреть в справочнике переменных и функций, выдвинув панель справа.
Также можно там увидеть, что тип переменной FolderItem - THmsScriptMediaItem.
Также в том же справочнике можно включить отображение классов и найти данный класс, посмотреть какие методы и свойства он имеет. Там то мы и увидим, что у данного класса есть метод DeleteChildItems, которые удаляет подчинённые элементы.
Вот и всё. Пользуясь справочником переменных и функций можно посмотреть какие функции мы можем использовать.
Далее привожу скрипт получения списка ресурсов - он у нас будет загружать плейлист, искать там ссылки и наименования и создавать эти ссылки:
int gnTotal=0;
// ----------------------------------------------------------------------------
void LoadPlaylist() {
string sData, sLink, sName, sImg; THmsScriptMediaItem Item;
// Если ссылка плейлиста начинается на http, то скачиваем, если нет - загружаем как локальный файл
if (LeftCopy(mpFilePath, 4)=='http') sData = HmsDownloadUrl(mpFilePath, '', true); // Загрузка плейлиста из интернета
else sData = HmsStringFromFile(mpFilePath); // Загрузка плейлиста с локального диска
sData = HmsUtf8Decode(sData); // Декодируем из UTF-8 кодировки
// Создаём объект для поиска по регулярному выражению (в C++Script символ \ экранирует, поэтому двойной)
TRegExpr RE = TRegExpr.Create('#EXTINF:(.*?)[\\r\\n]+(.*?)([\\r\\n]|$)');
try {
if (RE.Search(sData)) do { // Запускаем поиск и создаём цикл, пока найдены совпадения
sName = Trim(RE.Match(1)); // Получаем значение первой группировки регулярного выражения (первая строка после #EXTINF:)
sLink = Trim(RE.Match(2)); // Получаем значение второй группировки регулярного выражения (вся вторая строка)
// В наименовании берём только ту часть, которая идёт после последней запятой
HmsRegExMatch('.*,(.*)', sName, sName);
// Пытаемся получить ссылку на картинку из служебного тега tvg-logo, если нету - то своё значение
if (!HmsRegExMatch('tvg-logo="(.*?)"', RE.Match, sImg)) sImg = 'http://wonky.lostcut.net/TVimg/'+HmsPercentEncode(sName, '"A-Z", "a-z", "0-9", "-", ".", "_", "~", "\'"')+'.png';
// Создаём ссылку
Item = HmsCreateMediaItem(sLink, FolderItem.ItemID);
Item[mpiTitle ] = sName; // Наименование
Item[mpiThumbnail] = sImg; // Картинка
gnTotal++; // Увеличиваем счетчик созданных ссылок
} while (RE.SearchAgain()); // Повторяем цикл, пока найдены следующие совпадения
} finally { RE.Free(); } // Освобождаем объект из памяти, что бы ни случилось
HmsLogMessage(1, mpTitle+': создано ссылок '+IntToStr(gnTotal));
}
///////////////////////////////////////////////////////////////////////////////
// Г Л А В Н А Я П Р О Ц Е Д У Р А //
// ----------------------------------------------------------------------------
{
FolderItem.DeleteChildItems(); // Удаляем созданные ранее ссылки
LoadPlaylist(); // Вызов процедуры загрузки плейлиста и создания из него ссылок
}
Var
gnTotal: Integer=0; // Глобальная переменная счётчика созданных ссылок
// ----------------------------------------------------------------------------
Procedure LoadPlaylist();
Var
sData, sLink, sName, sImg: String; Item: THmsScriptMediaItem; RegExp: TRegExpr;
Begin
// Если ссылка плейлиста начинается на http, то скачиваем, если нет - загружаем как локальный файл
If LeftCopy(mpFilePath, 4)='http' Then sData := HmsDownloadUrl(mpFilePath, '', true) // Загрузка плейлиста из интернета
Else sData := HmsStringFromFile(mpFilePath); // Загрузка плейлиста с локального диска
sData := HmsUtf8Decode(sData); // Декодируем из UTF-8 кодировки
// Создаём объект для поиска по регулярному выражению
RegExp := TRegExpr.Create('#EXTINF:(.*?)[\r\n]+(.*?)([\r\n]|$)');
Try
If RegExp.Search(sData) Then Repeat Begin
sName := Trim(RegExp.Match(1)); // Получаем значение первой группировки регулярного выражения (первая строка после #EXTINF:)
sLink := Trim(RegExp.Match(2)); // Получаем значение второй группировки регулярного выражения (вся вторая строка)
// В наименовании берём только ту часть, которая идёт после последней запятой
HmsRegExMatch('.*,(.*)', sName, sName);
// Пытаемся получить ссылку на картинку из служебного тега tvg-logo, если нету - то своё значение
If Not HmsRegExMatch('tvg-logo="(.*?)"', RegExp.Match, sImg) Then sImg := 'http://wonky.lostcut.net/TVimg/'+HmsPercentEncode(sName, '"A-Z", "a-z", "0-9", "-", ".", "_", "~", "''"')+'.png';
// Создаём ссылку
Item := HmsCreateMediaItem(sLink, FolderItem.ItemID);
Item[mpiTitle ] := sName; // Наименование
Item[mpiThumbnail] := sImg; // Картинка
Inc(gnTotal); // Увеличиваем счетчик созданных ссылок
End Until Not RegExp.SearchAgain; // Повторяем цикл, пока будем находить по регулярке
Finally
RegExp.Free; // Чтобы ни случилось - освобождаем объект из памяти
End;
// Вывод сообщения в лог окно программы
HmsLogMessage(1, mpTitle+': создано ссылок '+IntToStr(gnTotal));
End;
///////////////////////////////////////////////////////////////////////////////
// Г Л А В Н А Я П Р О Ц Е Д У Р А //
// ----------------------------------------------------------------------------
Begin
FolderItem.DeleteChildItems(); // Удаляем созданные ранее ссылки
LoadPlaylist(); // Вызов процедуры загрузки плейлиста и создания из него ссылок
End.
Все пояснения я дал в комментариях кода. Если будут какие-то вопросы или захочется уточнить по работе какой-либо функции или будет вопрос как сделать что-то - милости прошу, задавайте вопросы.
Как только мы сохраним данный текст как скрипт получения списка ресурсов, обновим подкаст - мы можем увидеть список созданных ссылок из него.
Но это ещё не всё. По-умолчанию, программа думает, что в поле "Ссылка" созданных элементов находится ссылка на видео файл. Но на самом деле там ссылка на HLS поток. Поэтому мы должны программе подсказать, как это воспроизвести.
На самом деле, профили транскодирования "Фильм (основной)" и его производные умеют понимать HLS поток. Только нужно указать, что бы программа использовала файл HLS потока как параметр входящего файла.
Сделать это не просто легко, а очень.
Достаточно в скрипте получения ссылки присвоить встроенной переменной MediaResourceLink значение на ссылку потока, только в начале поставить пробел.
Скрипт получения ссылки на медиа-ресурс будет у нас такой:
C++Script:
{
MediaResourceLink = ' '+mpFilePath;
}
PascalScript:
///////////////////////////////////////////////////////////////////////////////
// Г Л А В Н А Я П Р О Ц Е Д У Р А //
// ----------------------------------------------------------------------------
Begin
MediaResourceLink := ' '+mpFilePath;
End.
P.S.: Как выяснилось, необязательно ставить пробел перед значением ссылки для присвоения значения переменной MediaResourceLink в случае когда это ссылка на плейлист m3u8 потока HLS.
Скрипт, который просто присваивает значение mpFilePath как есть переменной MediaResourceLink равносилен отключению вообще использования скрипта получения ссылки. Так что тут можно экспериментировать и пробовать отключать этот скрипт.
Также, видимо, с разными устройствами немного по-разному ведут себя профили транскодирования с подобными потоками HLS. Например в прикреплённом подкасте выставлено использование профиля "Фильмы (основной) - FFMPEG" (которому нужен файл ffmpeg.exe в папке "Transcoders" программы).
Этот файл можно взять здесь.
ffmpeg-2.7-win32-static.7z
У меня на телевизоре Sony обычный профиль "Фильмы (основной)" умеет проигрывать файлы HLS, но, почему-то, очень долго думая перед стартом.
Профиль "Фильмы (основной) - FFMPEG" полностью повторяет встроенный профиль "Фильмы (основной)", только использует ffmpeg, вместо hmsmpeg.
Для других устройств желательно попробовать в подкасте сменить профиль на стандартный или другой, который будет у вас работать лучше.
Вот и всё. Теперь наш подкаст будет уметь воспроизводить ссылки на m3u8 плейлисты HLS потока.
Прикрепил рабочий созданный подкаст. Если что, задавайте вопросы.