From f3033187af8e34cecd2e4461969e1a08ace5c0f8 Mon Sep 17 00:00:00 2001 From: MrLetsplay Date: Fri, 14 Feb 2025 21:56:34 +0100 Subject: [PATCH] Add video stream, Add Docker build --- .gitea/workflows/build.yml | 37 +++++++++++++++++++ docker/Dockerfile | 33 +++++++++++++++++ docker/launcher_config.json | 11 ++++++ .../me/mrletsplay/videobase/VideoBase.java | 11 ++++-- .../mrletsplay/videobase/rest/LibraryAPI.java | 28 ++++++++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 .gitea/workflows/build.yml create mode 100644 docker/Dockerfile create mode 100644 docker/launcher_config.json diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..607d6ed --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,37 @@ +name: Build and push container +run-name: ${{ gitea.actor }} is building the container +on: [push] + +jobs: + Build-Docker-Container: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: mrletsplay/aadl + tags: | + type=sha + type=raw,latest + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: docker/Dockerfile + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..baf6db4 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,33 @@ +FROM maven:latest as builder + +COPY . /workspace + +WORKDIR /workspace + +RUN mvn clean package + + + +FROM alpine:latest + +ENV UID=1000 +ENV GID=1000 + +RUN apk update && apk add openjdk17 shadow sudo + +COPY --from=builder /workspace/target/VideoBase-*.jar /app/VideoBase.jar +COPY --from=mrletsplay/docker_launcher /usr/local/bin/docker_launcher /usr/local/bin/docker_launcher +COPY ./docker/launcher_config.json /app/launcher_config.json + +RUN useradd app + +RUN mkdir /app/data && chown -R app /app/data + +VOLUME ["/app/data"] + +WORKDIR /app/data + +EXPOSE 3706 + +ENTRYPOINT [ "docker_launcher", "--config", "/app/launcher_config.json", "sudo", "-u", "app" ] +CMD [ "java", "-jar", "/app/VideoBase.jar" ] diff --git a/docker/launcher_config.json b/docker/launcher_config.json new file mode 100644 index 0000000..a7865c8 --- /dev/null +++ b/docker/launcher_config.json @@ -0,0 +1,11 @@ +{ + "updateUID": true, + "user": "app", + + "updateGID": true, + "group": "app", + + "runBefore": [ + [ "chown", "-R", "app:app", "/app" ] + ] +} diff --git a/src/main/java/me/mrletsplay/videobase/VideoBase.java b/src/main/java/me/mrletsplay/videobase/VideoBase.java index fac54b6..7fc3749 100644 --- a/src/main/java/me/mrletsplay/videobase/VideoBase.java +++ b/src/main/java/me/mrletsplay/videobase/VideoBase.java @@ -10,8 +10,8 @@ import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.helpers.NOPLogger; +import ch.qos.logback.classic.Level; import me.mrletsplay.mrcore.json.JSONObject; import me.mrletsplay.mrcore.json.converter.JSONConverter; import me.mrletsplay.mrcore.json.converter.SerializationOption; @@ -55,12 +55,15 @@ public class VideoBase { loadLibrary(); executor.scheduleWithFixedDelay(VideoBase::loadLibrary, 10, 10, TimeUnit.MINUTES); + ((ch.qos.logback.classic.Logger) LOGGER).setLevel(Level.INFO); + HttpServer server = new HttpServer(HttpServer.newConfigurationBuilder() .hostBindAll() .port(6969) - .poolSize(20) - .ioWorkers(3) - .logger(NOPLogger.NOP_LOGGER) + .poolSize(2) + .ioWorkers(1) + .logger(LOGGER) +// .logger(NOPLogger.NOP_LOGGER) .defaultCorsConfiguration(CorsConfiguration.createAllowAll()) .create()); diff --git a/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java b/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java index 39745f8..854d3a4 100644 --- a/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java +++ b/src/main/java/me/mrletsplay/videobase/rest/LibraryAPI.java @@ -3,6 +3,9 @@ package me.mrletsplay.videobase.rest; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import javax.imageio.ImageIO; @@ -154,6 +157,31 @@ public class LibraryAPI implements EndpointCollection { ctx.getServerHeader().setContent(MimeType.PNG, bOut.toByteArray()); } + @Endpoint(method = HttpRequestMethod.GET, path = "/video/{video}/stream", pathPattern = true) + public void getStream(HttpRequestContext ctx, @RequestParameter("video") String videoId) { + Video video = VideoBase.getLibrary().findVideoById(videoId); + if(video == null) { + ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("Not found")); + return; + } + + Path videoPath = video.getPath(); + if(!Files.isRegularFile(videoPath)) { + ctx.respond(HttpStatusCodes.NOT_FOUND_404, new TextResponse("Video file not found")); + return; + } + + try { + long length = Files.size(videoPath); + InputStream in = Files.newInputStream(videoPath); + ctx.getServerHeader().setCompressionEnabled(false); + ctx.getServerHeader().setContent(MimeType.of("video/mpeg"), in, length); + } catch (IOException e) { + ctx.respond(HttpStatusCodes.INTERNAL_SERVER_ERROR_500, new TextResponse("Failed to load video")); + return; + } + } + private JSONObject videoToJSON(Video video) { JSONObject v = new JSONObject(); v.put("id", video.getId());