music-library/main.go

173 lines
4.1 KiB
Go
Raw Normal View History

2023-02-14 21:59:56 +01:00
package main
import (
"bytes"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
2023-02-16 12:16:04 +01:00
"math/rand"
2023-02-14 21:59:56 +01:00
"net/http"
2023-02-16 14:08:01 +01:00
"net/url"
2023-02-16 12:16:04 +01:00
"os"
"os/exec"
2023-02-14 21:59:56 +01:00
"path/filepath"
"git.milar.in/milarin/envvars/v2"
2023-02-16 12:16:04 +01:00
"git.milar.in/milarin/slices"
2023-02-14 21:59:56 +01:00
"github.com/gorilla/mux"
)
var (
HttpIntf = envvars.String("HTTP_INTERFACE", "")
HttpPort = envvars.Uint16("HTTP_PORT", 80)
LibraryPath = envvars.String("LIBRARY_PATH", "/music")
)
func main() {
2023-02-16 12:16:04 +01:00
if _, err := exec.LookPath("ffmpeg"); err != nil {
panic("ffmpeg not found in PATH")
}
2023-02-14 21:59:56 +01:00
r := mux.NewRouter()
r.HandleFunc("/all/", GetAllHandler).Methods("GET")
r.HandleFunc("/all/hash/", GetAllHashHandler).Methods("GET")
2023-02-16 12:16:04 +01:00
r.HandleFunc("/playlist-names/", GetPlaylistNamesHandler).Methods("GET")
2023-02-14 21:59:56 +01:00
r.HandleFunc("/playlist/{playlist}/", GetPlaylistHandler).Methods("GET")
2023-02-16 12:16:04 +01:00
r.HandleFunc("/file/{file}/", GetFileHandler).Methods("GET")
r.HandleFunc("/file/{file}/{format}/", GetEncodeFileHandler).Methods("GET")
r.HandleFunc("/file/", GetAllFilesHandler).Methods("GET")
2023-02-14 21:59:56 +01:00
fmt.Printf("Starting music server on port %d\n", HttpPort)
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", HttpIntf, HttpPort), r); err != nil {
panic(err)
}
}
func GetAllHashHandler(w http.ResponseWriter, r *http.Request) {
2023-02-16 12:16:04 +01:00
playlists, err := GetAllPlaylists()
2023-02-14 21:59:56 +01:00
if err != nil {
InternalServerError(w, err)
return
}
b := &bytes.Buffer{}
if err := json.NewEncoder(b).Encode(playlists); err != nil {
InternalServerError(w, err)
return
}
hash := sha512.Sum512(b.Bytes())
if _, err := fmt.Fprint(w, hex.EncodeToString(hash[:])); err != nil {
InternalServerError(w, err)
return
}
}
func GetAllHandler(w http.ResponseWriter, r *http.Request) {
2023-02-16 12:16:04 +01:00
playlists, err := GetAllPlaylists()
2023-02-14 21:59:56 +01:00
if err != nil {
InternalServerError(w, err)
return
}
w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(playlists); err != nil {
InternalServerError(w, err)
return
}
}
2023-02-16 12:16:04 +01:00
func GetFileHandler(w http.ResponseWriter, r *http.Request) {
fileName, err := url.QueryUnescape(mux.Vars(r)["file"])
if err != nil {
InternalServerError(w, err)
return
}
2023-02-14 21:59:56 +01:00
http.ServeFile(w, r, filepath.Join(LibraryPath, ".songs", fileName))
2023-02-14 21:59:56 +01:00
}
func GetPlaylistHandler(w http.ResponseWriter, r *http.Request) {
2023-02-16 12:16:04 +01:00
playlistName := mux.Vars(r)["playlist"]
2023-02-14 21:59:56 +01:00
2023-02-16 12:16:04 +01:00
songs, err := GetSongsByPlaylist(playlistName)
2023-02-14 21:59:56 +01:00
if err != nil {
InternalServerError(w, err)
return
}
2023-02-16 12:16:04 +01:00
playlist := Playlist{
Name: playlistName,
Songs: songs,
}
2023-02-14 21:59:56 +01:00
w.Header().Add("Content-Type", "application/json")
2023-02-16 12:16:04 +01:00
if err := json.NewEncoder(w).Encode(playlist); err != nil {
2023-02-14 21:59:56 +01:00
InternalServerError(w, err)
return
}
}
2023-02-16 12:16:04 +01:00
func GetPlaylistNamesHandler(w http.ResponseWriter, r *http.Request) {
playlistNames, err := GetAllPlaylistNames()
2023-02-14 21:59:56 +01:00
if err != nil {
InternalServerError(w, err)
return
}
w.Header().Add("Content-Type", "application/json")
2023-02-16 12:16:04 +01:00
if err := json.NewEncoder(w).Encode(playlistNames); err != nil {
InternalServerError(w, err)
return
}
}
func GetAllFilesHandler(w http.ResponseWriter, r *http.Request) {
fileDirectory := filepath.Join(LibraryPath, ".songs")
entries, err := os.ReadDir(fileDirectory)
if err != nil {
InternalServerError(w, err)
return
}
files := slices.Filter(entries, IsRegular)
fileNames := slices.Map(files, FsEntry2Name)
w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(fileNames); err != nil {
2023-02-14 21:59:56 +01:00
InternalServerError(w, err)
return
}
}
2023-02-16 12:16:04 +01:00
func GetEncodeFileHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
format := vars["format"]
fileName, err := url.QueryUnescape(vars["file"])
2023-02-16 14:08:01 +01:00
if err != nil {
InternalServerError(w, err)
return
}
2023-02-16 12:16:04 +01:00
inputFilePath := filepath.Join(LibraryPath, ".songs", fileName)
outputFilePath := filepath.Join(os.TempDir(), fmt.Sprintf("%d.%s", rand.Int(), format))
defer os.Remove(outputFilePath)
fmt.Printf("encode file to %s: '%s' -> '%s'\n", format, inputFilePath, outputFilePath)
cmd := exec.Command("ffmpeg", "-i", inputFilePath, "-vn", outputFilePath)
if err := cmd.Start(); err != nil {
InternalServerError(w, err)
return
}
if err := cmd.Wait(); err != nil {
InternalServerError(w, err)
return
}
http.ServeFile(w, r, outputFilePath)
}