Rewriting my youtube-dl wrapper in Go
Von Carsten
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 this 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. 😀