====== Beispiel-Programme in Go ======
* [[https://gobyexample.com/|Go-Beispiele]]
==== Hallo Welt ====
> vi Hallo_Welt.go
//Hallo Welt
package main
import "fmt"
func main()
{
var welt string = "Welt"
fmt.Printf("Hallo %v!\n", welt)
}
> go build Hallo_Welt.go
> ./Hallo_Welt
Hallo Welt!
==== Funktionstest ====
> vi Funktionstest.go
// Funktionstest
package main
import "fmt"
func Hallo(name string) {
begruessung := "Hallo %v!\n"
fmt.Printf(begruessung, name)
}
func Tschuess(name string) {
verabschiedung := "Tschüß, %v!\n"
fmt.Printf(verabschiedung, name)
}
func main() {
var Welt string = "verbesserte Welt"
Hallo(Welt)
Tschuess(Welt)
}
> go build Funktionstest.go
> ./Funktionstest
Hallo verbesserte Welt!
Tschüß, verbesserte Welt!
==== Verzweigungen ====
> vi verzweigung_1.go
// Verzweigung mit switch
package main
import "fmt"
func Hallo(gender int, name string) {
var addr string
switch gender {
default:
addr = ""
case 1:
addr = "Frau "
case 2:
addr = "Herr "
}
fmt.Printf("Hallo, %v%v!\n", addr, name)
}
func main() {
Hallo(1, "Fritz")
Hallo(2, "Fratz")
Hallo(3, "Fummel")
}
> go build verzweigung_1.go
> ./verzweigung_1
Hallo, Frau Fritz!
Hallo, Herr Fratz!
Hallo, Fummel!
----
> vi verzweigung_2.go
// Verzweigung mit if
package main
import "fmt"
func Hallo(gender int, name string) {
var addr string
if gender == 1 {
addr = "Frau "
} else {
if gender == 2 {
addr = "Herr "
} else {
addr = ""
}
}
fmt.Printf("Hallo, %v%v!\n", addr, name)
}
func main() {
Hallo(1, "Fritz")
Hallo(2, "Fratz")
Hallo(3, "Fummel")
}
> go build verzweigung_2.go
> ./verzweigung_2
Hallo, Frau Fritz!
Hallo, Herr Fratz!
Hallo, Fummel!
==== Schleifen ====
=== Zählschleife ===
> vi zaehlschleife.go
// Zählschleife
package main
import "fmt"
func main() {
// initializiere i nit 0
// kontrolliere jedes mal ob i kleiner als 5 ist
// i bei jedem Durchlauf um 1 vergrößern
for i := 0; i <= 5; i++ {
fmt.Println("Der Wert von 'i' ist jetzt:", i)
}
}
> go build zaehlschleife.go
> ./zaehlschleife
Der Wert von 'i' ist jetzt: 0
Der Wert von 'i' ist jetzt: 1
Der Wert von 'i' ist jetzt: 2
Der Wert von 'i' ist jetzt: 3
Der Wert von 'i' ist jetzt: 4
Der Wert von 'i' ist jetzt: 5
=== nebenläufige Abarbeitung (parallele Programmierung) von Daten aus einem Kanal ===
> vi kanal_10.go
// nebenläufige Abarbeitung (parallele Programmierung) von Daten aus einem Kanal
package main
import "fmt"
func zehnMal(kanal chan string) {
// Argument empfangen
sag := <-kanal
// Zehn mal zurückschreiben
for i := 0; i < 10; i++ {
kanal <- sag
}
// Kanal schließen
close(kanal)
}
func main() {
// synchronen Kanal öffnen
//kanal := make(chan string)
kanal := make(chan string, 0)
// Starten der parallelen Go-Routine „zehnMal()“
go zehnMal(kanal)
// Senden eines Strings
kanal <- "Hallo"
// Empfangen der Strings, bis der Channel geschlossen wird
for s := range kanal {
fmt.Println(s)
}
fmt.Println("Fertig!")
}
> go build kanal_10.go
> ./kanal_10
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Hallo
Fertig!
=== zeilenweise Abarbeitung von einem Array (Liste) ===
== einfache iterierende for-Schleife ==
> vi for-schleife_1.go
// einfache iterierende for-Schleife
package main
import (
"fmt"
)
var timeZone = []string{
"UTC: 0*60*60",
"EST: -5*60*60",
"CST: -6*60*60",
"MST: -7*60*60",
"PST: -8*60*60",
}
func main() {
for _, j := range timeZone {
fmt.Printf("%v\n",j)
}
}
> go build for-schleife_1.go
> ./for-schleife_1
== iterierende for-Schleife mit Index ==
> vi for-schleife_2.go
// iterierende for-Schleife mit Index
package main
import (
"fmt"
)
var Zeitspannen = map[string]int{
"Tag" : 1*60*60*24,
"Stunde" : 1*60*60,
"Minute" : 1*60,
"Sekunde": 1,
}
func main() {
for i, j := range Zeitspannen {
fmt.Printf("%v %v\n",i,j)
}
}
> go build for-schleife_2.go
> ./for-schleife_2
==== Parameter-Übergabe ====
> vi parameter.go
// Parameter-Übergabe
package main
import (
"fmt"
"flag"
)
func main() {
// definiere das Flag "-par1" mit Voreinstellung und Hilfetext
par1p := flag.Int("par1", 12, "dieser Parameter kann nur Integer-Werte annehmen")
//
// definiere das Flag "-par2" mit Voreinstellung und Hilfetext
par2p := flag.String("par2", "gute Sache", "dieser Parameter kann nur Zeichenketten annehmen")
// Ausgabe des Vorgabewertes vom Parameter
fmt.Println("'par1' hat den folgenden Vorgabewert:", *par1p)
fmt.Println("'par2' hat den folgenden Vorgabewert:", *par2p)
// Parse
flag.Parse()
// Ausgabe des übergebenen Wertes vom Parameter
fmt.Println()
fmt.Println("'par1' wurde der Wert", *par1p ,"übergeben.")
fmt.Println("'par2' wurde der Wert '"+*par2p+"' übergeben.")
}
> go build parameter.go
> ./parameter
'par1' hat den folgenden Vorgabewert: 12
'par2' hat den folgenden Vorgabewert: gute Sache
'par1' wurde der Wert 12 übergeben.
'par2' wurde der Wert gute Sache übergeben.
> ./parameter -par1=20 -par2="alles klar"
'par1' hat den folgenden Vorgabewert: 12
'par2' hat den folgenden Vorgabewert: gute Sache
'par1' wurde der Wert 20 übergeben.
'par2' wurde der Wert alles klar übergeben.
> ./parameter -par1=20 -par2=12
'par1' hat den folgenden Vorgabewert: 12
'par2' hat den folgenden Vorgabewert: gute Sache
'par1' wurde der Wert 20 übergeben.
'par2' wurde der Wert 12 übergeben.
> ./parameter -par1="alles super" -par2=1
'par1' hat den folgenden Vorgabewert: 12
'par2' hat den folgenden Vorgabewert: gute Sache
invalid value "alles super" for flag -par1: strconv.ParseInt: parsing "alles super": invalid syntax
Usage of ./parameter:
-par1=12: dieser Parameter kann nur Integer-Werte annehmen
-par2="gute Sache": dieser Parameter kann nur Zeichenketten annehmen
==== Argumenten-Übergabe ====
> vi Argumente.go
// Argumenten-Übergabe
package main
import "os"
import "fmt"
func main() {
for i := range os.Args[1:] {
i += 1
arg := os.Args[i]
fmt.Printf("Das %v. Argument ist %v.\n", i, arg)
}
}
> go build Argumente.go
> ./Argumente A B C D E F G H I J K L M
Das 1. Argument ist A.
Das 2. Argument ist B.
Das 3. Argument ist C.
Das 4. Argument ist D.
Das 5. Argument ist E.
Das 6. Argument ist F.
Das 7. Argument ist G.
Das 8. Argument ist H.
Das 9. Argument ist I.
Das 10. Argument ist J.
Das 11. Argument ist K.
Das 12. Argument ist L.
Das 13. Argument ist M.
==== Dateioperationen ====
// Dateiinhalt in eine Variable einlesen
package main
import (
"fmt"
"os"
"io/ioutil"
//"strings"
)
func main() {
var (
file string
err error
)
file = os.Args[1]
datei, err := ioutil.ReadFile(file)
if err != nil {
panic(err)
}
inhalt := string(datei)
fmt.Println(inhalt)
//fmt.Println(strings.Replace(inhalt, "\n", "|", -1))
// Zeilenumbrüche gegen "|" austauschen
//zeile := (strings.Replace(inhalt, "\n", "|", -1))
// dort, wo vorher Leerzeilen waren, sind jetzt "||"
// "||" werden hier wieder durch Zeilenumbrüche ersetzt
//daten := (strings.Replace(zeile, "||", "\n", -1))
//fmt.Println(daten)
}
// Dateiinhalt zeilenweise in ein Array einlesen
package main
import (
"os"
"fmt"
"bufio"
"bytes"
"io"
//"io/ioutil"
//"strings"
)
func readLines(path string) (lines []string, err error) {
var (
file *os.File
part []byte
prefix bool
)
if file, err = os.Open(path); err != nil {
return
}
defer file.Close()
reader := bufio.NewReader(file)
buffer := bytes.NewBuffer(make([]byte, 0))
for {
if part, prefix, err = reader.ReadLine(); err != nil {
break
}
buffer.Write(part)
if !prefix {
lines = append(lines, buffer.String())
buffer.Reset()
}
}
if err == io.EOF {
err = nil
}
return
}
func main() {
dateiinhalt_alle_zeilen, _ := readLines(os.Args[1])
fmt.Println(dateiinhalt_alle_zeilen)
// alle Zeilen aus dem Array "dateiinhalt_alle_zeilen"
// werden, durch "|" getrennt, aneinander gereiht
//dateiinhalt_alles_in_einer_zeile := strings.Join(dateiinhalt_alle_zeilen, "|")
//fmt.Println(dateiinhalt_alles_in_einer_zeile)
//fmt.Println(strings.Replace(dateiinhalt_alles_in_einer_zeile, `\n`, "|", -1))
}
=== vorhandene Datei überschreiben bzw. Datei neu anlegen ===
> vi datei_ueberschreiben.go
// vorhandene Datei überschreiben bzw. Datei neu anlegen
package main
import (
"fmt"
"os"
"log"
)
func main() {
var datei string
var text string
datei = "/tmp/go-test.txt"
text = "Hallo Welt!\n"
// die Datei wird neu angelegt, dabei geht der alte Inhalt verloren
os.Create(datei)
// wenn die Datei existiert, dann wird sie überschrieben
f, err1 := os.OpenFile(datei, os.O_RDWR, 0644)
//
// hier wird überprüft, ob es beim öffnen der Datei, einen Fehler gegeben hat
if err1 != nil {
log.Fatal(err1)
}
// hier wird die Datei-Kennzeichnung, der geöffneten Datei, ausgegeben
// fmt.Printf("file descriptor: %v\n", f)
// hier wird in die Datei geschrieben
g, err2 := f.WriteString(text)
fmt.Printf("%v Zeichen wurden geschrieben: %v", g, text)
// hier wird überprüft, ob es beim Schreibvorgang in die Datei, einen Fehler gegeben hat
if err2 != nil {
log.Fatal(err2)
}
}
> go build datei_ueberschreiben.go
> ./datei_ueberschreiben
=== Datei von Beginn an überschreiben (auch unvollständig) ===
> vi datei_schreiben.go
// Datei von Beginn an überschreiben (auch unvollständig)
package main
import (
"fmt"
"os"
"log"
)
func main() {
var datei string
var text string
datei = "/tmp/go-test.txt"
text = "Hallo Welt!\n"
// wenn die Datei noch nicht existiert, dann wird sie angelegt
// wenn die Datei bereits vorhanden ist, dann werden die neuen Daten vom
// Beginn der Datei an geschrieben; werden weniger Daten geschrieben, als
// sich bereits in der Datei befanden, dann bleibt der Rest weiterhin bestehen
f, err1 := os.OpenFile(datei, os.O_RDWR|os.O_CREATE, 0644)
//
// hier wird überprüft, ob es beim öffnen der Datei, einen Fehler gegeben hat
if err1 != nil {
log.Fatal(err1)
}
// hier wird die Datei-Kennzeichnung, der geöffneten Datei, ausgegeben
// fmt.Printf("file descriptor: %v\n", f)
// hier wird in die Datei geschrieben
g, err2 := f.WriteString(text)
fmt.Printf("%v Zeichen wurden geschrieben: %v", g, text)
// hier wird überprüft, ob es beim Schreibvorgang in die Datei, einen Fehler gegeben hat
if err2 != nil {
log.Fatal(err2)
}
}
> go build datei_schreiben.go
> ./datei_schreiben
=== an Datei anhängen ===
> vi datei_anhaengen.go
// an Datei anhängen
package main
import (
"fmt"
"os"
"log"
)
func main() {
var datei string
var text string
datei = "/tmp/go-test.txt"
text = "Hallo Welt!\n"
// wenn die Datei noch nicht existiert, dann wird sie angelegt
// wenn die Datei bereits vorhanden ist, dann werden die neuen Daten vom
// Beginn der Datei an geschrieben; werden weniger Daten geschrieben, als
// sich bereits in der Datei befanden, dann bleibt der Rest weiterhin bestehen
f, err1 := os.OpenFile(datei, os.O_RDWR|os.O_CREATE, 0644)
//
// gehe ans Ende
f.Seek(0, 2)
//
// hier wird überprüft, ob es beim öffnen der Datei, einen Fehler gegeben hat
if err1 != nil {
log.Fatal(err1)
}
// hier wird die Datei-Kennzeichnung, der geöffneten Datei, ausgegeben
// fmt.Printf("file descriptor: %v\n", f)
// hier wird in die Datei geschrieben
g, err2 := f.WriteString(text)
fmt.Printf("%v Zeichen wurden geschrieben: %v", g, text)
// hier wird überprüft, ob es beim Schreibvorgang in die Datei, einen Fehler gegeben hat
if err2 != nil {
log.Fatal(err2)
}
}
> go build datei_anhaengen.go
> ./datei_anhaengen
=== Datei zeilenweise auslesen ===
* [[http://rosettacode.org/wiki/Read_a_file_line_by_line#Go]]
> vi date_zeilenweise_lesen.go
// Datei zeilenweise auslesen
package main
import (
"bufio"
"fmt"
"io"
"log"
"os"
)
func main() {
var datei string
datei = "/tmp/go-test.txt"
// Datei öffnen
f, err := os.Open(datei)
// kontrollieren, ob beim öffnen der Datei alles gut gegangen ist
if err != nil {
log.Fatal(err)
}
// Dateiinhalt aus der Datei in einen Puffer lesen
bf := bufio.NewReader(f)
// Endlosschleife starten
// die geht Zeile für Zeile durch die Datei
for {
// den Puffer zeilenweise auslesen
line, isPrefix, err := bf.ReadLine()
// kontrollieren, ob das Dateiende erreicht wurde
if err == io.EOF {
break
}
// kontrollieren, ob beim lesen aus dem Puffer alles gut gegangen ist
if err != nil {
log.Fatal(err)
}
// nach weiteren Fehler sehen;
// Bufio ist normalerweise 4K groß, ist eine Zeile größer als 4K
// gibt es hier eine Fehlermeldung;
// sollte es einen Fehler geben obwohl die Zeile kleiner als 4K ist,
// dann kann die Datei kaputt sein oder es wir die falsche Datei
// gelesen
if isPrefix {
log.Fatal("Error: Unexpected long line reading", f.Name())
}
// Fertig
// aber beachte, dass die Daten hier als Byte-Stück (byte slice)
// und nicht als "string" vorliegen...
// Bist Du damit zufrieden, dann kannst Du das hier einsetzen.
fmt.Println(string(line))
}
}
> go build date_zeilenweise_lesen.go
> ./date_zeilenweise_lesen
==== Webseite aufrufen ====
hiermit wird eine Webseite aufgerufen und dessen Quellkode ausgegeben
// Web-Aufruf
//
// Beispiele für Aufrufe:
// ./go_web -HOST=www.heise.de -PFAD=/index.html
// ./go_web -HOST www.heise.de -PFAD /
//
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"crypto/tls"
"strings"
"os"
"flag"
)
func GetWebsite(HOST , PFAD string) string {
NoCertVerify := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
NoCertVerifyHttp := &http.Client{Transport: NoCertVerify}
JoinStrings := []string{"https://", HOST, PFAD}
Response, Error := NoCertVerifyHttp.Get(strings.Join(JoinStrings, ""))
if Error != nil {
log.Fatal(Error)
}
Output, Error := ioutil.ReadAll(Response.Body)
Response.Body.Close()
if Error != nil {
log.Fatal(Error)
}
return string(Output)
}
func main() {
HOSTPtr := flag.String("HOST", "NoHOST", "HOST to check")
PFADPtr := flag.String("PFAD", "NoPFAD", "PFAD to check")
flag.Parse()
if *HOSTPtr != "NoHOST" && *PFADPtr != "NoPFAD" {
} else {
fmt.Println("Es wurden nicht alle Schalter benutzt!")
os.Exit(2)
}
WebsiteContent := GetWebsite(*HOSTPtr, *PFADPtr)
if WebsiteContent != "" {
fmt.Println(WebsiteContent)
os.Exit(0)
} else {
fmt.Println("Fehler!")
os.Exit(1)
}
}
> go build go_web.go
> ./go_web -IP=www.heise.de -PFAD=/index.html
> ./go_web -IP www.heise.de -PFAD /
=== Eine Zeichenfolge im Quellkode einer Webseite suchen ===
hiermit wird überprüft, ob eine bestimmte Zeichenfolge im Quellkode einer Webseite enthalten ist:
// Web-Check
//
// Beispiele für Aufrufe:
// go build web_check.go
// ./web_check -HOST=www.heise.de -PFAD=/index.html -STRING='Newsticker'
// ./web_check -HOST www.heise.de -PFAD / -STRING 'Newsticker'
//
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"crypto/tls"
"strings"
"os"
"flag"
)
func GetWebsite(HOST , PFAD string) string {
NoCertVerify := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
NoCertVerifyHttp := &http.Client{Transport: NoCertVerify}
JoinStrings := []string{"https://", HOST, PFAD}
Response, Error := NoCertVerifyHttp.Get(strings.Join(JoinStrings, ""))
if Error != nil {
log.Fatal(Error)
}
Output, Error := ioutil.ReadAll(Response.Body)
Response.Body.Close()
if Error != nil {
log.Fatal(Error)
}
return string(Output)
}
func StringContains(WebsiteString, StringToCompare string) bool {
if strings.Contains(WebsiteString, StringToCompare) {
return true
} else {
return false
}
}
func main() {
HOSTPtr := flag.String("HOST", "NoHOST", "HOST to check")
PFADPtr := flag.String("PFAD", "NoPFAD", "PFAD to check")
STRINGPtr := flag.String("STRING", "NosTRING", "STRING to check")
flag.Parse()
if *HOSTPtr != "NoHOST" && *PFADPtr != "NoPFAD" && *STRINGPtr != "NoSTRING" {
} else {
fmt.Println("Es wurden nicht alle Schalter benutzt!")
os.Exit(2)
}
WebsiteContent := GetWebsite(*HOSTPtr, *PFADPtr)
Status := StringContains(WebsiteContent, *STRINGPtr)
if Status {
//fmt.Println("Läuft!")
os.Exit(0)
} else {
//fmt.Println("Läuft nicht!")
os.Exit(1)
}
}
> go build web_check.go
> ./web_check -HOST www.heise.de -PFAD / -STRING 'Newsticker' && echo OK || echo KO
OK
> ./web_check -HOST www.heise.de -PFAD / -STRING '§' && echo OK || echo KO
KO
==== Golang os exec Examples: Command Start and Run ====
[[https://thedeveloperblog.com/go/os-exec-go]]
package main
import (
"fmt"
"log"
"os"
"os/exec"
)
func main() {
//var psize string
//psize = "9223372036G"
var psize string = "9223372036G"
var film = os.Args[1]
prog := "ffprobe"
cmd := exec.Command(prog, "-v", "error", "-probesize", psize, "-analyzeduration", psize, "-i", film, "-show_streams")
out, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", out)
}
> go build programmstart.go
./programmstart title_t01.mpg
[STREAM]
index=0
codec_name=mpeg2video
codec_long_name=MPEG-2 video
profile=Main
codec_type=video
codec_tag_string=[0][0][0][0]
codec_tag=0x0000
width=720
height=576
...
//In der Praxis wird man eine solche Aufgabe nicht so lösen, sondern dafür eine entsprechende Bibliothek einsetzen.//
=== Verwendung einer Bibliothek ===
[[https://pkg.go.dev/gopkg.in/vansante/go-ffprobe.v2|go-ffprobe]]
package main
import (
"context"
"fmt"
"log"
"os"
"regexp"
"strconv"
"time"
"gopkg.in/vansante/go-ffprobe.v2"
)
func string2int(s string) int {
i, err := strconv.Atoi(s)
if err != nil {
i = 0
}
return i
}
func main() {
var film = os.Args[1]
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancelFn()
data, err := ffprobe.ProbeURL(ctx, film)
if err != nil {
log.Panicf("Error getting data: %v", err)
}
//fmt.Printf("%s\n", data.Streams[0].CodecType)
//fmt.Printf("%s\n", data.FirstSubtitleStream())
for i, s := range data.Streams {
codectype := s.CodecType
sprache, _ := s.TagList.GetString("language")
fmt.Printf("Spur: %v, Typ: %v, Sprache: %v\n", i, codectype, sprache)
if "video" == s.CodecType {
fmt.Printf("Spur: %v, Typ: %v, Width: %v\n", i, codectype, data.Streams[i].Width)
fmt.Printf("Spur: %v, Typ: %v, Height: %v\n", i, codectype, data.Streams[i].Height)
fmt.Printf("Spur: %v, Typ: %v, PAR: %v\n", i, codectype, data.Streams[i].SampleAspectRatio)
fmt.Printf("Spur: %v, Typ: %v, DAR: %v\n", i, codectype, data.Streams[i].DisplayAspectRatio)
fmt.Printf("Spur: %v, Typ: %v, Level: %v\n", i, codectype, data.Streams[i].Level)
fmt.Printf("Spur: %v, Typ: %v, FieldOrder: %v\n", i, codectype, data.Streams[i].FieldOrder)
fmt.Printf("Spur: %v, Typ: %v, FPS: %v\n", i, codectype, data.Streams[i].RFrameRate)
fmt.Printf("Spur: %v, Typ: %v, avg FSP: %v\n", i, codectype, data.Streams[i].AvgFrameRate)
drehung := 0
rotation, _ := s.TagList.GetString("Rotate")
drehlength := len([]rune(rotation))
if drehlength > 0 {
drehung = string2int(rotation)
}
fmt.Printf("Spur: %v, Typ: %v, Drehung: %v Grad\n", i, codectype, drehung)
spieldauer := 0
duration, _ := s.TagList.GetString("DURATION")
durlength := len([]rune(duration))
if durlength > 0 {
var hms [3]int
rsplit := regexp.MustCompile("[:.]")
for g, h := range rsplit.Split(duration, -1)[:3] {
hms[g] = string2int(h)
}
// hms -> H:M:S -> Stunde:Minute:Sekunde
spieldauer = hms[0]*3600 + hms[1]*60 + hms[2]
}
fmt.Printf("Spur: %v, Typ: %v, Spieldauer: %v Sekunden\n", i, codectype, spieldauer)
// Alle TAGs aus dem Container anzeigen
//for n, t := range s.TagList {
// fmt.Printf(" %v, %v\n", n, t)
//}
}
}
}
> go build FFprobeTest.go
> ./FFprobeTest titlet01.mkv
Spur: 0, Typ: video, Sprache: eng
Spur: 0, Typ: video, Width: 720
Spur: 0, Typ: video, Height: 576
Spur: 0, Typ: video, PAR: 16:15
Spur: 0, Typ: video, DAR: 4:3
Spur: 0, Typ: video, Level: 8
Spur: 0, Typ: video, FieldOrder: tt
Spur: 0, Typ: video, FPS: 25/1
Spur: 0, Typ: video, avg FSP: 25/1
Spur: 0, Typ: video, Drehung: 0 Grad
Spur: 0, Typ: video, Spieldauer: 122 Sekunden
Spur: 1, Typ: audio, Sprache: eng
Spur: 2, Typ: subtitle, Sprache: eng
Spur: 3, Typ: subtitle, Sprache: ger