asingcrow/dlserver/main.go

230 lines
6.3 KiB
Go

package main
import (
"bufio"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"regexp"
"runtime"
"strconv"
"time"
"github.com/gorilla/mux"
)
func limitLength(s string, length int) string {
if len(s) < length {
return s
}
return s[:length]
}
// globals, huh, faster than channels and oddly more appropriate, we care less about races than feedback.
var lastPercentage float64
var lastFilename string
func convertFile(sourceFileName string, destinationFilename string) error {
launch := "./ffmpeg"
if runtime.GOOS == "windows" {
launch += ".exe"
}
os.Remove(destinationFilename)
DurationRe := regexp.MustCompile(`DURATION *: ([0-9]+):([0-9]+):([0-9]+)\.([0-9]+)`)
FrameRe := regexp.MustCompile(`frame= ?([0-9]+) `)
FPSValue := 0.0
finalFramesValue := 0.0
FPSRe := regexp.MustCompile(`, ([0-9\.]+) fps,`)
fmt.Printf("%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", launch, "-loglevel", "repeat+level+verbose", "-i", sourceFileName, "-c:v", "libvpx", "-crf", "10", "-b:v", "1M", "-c:a", "libvorbis", "-y", destinationFilename)
cmd := exec.Command(launch, "-progress", "pipe:2", "-i", sourceFileName, "-c:v", "libvpx", "-crf", "10", "-b:v", "1M", "-c:a", "libvorbis", "-y", destinationFilename)
stdout, err := cmd.StderrPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
// Start reading from the file with a reader.
reader := bufio.NewReader(stdout)
var line string
for {
line, err = reader.ReadString('\n')
if err != nil && err != io.EOF {
break
}
fpses := FPSRe.FindAllStringSubmatch(line, 1)
if len(fpses) > 0 {
fmt.Printf("FPS' is %v\n", fpses)
FPSValue, _ = strconv.ParseFloat(fpses[0][1], 64)
}
durations := DurationRe.FindAllStringSubmatch(line, 1)
if len(durations) > 0 {
fmt.Printf("Durations is %v\n", durations)
if FPSValue != 0.0 {
// we have an fps, we can work out max frames!
hrs, _ := strconv.ParseFloat(durations[0][1], 64)
mins, _ := strconv.ParseFloat(durations[0][2], 64)
secs, _ := strconv.ParseFloat(durations[0][3], 64)
aaand, _ := strconv.ParseFloat("0."+durations[0][4], 64)
total := (hrs * 60 * 60) + (mins * 60) + secs + aaand
finalFramesValue = total * FPSValue
fmt.Printf("%0f:%0f:%0f%0.4f * %0.2f = %0.2f seconds = %0.2f frames\n", hrs, mins, secs, aaand, FPSValue, total, finalFramesValue)
}
}
currentFrames := FrameRe.FindAllStringSubmatch(line, 1)
if len(currentFrames) > 0 {
currentFrame, _ := strconv.ParseFloat(currentFrames[0][1], 64)
percentage := currentFrame / finalFramesValue * 100
lastPercentage = (percentage / 2) + 50
fmt.Printf("Frame %0f / %0f = Percent : %0.2f\n", currentFrame, finalFramesValue, lastPercentage)
}
if len(fpses) == 0 && len(durations) == 0 && len(currentFrames) == 0 {
//fmt.Printf("X: %s", line)
}
if err != nil {
break
}
}
if err != io.EOF {
fmt.Printf(" > Failed with error: %v\n", err)
return err
}
lastPercentage = 100
fmt.Printf("Setting to %0f percent.\n", lastPercentage)
os.Remove(sourceFileName)
go func() {
time.Sleep(10 * time.Second)
lastPercentage = 0
}()
return nil
}
func downloadFile(sourceURL string, outputFile string, statusChannel chan float64, nameChannel chan string) error {
progressBackwards := false
lastLastPercentage := 0.0
launch := "./youtube-dl"
if runtime.GOOS == "windows" {
launch += ".exe"
}
fmt.Printf("%s %s %s %s %s\n", launch, sourceURL, "-o", outputFile, "--newline")
cmd := exec.Command(launch, sourceURL, "-o", outputFile, "--newline")
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
re := regexp.MustCompile(`([0-9.]+)%`)
reName := regexp.MustCompile(`Merging formats into "([^"]+)"`)
// Start reading from the file with a reader.
reader := bufio.NewReader(stdout)
var line string
for {
line, err = reader.ReadString('\n')
if err != nil && err != io.EOF {
break
}
percentages := re.FindAllStringSubmatch(line, 1)
if len(percentages) > 0 {
p, err := strconv.ParseFloat(string(percentages[0][1]), 64)
if err != nil {
fmt.Printf("What kind of number is %v", percentages[0][1])
} else {
if lastLastPercentage > p {
progressBackwards = true
}
lastLastPercentage = p
if progressBackwards {
// youtube-dl does video, then audio - it's not really half but an estimation
lastPercentage = float64(25) + p/4
} else {
lastPercentage = p / 4
}
statusChannel <- lastPercentage
}
}
fmt.Printf("%s\n", line)
if err != nil {
break
}
filenames := reName.FindAllStringSubmatch(line, 1)
if len(filenames) > 0 {
fmt.Printf("Filename: %v", filenames)
lastFilename = filenames[0][1]
nameChannel <- filenames[0][1]
}
}
if err := cmd.Wait(); err != nil {
log.Fatal(err)
}
if err != io.EOF {
fmt.Printf(" > Failed with error: %v\n", err)
return err
}
return convertFile(lastFilename, "converted.webm")
}
func HomeHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "Hello %s\n", "world")
}
var c chan float64
var n chan string
func FetchHandler(w http.ResponseWriter, r *http.Request) {
//vars := mux.Vars(r)
w.WriteHeader(http.StatusOK)
if lastFilename != "" {
os.Remove(lastFilename)
}
go downloadFile(r.FormValue("url"), "test", c, n)
fmt.Fprint(w, "OK\n")
}
func StatusHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type", "application/json")
w.WriteHeader(http.StatusOK)
// Oddly, using channels here is slower and we don't actually care if there's a race condition.
/* if len(c) > 0 {
lastPercentage = <-c
}
if len(n) > 0 {
lastFilename = <-n
} */
fmt.Printf("Returning %0.2f for %s\n", lastPercentage, lastFilename)
fmt.Fprintf(w, `{"filename": "%s","percentage":%0.2f}`+"\n", lastFilename, lastPercentage)
}
func main() {
if len(os.Args) > 0 {
os.Chdir(os.Args[1])
}
r := mux.NewRouter()
r.SkipClean(true)
r.Path("/get").Queries("url", "{.*}").HandlerFunc(FetchHandler)
r.HandleFunc("/status/", StatusHandler)
r.HandleFunc("/", HomeHandler)
http.Handle("/", r)
c = make(chan float64, 999)
n = make(chan string, 999)
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:10435",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}