Samstag, 14. Dezember 2013
Spontane Abstimmungen durchführen - mit kostenloses Online Tool TEDzi
Ja, für einige ist das ein Teil der Moderation. Für andere aber ein Hindernis.
AdHoc eine TED (Abstimmung) starten, das wäre jetzt nicht schlecht. Alle Anwesende verfügen über ein SmartPhone und Internet. Warum nicht dies nutzen?
Ob Hochschule oder private "Wer wird Millionär"-Runde. Ein eigenes TED-System ist nützlich. Bei www.tedzi.com kann man ohne Registrierung eine TED-Session eröffnen. Die Teilnehmer verbinden sich über den TED-Session Code, der dem Moderator angezeigt wird.
Man kann beliebig viele TEDs starten. Das geht total einfach. Mit dem Daumen auf dem Smartphone, aber auch im Browser. Die Webseite ist für das Display eines Smartphones optimiert und damit muss man auch keine App installieren.
Die Optionenwahl bietet alles:
- Ja, Nein, Vielleicht, Enthaltung
- Option A..F
- Werte 1..12
Die Abstimmung kann im Singlemode laufen, also nur eine Wahlmöglichkeit anwählbar für die Teilnehmer, oder auch Multiplichoice TEDs sind möglich. Die Entscheidung kann jederzeit im laufenden TED mit einem Klick geändert werden.
Die Benutzerführung ist sehr aufgeräumt und intuitiv. Schön ist auch die Möglichkeit die Auswertung in der laufenden Abstimmung an alle Teilnehmer live zu senden. Damit kann man sich leichter für eine Option entscheiden, wenn es um einen Termin oder eine Location geht.
Probiert es aus:
www.tedzi.com
Sonntag, 8. September 2013
Schluss mit der unmenschlichen Politik in Deutschland
Die Regierung besteht nur aus bestochenen und geschmierten Politikern. Der Wähler wird nicht gefragt und belogen solange es geht und darüber hinaus.
Bestechungen und Lobbytum ist sehr einfach. In Deutschland wird die Macht auf wenige nicht direkt gewählte Personen verteilt. Ihr Handeln ist durch Immunität geschützt.
Deutschland hatte genug Führer. Wir brauchen Volksvertreter. Politiker sollten Verwaltungsbeamte sein, die den Volkswillen umsetzen. Dieser Wille kann heute mit den neuen Mitteln sicher und zuverlässig ermittelt werden. Politiker sollten die von der Mehrheit erwünschten Themen gesetzeskonform formulieren und Umsetzung verantworten.
Der egoistische Wille eines Einzelnen sollte nicht unser Leben bestimmen. Es gibt immer wieder Menschen, die Wähler als unmündig und als Untermenschen ansehen, welche nicht richtig entscheiden könnten und die Tatsachen nicht verstehen würden. Diese selbsternannten Übermenschen wollen uns vor uns selbst schützen.
Genau dies findet in Deutschland statt. Wir sollten dieses Verhalten beenden. Alle Macht der echten und wahren Demokratie. Ich bin für die direkte Demokratie und die Abschaffung der über bezahlten Lobby-Politiker und das marode Anti-Demokratie System.
Dienstag, 27. August 2013
Scrum for One - Scrum alleine einsetzen Teil 3
Hier möchte ich meinen Weg aufzeigen. Mit den Scrum Mittel kann sich jeder sein eigenes System erstellen.
Startphase
Release-Planung
Schätzung der Userstories
Sprint vorbereiten
Zeitschätzung jederzeit möglich
Sprint machen
Daily Scrum
Sprint Review
Sprint Retrospektive
Fazit
Freitag, 16. August 2013
Scrum for One - Scrum alleine einsetzen Teil 2
Definition of Done
Daily Scrum
- Was habe ich seit der letzten Besprechung erreicht?
- Was wird bis zur nächsten Besprechung erledigt?
- Welche Hindernisse stehen im Weg?
- Sprint Backlog
- Sprint Burndown
- Impediment Backlog (Hindernisse)
Sprint Burndown
Inkrement
Sprint Review
Sprint Retrospektive
Grooming
Release Burndown
Ähnlich wie der Sprint Burndown wird auch der Release Burndown erstellt. Vor jedem Sprint trägt der Product Owner die zu erreichenden Storypoints ein. Auf der vertikalen Achse steht die verbleibenden Arbeit für das Release. Auf der horizontalen Achse stehen die Sprints. Damit kann die Abarbeitungsgeschwindigkeit ermittelt werden. Je mehr Sprints gemacht wurden, desto genauer wird die Zeitplanung.
Im Teil 3 folgt:
Umsetzung von Scrum mit nur einer Person
Montag, 12. August 2013
Scrum for One - Scrum alleine einsetzen Teil 1
Scrum Zyklen
Scrum Team besteht aus mehreren Rollen
Alle Scrum Rollen in Einer
Ohne Disziplin geht Scrum nicht
Anforderungen einsammeln bevor Scrum beginnt
Wie erstellt man Userstories?
- Wer möchte welches Feature, damit Was erreicht wird
- Als Nutzer will ich Ziel/Wunsch damit Nutzen entsteht
- Als User benötige ich eine Kontoübersicht, damit ich meinen Kontostand einsehen kann
- Als Administrator benötige ich eine Liste mit geblockten Accounts, um schneller einen Überlick zu erhalten
- Der Service benötigt eine Backup-Option, um Datensicherungen von Kunden erstellen zu können
Die Userstory erklärt auch den Grund für das Bedürfnis.
Damit kann der Entwickler besser verstehen, was mit diesem Feature erreicht werden soll. So ist er in der Lage die erwartete Funktionalität optimaler umzusetzen. Es entstehen weniger Missverständnisse.Product Backlog von Scrum
Product RoadMap bietet Übersicht des kompletten Produkts
Die Product RoadMap enthält das Product Backlog. Die Backlog Einträge werden hier in Pakete gepackt. Diese Packet entsprechen den Versionen des Products. Jedes Packet kann in mehrere Bereiche unterteilt werden. In diese Bereiche kommen die Product Backlog Einträge. Damit kann man schnell erkennen, was das Produkt wann können wird. Dabei zeichnet man am Besten eine Kreuztabelle.Beispiel:
Nutzerverwaltung | Verkauf | ... | |
Skeleton | Kunden erstellen, Löschen, Bearbeiten Kunden listen |
... | |
Version 1 | Kunden sperren | Newsletter erfassen Newsletter versenden Shop verlinken, Affiliate tracken |
... |
... |
Scrum Sprint
Scrum Sprint Planung 1
Scrum Sprint Backlog
Scrum Sprint Ziel
Scrum Sprint Planung 2
Die Einträge aus dem Sprint Backlog werden in kleinere Tasks umgeschrieben und entsprechend in der Umsetzungsreihenfolge erfasst. Durch diese Übersicht, verliert man das Ziel nicht aus den Augen und verhindert, dass man sich in kleine Aufgaben verzettelt. Auch werden so nicht immer wieder neue Baustellen begonnen. Die Vorbereitung der umzusetzenden Aufgaben ist sehr wichtig.Im Teil 2 folgt:
Definition of Done
Daily Scrum
Sprint Burndown
Inkrement
Sprint Review
Sprint Retrospektive
Grooming
Release Burndown
Mittwoch, 17. Juli 2013
Lernen für Klausuren mit Struktur
Vorteile
- Man gewinnt Zeit
- Befasst sich mit dem Stoff mehrmals
- Es bleibt was hängen
- Für die Klausur wird der "Spickzettel" meist nicht mehr benötigt, da die Infos sich in das Gehirn eingebrannt haben
Vorgehen
- Überblick verschaffen
- Lerneinheit lesen und sich Stichpunkte mit einer Erklärung aufschreiben. Stichpunkte können auch unterstrichen werden und die Erklärung folgt nach dem Doppelpunkt
- 2 Tage später diese Zusammenfassung durch Umformulierungen auf eine halbe DIN A4 Seite bringen
- Vor der Prüfung, also in der Lernphase diese halben Seiten nochmal um die Hälfte reduzieren
- Diese reduzierten Bereiche auf die "Spickzettel" übertragen
- Beim Übertragen kann man sich die Bereiche entsprechend arrangieren, dass beim darauf schauen eine Gedankenstütze sein kann
- Jeden Tag diese Informationen durch Umformulierungen kürzen und damit auf immer weniger Blätter verteilen
- Vorgang wiederholen bis 2 Seiten DIN A4 alle Informationen enthalten
- Keine Rückseite nutzen. Damit kann man die komplette Information auf dem vor sich Tisch liegen haben, ohne Blättern zu müssen
- Extra-Tipp: Lernen auf dem Weg zur Arbeit oder Schule.
Überblick verschaffen
Lerneinheit lesen
Zusammenfassung Ihre Notizen
Spickzettel beginnen
Reduzieren ist Lernen
Keine Rückseite
Lerntipp
Donnerstag, 27. Juni 2013
Texte effektiver formulieren - Erreichen Sie dadurch mehr Akzeptanz bei Ihren Lesern
Nutzen Sie die aktive Stimme
Setzen Sie starke Verben ein
Aktive und Passive Stimme
Beispiel der aktiven Stimme:Der Vater weckt seine Tochter.
Beispiel passiver Stimme:
Die Tochter wird vom Vater geweckt
Die passive Stimme benötigt Hilfsverben. Das eigentliche Subjekt rückt in den Hintergrund. Die ausführende Person wird durch das Wort "vom" eingeführt. Manchmal wird auch der Ausführer weggelassen. Dann kann man nicht mehr erkennen wer was macht.
Verbannen Sie nicht die passive Stimme, nutzen Sie diese sparsam
Die passive Stimme macht durchaus Sinn. Untergeordnete Personen können damit in den Vordergrund des Satzes gebracht werden. Nutzen Sie die passive Stimme nur, wenn Sie einen guten Grund haben. Im Zweifel wählen Sie die aktive Stimme.Vermeiden Sie überflüssige Wörter
Wörter sind überflüssig, wenn diese durch weniger Wörter ersetzt werden können, die das Gleiche bedeuten. Manchmal gibt es einfachere Wörter für bestimmte FormulierungenStatt: "um zu erreichen"
Besser: "damit"
Schreiben Sie positiv
Positive Sätze sind kürzer und leichter zu verstehen, als die negativen Gegenstücke. Zum Beispiel:vorher
Anders als die Hauptakteure werden die B-Aktionäre vermutlich keine Dividenden erhalten.
nachher
Nur die Hauptakteure erhalten vermutlich eine Dividende.
Die Sätze weden kürzer und leichter verstanden, wenn Sie negative Teile durch ein einziges Wort, welches das Gleiche bedeutet, ersetzen.
Beispiel:
Nicht möglich wird zu unmöglich
Fazit
Buchempfehlung
Dieses Werk zeigt Ihnen, wie Sie einfache und klare Texte verfassen. Dabei sollen nicht die komplexen Sachverhalten vereinfacht werden, sondern die Art und Weise, wie diese präsentiert werden. Schreiben Sie Texte die auch verstanden werden.
Mittwoch, 19. Juni 2013
FFMPEG - Thumbnails aus Filme erstellen und Filme schneiden mit ffmpeg
Kleine Auswahl was FFMPEG kann:
- Streaming Server
- Video schneiden
- Thumbnails, also Screenshots heraus holen
- Videoaufnahme
- Videos konvertieren, also in andere Formate umwandeln
FFMPEG für jedes Betriebssystem
- FFMPEG Download-Seite für alles
http://www.ffmpeg.org/download.html - Als ausführbares Windows Programm (Binäries)
http://ffmpeg.zeranoe.com/builds/ - Für die Mac-User
http://www.evermeet.cx/ffmpeg/
Viele FFMPEG Parameter
- Direkt die FFMPEG Parameter
http://www.ffmpeg.org/ffmpeg.html - Hier ist die grosse Übersicht für alle FFMPEG Parameter:
http://www.ffmpeg.org/documentation.html
Mit FFMPEG einen Screenshot aus einem Video erstellen
ffmpeg -ss SEKUNDEN_START|HH:MM:SS -i PFAD_ZUM_VIDEO -t 0.001 -s WIDTHxHEIGHT pic.jpg
- -ss Startpunkt im Film als Zeitangabe
Entweder Sekunden oder eine Uhrzeit - -i Pfad zum Film selbst
- -t Dauer der Aufnahme in Sekunden.
Wir benötigen ja nur ein Bild, also die kleinste Dauer - -s Die Ausgabegrösse für das Bild in Pixel
Breite mal Höhe - Der Bilddateiname muss eine bekannte Bildformat-Dateiendung haben!
Am besten .jpg nehmen
Beispiel für einen Screen nach 20 Sekunden im Film
ffmpeg -ss 20 -i meinFilm.mp4 -t 0.001 -s 320x240 pic.jpg
Mit FFMPEG einen kleines Vorschauvideo aus einem Video schneiden
ffmpeg -ss SEKUNDEN_START|HH:MM:SS -i PFAD_ZUM_VIDEO -t DAUER -s WIDTHxHEIGHT pre.mp4
- -ss Startpunkt im Film als Zeitangabe
Entweder Sekunden oder eine Uhrzeit - -i Pfad zum Film selbst
- -t Dauer der Aufnahme in Sekunden.
Wir benötigen nur 20 Sekunden, also die kleinste Dauer - -s Die Ausgabegrösse für das Bild in Pixel
Beispiel für einen 20 Sekunden Schnitt nach 20 Sekunden im Film
ffmpeg -ss 20 -i meinFilm.mp4 -t 20 -s 320x240 pre.mp4
Mit FFMPEG sich selbst über Webcam aufnehmen
Video mit der Webcam aufnehmen für 10 Sekunden:
ffmpeg -f dshow -i video="Integrated Camera" -t 10 output.flv
Foto (320x240 gross) mit der Webcam aufnehmen nach 10 Sekunden:
ffmpeg -f dshow -i video="Integrated Camera" -ss 10 -s 320x240 -t 0.001 output.jpg
Buchtipps
Wenn man seinen eigenen Video-Server betreiben will, könnte dieses Wissen nützlich sein:Fazit
http://sasonikolov.blogspot.de/2013/03/youtube-clone-selber-gemacht-mit.html
Montag, 17. Juni 2013
CAS Single SignOn SSO verstehen
Die Implementierung wird durch viele Klassen und Bibliotheken abgebildet. Doch manchmal möchte man einfacher und simpler an die Sache heran gehen. Dazu ist das grundlegende Verständnis wichtig. Wie funktioniert das mit SSO und CAS?
Grundsätzlich wozu braucht man SSO?
CAS Server bietet den Zugang zum SingleSignOn (SSO)
- Loginverfahren
- Daten über den User für die Anwendungen und Programme
Anwendung
Ablauf technisch
Mittwoch, 29. Mai 2013
IRC-Bot mit NodeJS
Was wird erstellt?
Ein IRC-Client, der auch bestimmte Befehle reagieren kann. Dieser ruft eine Webseite auf und liefert das Ergebnis als Chatnachricht zurück.Erkenntnisse
Vorarbeiten
- NodeJS (installiert oder auch nur die ausführbare Datei)
http://nodejs.org/download/ - IRC Account auf einem Server
- Webseite, die einem was interessantes liefern
Konstrukt
Hier sind einige Elemente verbaut.Mehrer Hilfsfunktionen und viele anonyme Funktionen.
Das Konstrukt soll auch zeigen, was alles möglich mit Javascript ist.
Zuerst generieren wir uns ein Objekt mit den Server-Zugangsdaten.
Der ganze Server ist einer Funktion eingebettet.
Die Hilfsbereiche sind zum Teil direkt in der Daten-Funktion eingebunden und zum Teil in einem Hilfsobjekt ausgelagert. Das soll zeigen, dass mehrer Methoden möglich sind.
Ablauf
- Verbindung zum Server
- Login
- Registrieren von Event-Handler für den eingehenden Datenverkehr
- Auswerten der eingehenden Texte
- Aufrufen von entsprechenden Funktionen
- Antworten in den Chat senden
Code
var config = {
user: {
nick: 'sasobotli',
user: 'Saso_T_2013',
real: 'saso Bot - SN2013',
pass: '20cC12#'
},
server: {
addr: 'testirc.MEINSERVERNAME.de',
port: 6697
},
chans: ['#team-chat']
//chans: ['#chan1','#chan2']
, keywords:{}
}
// http://www.ietf.org/rfc/rfc1459.txt
irc3(); // startet den IRC-Bot
function irc3() {
// SSL Verbindung
var tls = require('tls');
var cleartextStream = tls.connect(
config.server.port,
config.server.addr,
null,
// anonyme Funktion, die das Anmelden auf dem Server macht
function(){
irc.stream = cleartextStream;
// ein paar Log-Ausgaben, um mehr zu verstehen
console.log("client connected mit "+
cleartextStream.remoteAddress+":"+
cleartextStream.remotePort);
console.log("meine adresse: ",
cleartextStream.address());
console.log('client connected',
cleartextStream.authorized ? 'authorized' : 'unauthorized');
// nun senden wir unsere Zugangsdaten
irc.senden('PASS '+config.user.pass);
irc.senden('NICK '+config.user.nick);
irc.senden('USER '+config.user.user+
' localhost '+config.server.addr+
' '+config.user.real);
// in alle channels anmelden
for (var a=0;a<config.chans.length;a++) {
irc.senden('JOIN '+config.chans[a]);
}
// kommandozeile einbinden,
// so können wir auch direkt mit dem Server sprechen :)
process.stdin.setEncoding('utf8');
process.stdin.pipe(cleartextStream);
process.stdin.resume();
}
);
// das Encoding setzen
cleartextStream.setEncoding('utf8');
// nun registrieren wir eine Funktion,
// die auf eingehende Daten reagieren soll
cleartextStream.on('data', function(data){
console.log("incoming");
// daten zeilenweise abarbeiten
data = data.split('\n');
for (var i = 0; i < data.length; i++) {
// leere zeilen überspringen
if (data[i].trim() == "")
continue;
console.log('RECV -', data[i]);
if (data !== '') {
// anforderungen / request vom Server sich zu melden
var pos = data[i].indexOf(":");
var key = data[i].substr(0,pos).trim().toUpperCase();
var value = data[i].substr(pos+1).trim();
if (key != "") {
switch (key) {
case "PING":
irc.senden('PONG :'+value);
break;
default:
console.log("Nicht abgefangen: "+key);
}
} else {
// antwort oder info vom Server
var teile = value.split(" ");
//console.log(teile);
switch(teile[1]) {
case '353': // name reply, für NAMES,
// also alle Chatter in einem Raum erhalten
//console.log("Namen eingetroffen");
var namen = value.split(":")[1].trim().split(" ");
//console.log("gefunden: "+namen.length, namen);
for (var a=0;a<namen.length;a++){
var oname = namen[a].replace(/@/,"");
var admin = false;
if (namen[a].match(/@/))
admin = true;
// irc ist unten als Objekt definiert
irc.users[oname] = {'name':oname, 'admin':admin, 'nachrichten':[]};
}
//console.log(irc.users);
break;
case 'JOIN': // Raum beigetreten
var raum = teile[2].substr(1);
// ein paar Begrüssungen
var sp = ['Hallo', 'Hey', 'hi', 'Wie gehts?',
'huhu','Toll, dass du da bist'];
// eine Begrüssung per Zufall auswählen
var pos = Math.round(Math.random()*(sp.length-1));
// begrüssen
irc.senden("PRIVMSG "+raum+" "+sp[pos]);
// alle Namen im Raum abrufen
irc.senden("NAMES "+raum);
break;
case 'PRIVMSG':
// nachricht eingetroffen
// wir speicher wann zuletzt was gesagt wurde
irc.zaehler.lastword = time();
// ermittel ob direkt oder raum chat
var sender = teile[0];
var empfaenger = teile[2];
var user_sender = sender.split("!")[0];
var antwort = teile.slice(3)
.join(" ").trim()
.substr(1); // ohne führendes :
// wir speichern den User in unsere Liste
if (!irc.users[user_sender]) {
irc.users[user_sender] = {
'name':user_sender,
'admin':false,
'nachrichten':[], keywords:{}};
}
// speichern die Nachricht in seine History
irc.users[user_sender].nachrichten.push(antwort);
//console.log("nachricht eingegangen. Von "+sender, antwort);
// wurden wir direkt angesproche? also privatchat?
if (empfaenger.toLowerCase() ==
config.user.nick.toLowerCase()) {
var meineantwort = "danke, selber";
// senden unsere Antwort
irc.senden('PRIVMSG '+user_sender+" "+meineantwort);
} else {
// channel message eingetroffen
//irc.senden('PRIVMSG '+empfaenger+" Roger that");
console.log("wuerden senden: "+'PRIVMSG '+empfaenger+" Roger that");
}
// hier können wir nun das Gesagt analysieren und
// dementsprechend antworten
// hat jemand geschrieben: sag was,
// starten wir den Sprüchemodus
if (antwort.toLowerCase() == "sag was"){
console.log("sprueche intervall modus aktiv");
// einen Spruch holen und senden
irc.sagwas();
}
if (antwort.toLowerCase() == "sag sofort was"){
irc.sagwas(true);
}
if (antwort.toLowerCase() == "stopp"){
console.log("sprueche modus deaktiviert");
irc.zaehler.aktiv = false;
}
if (antwort.toLowerCase() == "saso start"){
console.log("sprueche modus aktiviert");
irc.zaehler.aktiv = true;
}
// wenn jemand fragt, woher die Sprüche kommen
// geben wir die URL als Antwort
// diese Bereich kann man gut ausbauen und
// dem Bot Befehle zum Auswerten von anderen Diensten
// geben. So dass er die Ergebnisse hier postet
if (antwort.toLowerCase().match(/woher\s+sind\s+die/)){
irc.senden("PRIVMSG "+empfaenger+" http://sprueche.woxikon.de");
}
// wenn jemand die History haben will ...
if (antwort.toLowerCase().match(/history:/)) {
// könnten wir hier diese auswerten und posten...
}
break;
}
}
}
}
});
cleartextStream.on('end', function(){
console.log("session beendet");
process.exit(0);
});
// Objekt, dass uns als zwischenspeicher dient
// und auch hilfsfunktionen enthält
var irc = {
zaehler: {
'aktiv':false,
'lastword':time()
},
stream: null,
// sendet die Nachrichten an den IRC-Server
senden: function(text) {
console.log("SEND -", text);
this.stream.write(text+'\n');
if (text.match(/^JOIN/i)) {
// hole die benutzer
this.senden("NAMES"+text.substr(4));
}
},
// hilfsfunktion, die später für das Chatten genutzt wird
sagwas: function(force){
if (!force) {
// zufall, wann wieder ein neuer Spruch gesagt wird
var nextcall = Math.round(Math.random()*(10))+3; // minuten
setTimeout(irc.sagwas, nextcall*1000*60);
console.log("nächster spruch ca. in "+nextcall+" minuten");
}
// nur wenn vor 2 minuten niemand was gesagt hat
if (!force && irc.zaehler.lastword > time()-(2*1000*60))
return;
if (!force && !irc.zaehler.aktiv)
return;
irc.zaehler.lastword = time();
// ein paar sprüche seiten
var urls = [
{'url':'http://sprueche.woxikon.de/poesiealbum','satz':'Das hier find ich gut:'},
{'url':'http://sprueche.woxikon.de/emo', 'satz':'kennt ihr den?'}];
var pos = Math.round(Math.random()*(urls.length-1));
var spruchurl = urls[pos];
// hier holen wir die Sprüche ab und
// senden diese dann direkt als Message
URLLaden(spruchurl.url, function(html) {
// suchen im HTML nach den Sprüchen
var treffer = html.match(/<div\s+class="jingle-content">([\S\s]*?)<\/div>/gi);
var sprueche = [];
for (var a=0;a<treffer.length;a++) {
//console.log(treffer[a]);
sprueche.push(treffer[a].replace(/(<\/?[^>]+>)/gi, ''));
}
// wenn es einige Treffer gibt, wählen wir per zufall einen aus
if (sprueche.length>0) {
//console.log(sprueche);
var pos = Math.round(Math.random()*(sprueche.length-1));
var spruch = sprueche[pos].trim().replace(/\s/," ");
//console.log("kennt ihr den? "+spruch);
irc.senden('PRIVMSG '+config.chans[0]+" "+spruchurl.satz+" "+spruch);
}
});
},
users: {}
}
}
function time(onlyseconds) {
var datum = new Date();
var milliseconds = datum.getTime();
if (onlyseconds)
return intval(milliseconds/1000);
return milliseconds;
}
// diese Funktion, lädt eine HTML Seite
function URLLaden(url, cbfkt) {
if (url.match("://")) {
var http;
if (url.match("s://")){
http = require('https'); // die URL verlangt SSL
} else {
http = require('http');
}
http.get(url, function(res) {
//console.log("Got response: " + res.statusCode);
var html = "";
res.on("data", function(chunk){
html += chunk; // inhaltteile der Seite zusammen kleben
});
res.on("end", function(){
// alle Teile sind angekommen und wir haben die HTML-Seite
// cbfkt ist eine anonyme Fkt, die wir aufrufen
// als ergebnis, senden wir das HTML
cbfkt(html);
});
res.on("close", function(err) {
// verbindung beendet
cbfkt(html);
console.log("Error holen "+url);
});
}).on('error', function(e) {
// verbindung getrennt
console.log("Got error: " + e.message);
});
} else {
// falls die URL auf eine lokale Datei zeigt, laden wir diese halt
fs.readFile(url, function(err, data){
if (err){
console.log(err);
} else {
cbfkt(data);
}
});
}
}
Weiterführende Lektüre:
- Node.js & Co: Skalierbare, hochperformante und echtzeitfähige Webanwendungen professionell in JavaScript entwickeln
- IRC Hacks: 100 Industrial-Strength Tips & Tools
Fazit
Saso
Montag, 13. Mai 2013
Debugging mit NodeJS
NodeJS bietet von Haus aus an, das Programm im Debugmodus zu starten. Es gibt einen ziemlich guten Debugger, der auch optisch sehr gut ist. Leider ist die Dokumentation für das Starten nicht sofort verständlich. Vor allem wenn man noch nie einen Debugger per Konsole gestartet hat.
Debugger installieren
Dort gibt es nun einen Ordner namens node-inspector. Da liegt auch der Debugger als NodeJS Programm.
Der Debugger muss nicht im gleichen Ordner liegen, wie das Programm, welches debugged wird.
Debugger starten
Programm debuggen
Debuggen
Fazit
Mittwoch, 24. April 2013
Vorlage für eine Todo-Liste
Man hat schnell einen Überblick, kann Änderungen schnell vornehmen und alleine durch einen geschwungene Linie Einträge verbinden oder durch Einkreisen gruppieren.
Ich habe oft die Probleme mit der Priorisierung der TODOs gehabt und möchte hier mal meine neueste Vorlage vorstellen.
Die Kopzeile
Prio | #ID | #NF | Titel | fällig bis |
Prio - Priorität
#ID - Todo-Nummer
#NF - Nachfolger-Todo-Nummer
Titel - TODO-Eintrag
Fällig bis - Datum
Benutzen
Beispiel
Prio | #ID | #NF | Titel | fällig bis |
III | 1 | 3 | Bloggen über TODO | |
2 | Einkaufen | 24.04.13 | ||
I | 3 | Twittern über TODO |
Viel Spass damit.
Dienstag, 23. April 2013
Firmengründung für alle
Die Anregungen stammen aus:
- Eigene Erfahrung
- Buch von Thimothy Ferriss: 4-Stunden Woche
Hier Buch einsehen: Die 4-Stunden-Woche: Mehr Zeit, mehr Geld, mehr Leben - Buch von Prof. Günter Faltin: Kopf statt Kapital
Hier Buch einsehen: Kopf schlägt Kapital: Die ganz andere Art, ein Unternehmen zu gründen Von der Lust, ein Entrepreneur zu sein
Wenn man beide Bücher zusammen mischt und die Doppelungen entfernt, hat man alles was man zum erfolgreichen und einfachen Start ins eigene Unternehmen braucht. Und das mit minimalsten Aufwand. Minimaler Aufwand beim Gründen und vor allem beim Betreiben.
Ich leihe mir sehr gute Begrifflichkeiten aus beiden Büchern und versuche kurz ein Vorgehensmodell zu skizzieren.
- Man kommt mit einer vagen Idee.
Die Idee muß einem Spaß machen! Dabei sind Ideen am besten aus eigener Not oder Wunsch heraus entstanden. Bsp: Bessere Schuhe zum Klettern, günstigerer Tee, einfacher Verkauf von Dateien, etc. - Die Idee sollte etwas Bestehendes verbessern. Anderes billiger machen oder einfach mehr Leben ins Leben bringen.
- Dann versuchen Sie die Idee im Kopf zu optimieren, indem Sie ausformulieren, was das Beste an der Idee ist. Warum braucht man gerade Ihre Idee.
- Die wertvollen Vorteile hervor heben. Ermitteln Sie ehrlich, was die Konkurrenz machen wird, um gegen Sie anzutreten.
- Dann nochmal die Idee gedanklich ausbauen und noch mehr einen Vorteil ausarbeiten.
Wichtig: Halten Sie Ihre Idee einfach her vom Produkt. Also nur 1 Produkt anstatt sehr viele. Konzentrieren Sie sich auf einen Kerndienst Ihrer Idee und spitzen Sie das Angebot zu.
Damit haben Sie viele Vorteile. - Dann malen wir den harten Weg auf. Am besten als Schaubild.
Bsp: Produkt in China bestellen. Dort abholen. Ins eigene Lager. Verkaufsräume mieten. Personal einstellen. Für die Logistik gleich ein Büroteam aufbauen. Buchhaltungsteam einstellen. Rechtsabteilung. Marketing organisieren. Etc.
- Zuerst Minimarktforschung: Internetseite mit den vielen kostenlosen Tools aufsetzten.
- 1 bis 2 Seiten genügen.
- Produkt beschreiben.
- Bestellmaske rein.
Wenn einer diese ausfüllt, wird angezeigt: "Derzeit nicht mehr vorrätig. Wir melden uns." - Die Statistik der Seitenaufrufe zeigt später ob Interesse besteht.
- Nun für 25 Euro Werbung (google adwords). Und den Markttest starten.
- Die Posten auf dem Schaubild werden durch Komponenten ausgetauscht.
- Produktion wird an den Fabrikant als Auftrag gegeben.
Viele arbeiten auch on-demand. Je nach Produkt.
Bei teuren Sachen nehmen Sie Vorkasse vom Kunden und bestellen erst dann. - Unzählige Fullfilment-Dienstleister. Wählen Sie einen.
- Bestellung werden auf den Dienstleister umgelenkt.
Diese liefert auch aus. - Produzierte Produkte gehen ins Lager vom Dienstleister. Etc.
Optimieren Sie solange bis es passt, leicht und günstig geworden ist.
Hier sind digitale Produkte natürlich von Vorteil. Aber Videos oder so auch. Schulungsvideos, Lehrbücher, Seminare. Kongresse. Denken Sie und lassen Sie ausführen.
Wichtig:
Investieren Sie so wenig wie möglich. Machen Sie ein Hobby draus. Wenn es mal nicht klappt haben Sie nur sehr wenig Geld verloren und Zeit. Aber hatten Spaß.
Viel Spass beim Ausprobieren.
Montag, 4. März 2013
Youtube Clone selber gemacht mit Javascript
Einfacher Video-Portal Server schnell selbst gemacht
Features des Servers
- Upload Videos
- Konvertierung von Videos in MP4 Format
- Liste der vorhandenen Videos mit Vorschaubild
- Abspielen der Videos in HTML5
- Dynamisch erweiterbar
- Statischer Server
- Gesicherter statischer Bereich für Default Dateien
- Dynamisches Laden von JS-Modulen in den Webserver
- Automatisches Nachladen von JS-Modulen die sich geändert haben (updates)
Die Umsetzung des Video Portals zeigt folgende Elemente
- Dynamische Erweiterung vom statischer Ansatz von NodeJS Server
Einfacher Nodejs-Webserver - Neuen Prozess starten für die Konvertierung
- HTTP Range-Control
- HTML5 Video abspielen mit Javascript
Wir benötigen NodeJs und 3 Zutaten
- NodeJS für den Server.
Download NodeJS - NodeJS Modul Express, um schneller den Webservice aufbauen zu können.
Howto Express
$ npm install express
- NodeJS Modul Mime, um den Mimetype einfacher ermitteln zu können.
$ npm install mime
- FFmpeg, für die Video Konvertierung.
- Download FFmpeg für Windows (Zeranoe FFmpeg)
- Download FFmpeg für alle Betriebssystem
Fertige Verzeichnisstruktur
- /ffmpeg
- /server
- /www (hier liegen statische WWW Inhalte)
- /filmkonvert
- /upload
- (Filme etc.)
- /wwwtemplates
- favicon.ico
- videos.html (Anzeige der Videos und Upload Formular)
- /node_modules
- /express
- /mime
- server_express.js (der eigentliche Server als Javascript Datei)
Installation
- ffmpeg.exe
- ffplay.exe
- ffprobe.exe
Code: Einfacher NodeJS Server mit Range Control
var express = require('express');
var app = express();
var PATH = require('path');
var FS = require('fs');
var MIME = require('mime');
var system = {
port:3000
,tmpdir:"tmp"
,wwwdir:"www"
,wwwtemplatesdir:"wwwtemplates" // hier liegen die dateien zum ausliefern
,uploaddir:"uploads"
,zaehler:0
,JSTeile:{}
,args:[]
,jsdir:"js"
,tools:{
'ffmpeg':{pfad:'..\\ffmpeg\\ffmpeg.exe'}
}
}
checkARGs();
starten();
function starten() {
if (!FS.existsSync(system.uploaddir)) {
FS.mkdirSync(system.uploaddir);
}
if (!FS.existsSync(system.tmpdir)) {
FS.mkdirSync(system.tmpdir);
}
// leere die tmpdir und uploaddir
// auch per timeout
// json-callback-namen aus parameter cbf nehmen
app.set('jsonp callback name', 'cbf');
//app.use(express.logger());
app.use(function(req,res,next) { // mein logger
// req.params
// req.body
// reg.query
// req.files
// req.cookies
system.zaehler++;
res.locals.zaehler = system.zaehler;
console.log("["+Date2Text()+"]", req.ip+":"+req.connection.remotePort, req.host, req.protocol, req.method, req.originalUrl);
next();
});
// zuerst static content
app.use(express.bodyParser({ keepExtensions: true, uploadDir: system.uploaddir }));
app.use(express.cookieParser());
app.use(function(req, res,next){
var range = req.header('Range');
if (!range)
return next();
var file = PATH.join(system.wwwdir,req.path);
if (!FS.existsSync(file))
return next();
var stat = FS.statSync(file);
if (!stat.isFile()) return next();
var start = parseInt(range.slice(range.indexOf('bytes=')+6, range.indexOf('-')));
var end = parseInt(range.slice(range.indexOf('-')+1, range.length));
if (isNaN(end) || end == 0) end = stat.size-1;
if (start > end) return;
console.log("["+Date2Text()+"]",'Browser requested bytes from ' + start + ' to ' +end + ' of file ' + file);
var date = new Date();
res.writeHead(206, { // NOTE: a partial http response
// 'Date':date.toUTCString(),
'Connection':'close',
// 'Cache-Control':'private',
'Content-Type':MIME.lookup(file),
'Content-Length':end - start,
'Content-Range':'bytes '+start+'-'+end+'/'+stat.size,
'Accept-Ranges':'bytes',
// 'Server':'CustomStreamer/0.0.1',
'Transfer-Encoding':'chunked'
});
var stream = FS.createReadStream(file,
{ flags: 'r', start: start, end: end});
stream.on("open", function (fd) {
stream.pipe(res);
});
stream.on("error", function(){
res.end();
//console.log("#"+res.locals.zaehler+" Fehler beim ausliefern der Datei");
});
stream.on("data", function(data){ // daten kommen hier vorbei
res.bytezaehler += data.length;
//console.log("#"+res.locals.zaehler, data.length, bytezaehler, stats.size);
});
stream.on("end", function(){
res.end();
//console.log("#"+res.locals.zaehler+" Datei-Auslieferung beendet. Gesendet:",res.bytezaehler);
});
});
app.use(express.static(system.wwwdir));
app.use(express.static(system.wwwtemplatesdir)); // vom system bereit gestellte elemente
app.use(function(req,res,next) {
// auswerten req
if (req.query.cbf && req.query.cbid) {
var antwort="";
if (req.query['function']) {
switch(req.query['function']){
case "mdirlist":
//new DynServer(req,res).zeigeDirListe(PATH.join(system.wwwdir,req.path));
if (!system.DynServer)
system.DynServer = new DynServer(req,res,system);
return system.DynServer.zeigeDirListe(system.wwwdir);
break;
case "livecam":
//new DynServer(req,res).zeigeDirListe(PATH.join(system.wwwdir,req.path));
//if (!system.DynServer)
// system.DynServer = new DynServer(req,res,system);
//return system.DynServer.streamLiveCam();
break;
default:
JSONPErrorAnworten(req,res,"function not supported");
}
}
}
if (req.query['dynserver'] || req.body['dynserver']) {
if (!system.DynServer)
system.DynServer = new DynServer(req,res,system);
system.DynServer.auswerten();
}
else {
next();
}
});
app.use(function(req,res){
res.send(404, 'Not found');
});
app.listen(system.port);
console.log("server auf ",system.port," - wwwdir:",system.wwwdir);
}
function JSONPAnworten(req,res,antwort,status){
if (!status)
status = 200;
res.jsonp(status, { cbid: req.query.cbid, response: antwort });
}
function JSONPErrorAnworten(req,res,antwort,status){
if (!status)
status = 200;
res.jsonp(status, { cbid: req.query.cbid, error: antwort });
}
function URLLadenAndRun(pfad, cbfkt) {
URLLaden(pfad, function(html) {
console.log(pfad, "geladen");
var vm = require('vm');
var script = vm.createScript(html);
script.runInThisContext();
if (cbfkt)
cbfkt();
});
}
function URLLaden(url, cbfkt) {
if (url.match("://")) {
var http;
if (url.match("s://")){
http = require('https');
} else {
http = require('http');
}
http.get(url, function(res) {
//console.log("Got response: " + res.statusCode);
var html = "";
res.on("data", function(chunk){
html += chunk;
});
res.on("end", function(){
cbfkt(html);
});
res.on("close", function(err) {
cbfkt(html);
console.log("Error holen "+url);
});
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
} else {
var fs = require("fs");
fs.readFile(url, 'utf8', function(err, data){
if (err){
console.log(err);
} else {
cbfkt(data);
}
});
}
}
time = function(){
var t = new Date();
return t.getTime();
}
Date2Text = function(millisek, format) {
if (!millisek) {
var t = new Date();
millisek = t.getTime();
}
var d = new Date(millisek);
if (!format)
format = "%d.%m.%Y %H:%i";
var tage = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
var monate = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dez'];
var formate = {'d':((d.getDate()<10)?'0'+d.getDate():d.getDate()),
'j':d.getDate(),'D':tage[d.getDay()],'w':d.getDate(),'m':((d.getMonth()+1<10)?'0'+(d.getMonth()+1):d.getMonth()+1),'M':monate[d.getMonth()],
'n':d.getMonth()+1,'Y':d.getFullYear(),'y':((d.getYear()>100)?(d.getYear().toString().substr(d.getYear().toString().length-2)):d.getYear()),
'H':((d.getHours()<10)?'0'+d.getHours():d.getHours()),'h':((d.getHours()>12)?(d.getHours()-12):d.getHours()),
'i':((d.getMinutes()<10)?'0'+d.getMinutes():d.getMinutes()),'s':((d.getSeconds()<10)?'0'+d.getSeconds():d.getSeconds())
}
for (var akey in formate) {
var rg = new RegExp('%'+akey, "g");
format = format.replace(rg, formate[akey]);
}
return format;
}
function checkARGs() {
if (process.argv.length > 2) {
system.args = process.argv;
for (var a=0;a<system.args.length;a++){
if (system.args[a].substr(0,6) == "start=") {
var teile = system.args[a].substr(6).split(",");
for (var b=0;b<teile.length;b++) {
system.JSTeile[teile[b]] = starteJSTeil(teile[b]);
}
} else
if (system.args[a].substr(0,7) == "wwwdir=") {
system.wwwdir = system.args[a].substr(7);
}
}
}
}
function starteJSTeil(filename) {
var obj = {'file':filename, 'name':filename.substr(0,filename.length-3)};
var pfad = PATH.join(system.jsdir,filename);
//obj.fkt = require("./"+pfad);
URLLadenAndRun(pfad, function(){
obj.handle = FS.watch(pfad,
{ persistent: false },
function (event, filename) {
if (filename) {
console.log("Jobs Datei hat sich geändert. Neu einladen. "+event+":"+filename)
if (event == "rename"){
//
}
}
if (event == "change") {
//obj.fkt = require("./"+pfad);
URLLadenAndRun(pfad);
}
});
obj.handle.on("error", function (e){console.log(pfad,e);});
});
return obj;
}
var DynServer = function (req,res,system) {
// new DynServer().auswerten();
this.req = req;
this.res = res;
this.system = system;
this.paras = {};
this.livecamHandle = null;
if (req.query) {
for (key in req.query) {
this.paras[key] = req.query[key];
}
}
if (req.body) {
for (key in req.body) {
this.paras[key] = req.body[key];
}
}
this.zeigeDirListe = function(pfad) {
var crypto = require('crypto');
var self = this;
FS.readdir(pfad, function(err,files) {
if (err){
return JSONPErrorAnworten(self.req, self.res, "no files");
}
var nfiles = [];
for (var a=0;a<files.length;a++){
if (files[a].substr(0,1) == ".")
continue;
nfiles.push({'src':files[a],'md5':crypto.createHash('md5').update(files[a]).digest("hex")});
}
JSONPAnworten(self.req, self.res, nfiles);
});
}
this.filmkonvertieren = function(pfad, cbfkt) {
console.log("["+Date2Text()+"] starting converting", pfad);
var output = PATH.basename(pfad);
output = PATH.join(PATH.dirname(pfad), output.substr(0,output.lastIndexOf("."))+"-KONVERTING");
console.log("["+Date2Text()+"] starting converting", pfad, output);
var child_process = require('child_process');
var ffmpeg_pfad = this.system.tools.ffmpeg.pfad;
// http://videoencoding.websmith.de/encoding-praxis/linux-ffmpeg-encoding.html
var params = ["-y", "-i", pfad, "-acodec", "libmp3lame", output+".mp4"];
console.log("["+Date2Text()+"]", ffmpeg_pfad, params.join(" "));
childProcess = child_process.spawn(ffmpeg_pfad, params);
childProcess.stderr.on('data', function (data){
// hier kommen auch die log-meldungen an
// console.log('FFMPEG-Log: ' + data);
});
childProcess.stdout.on('data', function (data) {
// console.log('FFMPEG: ' + data);
});
childProcess.on('exit', function (code) {
console.log("["+Date2Text()+"] FFMPEG fertig alles ok:",code);
if (code != 0) {
// fehler mit code nummer
FS.unlink(output+".mp4");
if (cbfkt)
cbfkt(code, pfad);
} else {
// kopieren
FS.rename(output+".mp4", pfad, function(err) {
if (err){
console.log("["+Date2Text()+"] Konvertierung fehlgeschlagen");
FS.unlink(output+".mp4");
if (cbfkt)
cbfkt(code, pfad);
return;
}
console.log("["+Date2Text()+"] Konvertierung fertig");
if (cbfkt)
cbfkt(code, pfad);
});
}
});
}
this.upload = function () {
console.log("["+Date2Text()+"] upload starten",req.path);
// handle upload
if (req.files && !isEmptyObject(req.files)) {
//console.log(req.files);
var self = this;
FS.exists(PATH.join(self.system.wwwdir, req.path), function(exists) {
if (!exists) {
return res.send(403, 'upload does not folder exists '+req.path);
return;
}
var dateien = [];
for (var key in req.files) {
var file = req.files[key];
if (!file.filename || file.filename == "") {
FS.unlink(file.path);
//JSONPErrorAnworten(self.req, self.res, "file upload has no name");
res.send(403, "file upload has no name");
return;
}
// upload path
var datei = PATH.join(req.path, file.filename);
var pfad = PATH.join(self.system.wwwdir, datei);
try {
FS.renameSync(file.path, pfad);
console.log("["+Date2Text()+"] upload:",file.length, pfad);
} catch(e) {
console.log(e);
console.log("["+Date2Text()+"] Fehler beim upload verschieben - versuche tempupload zu entfernen");
FS.unlink(file.path);
continue;
}
dateien.push(datei);
// spezielles wenn jemand in filmconvert was hinterlegt
if (req.path == "/filmkonvert" || req.path == "/filmkonvert/") {
// starte film verarbeitung
switch(file.type.toLowerCase()){
case "video/mp4":
case "video/mov":
case "video/avi":
case "video/mpg":
case "video/ogg":
case "video/mpeg4":
case "video/wmv":
default:
if (file.type.substr(0,6) == "video/"){
self.filmkonvertieren(pfad, function(code, pfad) {
if (code != 0) {
// error
FS.unlink(pfad);
return;
}
var npfad = PATH.join(self.system.wwwdir, PATH.basename(pfad).replace(PATH.extname(pfad), ".mp4"));
FS.rename(pfad, npfad, function(err){
if (err) {
console.log("["+Date2Text()+"]", "Error not moved to", npfad);
FS.unlink(pfad);
} else {
console.log("["+Date2Text()+"]", "moved", npfad);
}
});
});
}
}
}
}
if (self.paras.data && self.paras.data.redirect) {
return res.redirect(self.paras.data.redirect);
} else
if (dateien.length > 0) {
//return JSONPAnworten(self.req, self.res, dateien);
return res.send(200, 'upload ok '+JSON.stringify(dateien));
} else {
return res.send(403, 'problems with upload');
}
});
} else {
//return JSONPErrorAnworten(self.req, self.res, "no files uploaded");
return res.send(403, 'no files uploaded');
}
}
this.auswerten = function(){
if (!this.paras['func'])
return JSONPErrorAnworten(this.req, this.res, "no func recognised");
switch(this.paras['func']) {
case "upload":
return this.upload();
break;
default:
return JSONPErrorAnworten(this.req, this.res, "func not supported");
}
// keine antwort gesendet, also fehler bei den clientdaten
return JSONPErrorAnworten(this.req, this.res, "func error");
}
return this;
}
function isEmptyObject(obj) {
// This works for arrays too.
for(var name in obj) {
return false
}
return true
}
Code: HTML Webseite für Video-Portal
<!DOCTYPE html>
<html>
<head>
<title>aus static template verzeichnis</title>
<script type="text/javascript">
var videos = [
{'src':'video2.mp4',"type":'video/mp4',md5:'m1'}
,{'src':'output.mp4',"type":'video/mp4',md5:'m2'}
,{'src':'http://podfiles.zdf.de/podcast/zdf_podcasts/130108_h19_414k_p20v9.mp4?2013-01-0919-08','type':'video/mp4',md5:'m3'}
];
var lastpos = 0;
function starten() {
// lade die Videoliste
ladeFilmliste(function(vids) {
var v = document.getElementById("v");
v.addEventListener("loadstart", function() {
document.getElementById("info").innerHTML = "Loading... "+v.src;
document.getElementById("navelem").style.visibility = "hidden";
v.controls = false;
});
v.addEventListener("canplay", function() {
v.controls = true;
document.getElementById("navelem").style.visibility = "visible";
var zeit = Math.round(v.duration);
var minuten = Math.floor(zeit/60);
var sek = zeit%60;
if (sek < 10)
sek = "0"+sek;
document.getElementById("info").innerHTML = minuten+":"+sek+" ["+v.src+"] - "+v.offsetWidth+"x"+v.offsetHeight;
});
v.addEventListener("ended", function(e) {
//console.log(e);
if (v.loop){
abspielen(v);
} else {
next();
}
});
//v.autoplay = true;
zeigeFilmListe();
lastpos--;
next(true);
});
window.setTimeout(aktualisiereFilmListe, 60000);
window.onresize = resize;
}
function resize() {
var v = document.getElementById("film_liste");
var vc = document.getElementById("film_liste_container");
var vpc = document.getElementById("filmp_container");
var hoehe = v.offsetHeight;
var fhoehe = window.innerHeight-60-40;
if (hoehe > fhoehe) {
vc.style.overflow = "hidden";
vc.style.overflowY = "scroll";
vc.style.height = fhoehe+"px";
vpc.style.overflow = "hidden";
vpc.style.overflowY = "scroll";
vpc.style.height = fhoehe+"px";
} else {
vc.style.overflow = "";
vc.style.overflowY = "";
vc.style.height = hoehe+"px";
vpc.style.overflow = "";
vpc.style.overflowY = "";
vpc.style.height = hoehe+"px";
}
var vp = document.getElementById("v");
//var ratio = v.offsetWidth / v.offsetHeight;
var breite = document.getElementById("info").offsetWidth-30;
vp.style.width = breite+"px";
}
function aktualisiereFilmListe() {
ladeFilmliste(function (vids){
zeigeFilmListe();
window.setTimeout(aktualisiereFilmListe, 60000);
});
}
function ladeFilmliste(cbfkt) {
apicall("request/?function=mdirlist", function(h){
if (h.object.error) {
alert(h.object.error);
} else {
initialisieren(h.object.response);
if (cbfkt) {
cbfkt(h.object.response);
}
}
});
}
function initialisieren(filme) {
if (filme) {
var vf = filme;
for (var a=0;a<vf.length;a++) {
var inlist = false;
for (var key in videos) {
if (vf[a].src == videos[key].src) {
inlist = true;
break;
}
}
if (inlist)
continue;
if (vf[a].src.toLowerCase().match(/\.mp4$/)) {
videos.push({'src':vf[a].src, "type":'video/mp4',md5:vf[a].md5});
} else
if (vf[a].src.toLowerCase().match(/\.ogg$/)) {
videos.push({'src':vf[a].src, "type":'video/ogg',md5:vf[a].md5});
} else
if (vf[a].src.toLowerCase().match(/\.webm$/)) {
videos.push({'src':vf[a].src, "type":'video/webm',md5:vf[a].md5});
}
}
}
}
function zeigeFilmListe() {
var elem = document.getElementById("film_liste");
var text = "";
var vs = document.getElementsByTagName("video");
for (var a=0;a<videos.length;a++) {
if (document.getElementById("p_"+videos[a].md5))
continue;
var inlist = false;
for (var key in vs) {
if (vs[key].src == videos[a].src){
inlist = true;
break;
}
}
if (inlist)
continue;
elem.appendChild(erstelleThumb(videos[a]));
}
resize();
}
function erstelleThumb(video) {
var elk = document.createElement("source");
elk.src = video.src;
elk.type = video.type;
var el = document.createElement("video");
el.appendChild(elk);
el.preload = "metadata";
el.style.width = "100%";
el.id = "p_"+video.md5;
el.src = video.src;
el.addEventListener("click", function(e){playMD5(video.md5);});
el.addEventListener("canplay", function() {
el.style.borderStyle = "solid";
el.style.borderColor = "white";
el.style.borderWidth = "medium";
el.currentTime = el.duration*.1;
el.play();
el.pause();
});
return el;
}
function play(btnelem) {
var v = document.getElementById("v");
if (v.paused) {
abspielen(v);
btnelem.innerHTML = "pause";
} else {
v.pause();
btnelem.innerHTML = "play";
}
}
function playMD5(md5) {
//debugger;
for (var a=0;a<videos.length;a++) {
if (videos[a].md5 == md5) {
playPos(a);
break;
}
}
}
function playPos(pos){
lastpos = pos-1;
next();
}
function next(dontplay) {
lastpos++;
if (lastpos >= videos.length)
lastpos = 0;
var kind = document.createElement("source");
kind.src = videos[lastpos].src;
kind.type = videos[lastpos].type;
var elem = document.getElementById("v");
v.replaceChild(kind,v.firstChild);
v.preload = "metadata";
v.src = videos[lastpos].src;
v.type = videos[lastpos].type;
if (!dontplay)
abspielen(v);
}
function abspielen(v) {
v.play();
for (var a=0;a<videos.length;a++){
if (document.getElementById("p"+a)) {
var ee = document.getElementById("p"+a);
var srcname = v.src.replace(/\\/g,'/').replace( /.*\//, '' );
if (videos[a].src == srcname) {
ee.style.borderColor = "green";
} else {
ee.style.borderColor = "white";
}
}
}
}
function loop(btnelem) {
var v = document.getElementById("v");
if (v.loop) {
btnelem.innerHTML = "loop";
} else {
btnelem.innerHTML = "stop loop";
}
v.loop = !v.loop;
}
function apicall(url, onSuccess, onError, onErrorTimeout, urlid) {
if (typeof(system) == "undefined")
system = {};
if (!system['dscript'])
system['dscript'] = {'id':1, 'cb':{}, 'urls':{}};
var id = system["dscript"]["id"];
system["dscript"]["cb"][id] = {"url": url, "onSuccess": onSuccess, "onError": onError, "time": new Date().getTime()};
var refurl = url;
if (!urlid)
urlid = refurl
system["dscript"]["urls"][urlid] = id; // für die statischen URLs
var head = document.getElementsByTagName("head")[0];
var script = document.createElement('script');
script.id = 'obj_dscript_'+id;
if (id > 36000 && !system["dscript"]["cb"][1])
system["dscript"]["id"] = 1;
script.type = 'text/javascript';
var srcurl = url;
if (srcurl.indexOf("?")<1)
srcurl += "?"
script.src = srcurl+"&cbf=SN_APIDispatcher&cbid="+id;
var d = new Date();
script.src += '&t='+d.getTime();
head.appendChild(script);
var timeout = 5000; // 5 sekunden
if (onErrorTimeout)
timeout = onErrorTimeout;
var errorcheck = setTimeout("SN_APIErroLoadCheck('"+id+"')", timeout);
system["dscript"]["cb"][id]['errorcheck'] = errorcheck;
system["dscript"]["id"]++;
}
function SN_APIDispatcher(result) {
var id = 0;
if (result.cbid) {
id = result.cbid;
} else {
id = result.id;
}
if (system["dscript"]["urls"][id])
id = system["dscript"]["urls"][id];
if (!document.getElementById('obj_dscript_'+id))
return false;
if (system["dscript"]["cb"][id])
system["dscript"]["cb"][id]['dontkill'] = true;
if (system["dscript"]["cb"][id] && system["dscript"]["cb"][id].errorcheck)
clearTimeout(system["dscript"]["cb"][id].errorcheck);
var old = document.getElementById('obj_dscript_'+id);
if (old != null) {
old.parentNode.removeChild(old);
delete old;
}
var oobj = null;
if(typeof result == 'function')
result = result();
else if(typeof result == 'object')
oobj = result;
var obj = {"responseText": result, "object": oobj};
if (system["dscript"]["cb"][id])
system["dscript"]["cb"][id].onSuccess(obj); // errorload could have killed it already
delete(system["dscript"]["cb"][id]);
}
function SN_APIErroLoadCheck(apicallid){
if (!system["dscript"]["cb"][apicallid])
return false;
var objekt = system["dscript"]["cb"][apicallid];
if (system["dscript"]["cb"][apicallid]['dontkill'])
return false;
// immernoch da, also wahrscheinlich fehler aufgetreten
if (objekt.onError)
objekt.onError(apicallid);
delete(system["dscript"]["cb"][apicallid]);
}
window.onload=starten;
</script>
</head>
<body>
<div id="navelem" style="visibility:hidden;">
<button onclick="play(this)">play</button>
<button onclick="loop(this)">loop</button>
<button onclick="next()">next</button>
</div>
<div>
<div style="width:80%;float:left;" id="filmp_container">
<video width="100%" id="v">Your browser does not support the video tag.</video>
<div id="info"></div>
<div style="margin:10px;border:medium solid orange;">
Upload to /
<form action="/" enctype="multipart/form-data" method="post">
<input type=hidden name="dynserver" value="1">
<input type=hidden name="func" value="upload">
<input type=file name="datei">
<br>
<input type=submit>
</form>
</div>
<div style="margin:10px;border:medium solid orange;">
Upload und Film konvertieren /filmkonvert
<form action="/filmkonvert" enctype="multipart/form-data" method="post">
<input type=hidden name="dynserver" value="1">
<input type=hidden name="data[redirect]" value="/videos2.html">
<input type=hidden name="func" value="upload">
<input type=file name="datei">
<br>
<input type=submit>
</form>
</div>
</div>
<div style="width:20%;float:left;" id="film_liste_container">
<div id="film_liste" style="text-align:center;margin:5px;margin-top:0;"></div>
</div>
<div style="clear:both;"></div>
</div>
<br>// http://www.w3schools.com/tags/ref_av_dom.asp
</body>
</html>
Serverablauf
Konvertierung
- -y
- -i FILMPFAD
- -acodec libmp3lame
- FILMPFADNEU.mp4
Portalablauf
Fazit
Mit diesem dynamischen Ansatz kann man nun beliebig weiter machen. Denkbar wäre auch ein Aufruf von anderen Interpretern und damit auch die Realisierung eines PHP-Moduls in NodeJS. Interessant ist die Möglichkeit neue Prozesse zu starten und damit mehr Flexibilität und Effizienz zu erhalten.