PHP скрипт для формирования EPG в формате JTV

1,194 views
Skip to first unread message

Alexander Novoselov

unread,
Oct 31, 2013, 1:32:30 AM10/31/13
to stalker-m...@googlegroups.com
Всем привет!

  Накидал скрипт для формирования EPG в JTV, улучшения приветствуются, это мой первый опыт в PHP. Из того что можно улучшить:
  1. Хотелось бы узнать откуда можно получить текущую временную зону - из системы или можно узнать из сталкера. (заменить магическое число 25200 = 7*60*60)
  2. Не обязательно, получать таблицу itv целиком, но как это сделать я не разобрался.
  3. Результат работы скприпта будет располагаться в /var/tmp , хотелось бы, что бы скрипт чистил за собой место и отдавал созданный файл, но знаний не хватает.
  Что бы скрипт заработал, надо в custom.ini добавить переменную enable_jtv_file = true. Сам скрипт надо скопировать в /stalker_portal/server/tools/ и добавить его в файл .htaccess 
 

<?php
set_time_limit
(0);

ob_start
();

include
"../common.php";

if (!Config::getSafe('enable_jtv_file', false)){
    header
("HTTP/1.0 404 Not Found");
   
exit;
}

// Путь для создания файлов формата JTV
$temp_path
= "/var/tmp/";

$jtv_zip
= new ZipArchive();
if ($jtv_zip->open ($temp_path."jtv.zip", ZipArchive::CREATE) != true)
{
 
exit ("Can not open jtv.zip\n");
}

// Выбрали все каналы, для которых есть программа
$channels
= Mysql::getInstance()->from('itv')->where(array('status' => 1, 'xmltv_id !=' => ''))->orderby('number')->get()->all();

foreach ($channels as $channel)
{
 
// Смещение на начало данных программы
  $offset
= 26;

 
// Получили все программы для текущего канала
  $channel_epgs
= Mysql::getInstance()->from('epg')->where(array('ch_id' => $channel['id']))->orderby('ch_id')->get()->all();

  $channel
['name'] = str_replace (array(' ', '.', ','), array('_', '_', '_'), $channel['name']);

 
// Получили имя файла для текущего канала
  $channel_filename
= $channel['name'];
  $pdt_filename
= $channel_filename.".pdt";
  $ndx_filename
= $channel_filename.".ndx";

 
// Открыли файлы для записи
  $pdt_file
= fopen ($temp_path.$pdt_filename, "wb");
  $ndx_file
= fopen ($temp_path.$ndx_filename, "wb");

 
// Записали заголовок файла *.pdt
  fwrite
($pdt_file, "JTV 3.x TV Program Data\x0A\x0A\x0A", $offset);

 
// Записали количество программ для текущего канала
  fwrite
($ndx_file, pack ("v", count ($channel_epgs)), 2);

 
foreach ($channel_epgs as $channel_epg)
 
{
   
// Записали заголовок записи
    fwrite
($ndx_file, pack ("v", 0), 2);

   
// Готовим время и дату начала передачи для записи в файл
    $filetime
= bcmul (bcadd (strtotime ($channel_epg['time']) + 25200, "11644473600"), "10000000");
   
for ($i = 0; $i < 8; $i++)
   
{
      $byte
= (int) bcmod ($filetime, "256");
      fwrite
($ndx_file, pack("c", $byte), 1);
      $filetime
= bcdiv ($filetime, "256");
   
}

   
// Записали смещение - указатель на длину названия передачи в файле *.pdt
    fwrite
($ndx_file, pack("v", $offset), 2);

   
// Получили длину названия передачи
    $epg_name_length
= strlen (iconv ("UTF-8", "windows-1251", $channel_epg['name']));

   
// Записали длину названия передачи
    fwrite
($pdt_file, pack("v", $epg_name_length), 2);

   
// Записали название передачи
    fwrite
($pdt_file, iconv ("UTF-8", "windows-1251", $channel_epg['name']), $epg_name_length);

    $offset
+= $epg_name_length + 2;
 
}

 
// Сбросили буфера
  fflush
($pdt_file);
  fflush
($ndx_file);

 
// Закрыли файлы
  fclose
($pdt_file);
  fclose
($ndx_file);
 
 
// Добваили файлы в архив

  $jtv_zip
->addFile ($temp_path.$ndx_filename, iconv ("UTF-8", "CP866", $ndx_filename));
  $jtv_zip
->addFile ($temp_path.$pdt_filename, iconv ("UTF-8", "CP866", $pdt_filename));



}

$jtv_zip
->close ();

?>



JDVU

unread,
Oct 31, 2013, 6:11:23 AM10/31/13
to stalker-m...@googlegroups.com
есть же уже готовые JTV

Aleksey Zhurbitsky

unread,
Oct 31, 2013, 9:28:20 AM10/31/13
to stalker-m...@googlegroups.com
1. Можно попробовать 
$timezone = new DateTimeZone(date_default_timezone_get());
$offset
= $timezone->getOffset(new DateTime()); //смещение в секундах от UTC

2. Тут в принципе ничего плохого нет, но можно и так
$channels = Mysql::getInstance()->from('itv')->where(array('status' => 1, 'xmltv_id !=' => ''))->orderby('number')->get();
while ($channel = $channels->next()){
   
...
}

Alexander Novoselov

unread,
Oct 31, 2013, 10:24:49 PM10/31/13
to stalker-m...@googlegroups.com
Да, есть. Но из сталкера уже выровненное по времени и те каналы, которые нужны точно есть.

четверг, 31 октября 2013 г., 17:11:23 UTC+7 пользователь JDVU написал:
есть же уже готовые JTV

Alexander Novoselov

unread,
Nov 1, 2013, 12:00:34 AM11/1/13
to stalker-m...@googlegroups.com
Спасибо, совет про таймзоны работает. А как перебирать каналы не так и важно, оставлю цикл foreach.

Вот новая версия скрипта. Из изменений:
  1. Таймзона определяется из настроек системы.
  2. Скрипт теперь отдаёт сформированный архив.
  3. Возможность задать имя получаемого файла через параметр name, например так "http://example.com/stalker_portal/server/tools/jtv.php?name=filename.ext", где filename.ext желаемое имя архива. В некоторых плеерах помогает получить программу, видать они завязаны на имя файла вроде "jtv.zip".
<?php
set_time_limit
(0);

ob_start
();

include
"../common.php";

if (!Config::getSafe('enable_jtv_file', false))
{
    header
("HTTP/1.0 404 Not Found");
   
exit;
}

// Проверяем переданный параметр с требуемым именем файла jtv
if (isset($_GET['name']) && !empty($_GET['name']))
{
 
// Получаем имя файла jtv из переменной
  $jtv_name
= $_GET['name'];
}
else
{
 
// Устанавливаем имя файла, если переменная пустая
  $jtv_name
= "jtv.zip";

}

// Путь для создания файлов формата JTV
$temp_path
= "/var/tmp/";

$jtv_zip
= new ZipArchive();
if ($jtv_zip->open ($temp_path.$jtv_name, ZipArchive::CREATE) != true)
{
 
exit ("Can not open ".$jtv_name." \n");
}

// Получили смещение времени в секундах для текущей временной зоны
$timezone
= new DateTimeZone(date_default_timezone_get());
$tz_offset
= $timezone->getOffset(new DateTime());


// Выбрали все каналы, для которых есть программа
$channels
= Mysql::getInstance()->from('itv')->where(array('status' => 1, 'xmltv_id !=' => ''))->orderby('number')->get()->all();

foreach ($channels as $channel)
{
 
// Смещение на начало данных программы
  $offset
= 26;

 
// Получили все программы для текущего канала
  $channel_epgs
= Mysql::getInstance()->from('epg')->where(array('ch_id' => $channel['id']))->orderby('ch_id')->get()->all();

  $channel
['name'] = str_replace (array(' ', '.', ','), array('_', '_', '_'), $channel['name']);

 
// Получили имя файла для текущего канала
  $channel_filename
= $channel['name'];
  $pdt_filename
= $channel_filename.".pdt";
  $ndx_filename
= $channel_filename.".ndx";

 
// Открыли файлы для записи
  $pdt_file
= fopen ($temp_path.$pdt_filename, "wb");
  $ndx_file
= fopen ($temp_path.$ndx_filename, "wb");

 
// Записали заголовок файла *.pdt
  fwrite
($pdt_file, "JTV 3.x TV Program Data\x0A\x0A\x0A", $offset);

 
// Записали количество программ для текущего канала
  fwrite
($ndx_file, pack ("v", count ($channel_epgs)), 2);

 
foreach ($channel_epgs as $channel_epg)
 
{
   
// Записали заголовок записи
    fwrite
($ndx_file, pack ("v", 0), 2);

   
// Готовим время и дату начала передачи для записи в файл

    $filetime
= bcmul (bcadd (strtotime ($channel_epg['time']) + $tz_offset, "11644473600"), "10000000");

   
for ($i = 0; $i < 8; $i++)
   
{
      $byte
= (int) bcmod ($filetime, "256");
      fwrite
($ndx_file, pack("c", $byte), 1);
      $filetime
= bcdiv ($filetime, "256");
   
}

   
// Записали смещение - указатель на длину названия передачи в файле *.pdt
    fwrite
($ndx_file, pack("v", $offset), 2);

   
// Получили длину названия передачи

    $programme_name
= iconv ("UTF-8", "windows-1251", $channel_epg['name']);
    $programme_name_length
= strlen ($programme_name);


   
// Записали длину названия передачи

    fwrite
($pdt_file, pack("v", $programme_name_length), 2);

   
// Записали название передачи
    fwrite
($pdt_file, $programme_name, $programme_name_length);

    $offset
+= $programme_name_length + 2;

 
}

 
// Сбросили буфера
  fflush
($pdt_file);
  fflush
($ndx_file);

 
// Закрыли файлы
  fclose
($pdt_file);
  fclose
($ndx_file);


  $jtv_zip
->addFile ($temp_path.$ndx_filename, iconv ("UTF-8", "CP866", $ndx_filename));

  $jtv_zip
->addFile ($temp_path.$pdt_filename, iconv ("UTF-8", "CP866", $pdt_filename));


 
//echo $ndx_filename;
}

$jtv_zip
->close ();

$jtv_path
= $temp_path.$jtv_name;


// Проверяем, что файл существует

if (file_exists ($jtv_path))
{
 
if (ob_get_level())
 
{
    ob_end_clean
();
 
}

 
// заставляем браузер показать окно сохранения файла
  header
('Content-Description: File Transfer');
  header
('Content-Type: application/zip');
  header
('Content-Disposition: attachment; filename='.basename($jtv_path));
  header
('Content-Transfer-Encoding: binary');
  header
('Expires: 0');
  header
('Cache-Control: must-revalidate');
  header
('Pragma: public');
  header
('Content-Length: '.filesize($jtv_path));

 
// читаем файл и отправляем его пользователю
  readfile
($jtv_path);
}

?>







четверг, 31 октября 2013 г., 20:28:20 UTC+7 пользователь Aleksey Zhurbitsky написал:

JDVU

unread,
Nov 1, 2013, 12:15:06 PM11/1/13
to stalker-m...@googlegroups.com
косяк выпадает если в названии канала есть спецсимволы
надо экранировать
http://pastebin.com/NFfMrGkC
и если ошибка встречается то уже файл не отдаст 

JDVU

unread,
Nov 1, 2013, 12:24:36 PM11/1/13
to stalker-m...@googlegroups.com
ну и для очистки в самый низ добавить
exec ('rm '.$temp_path.'*'); //с этим аккуратно :)

Alexander Novoselov

unread,
Nov 2, 2013, 10:19:39 AM11/2/13
to stalker-m...@googlegroups.com
Можно список символов, которые надо заменять? Гугление на даёт правил для замены символов. Сейчас заменяются "пробел", "точка", "запятая" на "_".

пятница, 1 ноября 2013 г., 23:15:06 UTC+7 пользователь JDVU написал:

Alexander Novoselov

unread,
Nov 2, 2013, 10:22:07 AM11/2/13
to stalker-m...@googlegroups.com
Это прям как-то опасно. Появилась мысль хранить список созданных файлов, и после посыла архива удалять по списку. Сделаю и выложу. 

пятница, 1 ноября 2013 г., 23:24:36 UTC+7 пользователь JDVU написал:
Message has been deleted

JDVU

unread,
Nov 4, 2013, 3:16:33 AM11/4/13
to stalker-m...@googlegroups.com
зачем вам список
тогда уж совсем обезопаситься, то удалять только файлы по расширению
exec ('rm '.$temp_path.'*.pdt');
exec ('rm '.$temp_path.'*.ndx');

Alexander Novoselov

unread,
Nov 4, 2013, 9:35:23 AM11/4/13
to stalker-m...@googlegroups.com
Нет гарантий, что кто-то еще не создаст файлов с такими же расширениями.

понедельник, 4 ноября 2013 г., 15:16:33 UTC+7 пользователь JDVU написал:

Alexander Novoselov

unread,
Nov 5, 2013, 12:00:45 AM11/5/13
to stalker-m...@googlegroups.com
Новая версия скрипта.

  Добавлены следующие возможности:
  1. Первым делом проверяется наличие и время создания файла программы JTV, если файл существует и с момента его создания прошло меньше суток - отдаётся этот файл. В противном случае файл создаётся заново.
  2. Все созданные *.pdt и *.ndx файлы после работы скрипта удаляются.


<?php
set_time_limit
(0);

ob_start
();

include
"../common.php";

if (!Config::getSafe('enable_jtv_file', false))
{
  header
("HTTP/1.0 404 Not Found");
 
exit;
}

function SendFile($filepath)
{
 
if (file_exists ($filepath))

 
{
   
if (ob_get_level())
   
{
      ob_end_clean
();
   
}

   
// заставляем браузер показать окно сохранения файла
    header
('Content-Description: File Transfer');
    header
('Content-Type: application/zip');

    header
('Content-Disposition: attachment; filename='.basename($filepath));

    header
('Content-Transfer-Encoding: binary');
    header
('Expires: 0');
    header
('Cache-Control: must-revalidate');
    header
('Pragma: public');

    header
('Content-Length: '.filesize($filepath));


   
// читаем файл и отправляем его пользователю

    readfile
($filepath);

 
}
}

// Проверяем переданный параметр с требуемым именем файла jtv
if (isset($_GET['name']) && !empty($_GET['name']))
{
 
// Получаем имя файла jtv из переменной
  $jtv_name
= $_GET['name'];
}
else
{
 
// Устанавливаем имя файла, если переменная пустая
  $jtv_name
= "jtv.zip";
}

// Путь для создания файлов формата JTV
$temp_path
= "/var/tmp/";

// Проверяем наличие файла архива и дату его создания, если прошло меньше 24 часов - отдаём файл пользователю, иначе создаём новый
if (file_exists ($temp_path.$jtv_name) && filemtime ($temp_path.$jtv_name) > time() - (24 * 60 * 60))
{
 
SendFile ($temp_path.$jtv_name);
 
exit;
}

//Список файлов для удаления
$files_to_remove
= array();

$jtv_zip
= new ZipArchive();

if ($jtv_zip->open ($temp_path.$jtv_name, ZipArchive::CREATE) != true)
{
 
exit ("Can not open ".$jtv_name." \n");
}

// Получили смещение времени в секундах для текущей временной зоны
$timezone
= new DateTimeZone(date_default_timezone_get());
$tz_offset
= $timezone->getOffset(new DateTime());

// Выбрали все каналы, для которых есть программа
$channels
= Mysql::getInstance()->from('itv')->where(array('status' => 1, 'xmltv_id !=' => ''))->orderby('number')->get()->all();

foreach ($channels as $channel)
{
 
// Смещение на начало данных программы
  $offset
= 26;

 
// Получили все программы для текущего канала
  $channel_epgs
= Mysql::getInstance()->from('epg')->where(array('ch_id' => $channel['id']))->orderby('ch_id')->get()->all();

  $channel
['name'] = str_replace (array(' ', '.', ','), array('_', '_', '_'), $channel['name']);

 
// Получили имя файла для текущего канала
  $channel_filename
= $channel['name'];
  $pdt_filename
= $channel_filename.".pdt";
  $ndx_filename
= $channel_filename.".ndx";


 
// Добавили имена фалов в список на удаление
  $files_to_remove
[] = $pdt_filename;
  $files_to_remove
[] = $ndx_filename;


 
// Открыли файлы для записи
  $pdt_file
= fopen ($temp_path.$pdt_filename, "wb");
  $ndx_file
= fopen ($temp_path.$ndx_filename, "wb");

 
// Записали заголовок файла *.pdt
  fwrite
($pdt_file, "JTV 3.x TV Program Data\x0A\x0A\x0A", $offset);

 
// Записали количество программ для текущего канала
  fwrite
($ndx_file, pack ("v", count ($channel_epgs)), 2);

 
foreach ($channel_epgs as $channel_epg)
 
{
   
// Записали заголовок записи
    fwrite
($ndx_file, pack ("v", 0), 2);

   
// Готовим время и дату начала передачи для записи в файл

    $filetime
= bcmul (bcadd (strtotime ($channel_epg['time']) + $tz_offset, "11644473600"), "10000000");

   
for ($i = 0; $i < 8; $i++)
   
{
      $byte
= (int) bcmod ($filetime, "256");
      fwrite
($ndx_file, pack("c", $byte), 1);
      $filetime
= bcdiv ($filetime, "256");
   
}

   
// Записали смещение - указатель на длину названия передачи в файле *.pdt
    fwrite
($ndx_file, pack("v", $offset), 2);

   
// Получили длину названия передачи

    $programme_name
= iconv ("UTF-8", "windows-1251", $channel_epg['name']);
    $programme_name_length
= strlen ($programme_name);


   
// Записали длину названия передачи
    fwrite
($pdt_file, pack("v", $programme_name_length), 2);

   
// Записали название передачи
    fwrite
($pdt_file, $programme_name, $programme_name_length);

    $offset
+= $programme_name_length + 2;

 
}

 
// Сбросили буфера
  fflush
($pdt_file);
  fflush
($ndx_file);

 
// Закрыли файлы
  fclose
($pdt_file);
  fclose
($ndx_file);


  $jtv_zip
->addFile ($temp_path.$ndx_filename, iconv ("UTF-8", "CP866", $ndx_filename));

  $jtv_zip
->addFile ($temp_path.$pdt_filename, iconv ("UTF-8", "CP866", $pdt_filename));
}


$archive_closed
= $jtv_zip->close ();

SendFile ($temp_path.$jtv_name);

// Удаляем файлы по списку
foreach ($files_to_remove as $file_to_remove)
{
  unlink
($temp_path.$file_to_remove);
}

?>





суббота, 2 ноября 2013 г., 21:22:07 UTC+7 пользователь Alexander Novoselov написал:

JDVU

unread,
Nov 13, 2013, 7:12:56 AM11/13/13
to stalker-m...@googlegroups.com
не могу понять где косяк
все программы нормально конвертирует, кроме канала Enter-фiльм
на приставке полное соответствие, а на iptv player постоянно одна программа, и та вечна, смещение даже не помогает

Alexander Novoselov

unread,
Nov 13, 2013, 10:40:53 PM11/13/13
to stalker-m...@googlegroups.com
Не понятно что значит одна и та же программа - каждый день повторяются одни и те же передачи или же программа повторяется каждую неделю или каждый день в программе одна и та же передача?
Могу только предположить, что, возможно, эта проблема может быть связана с кодировками, этот момент был одним из самых сложных и не понятных, пришлось перебирать варианты, пока всё заработало.


среда, 13 ноября 2013 г., 19:12:56 UTC+7 пользователь JDVU написал:

JDVU

unread,
Nov 14, 2013, 3:22:55 AM11/14/13
to stalker-m...@googlegroups.com
вот скрин чтобы было понятно
как выглядит в сталкере, и как после конвертации
http://joxi.ru/PYaEUtg5CbBoF2FklDw

кстати раньше я конвертировал файлы программы таким образом, и вроде как ошибок не было

convmv --notest -r -f cp-1252 -t cp-850 /var/www/tvprog/temp/*
convmv --notest -f cp866 -t utf8 /var/www/tvprog/temp/*
convmv --notest -f utf8 -t cp866 /var/www/tvprog/tv/*

не спрашивайте почему именно так, не помню

Alexander Novoselov

unread,
Nov 14, 2013, 10:01:06 PM11/14/13
to stalker-m...@googlegroups.com
Мне посоветовали изменить строчку выборки программ для текущего канала на


$channel_epgs
= Mysql::getInstance()->from('epg')->where(array('ch_id' => $channel['id']))->orderby('time')->get()->all();

чтобы сортировка была по времени. Проверил у себя, на канале 2х2 была неверная программа, после сортировки по времени всё пришло в норму. Попробуй, думаю это поможет.

четверг, 14 ноября 2013 г., 15:22:55 UTC+7 пользователь JDVU написал:

JDVU

unread,
Nov 15, 2013, 5:52:09 AM11/15/13
to stalker-m...@googlegroups.com
на данный момент все отлично. будем тестировать дальше
Reply all
Reply to author
Forward
0 new messages