music-library/main.go

204 lines
4.8 KiB
Go
Raw Permalink Normal View History

2023-02-14 21:59:56 +01:00
package main
import (
"bytes"
"crypto/sha512"
"encoding/json"
"fmt"
2023-02-16 14:27:18 +01:00
"io"
2023-02-25 12:13:04 +01:00
"log"
2023-02-16 12:16:04 +01:00
"math/rand"
2023-02-14 21:59:56 +01:00
"net/http"
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")
2023-02-16 14:27:18 +01:00
r.HandleFunc("/file/{file}/hash/", GetFileHashHandler).Methods("GET")
r.HandleFunc("/file/{file}/format/{format}/", GetEncodeFileHandler).Methods("GET")
2023-02-16 12:16:04 +01:00
r.HandleFunc("/file/", GetAllFilesHandler).Methods("GET")
2023-02-14 21:59:56 +01:00
2023-02-25 12:13:04 +01:00
log.Printf("Starting music server on port %d\n", HttpPort)
2023-02-14 21:59:56 +01:00
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
}
2023-02-16 14:36:52 +01:00
w.Header().Add("Content-Type", "application/json")
2023-02-14 21:59:56 +01:00
hash := sha512.Sum512(b.Bytes())
2023-02-16 14:36:52 +01:00
if err := json.NewEncoder(w).Encode(NewHash(hash[:])); err != nil {
2023-02-14 21:59:56 +01:00
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) {
2023-02-16 15:15:41 +01:00
fileName, err := DecodeBase64String(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
}
2023-02-16 14:27:18 +01:00
func GetFileHashHandler(w http.ResponseWriter, r *http.Request) {
2023-02-16 15:15:41 +01:00
fileName, err := DecodeBase64String(mux.Vars(r)["file"])
2023-02-16 14:27:18 +01:00
if err != nil {
InternalServerError(w, err)
return
}
filePath := filepath.Join(LibraryPath, ".songs", fileName)
f, err := os.Open(filePath)
if err != nil {
InternalServerError(w, err)
return
}
defer f.Close()
hasher := sha512.New()
if _, err := io.Copy(hasher, f); err != nil {
InternalServerError(w, err)
return
}
2023-02-16 14:36:52 +01:00
w.Header().Add("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(NewHash(hasher.Sum(nil))); err != nil {
2023-02-16 14:27:18 +01:00
InternalServerError(w, err)
return
}
}
2023-02-14 21:59:56 +01:00
func GetPlaylistHandler(w http.ResponseWriter, r *http.Request) {
2023-02-16 15:15:41 +01:00
playlistName, err := DecodeBase64String(mux.Vars(r)["playlist"])
if err != nil {
InternalServerError(w, err)
return
}
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"]
2023-02-16 15:15:41 +01:00
fileName, err := DecodeBase64String(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)
2023-02-25 12:13:04 +01:00
log.Printf("encode file to %s: '%s' -> '%s'\n", format, inputFilePath, outputFilePath)
2023-02-16 12:16:04 +01:00
if err := EncodeVideo2Mp3(inputFilePath, outputFilePath); err != nil {
2023-02-16 12:16:04 +01:00
InternalServerError(w, err)
return
}
http.ServeFile(w, r, outputFilePath)
}