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