Skip to content

Java Integration

Complete Java examples for Lunar Stream API integration using Spring Boot.

Dependencies (Maven)

xml
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents.client5</groupId>
        <artifactId>httpclient5</artifactId>
        <version>5.2.1</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

API Client

java
// LunarStreamClient.java
package com.example.lunarstream;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hc.client5.http.classic.methods.*;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class LunarStreamClient {
    private final String baseUrl;
    private final String apiKey;
    private final String projectId;
    private final ObjectMapper objectMapper;
    private final CloseableHttpClient httpClient;

    public LunarStreamClient(String apiKey, String projectId) {
        this.baseUrl = System.getenv().getOrDefault(
            "LUNAR_STREAM_API_URL", 
            "https://api.lunarstream.kozow.com/api/v1"
        );
        this.apiKey = apiKey;
        this.projectId = projectId;
        this.objectMapper = new ObjectMapper();
        this.httpClient = HttpClients.createDefault();
    }

    public JsonNode getMediaServers() throws IOException {
        String url = baseUrl + "/media-servers/available?projectId=" + projectId;
        HttpGet request = new HttpGet(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        return executeRequest(request);
    }

    public JsonNode createLivestream(String name, String mediaServerId) throws IOException {
        String url = baseUrl + "/livestream";
        HttpPost request = new HttpPost(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        Map<String, String> body = new HashMap<>();
        body.put("name", name);
        body.put("projectId", projectId);
        body.put("mediaServerId", mediaServerId);

        request.setEntity(new StringEntity(
            objectMapper.writeValueAsString(body),
            ContentType.APPLICATION_JSON
        ));

        return executeRequest(request);
    }

    public JsonNode getLivestream(String id) throws IOException {
        String url = baseUrl + "/livestream/" + id + "?projectId=" + projectId;
        HttpGet request = new HttpGet(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        return executeRequest(request);
    }

    public JsonNode listLivestreams(int page, int limit) throws IOException {
        String url = String.format("%s/livestream?projectId=%s&page=%d&limit=%d",
            baseUrl, projectId, page, limit);
        HttpGet request = new HttpGet(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        return executeRequest(request);
    }

    public JsonNode deleteLivestream(String id) throws IOException {
        String url = baseUrl + "/livestream/" + id + "?projectId=" + projectId;
        HttpDelete request = new HttpDelete(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        return executeRequest(request);
    }

    public JsonNode generatePushToken(String streamKey, int expiresIn) throws IOException {
        String url = baseUrl + "/livestream/generate-push-token";
        HttpPost request = new HttpPost(url);
        request.setHeader("ls-api-key", apiKey);
        request.setHeader("Content-Type", "application/json");

        Map<String, Object> body = new HashMap<>();
        body.put("projectId", projectId);
        body.put("streamKey", streamKey);
        body.put("expiresIn", expiresIn);

        request.setEntity(new StringEntity(
            objectMapper.writeValueAsString(body),
            ContentType.APPLICATION_JSON
        ));

        return executeRequest(request);
    }

    public JsonNode getStreamInfo(String streamKey) throws IOException {
        String url = baseUrl + "/livestreams/stream-info/" + streamKey;
        HttpGet request = new HttpGet(url);
        request.setHeader("Content-Type", "application/json");

        return executeRequest(request);
    }

    private JsonNode executeRequest(HttpUriRequestBase request) throws IOException {
        return httpClient.execute(request, response -> {
            String json = EntityUtils.toString(response.getEntity());
            return objectMapper.readTree(json);
        });
    }

    public void close() throws IOException {
        httpClient.close();
    }
}

DTOs

java
// CreateStreamRequest.java
package com.example.lunarstream.dto;

import lombok.Data;

@Data
public class CreateStreamRequest {
    private String name;
}

// StreamResponse.java
package com.example.lunarstream.dto;

import lombok.Builder;
import lombok.Data;
import java.util.List;

@Data
@Builder
public class StreamResponse {
    private String id;
    private String name;
    private String status;
    private String rtmpUrl;
    private String hlsUrl;
    private List<HlsSource> hlsSources;

    @Data
    @Builder
    public static class HlsSource {
        private String name;
        private String url;
    }
}

Spring Boot Controller

java
// StreamController.java
package com.example.lunarstream.controller;

import com.example.lunarstream.LunarStreamClient;
import com.example.lunarstream.dto.CreateStreamRequest;
import com.example.lunarstream.dto.StreamResponse;
import com.fasterxml.jackson.databind.JsonNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/streams")
public class StreamController {

    @Value("${lunar.stream.api-key}")
    private String apiKey;

    @Value("${lunar.stream.project-id}")
    private String projectId;

    private LunarStreamClient client;

    @PostConstruct
    public void init() {
        client = new LunarStreamClient(apiKey, projectId);
    }

    @PreDestroy
    public void cleanup() throws IOException {
        if (client != null) {
            client.close();
        }
    }

    @PostMapping
    public ResponseEntity<?> createStream(@RequestBody CreateStreamRequest request) {
        try {
            JsonNode servers = client.getMediaServers();
            JsonNode serverList = servers.get("data");

            if (serverList == null || serverList.isEmpty()) {
                return ResponseEntity.status(503)
                    .body(Map.of("error", "No media servers available"));
            }

            String mediaServerId = serverList.get(0).get("id").asText();
            JsonNode result = client.createLivestream(request.getName(), mediaServerId);
            JsonNode stream = result.get("data");

            String rtmpUrl = stream.get("rmtpServer").asText() + "/" +
                           stream.get("streamKeyWithParams").asText();

            String hlsUrl = null;
            if (stream.has("hlsSources") && stream.get("hlsSources").size() > 0) {
                hlsUrl = stream.get("hlsSources").get(0).get("url").asText();
            }

            return ResponseEntity.ok(StreamResponse.builder()
                .id(stream.get("id").asText())
                .name(stream.get("name").asText())
                .status(stream.get("status").asText())
                .rtmpUrl(rtmpUrl)
                .hlsUrl(hlsUrl)
                .build());

        } catch (IOException e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "Failed to create stream: " + e.getMessage()));
        }
    }

    @GetMapping("/{streamKey}")
    public ResponseEntity<?> getStreamInfo(@PathVariable String streamKey) {
        try {
            JsonNode result = client.getStreamInfo(streamKey);
            JsonNode stream = result.get("data");

            List<StreamResponse.HlsSource> hlsSources = new ArrayList<>();
            if (stream.has("hlsSources")) {
                for (JsonNode source : stream.get("hlsSources")) {
                    hlsSources.add(StreamResponse.HlsSource.builder()
                        .name(source.get("name").asText())
                        .url(source.get("url").asText())
                        .build());
                }
            }

            return ResponseEntity.ok(StreamResponse.builder()
                .name(stream.get("name").asText())
                .status(stream.get("status").asText())
                .hlsSources(hlsSources)
                .build());

        } catch (IOException e) {
            return ResponseEntity.status(404)
                .body(Map.of("error", "Stream not found"));
        }
    }

    @GetMapping
    public ResponseEntity<?> listStreams(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int limit) {
        try {
            JsonNode result = client.listLivestreams(page, limit);
            return ResponseEntity.ok(result);
        } catch (IOException e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "Failed to list streams"));
        }
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteStream(@PathVariable String id) {
        try {
            client.deleteLivestream(id);
            return ResponseEntity.ok(Map.of("success", true));
        } catch (IOException e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "Failed to delete stream"));
        }
    }

    @PostMapping("/{streamKey}/refresh-token")
    public ResponseEntity<?> refreshToken(
            @PathVariable String streamKey,
            @RequestParam(defaultValue = "7200") int expiresIn) {
        try {
            JsonNode result = client.generatePushToken(streamKey, expiresIn);
            JsonNode data = result.get("data");

            return ResponseEntity.ok(Map.of(
                "rtmpUrl", data.get("rtmpUrl").asText(),
                "streamKeyWithToken", data.get("streamKeyWithToken").asText(),
                "expiresAt", data.get("expiresAt").asText()
            ));
        } catch (IOException e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "Failed to refresh token"));
        }
    }
}

Application Properties

properties
# application.properties
lunar.stream.api-key=${LUNAR_STREAM_API_KEY}
lunar.stream.project-id=${LUNAR_STREAM_PROJECT_ID}
server.port=8080

Usage

bash
# Run the application
mvn spring-boot:run

# 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.