- add documentation
- add more info to README.MD - splited into multiple files
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | ||||
| *.exe | ||||
| sounds/** | ||||
| sounds/** | ||||
| conf.json | ||||
|   | ||||
							
								
								
									
										28
									
								
								README.MD
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								README.MD
									
									
									
									
									
								
							| @@ -1,2 +1,28 @@ | ||||
|  | ||||
|  | ||||
| # Soundr | ||||
| An opensource audio server meant for professional applications | ||||
| Soundr is a simple, open-source, cross-platform audio playing server written in go. | ||||
| It aims to be simple to setup and work in many envoriments. It is also designed to be | ||||
| easy to use and maintain.  | ||||
| Soundr is able to play multiple audio files at the same time. It is able to intigrate well as it uses a REST endpoint. | ||||
| Swagger Documentation for that endpoint is available in `apiDocs.yml`. | ||||
| The software it self is written in go and uses the BEEP library. It is made to be shipped as a single executable. | ||||
| Another target was a minimal dependency tree. | ||||
| Initally it was written to be used with [Bitfocus Companion](https://bitfocus.io/companion) in a more professional envoriment. (A client for Companion is currently WiP) | ||||
|  | ||||
| # Installation | ||||
| Installation is as simple as it gets as it is a single executable. | ||||
| Download one of the releases, drop your sounds into the /sounds folder and run the executable. | ||||
|  | ||||
| # Configuration | ||||
| TODO, no config yet. Soon ports and other settings will be added. | ||||
|  | ||||
| # Usage | ||||
| Drop your sounds into the /sounds. You can play them by sending a GET request to the /v1/play endpoint. | ||||
| You need to know the base64 encoded file name of the sound you want to play. You can get started by querying /v1/list. It will return a list of all sounds with their respective base64 encoded file name. | ||||
| Use that base64 as the `file` parameter in the request. | ||||
|  | ||||
| **Note**: The sounds must be in the format `*.mp3` (more will be supported soon :tm:). | ||||
|  | ||||
| # ToDo | ||||
| - [ ] Add support for other audio formats | ||||
							
								
								
									
										144
									
								
								apiDocs.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								apiDocs.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| openapi: '3.0.2' | ||||
| info: | ||||
|   title: Soundr | ||||
|   version: '1.0' | ||||
| servers: | ||||
|   - url: http://localhost:8082/v1/ | ||||
| paths: | ||||
|   /play: | ||||
|     get: | ||||
|       summary: Plays a sound by it's base64'd name. Will load it to buffer first if not already loaded. | ||||
|       parameters: | ||||
|         - in: query | ||||
|           name: file | ||||
|           description: A base64 encoded version of the file name | ||||
|           schema: | ||||
|             type: string | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 type: object | ||||
|                 properties: | ||||
|                   id: | ||||
|                     type: number | ||||
|                     description: The ID of the playing sound | ||||
|         '400': | ||||
|           description: Bad Request | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 type: object | ||||
|                 properties: | ||||
|                   reason: | ||||
|                     type: string | ||||
|                     description: The error message, in this case probably "file not found" | ||||
|   /buffer: | ||||
|     get: | ||||
|       summary: Loads a sound into the buffer. | ||||
|       parameters: | ||||
|         - in: query | ||||
|           name: file | ||||
|           description: A base64 encoded version of the file name | ||||
|           schema: | ||||
|             type: string | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|            | ||||
|         '400': | ||||
|           description: Bad Request | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 type: object | ||||
|                 properties: | ||||
|                   reason: | ||||
|                     type: string | ||||
|                     description: The error message, in this case probably "file not found" | ||||
|   /bufferAll: | ||||
|     get: | ||||
|       summary: Loads all sounds into the buffer. | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|   /stop: | ||||
|     get: | ||||
|       summary: Stops a given sound by it's ID. | ||||
|       parameters: | ||||
|         - in: query | ||||
|           name: id | ||||
|           description: The ID of the sound to stop | ||||
|           schema: | ||||
|             type: number | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|         '400': | ||||
|           description: Bad Request | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 type: object | ||||
|                 properties: | ||||
|                   reason: | ||||
|                     type: string | ||||
|                     description: The error message | ||||
|   /stopAll: | ||||
|     get: | ||||
|       summary: Stops all sounds. | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|   /current: | ||||
|     get: | ||||
|       summary: Gets the current playing sound(s). | ||||
|       responses: | ||||
|         '200': | ||||
|           description: OK | ||||
|           content: | ||||
|             application/json: | ||||
|               schema: | ||||
|                 type: object | ||||
|                 properties: | ||||
|                   sounds: | ||||
|                     type: array | ||||
|                     items: | ||||
|                       type: object | ||||
|                       properties: | ||||
|                         id: | ||||
|                           type: number | ||||
|                           description: The ID of the sound | ||||
|                         name: | ||||
|                           type: string | ||||
|                           description: The name of the sound | ||||
|                         loaded: | ||||
|                           type: boolean | ||||
|                           description: Whether the sound is loaded into the buffer | ||||
|   /list: | ||||
|    get: # TODO REWORK!!!!! | ||||
|     summary: Lists all sounds in the buffer. | ||||
|     responses: | ||||
|       '200': | ||||
|         description: OK | ||||
|         content: | ||||
|           application/json: | ||||
|             schema: | ||||
|               type: object | ||||
|               properties: | ||||
|                 sounds: | ||||
|                   type: array | ||||
|                   items: | ||||
|                     type: object | ||||
|                     properties: | ||||
|                       name: | ||||
|                         type: string | ||||
|                         description: The name of the sound | ||||
|                       base64: | ||||
|                         type: string | ||||
|                         description: The base64 version of the name | ||||
|                       url: | ||||
|                         type: string | ||||
|                         description: The URL to the sound | ||||
							
								
								
									
										78
									
								
								handlerFunctions.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								handlerFunctions.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/faiface/beep" | ||||
| 	"github.com/faiface/beep/mp3" | ||||
| 	"github.com/faiface/beep/speaker" | ||||
| ) | ||||
|  | ||||
| func BufferSound(file string) bool { | ||||
| 	_, ok := streamMap[file] | ||||
| 	if !ok { | ||||
| 		fmt.Println("Not in memory, loading") | ||||
| 		f, err := os.Open("./sounds/" + file) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		fmt.Println("Opened file") | ||||
| 		streamer, format, _ := mp3.Decode(f) | ||||
| 		speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10)) | ||||
|  | ||||
| 		fmt.Println("Decoded file") | ||||
| 		buffer := beep.NewBuffer(format) | ||||
| 		buffer.Append(streamer) | ||||
| 		streamer.Close() | ||||
| 		fmt.Println("Bufferd file") | ||||
|  | ||||
| 		// Save to streamMap | ||||
| 		streamMap[file] = streamBuf{ | ||||
| 			Streamer: streamer, | ||||
| 			Format:   format, | ||||
| 			Buffer:   buffer, | ||||
| 		} | ||||
| 		return (true) | ||||
| 	} else { | ||||
| 		return (false) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func PlaySound(file string, index int) int { | ||||
| 	playbacks[index] = playback{ | ||||
| 		File:     file, | ||||
| 		IsLoaded: false, | ||||
| 		Streamer: nil, | ||||
| 		Control:  nil, | ||||
| 	} | ||||
|  | ||||
| 	fmt.Println("Playing sound: " + file) | ||||
| 	var buffer *beep.Buffer | ||||
| 	BufferSound(file) | ||||
| 	buffer = streamMap[file].Buffer | ||||
| 	streamer := streamMap[file].Streamer | ||||
|  | ||||
| 	fmt.Println("Trying to play sound") | ||||
| 	shot := buffer.Streamer(0, buffer.Len()) | ||||
|  | ||||
| 	done := make(chan bool) | ||||
| 	ctrl := &beep.Ctrl{Streamer: beep.Seq(shot, beep.Callback(func() { | ||||
| 		done <- true | ||||
| 	})), Paused: false} | ||||
|  | ||||
| 	playbacks[index] = playback{ | ||||
| 		File:     file, | ||||
| 		IsLoaded: true, | ||||
| 		Streamer: streamer, | ||||
| 		Control:  ctrl, | ||||
| 	} | ||||
| 	speaker.Play(ctrl) | ||||
| 	<-done | ||||
| 	fmt.Println("Finished playing sound: " + file) | ||||
| 	delete(playbacks, index) | ||||
| 	return 1 | ||||
| } | ||||
							
								
								
									
										181
									
								
								resources/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								resources/logo.svg
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| After Width: | Height: | Size: 29 KiB | 
							
								
								
									
										236
									
								
								soundr.go
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								soundr.go
									
									
									
									
									
								
							| @@ -1,22 +1,14 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"html" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/faiface/beep" | ||||
| 	"github.com/faiface/beep/mp3" | ||||
| 	"github.com/faiface/beep/speaker" | ||||
| ) | ||||
|  | ||||
| type playback struct { | ||||
| @@ -38,198 +30,66 @@ type streamBuf struct { | ||||
| 	Buffer   *beep.Buffer | ||||
| } | ||||
|  | ||||
| type Configuration struct { | ||||
| 	Port int | ||||
| } | ||||
|  | ||||
| var playbacks map[int]playback | ||||
| var mapMutex = sync.Mutex{} | ||||
|  | ||||
| var streamMap map[string]streamBuf | ||||
|  | ||||
| func BufferSound(file string) bool { | ||||
| 	_, ok := streamMap[file] | ||||
| 	if !ok { | ||||
| 		fmt.Println("Not in memory, loading") | ||||
| 		f, err := os.Open("./sounds/" + file) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		fmt.Println("Opened file") | ||||
| 		streamer, format, err := mp3.Decode(f) | ||||
| 		speaker.Init(format.SampleRate, format.SampleRate.N(time.Second/10)) | ||||
|  | ||||
| 		fmt.Println("Decoded file") | ||||
| 		buffer := beep.NewBuffer(format) | ||||
| 		buffer.Append(streamer) | ||||
| 		streamer.Close() | ||||
| 		fmt.Println("Bufferd file") | ||||
|  | ||||
| 		// Save to streamMap | ||||
| 		streamMap[file] = streamBuf{ | ||||
| 			Streamer: streamer, | ||||
| 			Format:   format, | ||||
| 			Buffer:   buffer, | ||||
| 		} | ||||
| 		return (true) | ||||
| 	} else { | ||||
| 		return (false) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func PlaySound(file string, index int) int { | ||||
| 	playbacks[index] = playback{ | ||||
| 		File:     file, | ||||
| 		IsLoaded: false, | ||||
| 		Streamer: nil, | ||||
| 		Control:  nil, | ||||
| 	} | ||||
|  | ||||
| 	fmt.Println("Playing sound: " + file) | ||||
| 	var buffer *beep.Buffer | ||||
| 	BufferSound(file) | ||||
| 	buffer = streamMap[file].Buffer | ||||
| 	streamer := streamMap[file].Streamer | ||||
|  | ||||
| 	fmt.Println("Trying to play sound") | ||||
| 	shot := buffer.Streamer(0, buffer.Len()) | ||||
|  | ||||
| 	done := make(chan bool) | ||||
| 	ctrl := &beep.Ctrl{Streamer: beep.Seq(shot, beep.Callback(func() { | ||||
| 		done <- true | ||||
| 	})), Paused: false} | ||||
|  | ||||
| 	playbacks[index] = playback{ | ||||
| 		File:     file, | ||||
| 		IsLoaded: true, | ||||
| 		Streamer: streamer, | ||||
| 		Control:  ctrl, | ||||
| 	} | ||||
| 	speaker.Play(ctrl) | ||||
| 	<-done | ||||
| 	fmt.Println("Finished playing sound: " + file) | ||||
| 	delete(playbacks, index) | ||||
| 	return 1 | ||||
| } | ||||
|  | ||||
| func main() { | ||||
|  | ||||
| 	fmt.Println("Welcome to Soundr!") | ||||
|  | ||||
| 	playbacks = make(map[int]playback) | ||||
| 	streamMap = make(map[string]streamBuf) | ||||
|  | ||||
| 	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Query().Get("name"))) | ||||
| 	}) | ||||
| 	// Create /sounds if not exists | ||||
| 	if _, err := os.Stat("./sounds"); os.IsNotExist(err) { | ||||
| 		fmt.Println("Created /sounds folder") | ||||
| 		os.Mkdir("./sounds", 0777) | ||||
| 	} | ||||
|  | ||||
| 	http.HandleFunc("/v1/play", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		var cnt = r.URL.Query().Get("file") | ||||
| 		bytArr, err := base64.StdEncoding.DecodeString(cnt) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 	// Handle config | ||||
| 	fmt.Println("Opening conf.json") | ||||
| 	file, fOpenError := os.Open("conf.json") // Try to open the file | ||||
|  | ||||
| 	if errors.Is(fOpenError, os.ErrNotExist) { // If it does not exist, create it | ||||
| 		fmt.Println("Creating conf.json") | ||||
| 		file, fOpenError = os.Create("conf.json") | ||||
| 		if fOpenError != nil { | ||||
| 			log.Fatal(fOpenError) | ||||
| 		} | ||||
| 		fmt.Println(string(bytArr[:])) | ||||
| 		t, err := os.Stat("./sounds/" + string(bytArr[:])) | ||||
| 		t = t | ||||
| 		if !errors.Is(err, os.ErrNotExist) { | ||||
| 			var currIndex = len(playbacks) | ||||
| 			fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) | ||||
| 		defer file.Close() | ||||
| 		fmt.Println("Writing to conf.json") | ||||
| 		// Write the default config to the file | ||||
| 		json.NewEncoder(file).Encode(Configuration{ | ||||
| 			Port: 8080, | ||||
| 		}) | ||||
| 		fmt.Println("Wrote to conf.json") | ||||
| 	} | ||||
| 	defer file.Close() | ||||
| 	// Decode the config | ||||
| 	decoder := json.NewDecoder(file) | ||||
| 	configuration := Configuration{} | ||||
| 	err := decoder.Decode(&configuration) | ||||
|  | ||||
| 			go PlaySound(string(bytArr[:]), currIndex) | ||||
| 	if err != nil { | ||||
| 		fmt.Println("error:", err) | ||||
| 	} | ||||
|  | ||||
| 		} else { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"file not found\"}") | ||||
| 		} | ||||
|  | ||||
| 	}) | ||||
|  | ||||
| 	http.HandleFunc("/v1/buffer", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		var cnt = r.URL.Query().Get("file") | ||||
| 		bytArr, err := base64.StdEncoding.DecodeString(cnt) | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
| 		t, err := os.Stat("./sounds/" + string(bytArr[:])) | ||||
| 		t = t | ||||
| 		if !errors.Is(err, os.ErrNotExist) { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"ok\"}") | ||||
| 			go BufferSound(string(bytArr[:])) | ||||
| 		} else { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"file not found\"}") | ||||
| 		} | ||||
|  | ||||
| 	}) | ||||
|  | ||||
| 	http.HandleFunc("/v1/stopAll", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		playbacks = make(map[int]playback) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"ok\"}") | ||||
| 		speaker.Clear() | ||||
| 		//fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) | ||||
|  | ||||
| 	}) | ||||
|  | ||||
| 	http.HandleFunc("/v1/stop", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		var cnt, err = strconv.Atoi(r.URL.Query().Get("id")) | ||||
| 		if err != nil { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"invalid id\"}") | ||||
| 		} | ||||
|  | ||||
| 		value, ok := playbacks[cnt] | ||||
| 		if !ok { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"audio not playing\"}") | ||||
| 		} else { | ||||
| 			fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", value) | ||||
| 			value.Control.Paused = true | ||||
| 			value.Control.Streamer = nil | ||||
| 			delete(playbacks, cnt) | ||||
| 		} | ||||
| 		//fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) | ||||
|  | ||||
| 	}) | ||||
|  | ||||
| 	http.HandleFunc("/v1/current", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
|  | ||||
| 		var tempResultSet map[int]playbackWebReturn | ||||
| 		tempResultSet = make(map[int]playbackWebReturn) | ||||
|  | ||||
| 		for index, element := range playbacks { | ||||
| 			tempResultSet[index] = playbackWebReturn{File: element.File, IsLoaded: element.IsLoaded, Id: index} | ||||
| 		} | ||||
|  | ||||
| 		j, err := json.Marshal(tempResultSet) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf("Error: %s", err.Error()) | ||||
| 		} else { | ||||
| 			fmt.Println(string(j)) | ||||
| 		} | ||||
| 		fmt.Fprintf(w, string(j)) | ||||
| 	}) | ||||
|  | ||||
| 	http.HandleFunc("/v1/list", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		var temp [][3]string | ||||
| 		files, err := ioutil.ReadDir("./sounds/") | ||||
| 		if err != nil { | ||||
| 			log.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		for _, f := range files { | ||||
| 			var soundObj [3]string | ||||
| 			soundObj[0] = f.Name() | ||||
| 			soundObj[1] = base64.StdEncoding.EncodeToString([]byte(f.Name())) | ||||
| 			soundObj[2] = r.URL.Host + "/v1/play?file=" + soundObj[1] | ||||
| 			temp = append(temp, soundObj) | ||||
| 		} | ||||
|  | ||||
| 		j, err := json.Marshal(temp) | ||||
| 		if err != nil { | ||||
| 			fmt.Printf("Error: %s", err.Error()) | ||||
| 		} else { | ||||
| 			fmt.Println(string(j)) | ||||
| 		} | ||||
| 		fmt.Fprintf(w, string(j)) | ||||
| 	}) | ||||
| 	log.Fatal(http.ListenAndServe(":8081", nil)) | ||||
| 	// Web server stuff | ||||
| 	// Play route, takes file as parameter, file is base64 encoded | ||||
| 	http.HandleFunc("/v1/play", handlePlay) | ||||
| 	// Buffer route, buffers file | ||||
| 	http.HandleFunc("/v1/buffer", handleBuffer) | ||||
| 	http.HandleFunc("/v1/bufferAll", handleBufferAll) | ||||
| 	http.HandleFunc("/v1/stop", handleStop) | ||||
| 	http.HandleFunc("/v1/stopAll", handleStopAll) | ||||
| 	http.HandleFunc("/v1/current", handleCurrent) | ||||
| 	http.HandleFunc("/v1/list", handleListing) | ||||
|  | ||||
| 	fmt.Println("Listening on port " + fmt.Sprint(configuration.Port)) | ||||
| 	log.Fatal(http.ListenAndServe(":"+fmt.Sprint(configuration.Port), nil)) | ||||
| } | ||||
|   | ||||
							
								
								
									
										208
									
								
								webRoutes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								webRoutes.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,208 @@ | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/faiface/beep/speaker" | ||||
| ) | ||||
|  | ||||
| func handlePlay(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json")  // Set the content type to json | ||||
| 	var cnt = r.URL.Query().Get("file")                 // Retrieve the file name from the query string | ||||
| 	bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists | ||||
| 	if errors.Is(err, os.ErrNotExist) { | ||||
| 		w.WriteHeader(400) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"file not found\"}") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if t.IsDir() { // Make sure it is not a folder we are trying to play | ||||
| 		w.WriteHeader(400) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"target is folder\"}") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var currIndex = len(playbacks)                              // Create a new index for the playback | ||||
| 	fmt.Fprintf(w, "{\"status\":\"ok\", \"id\":%d}", currIndex) // Return a JSON object to the user | ||||
|  | ||||
| 	go PlaySound(string(bytArr[:]), currIndex) // Play the sound | ||||
|  | ||||
| } | ||||
|  | ||||
| // Handle Buffering | ||||
|  | ||||
| func handleBufferAll(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json") // Set the content type to json | ||||
|  | ||||
| 	var temp []string | ||||
| 	files, err := ioutil.ReadDir("./sounds/") // Read the directory | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	// Loop through the files and add the file name to the temp array | ||||
| 	// Also triggers the buffer process for the file | ||||
| 	for _, f := range files { | ||||
| 		temp = append(temp, f.Name()) | ||||
| 		go BufferSound(f.Name()) | ||||
| 	} | ||||
| 	// Return the amount of files buffered | ||||
| 	fmt.Fprintf(w, "{\"status\":\"ok\", \"amount\":%d}", len(temp)) | ||||
|  | ||||
| } | ||||
|  | ||||
| func handleBuffer(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json")  // Set the content type to json | ||||
| 	var cnt = r.URL.Query().Get("file")                 // Retrieve the file name from the query string | ||||
| 	bytArr, err := base64.StdEncoding.DecodeString(cnt) // Decode the base64 string | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	t, err := os.Stat("./sounds/" + string(bytArr[:])) // Check if the file exists | ||||
| 	if errors.Is(err, os.ErrNotExist) { | ||||
| 		w.WriteHeader(400) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"file not found\"}") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	if t.IsDir() { // Make sure it is not a folder we are trying to play | ||||
| 		w.WriteHeader(400) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"target is folder\"}") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	fmt.Fprintf(w, "{\"status\":\"ok\"}") | ||||
| 	go BufferSound(string(bytArr[:])) | ||||
|  | ||||
| } | ||||
|  | ||||
| // Handeling Stop | ||||
|  | ||||
| func handleStop(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json")   // Set the content type to json | ||||
| 	var cnt, err = strconv.Atoi(r.URL.Query().Get("id")) // Retrieve the id, first convert it to an int | ||||
| 	if err != nil { | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"invalid id\"}") | ||||
| 	} | ||||
|  | ||||
| 	value, ok := playbacks[cnt] // Get value from playbacks map | ||||
| 	if !ok { | ||||
| 		w.WriteHeader(400) | ||||
| 		fmt.Fprintf(w, "{\"status\":\"fail\", \"reason\":\"audio not playing\"}") | ||||
| 	} else { | ||||
| 		fmt.Fprintf(w, "{\"status\":\"ok\"}") | ||||
| 		// Stop by pausing first then, set the streamer to nil. Finally delete it from the map | ||||
| 		value.Control.Paused = true | ||||
| 		value.Control.Streamer = nil | ||||
| 		delete(playbacks, cnt) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func handleStopAll(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json") // Set the content type to json | ||||
|  | ||||
| 	// Pause and stop all playbacks | ||||
| 	for _, v := range playbacks { | ||||
| 		v.Control.Paused = true | ||||
| 		v.Control.Streamer = nil | ||||
| 	} | ||||
| 	speaker.Clear() // Clear the speaker and make it shut up | ||||
|  | ||||
| 	// Reset the map | ||||
| 	playbacks = make(map[int]playback) | ||||
|  | ||||
| 	fmt.Fprintf(w, "{\"status\":\"ok\"}") | ||||
| } | ||||
|  | ||||
| func handleCurrent(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json") // Set the content type to json | ||||
|  | ||||
| 	var tempResultSet map[int]playbackWebReturn = make(map[int]playbackWebReturn) // Create a new map to store the results | ||||
| 	// Iterate through the playbacks map and add important information to the tempResultSet map | ||||
| 	for index, element := range playbacks { | ||||
| 		tempResultSet[index] = playbackWebReturn{File: element.File, IsLoaded: element.IsLoaded, Id: index} | ||||
| 	} | ||||
| 	// Convert the map to a JSON object and return it to the user | ||||
| 	j, err := json.Marshal(tempResultSet) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error: %s", err.Error()) | ||||
| 	} else { | ||||
| 		fmt.Println(string(j)) | ||||
| 		fmt.Fprintf(w, string(j)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func handleListing(w http.ResponseWriter, r *http.Request) { | ||||
| 	// Rejct everything else then GET requests | ||||
| 	if r.Method != "GET" { | ||||
| 		http.Error(w, "Method is not supported.", http.StatusNotFound) | ||||
| 		return | ||||
| 	} | ||||
| 	w.Header().Set("Content-Type", "application/json") // Set the content type to json | ||||
|  | ||||
| 	var temp [][3]string | ||||
| 	files, err := ioutil.ReadDir("./sounds/") // Find all files in the sounds directory | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	// Add the file data to the temp array | ||||
| 	for _, f := range files { | ||||
| 		var soundObj [3]string | ||||
| 		soundObj[0] = f.Name() | ||||
| 		soundObj[1] = base64.StdEncoding.EncodeToString([]byte(f.Name())) | ||||
| 		soundObj[2] = r.URL.Host + "/v1/play?file=" + soundObj[1] | ||||
| 		temp = append(temp, soundObj) | ||||
| 	} | ||||
| 	// Convert the array to a JSON object and return it to the user | ||||
| 	j, err := json.Marshal(temp) | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Error: %s", err.Error()) | ||||
| 	} else { | ||||
| 		fmt.Println(string(j)) | ||||
| 		fmt.Fprintf(w, string(j)) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user