Kategorien
Technology

Rewriting my youtube-dl wrapper in Go

Recently I started using my WD MyCloud EX2 network drive to download youtube videos to watch them via Plex. The first iteration of this was a hacky PHP script to download the video, now I’ve rewritten the script as a Go application.

The code is still hacky, it will only work without any concurrent requests – but it still works for me and maybe you are interested to have a starting point for your own adventures. So, here we go:

package main

import (
	"bufio"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strconv"

	"github.com/gorilla/mux"
)

var (
	Log *log.Logger
	Output string
)

func _execute(url string) (error) {
	// reset last output
	Output = ""
	cmd := exec.Command("/usr/local/bin/youtube-dl",
		"-f", "best",
		"--newline",
		"--no-call-home", "--no-check-certificate",
		"-o", "/mnt/HD/HD_a2/Public/Shared Videos/%(uploader)s/%(title)s-%(id)s.%(ext)s",
		url)
	out, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	cmd.Start()
	scanner := bufio.NewScanner(out)
	for scanner.Scan() {
		Output = fmt.Sprintf("%s\n%s", Output, scanner.Text())
	}
	cmd.Wait()

	return nil
}

func showOutput(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Header().Set("Content-Type", "text/plain")
	fmt.Fprintf(w, "%s", Output)
}

func download(w http.ResponseWriter, r *http.Request) {
	url := r.PostFormValue("url")
	go _execute(url)
	w.Header().Set("Location", "/static/success.html")
	w.WriteHeader(http.StatusFound)
}

func redirect(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Location", "/static/")
	w.WriteHeader(http.StatusFound)
}

func main() {
	Log = log.New(os.Stdout, "youtube-ex2", log.Lshortfile)
	port := 8080
	if len(os.Args) == 2 {
		args := os.Args[1:]
		port, _ = strconv.Atoi(args[0])
	}

	r := mux.NewRouter()
	r.HandleFunc("/", redirect)
	r.HandleFunc("/download", download).Methods("POST")
	r.HandleFunc("/progress", showOutput)
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	sport := fmt.Sprintf(":%d", port)
	log.Printf("Listening on %s", sport)
	log.Fatal(http.ListenAndServe(sport, r))
}

The app is meant to be installed in /opt/app and bootstrap has to be available in a subdirectory /opt/app/static/{css|js}. You also have to install youtube-dl, follow the description in my original article.

To compile it, I used the following two commands to make sure it will work on the MyCloud EX2, which is an ARM architecture:

go get
GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build

I also wrote a simple startup script to put into directory /etc/init.d/ and name it youtube.

#!/bin/bash
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="youtube-dl webserver"
NAME=youtube
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x /opt/app/app ] || exit 0

. /etc/system.conf

DAEMON_ARGS="8080"

#
# Function that starts the daemon/service
#
do_start_app()
{
    ps x | grep "/opt/app/app $DAEMON_ARGS" | grep -qv grep
    if [ $? == 0 ]; then
        echo "$NAME is already running "
        return 1
    fi

	echo "Starting $DESC " "$NAME"
	cd /opt/app
	/opt/app/app $DAEMON_ARGS &
	pid=$!
	status=$?
	if [ $status = 0 ]; then
		echo $pid > $PIDFILE
		echo "$NAME started"
	else
		echo "$NAME failed to start"
	fi
}

do_start()
{
	do_start_app
}

do_stop_app()
{
    [ ! -f $PIDFILE ] && echo "$NAME not running ..." && return 0
    PID=`cat $PIDFILE`
	if ps -e | grep -q $PID
	then
        echo "Stopping $DESC" "$NAME $PID"
		kill $PID
        rm $PIDFILE
	fi
}

#
# Function that stops the daemon/service
#
do_stop()
{
	do_stop_app
}

case "$1" in
  start)
	do_start
  ;;
  stop)
    do_stop
	;;
  status)
    ps x | grep "/opt/app/app $DAEMON_ARGS" | grep -qv grep
        if [ $? == 0 ]; then
            echo "$NAME is running "
        else
            echo "$NAME is not running "
        fi
    exit 0
    ;;
  restart|force-reload)
    do_stop && sleep 1 && do_start
    ;;
  *)
	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
	exit 1
	;;
esac

exit 0

Now you can execute the following command to start the Go webserver for the youtube-dl wrapper:

/etc/init.d/youtube start

If you now browse to http://yourmycloud:8080 you should see a simple form to paste the URL of a youtube video (or any other supported source) into and start the download. The webpage will show the output, so you can check if it’s done.

If you want to use this, feel free but make sure you know what you do before destroying your network drive. 😀

Eine Antwort auf „Rewriting my youtube-dl wrapper in Go“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.