package main import ( "bufio" "bytes" "crypto/md5" "embed" "encoding/json" "flag" "fmt" "html/template" "io" "io/ioutil" "log" "net/http" "os" "path" "strings" ) var ( templates *template.Template //go:embed templates fs embed.FS config *Config ) func main() { var configPath string var rescan bool flag.StringVar(&configPath, "config", "config.yaml", "Path to the configuration file") flag.BoolVar(&rescan, "rescan", false, "Rescan the data direcory") flag.Parse() var err error config, err = NewConfigParseFile(configPath) if err != nil { log.Fatal(err.Error()) } if rescan { rescanFiles() return } { entry, err := os.Stat(path.Join(config.DataDirectory, "tmp")) if os.IsNotExist(err) { err = os.Mkdir(path.Join(config.DataDirectory, "tmp"), os.ModeDir) if err != nil { log.Printf("FATAL: can't create tmp directory: %s", err.Error()) os.Exit(-1) } } if err != nil { log.Printf("FATAL: can't stat tmp directory: %s", err.Error()) os.Exit(-2) } if !entry.IsDir() { log.Printf("FATAL: tmp path isn't directory") os.Exit(-3) } } templates = template.Must(template.ParseFS(fs, "templates/*")) log.Printf("Loaded templates: %s", templates.DefinedTemplates()) videoFS := http.FileServer(http.Dir(config.DataDirectory)) adminMux := NewAdminApi() mux := http.NewServeMux() mux.HandleFunc("/", indexHandler) mux.HandleFunc("/video/index.json", videoIndexHandler) mux.Handle("/video/", videoFS) mux.Handle("/admin/", http.StripPrefix("/admin", adminMux)) if len(config.ApplicationPrefix) > 0 { http.ListenAndServe(config.ListenAddress, http.StripPrefix(config.ApplicationPrefix, NewLogger(mux))) } else { http.ListenAndServe(config.ListenAddress, NewLogger(mux)) } } func indexHandler(w http.ResponseWriter, r *http.Request) { var v struct { ApplicationName string } v.ApplicationName = config.ApplicationName err := executeTemplate(w, "index.html", &v) if err != nil { log.Printf("Error executing temlate \"index.html\": %s", err.Error()) } } func videoIndexHandler(w http.ResponseWriter, r *http.Request) { var index VideoIndex index.Success = true index.Generation = config.Generation dir, err := os.ReadDir(path.Join(config.DataDirectory, "video")) if err != nil { log.Printf("can't red video directory: %s", err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } for _, entry := range dir { if entry.Type().IsRegular() && path.Ext(entry.Name()) == ".json" && entry.Name() != "index.json" { var v Video rawInfo, err := ioutil.ReadFile(path.Join(config.DataDirectory, "video", entry.Name())) if err != nil { log.Printf("can't read info for %s: %s", entry.Name(), err.Error()) continue } err = json.Unmarshal(rawInfo, &v) if err != nil { log.Printf("can't read info for %s: %s", entry.Name(), err.Error()) continue } index.Videos = append(index.Videos, v) index.Count++ } } indexRaw, err := json.Marshal(index) if err != nil { log.Printf("can't marshal index: %s", err.Error()) http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write(indexRaw) } func executeTemplate(w http.ResponseWriter, name string, data interface{}) (err error) { var buf bytes.Buffer bufferRW := bufio.NewReadWriter(bufio.NewReader(&buf), bufio.NewWriter(&buf)) err = templates.ExecuteTemplate(bufferRW, name, data) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } bufferRW.Flush() w.WriteHeader(http.StatusOK) io.Copy(w, bufferRW) return } func rescanFiles() { var index VideoIndex index.Generation = config.Generation index.Success = true dirPath := path.Join(config.DataDirectory, "video") dir, err := os.ReadDir(dirPath) if err != nil { log.Printf("can't read data directory: %s", err.Error()) } for _, entry := range dir { if entry.Type().IsRegular() { if !strings.HasSuffix(entry.Name(), ".json") { var v Video file, err := os.Open(path.Join(dirPath, entry.Name())) if err != nil { log.Printf("can't read info file: %s", err.Error()) } v.Name = entry.Name() hashsum := md5.New() _, err = io.Copy(hashsum, file) if err != nil { log.Printf("can't read info file: %s", err.Error()) } hashstring := fmt.Sprintf("%x", hashsum.Sum(nil)) v.ID = hashstring ext := path.Ext(entry.Name()) err = os.Rename(path.Join(dirPath, entry.Name()), path.Join(dirPath, string(hashstring)+ext)) if err != nil { log.Printf("can't move file: %s", err.Error()) } if info, _ := os.Stat(path.Join(dirPath, v.ID+".json")); info == nil { rawInfo, err := json.Marshal(&v) if err != nil { log.Printf("can't write info: %s", err.Error()) } err = ioutil.WriteFile(path.Join(dirPath, v.ID+".json"), rawInfo, os.ModePerm) if err != nil { log.Printf("can't write info: %s", err.Error()) } } index.Videos = append(index.Videos, v) index.Count++ } } } rawIndex, err := json.Marshal(&index) if err != nil { log.Fatalf("can't create index: %s", err) } err = ioutil.WriteFile(path.Join(dirPath, "index.json"), rawIndex, os.ModePerm) if err != nil { log.Fatalf("can't create index: %s", err) } }