Skip to content

Go Integration

Complete Go examples for Lunar Stream API integration.

Installation

bash
go mod init your-project
go get github.com/go-resty/resty/v2

API Client

go
// lunarstream/client.go
package lunarstream

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/go-resty/resty/v2"
)

type Client struct {
	baseURL   string
	apiKey    string
	projectID string
	http      *resty.Client
}

type ApiResponse struct {
	Data json.RawMessage `json:"data"`
	Meta json.RawMessage `json:"meta"`
}

type MediaServer struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	ServerType string `json:"serverType"`
	Host       string `json:"host"`
	Port       int    `json:"port"`
}

type Livestream struct {
	ID                  string      `json:"id"`
	Name                string      `json:"name"`
	Status              string      `json:"status"`
	StreamKey           string      `json:"streamKey"`
	PushToken           string      `json:"pushToken"`
	RtmpServer          string      `json:"rmtpServer"`
	StreamKeyWithParams string      `json:"streamKeyWithParams"`
	HlsPaths            []string    `json:"hlsPaths"`
	HlsSources          []HlsSource `json:"hlsSources"`
	CreatedAt           string      `json:"createdAt"`
}

type HlsSource struct {
	Name string `json:"name"`
	URL  string `json:"url"`
}

type PushTokenResponse struct {
	LivestreamID       string `json:"livestreamId"`
	PushToken          string `json:"pushToken"`
	StreamKey          string `json:"streamKey"`
	ExpiresAt          string `json:"expiresAt"`
	RtmpURL            string `json:"rtmpUrl"`
	StreamKeyWithToken string `json:"streamKeyWithToken"`
}

func NewClient(apiKey, projectID string) *Client {
	baseURL := os.Getenv("LUNAR_STREAM_API_URL")
	if baseURL == "" {
		baseURL = "https://api.lunarstream.kozow.com/api/v1"
	}

	return &Client{
		baseURL:   baseURL,
		apiKey:    apiKey,
		projectID: projectID,
		http: resty.New().
			SetHeader("ls-api-key", apiKey).
			SetHeader("Content-Type", "application/json"),
	}
}

func (c *Client) GetMediaServers() ([]MediaServer, error) {
	var response ApiResponse
	_, err := c.http.R().
		SetQueryParam("projectId", c.projectID).
		SetResult(&response).
		Get(c.baseURL + "/media-servers/available")

	if err != nil {
		return nil, err
	}

	var servers []MediaServer
	if err := json.Unmarshal(response.Data, &servers); err != nil {
		return nil, err
	}

	return servers, nil
}

func (c *Client) CreateLivestream(name, mediaServerID string) (*Livestream, error) {
	var response ApiResponse
	_, err := c.http.R().
		SetBody(map[string]string{
			"name":          name,
			"projectId":     c.projectID,
			"mediaServerId": mediaServerID,
		}).
		SetResult(&response).
		Post(c.baseURL + "/livestream")

	if err != nil {
		return nil, err
	}

	var stream Livestream
	if err := json.Unmarshal(response.Data, &stream); err != nil {
		return nil, err
	}

	return &stream, nil
}

func (c *Client) GetLivestream(id string) (*Livestream, error) {
	var response ApiResponse
	_, err := c.http.R().
		SetQueryParam("projectId", c.projectID).
		SetResult(&response).
		Get(c.baseURL + "/livestream/" + id)

	if err != nil {
		return nil, err
	}

	var stream Livestream
	if err := json.Unmarshal(response.Data, &stream); err != nil {
		return nil, err
	}

	return &stream, nil
}

func (c *Client) ListLivestreams(page, limit int) ([]Livestream, error) {
	var response ApiResponse
	_, err := c.http.R().
		SetQueryParams(map[string]string{
			"projectId": c.projectID,
			"page":      fmt.Sprintf("%d", page),
			"limit":     fmt.Sprintf("%d", limit),
		}).
		SetResult(&response).
		Get(c.baseURL + "/livestream")

	if err != nil {
		return nil, err
	}

	var streams []Livestream
	if err := json.Unmarshal(response.Data, &streams); err != nil {
		return nil, err
	}

	return streams, nil
}

func (c *Client) DeleteLivestream(id string) error {
	_, err := c.http.R().
		SetQueryParam("projectId", c.projectID).
		Delete(c.baseURL + "/livestream/" + id)

	return err
}

func (c *Client) GeneratePushToken(streamKey string, expiresIn int) (*PushTokenResponse, error) {
	var response ApiResponse
	_, err := c.http.R().
		SetBody(map[string]interface{}{
			"projectId": c.projectID,
			"streamKey": streamKey,
			"expiresIn": expiresIn,
		}).
		SetResult(&response).
		Post(c.baseURL + "/livestream/generate-push-token")

	if err != nil {
		return nil, err
	}

	var tokenResp PushTokenResponse
	if err := json.Unmarshal(response.Data, &tokenResp); err != nil {
		return nil, err
	}

	return &tokenResp, nil
}

func (c *Client) GetStreamInfo(streamKey string) (*Livestream, error) {
	var response ApiResponse
	_, err := resty.New().R().
		SetHeader("Content-Type", "application/json").
		SetResult(&response).
		Get(c.baseURL + "/livestreams/stream-info/" + streamKey)

	if err != nil {
		return nil, err
	}

	var stream Livestream
	if err := json.Unmarshal(response.Data, &stream); err != nil {
		return nil, err
	}

	return &stream, nil
}

HTTP Server Example

go
// main.go
package main

import (
	"encoding/json"
	"log"
	"net/http"
	"os"
	"strconv"

	"your-project/lunarstream"
)

var client *lunarstream.Client

func main() {
	apiKey := os.Getenv("LUNAR_STREAM_API_KEY")
	projectID := os.Getenv("LUNAR_STREAM_PROJECT_ID")

	if apiKey == "" || projectID == "" {
		log.Fatal("LUNAR_STREAM_API_KEY and LUNAR_STREAM_PROJECT_ID are required")
	}

	client = lunarstream.NewClient(apiKey, projectID)

	http.HandleFunc("POST /api/streams", createStream)
	http.HandleFunc("GET /api/streams/{streamKey}", getStreamInfo)
	http.HandleFunc("GET /api/streams", listStreams)
	http.HandleFunc("DELETE /api/streams/{id}", deleteStream)
	http.HandleFunc("POST /api/streams/{streamKey}/refresh-token", refreshToken)

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	log.Printf("Server running on port %s", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}

type CreateStreamRequest struct {
	Name string `json:"name"`
}

type StreamResponse struct {
	ID      string `json:"id,omitempty"`
	Name    string `json:"name"`
	Status  string `json:"status"`
	RtmpURL string `json:"rtmpUrl,omitempty"`
	HlsURL  string `json:"hlsUrl,omitempty"`
}

func createStream(w http.ResponseWriter, r *http.Request) {
	var req CreateStreamRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		jsonError(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	servers, err := client.GetMediaServers()
	if err != nil || len(servers) == 0 {
		jsonError(w, "No media servers available", http.StatusServiceUnavailable)
		return
	}

	stream, err := client.CreateLivestream(req.Name, servers[0].ID)
	if err != nil {
		jsonError(w, "Failed to create stream", http.StatusInternalServerError)
		return
	}

	hlsURL := ""
	if len(stream.HlsSources) > 0 {
		hlsURL = stream.HlsSources[0].URL
	}

	jsonResponse(w, StreamResponse{
		ID:      stream.ID,
		Name:    stream.Name,
		Status:  stream.Status,
		RtmpURL: stream.RtmpServer + "/" + stream.StreamKeyWithParams,
		HlsURL:  hlsURL,
	})
}

func getStreamInfo(w http.ResponseWriter, r *http.Request) {
	streamKey := r.PathValue("streamKey")

	stream, err := client.GetStreamInfo(streamKey)
	if err != nil {
		jsonError(w, "Stream not found", http.StatusNotFound)
		return
	}

	jsonResponse(w, StreamResponse{
		Name:   stream.Name,
		Status: stream.Status,
	})
}

func listStreams(w http.ResponseWriter, r *http.Request) {
	page, _ := strconv.Atoi(r.URL.Query().Get("page"))
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))

	if page < 1 {
		page = 1
	}
	if limit < 1 {
		limit = 10
	}

	streams, err := client.ListLivestreams(page, limit)
	if err != nil {
		jsonError(w, "Failed to list streams", http.StatusInternalServerError)
		return
	}

	jsonResponse(w, map[string]interface{}{"streams": streams})
}

func deleteStream(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")

	if err := client.DeleteLivestream(id); err != nil {
		jsonError(w, "Failed to delete stream", http.StatusInternalServerError)
		return
	}

	jsonResponse(w, map[string]bool{"success": true})
}

func refreshToken(w http.ResponseWriter, r *http.Request) {
	streamKey := r.PathValue("streamKey")
	expiresIn := 7200

	if exp := r.URL.Query().Get("expiresIn"); exp != "" {
		if val, err := strconv.Atoi(exp); err == nil {
			expiresIn = val
		}
	}

	token, err := client.GeneratePushToken(streamKey, expiresIn)
	if err != nil {
		jsonError(w, "Failed to refresh token", http.StatusInternalServerError)
		return
	}

	jsonResponse(w, map[string]string{
		"rtmpUrl":            token.RtmpURL,
		"streamKeyWithToken": token.StreamKeyWithToken,
		"expiresAt":          token.ExpiresAt,
	})
}

func jsonResponse(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(data)
}

func jsonError(w http.ResponseWriter, message string, status int) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(map[string]string{"error": message})
}

Environment Variables

bash
export LUNAR_STREAM_API_KEY="ls_proj_v1_your_api_key_here"
export LUNAR_STREAM_PROJECT_ID="your-project-id-here"
export PORT="8080"

Usage

bash
# Run the server
go run main.go

# Create a stream
curl -X POST http://localhost:8080/api/streams \
  -H "Content-Type: application/json" \
  -d '{"name": "My Stream"}'

# Get stream info
curl http://localhost:8080/api/streams/ee04affe2a669854052102fe762bd715

# List streams
curl "http://localhost:8080/api/streams?page=1&limit=10"

# Refresh token
curl -X POST "http://localhost:8080/api/streams/ee04affe2a669854052102fe762bd715/refresh-token?expiresIn=7200"

Released under the MIT License.