Files
opal/internal/httpserver/api/libraries.go

159 lines
4.2 KiB
Go

package api
import (
"encoding/json"
"fmt"
"net/http"
"opal/internal/config"
"os"
"strings"
"github.com/gabriel-vasile/mimetype"
)
func RegisterLibraryEndpoints() {
ProbeLibraries()
http.HandleFunc("/libraries", EndpointLibraries)
http.HandleFunc("/libraries/", EndpointLibraryList)
}
type Library struct {
Name string `json:"Name"`
PathName string `json:"PathName"`
Path string `json:"-"` //when marshalled, this will be excluded
}
var Libraries []Library
type endpointLibraries struct {
AvailableLibraries []Library
}
type LibraryListEntry struct {
Name string
Type string //file, movie, show, directory, may add more filetypes in future
}
type endpointLibraryList struct {
LibraryInfo Library
Listing []LibraryListEntry
}
func EndpointLibraries(w http.ResponseWriter, r *http.Request) {
var ret endpointLibraries
ret.AvailableLibraries = Libraries
retJson, err := json.Marshal(ret)
if err != nil {
fmt.Println("API warning: failed to marshal /libraries response")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(retJson)
}
// TODO/NON-CRITICAL BUG FIX: web client sends /libraries//{path}/ that then redirects to /libraries/{path}/, should put this comment in the web client code but im too lazy
func EndpointLibraryList(w http.ResponseWriter, r *http.Request) {
url_split := strings.Split(r.URL.Path, "/")
if url_split[0] != "" || url_split[1] != "libraries" { //impossible for this to fail unless there is something really wrong
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if len(url_split) < 4 {
http.Error(w, "Malformed request", http.StatusBadRequest)
return
}
library_string := url_split[2]
path := strings.Join(url_split[3:], "/")
var ret endpointLibraryList
//TODO: use a map instead
for _, lib := range Libraries {
if lib.PathName != library_string {
continue
}
ret.LibraryInfo = lib
}
if ret.LibraryInfo.Name == "" { //not found in Libraries because string un-initialised
http.Error(w, "Malformed request", http.StatusBadRequest) //TODO: library not found (should return 404?)
return
}
//TODO: this is probably a vulnerability
directoryListing, err := os.ReadDir(ret.LibraryInfo.Path + "/" + path)
if err != nil {
fmt.Printf("API warning: potential server error or malicious client: %s\n", err.Error())
http.Error(w, "Malformed request", http.StatusBadRequest)
return
}
for _, directoryEntry := range directoryListing {
var entry LibraryListEntry
if directoryEntry.IsDir() {
entry.Name = directoryEntry.Name()
entry.Type = "directory"
}
//TODO: update to use opal.cfg files to determine if movie or show
if directoryEntry.Type().IsRegular() {
//TODO: formatted string?
filePath := ret.LibraryInfo.Path + "/" + path + "/" + directoryEntry.Name()
mtype, err := mimetype.DetectFile(filePath)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
entry.Name = directoryEntry.Name()
if strings.HasPrefix(mtype.String(), "video/") {
entry.Type = "video"
} else {
entry.Type = "file"
}
}
ret.Listing = append(ret.Listing, entry)
}
retJson, err := json.Marshal(ret)
if err != nil {
fmt.Printf("API warning: failed to marshal library listing response for %s\n", library_string)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(retJson)
}
func ProbeLibraries() {
libraries_config := config.FindNode("/libraries", &config.RootConfigNode)
if libraries_config == nil {
fmt.Println("Error: unable to find config node for libraries")
os.Exit(1)
}
for _, child := range libraries_config.Children {
var lib Library
lib.Name = config.FetchValue(child.Name, "name", false)
lib.PathName = config.FetchValue(child.Name, "path_name", false)
lib.Path = config.FetchValue(child.Name, "path", false)
if strings.HasPrefix("ERROR", lib.Name) || strings.HasPrefix("ERROR", lib.PathName) {
fmt.Printf("Error: %s: bad config\n", child.Name)
os.Exit(1)
}
Libraries = append(Libraries, lib)
}
}