diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..4b9aa74 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.*.~undo-tree~ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1e1e544 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM sbtscala/scala-sbt:eclipse-temurin-17.0.4_1.8.0_2.13.10 AS build + +RUN mkdir /app +WORKDIR /app +COPY ./build.sbt /app/ +COPY ./project /app/project +# TODO: highlight.jsどうする? +COPY ./src /app/src +COPY ./version.sbt /app/ + +RUN sbt compile assembly +RUN ls -lah /app/target/scala-2.13/ +RUN cp /app/target/scala-2.13/zmm-assembly-*.jar /app/target/zmm.jar +RUN java -jar /app/target/zmm.jar -v + +FROM amazoncorretto:17 + +COPY --from=build /app/target/zmm.jar /zmm.jar +COPY --from=build /app/target/scala-2.13/*.jar / +ENTRYPOINT ["java", "-jar", "/zmm.jar"] diff --git a/README.md b/README.md index 15557ec..6bf95f5 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,32 @@ ## How to -まずdocker composeを使ってVOICEVOXのエンジンを起動しておきます。 +まず Docker Compose を使ってVOICEVOXのエンジンを起動しておきます。 ```sh $ docker compose up voicevox-cpu ``` -次に、ダウンロードしたJARファイルを起動します。 +### Dockerを使う場合 + +Docker Hub にzmmのイメージがアップロードされているので、このイメージを使う場合はffmpegやchromiumのインストールは不要です。 + +```sh +$ docker compose run --rm zmm 原稿.xml +``` + +しばらく待つことで動画が `output_with_bgm.mp4` として出力されます。 + +### JARファイルを使う場合 + +Docker Compose を使わない場合は、ダウンロードしたJARファイルを起動します。 ```sh $ java -jar zmm-vx.y.z.jar 原稿.xml ``` +JARファイルを使う場合、ffmpegやchromiumがインストールされている必要があります。 + しばらく待つことで動画が `output_with_bgm.mp4` として出力されます。 ## 必要なソフトウェア diff --git a/build.sbt b/build.sbt index 9a04b7b..d3bedb5 100644 --- a/build.sbt +++ b/build.sbt @@ -1,5 +1,6 @@ import Dependencies._ import ReleaseTransformations._ +import com.typesafe.sbt.packager.docker._ ThisBuild / scalaVersion := "2.13.8" ThisBuild / organization := "com.github.windymelt" @@ -23,10 +24,13 @@ lazy val root = (project in file(".")) "com.mitchtalmadge" % "ascii-data" % "1.4.0", "org.slf4j" % "slf4j-simple" % "2.0.6", scalaTest % Test, - ) + ), + assembly / mainClass := Some("com.github.windymelt.zmm.Main"), ) .enablePlugins(SbtTwirl) .enablePlugins(BuildInfoPlugin) + .enablePlugins(JavaAppPackaging) // for DockerPlugin + .enablePlugins(DockerPlugin) .settings( buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, sbtVersion), buildInfoPackage := "com.github.windymelt.zmm" @@ -42,11 +46,39 @@ lazy val root = (project in file(".")) tagRelease, // : ReleaseStep // publishArtifacts, // : ReleaseStep, checks whether `publishTo` is properly set up releaseStepTask(assembly), + releaseStepTask(Docker / publish), setNextVersion, // : ReleaseStep commitNextVersion, // : ReleaseStep pushChanges // : ReleaseStep, also checks that an upstream branch is properly configured ) ) + .settings( + dockerBaseImage := "amazoncorretto:17", + Docker / daemonUser := "root", + Docker / maintainer := "Windymelt", + dockerRepository := Some("docker.io"), + dockerUsername := Some("windymelt"), + dockerUpdateLatest := true, + /* zmmではScala highlightのためにカスタムしたhighlight.jsを同梱しているが、mappingが今のところ壊れているのでDocker Imageでは直接highlight.jsをダウンロードさせる */ + dockerCommands ++= Seq( + Cmd("USER", "root"), + ExecCmd("RUN", "mkdir", "-p", "/app/artifacts/html"), + ExecCmd("RUN", "mkdir", "/app/assets"), + ExecCmd("ADD", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js", "/app/highlight.min.js"), + ExecCmd("RUN", "mkdir", "-p", "/app/highlight/styles"), + ExecCmd("ADD", "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css", "/app/highlight/styles/default.min.css"), + Cmd("WORKDIR", "/root"), + ExecCmd("RUN", "yum", "-y", "install", "wget", "tar", "xz"), + ExecCmd("RUN", "wget", "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"), + ExecCmd("RUN", "tar", "xvf", "ffmpeg-release-amd64-static.tar.xz"), + ExecCmd("RUN", "mv", "ffmpeg-5.1.1-amd64-static/ffmpeg", "/usr/bin/ffmpeg"), + ExecCmd("RUN", "mv", "ffmpeg-5.1.1-amd64-static/ffprobe", "/usr/bin/ffprobe"), + ExecCmd("RUN", "amazon-linux-extras", "install", "-y", "epel"), + ExecCmd("RUN", "yum", "update", "-y"), + ExecCmd("RUN", "yum", "install", "-y", "chromium"), + Cmd("WORKDIR", "/app"), + ), + ) ThisBuild / assemblyMergeStrategy := { case PathList("META-INF", "versions", "9", "module-info.class") => MergeStrategy.first diff --git a/docker-compose.yml b/docker-compose.yml index 268c081..224b0b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,16 @@ version: "3.9" services: + zmm: + image: windymelt/zmm:latest + volumes: + - "./:/app" + # 手元にあるフォントを使えるようにする措置 + - "$HOME/.fonts/:/root/.fonts:ro" + working_dir: /app + environment: + - "VOICEVOX_URI=http://voicevox-cpu:50021" + - "CHROMIUM_CMD=chromium-browser" + - "CHROMIUM_NOSANDBOX=1" # どっちかを動かしてください voicevox-cpu: image: voicevox/voicevox_engine:cpu-ubuntu20.04-latest diff --git a/project/plugins.sbt b/project/plugins.sbt index 620dac2..4ef1822 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,3 +5,5 @@ addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.0") addSbtPlugin("com.github.sbt" % "sbt-release" % "1.1.0") + +addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.4") diff --git a/src/main/scala/com/github/windymelt/zmm/Cli.scala b/src/main/scala/com/github/windymelt/zmm/Cli.scala index 222222c..f09bade 100644 --- a/src/main/scala/com/github/windymelt/zmm/Cli.scala +++ b/src/main/scala/com/github/windymelt/zmm/Cli.scala @@ -26,10 +26,11 @@ final class Cli val voiceVoxUri = sys.env.get("VOICEVOX_URI") getOrElse config.getString("voicevox.apiUri") + val chromiumCommand = sys.env.get("CHROMIUM_CMD").getOrElse(config.getString("chromium.command")) def voiceVox: VoiceVox = new ConcreteVoiceVox(voiceVoxUri) def ffmpeg = new ConcreteFFmpeg(config.getString("ffmpeg.command"), ConcreteFFmpeg.Quiet) val chromiumNoSandBox = sys.env.get("CHROMIUM_NOSANDBOX").map(_ == "1").getOrElse(config.getBoolean("chromium.nosandbox")) - def screenShot = new ChromeScreenShot(config.getString("chromium.command"), ChromeScreenShot.Quiet, chromiumNoSandBox) + def screenShot = new ChromeScreenShot(chromiumCommand, ChromeScreenShot.Quiet, chromiumNoSandBox) def showVoiceVoxSpeakers(): IO[Unit] = { import io.circe.JsonObject @@ -54,8 +55,9 @@ final class Cli for { _ <- showLogo - _ <- IO.println(s"""[configuration] voicevox api: ${config.getString("voicevox.apiUri")}""") - _ <- IO.println(s"""[configuration] chromium command: ${config.getString("chromium.command")}""") + _ <- IO.println(s"""[pwd] ${System.getProperty("user.dir")}""") + _ <- IO.println(s"""[configuration] voicevox api: ${voiceVoxUri}""") + _ <- IO.println(s"""[configuration] chromium command: ${chromiumCommand}""") _ <- IO.println(s"""[configuration] ffmpeg command: ${config.getString("ffmpeg.command")}""") _ <- IO.println("Invoking audio api...") x <- content