<%pre>

#include <livefeatures.h>
#include <setup.h>
#include <ffmpeg.h>
#include <tools.h>
#include <epg_events.h>
#include <recman.h>
#include <timers.h>

using namespace vdrlive;

</%pre>
<%args>
  int channel = -1;
  std::string channelid;
  std::string recid;
</%args>
<%session scope="global">
  bool logged_in(false);
  std::string prev_recording;
  std::string next_recording;
  FFmpegThread *f_worker = nullptr;
</%session>
<%request scope="global">
  tChannelID channelID;
  int channelNumber;
  std::string g_recid;
</%request>
<%include>page_init.eh</%include>
<%cpp>
  if (!logged_in && LiveSetup().UseAuth()) {
    cToSvConcat targetUrl = "/login.html?redirect=";
    targetUrl.appendUrlEscaped(request.getQuery());
    return reply.redirect(targetUrl.data());
  }

  pageTitle = trVDR("Stream");

  // check for session cookie.
  std::string session;
  if (request.hasCookie("tntnet")) {
    session = request.getCookie("tntnet");
    dsyslog("vdrlive::stream::session(%s)", session.c_str());
  }
  else {
    esyslog("vdrlive::stream: no session cookie found");
    return reply.redirect("");
  }

  std::string url;
  int channelVtype = 0;
  if ( qparam.has("recid") ) {
    {
      g_recid = recid;
      LOCK_RECORDINGS_READ;
      const cRecording* Recording = RecordingsManager::GetByHash(recid, Recordings);
      if (!Recording)
        throw HtmlError( tr("Couldn't find recording or no recordings available. Maybe you mistyped your request?") );
      struct stat st;
      const char* filename = Recording->FileName();
      if ( filename && stat( filename, &st ) == 0  ) {
        char buffer[50];  // suitable for accommodating url with 2x ULLONG_MAX
        int length = snprintf( buffer, sizeof( buffer ), "%lu:%llu.rec", ( unsigned long )st.st_dev, ( unsigned long long )st.st_ino );
        if ( 0 < length && length < ( int )sizeof( buffer ) ) {
          url += buffer;
        }
      }
      channelID = Recording->Info()->ChannelID();
    }
    LOCK_CHANNELS_READ;
    const cChannel* Channel = Channels->GetByChannelID( channelID );
    if ( !Channel )
      throw HtmlError( tr("Couldn't find channel for recording.") );
    channelNumber = Channel->Number();
    channelVtype = Channel->Vtype();
  } else {
    const cChannel* Channel = nullptr;
    LOCK_CHANNELS_READ;
    if ( qparam.has("channelid") ) {
      channelID = channelID.FromString(channelid.c_str());
      Channel = Channels->GetByChannelID( channelID );
    } else if ( channel > 0 ) {
      Channel = Channels->GetByNumber( channel );
    } else if (cDevice::CurrentChannel()) {
      Channel = Channels->GetByNumber(cDevice::CurrentChannel());
    } else {
      Channel = Channels->Get( Channels->GetNextNormal( -1 ) );
    }
    if ( !Channel )
      throw HtmlError( tr("Couldn't find channel or no channels available. Maybe you mistyped your request?") );
    channelID = Channel->GetChannelID();
    channelNumber = Channel->Number();
    channelVtype = Channel->Vtype();
    channelid = *channelID.ToString();
    url += channelid;
  }

  url += ".";
  for ( auto c : LiveSetup().GetStreamdevType() )
    url += std::tolower( c );
  dsyslog("vdrlive::stream::vtype(%d)", channelVtype);
  dsyslog("vdrlive::stream::f_worker(%p)", f_worker);
  if ( !f_worker) {
    f_worker = new FFmpegThread();
    dsyslog("vdrlive::stream: new FFmpegThread created");
  }

  switch(channelVtype) {
    case 27: // h264
      f_worker->StartFFmpeg(session, url, qparam.has("recid") ? tagRecordingH264 : tagChannelH264);
      break;
    case 36: // h265
      f_worker->StartFFmpeg(session, url, qparam.has("recid") ? tagRecordingHVEC : tagChannelHVEC);
      break;
    case 2: // mpeg2
      f_worker->StartFFmpeg(session, url, qparam.has("recid") ? tagRecordingMPG2 : tagChannelMPG2);
      break;
    default: // others
      f_worker->StartFFmpeg(session, url, qparam.has("recid") ? tagRecordingDFLT : tagChannelDFLT);
  }
  dsyslog("vdrlive::stream::StartFFmpeg(%s)", url.c_str());

</%cpp>
<# <& pageelems.doc_type &> #>
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>VDR Live - <$ pageTitle $></title>
    <& pageelems.stylesheets &>
    <& pageelems.ajax_js &>
    <& pageelems.create_html_js &>
    <script> window.onbeforeunload = function() { player.stop(); } </script>
  </head>
  <body onpagehide="saveScrollPosition('content')" onpageshow="restoreScrollPosition()">
    <& pageelems.logo &>
%  if ( qparam.has("recid") ) {
    <& menu active=("stream") component=("stream.recording_selection") &>
%  } else {
    <& menu active=("stream") component=("stream.channel_selection") &>
%  }
    <div id="content">
      <div class="spacebar"><# spacer with fade-out effect #></div>
      <div class="player-container">
      <div id="player" class="player">

        <script src="https://cdn.jsdelivr.net/npm/clappr@latest/dist/clappr.min.js"></script>
        <script>
        Clappr.Log.setLevel(Clappr.Log.LEVEL_INFO);
        var player = new Clappr.Player({
          source: "media/<$ session $>/master_<$ url $>.m3u8",
          parentId: "#player",
          width: "100%", height: "100%",
          autoPlay: "true",
          events: {
            onError: function(error) { console.log('Calppr.Player error: ', error); player.play(); },
            onEnded: function() { console.log('Clappr.Player ended'); player.play(); },
          }
        });
        </script>
      </div>
      </div>
<%cpp>
if (qparam.has("recid")) {
dsyslog("vdrlive::stream: generating recording info");
RecordingsItemRec * rPtr = RecordingsManager::GetByIdHash(recid);
cToSvConcat<0> recording_item;
rPtr->AppendAsJSArray(recording_item);
</%cpp>
      <table class="listing streaming recording" cellspacing="0" cellpadding="0">
        <tbody>
          <script>
            // column span 0 indicates discrding the label
            document.write(existingRecordingString(0, "", <$$ recording_item.c_str() $>))
          </script>
        </tbody>
      </table>
<%cpp>
} else {
dsyslog("vdrlive::stream: generating EPG info");
const cChannel* Channel = nullptr;
const cEvent* Event = nullptr;
EpgInfo epgEvent;
{
  LOCK_CHANNELS_READ;
  Channel = Channels->GetByChannelID(channelID);
  if (Channel) {
    LOCK_SCHEDULES_READ;
    const cSchedule *Schedule = Schedules->GetSchedule(Channel);
    if (Schedule) {
      Event = Schedule->GetPresentEvent();
      if (Event) epgEvent.CreateEpgInfo(Channel, Event);
    }
  }
}
if (!epgEvent.Id().empty() ) {
  std::string startTime(epgEvent.StartTime(tr("%I:%M %p")));
  std::string endTime(epgEvent.EndTime(tr("%I:%M %p")));

  std::string startDate(epgEvent.StartTime(tr("%a, %x")));
  std::string timeSpan(startTime + tr(" - ") + endTime);
  if (startTime.empty() && endTime.empty()) {
    char const * timeFormat = tr("%I:%M %p");
    char const * dateFormat = tr("%A, %x");
    timeSpan = std::string(cToSvDateTime(timeFormat, time(0)));
    startDate = std::string(cToSvDateTime(dateFormat, time(0)));
  }
  cToSvConcat longDescription;
  longDescription << "<p>";
  AppendHtmlEscapedAndCorrectNonUTF8(longDescription, StringWordTruncate(epgEvent.LongDescr(), LiveSetup().GetMaxTooltipChars()), "</p><p>");
  longDescription << "</p><p class=\"click-details\">";
  AppendHtmlEscapedAndCorrectNonUTF8(longDescription, tr("Click to view details"));
  longDescription << "</p>";

  bool timerRecording = false;
  if (Channel && Event) {
    LOCK_TIMERS_READ;
    const cTimer* timer = TimerManager().GetTimer(Event, Channel, Timers);
    timerRecording = timer && timer->Recording();
  }
  </%cpp>
  <table class="listing streaming channel" cellspacing="0" cellpadding="0">
    <tr class="<? timerRecording ? "recording" ?>">
      <td class="leftcol action">
        <div class="event_actions">
          <& pageelems.epg_tool_box detail=(0) epgid=(epgEvent.Id()) title=(epgEvent.Title()) startTime=(epgEvent.GetStartTime()) endTime=(epgEvent.GetEndTime()) lastCurrentChanel=(0) &>
        </div>
      </td>
      <td class="topaligned">
        <div class="withmargin">
          <div class="nomargin nowrap"><$ (timeSpan) $></div>
          <div class="duration"><div class="progress"><& pageelems.progressbar progress=(epgEvent.Elapsed()) duration=(epgEvent.Duration()) &></div></div>
        </div>
      </td>
      <td class="topaligned more">
        <div class="withmargin">
        <a
%             if (!longDescription.empty()) {
            <& tooltip.hint text=(longDescription) &><& tooltip.display domId=(epgEvent.Id()) &>
%             }
          ><span class="title"><$ (epgEvent.Title()) $></span><br /><span class="short"><$ (epgEvent.ShortDescr()) $></span></a>
        </div>
      </td>
      <td class="topaligned rightcol"><div class="station withmargin"><a  href="schedule.html?channelid=<$ channelid $>" <& tooltip.hint text=(tr("View the schedule of this channel")) &>><$ cSv(cToSvInt(channelNumber)) $><$ tr(" - ") $><$ (epgEvent.Caption()) $></a></div>
      </td>
    </tr>
  </table>
%} // if (!epgEvent.Id().empty() )
%} // if ( qparam.has("recid")
      <div class="spacebar"><# spacer with fade-out effect #></div>
    </div>
  </body>
</html>
<%include>page_exit.eh</%include>

<%def channel_selection>
<form action="stream.html" method="get" id="channels">
  <span>
    <label for="channel"><$ tr("Channel") $>:&nbsp;<span class="bold"><$ cSv(cToSvInt(channelNumber)) $></span></label>
    <& channels_widget name=("channel") selected=(*channelID.ToString() ) onchange=("player.stop(); document.forms.channels.submit()") &>
%   if (cDevice::PrimaryDevice() && cDevice::PrimaryDevice()->HasDecoder() ) {
      <& pageelems.ajax_action_href action="switch_channel" tip=(tr("Switch to this channel")) param=(channelID) image="zap.png" className="icon" alt="" &>
%   }
  </span>
</form>
<%cpp>
    int maxChannelNumber;
    {  LOCK_CHANNELS_READ;
       maxChannelNumber = Channels->MaxNumber();
    }
    bool prev = channelNumber > 1;
    bool next = channelNumber < maxChannelNumber;
</%cpp>
<span class="sep">|</span>
<?? prev ? "<a href=\"stream.html?channel=" << channelNumber - 1 << "\">" ?>«&nbsp;<$ tr("Prev. entry") $><?? prev ? "</a>" ?>
<span class="sep">|</span>
<?? next ? "<a href=\"stream.html?channel=" << channelNumber + 1 << "\">" ?><$ tr("Next entry") $>&nbsp;»<?? next ? "</a>" ?>
</%def>

<%def recording_selection>
<form action="stream.html=" method="get" id="recordings">
  <span>
    <label for="recording"><$ tr("Recording") $>:&nbsp;</label>
    <& recordings_widget name=("recid") selected=(g_recid) onchange=("player.stop(); document.forms.recordings.submit()") &>
  </span>
</form>
<span class="sep">|</span>
<?? !prev_recording.empty() ? "<a href=\"stream.html?recid=recording_" << prev_recording << "\">" ?>«&nbsp;<$ tr("Prev. entry") $><?? !prev_recording.empty() ? "</a>" ?>
<span class="sep">|</span>
<?? !next_recording.empty() ? "<a href=\"stream.html?recid=recording_" << next_recording << "\">" ?><$ tr("Next entry") $>&nbsp;»<?? !next_recording.empty() ? "</a>" ?>
</%def>
