ulrich
5 hours ago b7d3c32dca2444b105e921489e4a5d53ba457c0e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
  Aufzeichnungsplaner
  Copyright (C) 2020  Ulrich Hilger, https://uhilger.de
 
  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.
 
  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
 
function App() {
  var self = this;
  var wochentag;
  var mone;
  var channels;
  var outPath;
  var senderliste;
 
  /**
   * Den Aufnahmeplaner initialisieren
   */
  this.init = function () {
    self.wochentag = ['Sonntag','Montag','Dienstag','Mittwoch',
      'Donnerstag','Freitag','Samstag' ];
    
    // Monatskuerzel in englisch fuer den 'at'-Befehl
    self.mone = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 
      'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
    
    self.senderliste =  new Array();
    /*
     * die folgenden Konstanten koennten auch ueber eine Konfigurationsdatei
     * oder eine API vom Server bereitgestellt werden
     */
    self.channels = "/media/extssd/mc/channels.conf";
    self.outPath = "/opt/tv/";
    
    self.channelsLesen();
    
    // Bedienelemente an Programmlogik binden
    var elem = self.elementAnbinden("startdatum");
    elem.valueAsDate = new Date();   
    self.elementAnbinden("bez");
    self.elementAnbinden("startzeit");    
    self.elementAnbinden("dauer");
    self.elementAnbinden("sender");
    
    /*
     * initial die Einstellungen einsammeln und 
     *  an der Bedienoberflaeche zeigen
     */
    //self.collectSettings();
  };
  
  /**
   * Ein Element aus dem Document Object Model (DOM) an das Eingabe-Ereignis 
   * (oninput) binden um auf diese Weise dessen geaenderten Inhalt in die 
   * Ausgabe des Infotextes zu uebernehmen
   * 
   * @param {type} ename  name des anzubindenden Elements
   * @returns {elem|Element}  das angebundene Element
   */
  this.elementAnbinden = function(ename) {
    elem = document.getElementById(ename);
    elem.oninput = self.collectSettings;
    return elem;
  };
  
  /*
   * collectSettings nimmt die aktuellen Einstellungen aus den Eingabeelementen 
   * und bildet eine Textausgabe aller aktuell eingegebenen Parameter 
   * sowie das Kommando fuer gnutv und gibt diese in den Elementen 
   * 'info' und 'cmd' aus.
   */
  this.collectSettings = function() {
    // das Startdatum bestimmen und in den Infotext uebernehmen
    var start = self.getStart();   
    var infotext = "<br>Start: " + self.wochentag[start.getDay()] + ", " 
            + self.datumZuDatumText(start) 
            + ", " + self.zeitZuText(start, ":") + " Uhr";
    
    // den Endezeitpunkt bestimmen und in den Infotext uebernehmen
    var ende = self.getEnde();
    var endzeit = self.wochentag[ende.getDay()] + ", " 
            + self.datumZuDatumText(ende) + ", " 
            + self.zeitZuText(ende, ":");
    infotext = infotext + "<br>Ende: " + endzeit + " Uhr";
    
    // die Dauer in Sekunden sowie in Minuten bestimmen und dem Infotext anfuegen
    var dauerSekunden = document.getElementById("dauer").value;
    infotext = infotext + "<br>Dauer: " + dauerSekunden / 60 + " Minuten" 
            + " (" + dauerSekunden + " Sekunden)";    
    
    /*
     * Die eingegebene Bezeichnung fuer die Aufnahme bestimmen und um 
     * Startdatum, -zeit und Sender ergaenzen und das Ganze in den Infotext
     * uebernehmen
     * Es wird '.ts' fuer 'transport stream' angefuegt, wenn keine 
     * Dateierweiterung in der Bezeichnung angegeben ist. Bei DVB wird der 
     * Inhalt ueblicherweise als 'transport stream' bezeichnet.
     */
    var bez = document.getElementById("bez").value;
    var dotpos = bez.indexOf(".");
    var fname = self.outPath;
    var senderElem = document.getElementById("sender");
    var sender = senderElem.options[senderElem.selectedIndex].text;
    
    var streamData = self.senderliste[senderElem.selectedIndex];
    var streamElems = streamData.split("|");
    var streamFreq = streamElems[1];
  
    
    if(dotpos > -1) {
      fname = fname + bez.substring(0, dotpos) + "-" + self.fts(start) + "-" 
              + sender + bez.substring(dotpos);
    } else {
      fname = fname + bez + "-" + self.fts(start) + "-" + sender + ".mp4";
    }
    infotext = infotext + "<br>" + fname; 
    
    // den Infotext im DOM-Element 'info' anzeigen
    document.getElementById("info").innerHTML = infotext;
    
    /*
     * im DOM-Element 'cmd' wird der Befehl zusammengstellt und angezeigt 
     * wie er fuer die Programmierung einer Aufzeichnung mit Hilfe von 
     * gnutv und at benoetigt wird.
     *
     * Beispiel:
     * echo "gnutv -channels /media/extssd/mc/channels.conf 
     * -out file /home/fred/work/test-2020-02-01-1050-arteHD.ts 
     * -timeout 300 arteHD" | at 1050 feb 01
     * 
     * echo "ffmpeg -i https://artesimulcast.akamaized.net/hls/live/2030993/artelive_de/index.m3u8 
     * -t 10800 -acodec copy -vcodec copy 
     * /home/ulli/tv/triangle-of-sadness-2024-11-17-2005-arte-HD.mp4" | at 2005 nov 17
     * 
     * 
     */
    /*document.getElementById("cmd").innerHTML = 'echo "gnutv -channels ' 
            + self.channels + ' -out file ' 
            + fname + ' -timeout ' + dauerSekunden + ' ' + sender 
            + '" | at ' + self.zeitZuText(start) + ' ' 
            + self.mone[start.getMonth()] + ' ' + start.getDate();*/
    document.getElementById("cmd").innerHTML = 'echo "ffmpeg -i ' 
            + streamFreq + ' -t ' + dauerSekunden + 
            ' -acodec copy -vcodec copy ' + fname + '" | at ' + self.zeitZuText(start) + ' ' 
            + self.mone[start.getMonth()] + ' ' + start.getDate();
            
            /*
            + ' -out file ' 
            + fname + ' -timeout ' + dauerSekunden + ' ' + sender 
            + '" | at ' + self.zeitZuText(start) + ' ' 
            + self.mone[start.getMonth()] + ' ' + start.getDate();
    */
  };
  
  /*
   * getEnde fuegt dem Startzeitpunkt aus getStart() die Dauer hinzu 
   * und bildet so den Endzeitpunkt, der als Datumsobjekt zurueckgegeben wird  
   */
  this.getEnde = function() {
    var endeZeitpunkt = self.getStart();
    endeZeitpunkt.setMilliseconds(endeZeitpunkt.getMilliseconds() 
            + (document.getElementById("dauer").value * 1000));
    return endeZeitpunkt;
  };
  
  /*
   * getStart setzt aus dem Datumseingabefeld und dem Schieberegler fuer die 
   * Startzeit den Startzeitpunkt zusammen und gibt diesen als Datumsobjekt
   * zurueck
   * 
   * Wenn das Datumseingabefeld kein Datum enthaelt, wird das heutige Datum 
   * mit der Zeit des Schiebereglers kombiniert.
   */  
  this.getStart = function() {
    var startdatum = document.getElementById("startdatum").valueAsDate;
    startdatum.setHours(0);
    startdatum.setMilliseconds(startdatum.getMilliseconds() 
            + (document.getElementById("startzeit").value * 1000));
    return startdatum;
  };
 
  /*
   * Die Sender aus der Datei channels.conf lesen und in eine 
   * HTML-Auswahlliste ueberfuehren
   */
  this.channelsLesen = function() {
    var url = 'channels.conf';
    self.http_get(url, function (antwort) {      
      var zeilen = antwort.split("\n");
      zeilen.sort();
      self.senderliste = zeilen;
      var senderAuswahl = document.getElementById("sender");
      var sender;
      var zElems;
      for(var i = 0; i < zeilen.length; i++) {
        zElems = zeilen[i].split("|");
        sender = document.createElement("option");        
        sender.textContent = zElems[0];
        sender.value = zElems[1];
        senderAuswahl.appendChild(sender);
      }      
    });
  };
  
  /**
   * Die Funktion http_get ruft einen Inhalt ueber HTTP GET ab 
   * 
   * @param {type} url der aufzurufende URL
   * @param {type} process  die Funktion, der die Antwort uebergeben 
   * werden soll
   */
  this.http_get = function (url, process) {    
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
      if (this.readyState == 4 && this.status == 200) {
        process(this.responseText);
      }
    };
    xmlhttp.open("GET", url, true);
    xmlhttp.send();
  };
 
  /* -----------------------------------------------------------------------
   * Nachfolgend einige Hilfsfunktionen zur Umwandlung von Datum-Objekten 
   * zu Text.
   * 
   * Solche Funktionen gibt es auch fertig in Bibliotheken wie 
   * z.B. Moment.js. Hier werden allerdings nur wenige solche 
   * Funktionen benoetigt so dass sich die zusaetzliche 
   * Abhaengigkeit nicht lohnt, die mit der Verwendung der Bibliothek 
   * entstuende.
   * ---------------------------------------------------------------------- */
  
  /*
   * Aus einem Datums-Objekt einen Text im Format JJJJ-MM-TT machen
   */
  this.datumZuText = function(datum) {
    var tag = self.textZweistellig(datum.getDate());
    var monat = self.textZweistellig(datum.getMonth() + 1);
    var jahr = datum.getFullYear();
    return jahr + "-" + monat + "-" + tag;
  };
  
  /*
   * Aus einem Datums-Objekt einen Text im Format TT.MM.JJJJ machen
   */
  this.datumZuDatumText = function(datum) {
    var tag = self.textZweistellig(datum.getDate());
    var monat = self.textZweistellig(datum.getMonth() + 1);
    var jahr = datum.getFullYear();
    return tag + "." + monat + "." + jahr;
  };
  
  /*
   * Aus einem Datums-Objekt einen Text im Format SS:MM machen
   * 
   * Das Trennzeichen, z.B. ":" kann im Parameter trennzeichen 
   * uebergeben werden. trennzeichen darf auch leer bleiben.
   */
  this.zeitZuText = function(datum, trennzeichen) {
    var std = self.textZweistellig(datum.getHours());
    var min = self.textZweistellig(datum.getMinutes());
    if(trennzeichen) {
      return std + trennzeichen + min;
    } else {
      return std + "" + min;
    }
  };
  
  /*
   * Aus einer Zahl einen Text machen und dabei einstellige 
   * Werte mit einer Null auffuellen
   */
  this.textZweistellig = function(zahl) {
    if(zahl < 10) {
      return "0" + zahl;
    } else {
      return zahl;
    }
  };
  
  /**
   * Aus einem Datum den Textausdruck JJJJ-MM-TT-hhmm machen
   * (fts: file name timestamp)
   * 
   * @param {type} datum   das Datumsobjekt aus dem der Text gemacht werden soll
   * @returns {String} der Text im Format JJJJ-MM-TT-hhmm
   */
  this.fts = function(datum) {
    return self.datumZuText(datum) + "-" + self.zeitZuText(datum);
  };  
}