github.com/serge-v/zero@v1.0.2-0.20220911142406-af4b6a19e68a/examples/audio/main.go (about)

     1  package main
     2  
     3  import (
     4  	"embed"
     5  	"flag"
     6  	"html/template"
     7  	"io"
     8  	"io/fs"
     9  	"io/ioutil"
    10  	"log"
    11  	"net/http"
    12  	"net/url"
    13  	"path/filepath"
    14  	"regexp"
    15  	"sort"
    16  	"strconv"
    17  	"strings"
    18  
    19  	_ "embed"
    20  	_ "time/tzdata"
    21  )
    22  
    23  type fit []fs.FileInfo
    24  
    25  func (fi fit) Len() int {
    26  	return len(fi)
    27  }
    28  
    29  func (fi fit) Swap(i, j int) {
    30  	fi[i], fi[j] = fi[j], fi[i]
    31  }
    32  
    33  var rex = regexp.MustCompile("[0-9]+")
    34  
    35  func (fi fit) Less(i, j int) bool {
    36  	s1 := rex.FindString(fi[i].Name())
    37  	s2 := rex.FindString(fi[j].Name())
    38  	n1, _ := strconv.Atoi(s1)
    39  	n2, _ := strconv.Atoi(s2)
    40  	return n1 < n2
    41  }
    42  
    43  func getFiles(subdir string) ([]fs.FileInfo, error) {
    44  	files, err := ioutil.ReadDir(filepath.Join(dir, subdir))
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	sort.Sort(fit(files))
    50  	return files, nil
    51  }
    52  
    53  //go:embed *.html *.js favicon.ico
    54  var resourses embed.FS
    55  var templates *template.Template
    56  
    57  //go:embed login_token~.txt
    58  var loginToken string
    59  
    60  func reloadTemplates(w http.ResponseWriter) {
    61  	var err error
    62  	templates, err = template.ParseFiles("player.html")
    63  	if err != nil {
    64  		http.Error(w, err.Error(), http.StatusInternalServerError)
    65  		return
    66  	}
    67  }
    68  
    69  func handlePlayer(w http.ResponseWriter, r *http.Request) {
    70  	if *debug {
    71  		reloadTemplates(w)
    72  	}
    73  
    74  	var err error
    75  
    76  	d := struct {
    77  		Dirs []string
    78  	}{}
    79  
    80  	d.Dirs, err = getDirs()
    81  	if err != nil {
    82  		http.Error(w, err.Error(), http.StatusInternalServerError)
    83  		return
    84  	}
    85  
    86  	if err := templates.ExecuteTemplate(w, "player", d); err != nil {
    87  		http.Error(w, err.Error(), http.StatusInternalServerError)
    88  		return
    89  	}
    90  }
    91  
    92  func getDirs() ([]string, error) {
    93  	files, err := ioutil.ReadDir(dir)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	var list []string
    98  	for _, f := range files {
    99  		if f.IsDir() {
   100  			list = append(list, f.Name())
   101  		}
   102  	}
   103  
   104  	sort.Strings(list)
   105  	return list, nil
   106  }
   107  
   108  func handleFileList(w http.ResponseWriter, r *http.Request) {
   109  	subdir := r.URL.Query().Get("dir")
   110  	files, err := getFiles(subdir)
   111  	if err != nil {
   112  		http.Error(w, err.Error(), http.StatusInternalServerError)
   113  		return
   114  	}
   115  
   116  	var fnames []string
   117  	for _, f := range files {
   118  		if filepath.Ext(f.Name()) != ".mp3" && filepath.Ext(f.Name()) != ".webm" {
   119  			continue
   120  		}
   121  		fnames = append(fnames, `	"`+url.PathEscape(filepath.Join(subdir, f.Name()))+`"`)
   122  	}
   123  	io.WriteString(w, "[\n"+strings.Join(fnames, ",\n")+"\n]\n")
   124  }
   125  
   126  func handleSongList(w http.ResponseWriter, r *http.Request) {
   127  	subdir := r.URL.Query().Get("dir")
   128  	files, err := getFiles(subdir)
   129  	if err != nil {
   130  		http.Error(w, err.Error(), http.StatusInternalServerError)
   131  		return
   132  	}
   133  
   134  	var fnames []string
   135  	for _, f := range files {
   136  		if filepath.Ext(f.Name()) != ".mp3" && filepath.Ext(f.Name()) != ".webm" {
   137  			continue
   138  		}
   139  		fname := strings.TrimSuffix(f.Name(), ".mp3")
   140  		fname = strings.TrimSuffix(fname, ".web,")
   141  		fnames = append(fnames, fname)
   142  	}
   143  	w.Header().Set("Content-Type", "text/html")
   144  	io.WriteString(w, strings.Join(fnames, "<hr>\n"))
   145  }
   146  
   147  func authHandler(next http.Handler) http.Handler {
   148  	f := func(w http.ResponseWriter, r *http.Request) {
   149  		token := r.URL.Query().Get("token")
   150  		if token != "" && loginToken == token {
   151  			c := http.Cookie{Name: "token", Value: token}
   152  			http.SetCookie(w, &c)
   153  			http.Redirect(w, r, "/", http.StatusFound)
   154  			return
   155  		}
   156  
   157  		c, err := r.Cookie("token")
   158  		if err != nil || c.Value != loginToken {
   159  			http.Error(w, "invalid cookie", http.StatusForbidden)
   160  			return
   161  		}
   162  
   163  		next.ServeHTTP(w, r)
   164  	}
   165  	return http.HandlerFunc(f)
   166  }
   167  
   168  var debug = flag.Bool("d", false, "debug")
   169  var dir = "/audiofiles/"
   170  
   171  func main() {
   172  	flag.Parse()
   173  
   174  	var err error
   175  
   176  	templates, err = template.ParseFS(resourses, "player.html")
   177  	if err != nil {
   178  		log.Fatal(err)
   179  	}
   180  
   181  	if *debug {
   182  		dir = "." + dir
   183  	}
   184  
   185  	mux := http.NewServeMux()
   186  
   187  	res := http.FileServer(http.FS(resourses))
   188  	mux.Handle("/favicon.ico", res)
   189  	mux.Handle("/player.js", res)
   190  	mux.HandleFunc("/", handlePlayer)
   191  	mux.HandleFunc("/files", handleFileList)
   192  	mux.HandleFunc("/songlist", handleSongList)
   193  	mux.Handle("/audio/", http.StripPrefix("/audio/", http.FileServer(http.Dir(dir))))
   194  
   195  	log.Println("starting on 8101")
   196  	if err := http.ListenAndServe("127.0.0.1:8101", authHandler(mux)); err != nil {
   197  		log.Fatal(err)
   198  	}
   199  }