Strip down docker container size
All checks were successful
Build and Publish / build_and_publish (push) Successful in 4m1s

This commit is contained in:
Nexus 2024-07-01 18:50:22 +01:00
parent 01e3e85f19
commit d46ef03ba1
Signed by: nex
GPG key ID: 0FA334385D0B689F
7 changed files with 101 additions and 56 deletions

View file

@ -1,4 +1,4 @@
FROM python:3.11-slim FROM python:3.12
LABEL org.opencontainers.image.source https://git.i-am.nexus/nex/college-bot-v2 LABEL org.opencontainers.image.source https://git.i-am.nexus/nex/college-bot-v2
LABEL org.opencontainers.image.url https://git.i-am.nexus/nex/college-bot-v2 LABEL org.opencontainers.image.url https://git.i-am.nexus/nex/college-bot-v2
@ -11,50 +11,50 @@ WORKDIR /app
RUN DEBIAN_FRONTEND=noninteractive apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get update
# Install chrome dependencies # Install chrome dependencies
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ #RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
webext-ublock-origin-chromium \ #webext-ublock-origin-chromium \
gconf-service \ #gconf-service \
libasound2 \ #libasound2 \
libatk1.0-0 \ #libatk1.0-0 \
libc6 \ #libc6 \
libcairo2 \ #libcairo2 \
libcups2 \ #libcups2 \
libdbus-1-3 \ #libdbus-1-3 \
libexpat1 \ #libexpat1 \
libfontconfig1 \ #libfontconfig1 \
libgcc1 \ #libgcc1 \
libgconf-2-4 \ #libgconf-2-4 \
libgdk-pixbuf2.0-0 \ #libgdk-pixbuf2.0-0 \
libglib2.0-0 \ #libglib2.0-0 \
libgtk-3-0 \ #libgtk-3-0 \
libnspr4 \ #libnspr4 \
libpango-1.0-0 \ #libpango-1.0-0 \
libpangocairo-1.0-0 \ #libpangocairo-1.0-0 \
libstdc++6 \ #libstdc++6 \
libx11-6 \ #libx11-6 \
libx11-xcb1 \ #libx11-xcb1 \
libxcb1 \ #libxcb1 \
libxcomposite1 \ #libxcomposite1 \
libxcursor1 \ #libxcursor1 \
libxdamage1 \ #libxdamage1 \
libxext6 \ #libxext6 \
libxfixes3 \ #libxfixes3 \
libxi6 \ #libxi6 \
libxrandr2 \ #libxrandr2 \
libxrender1 \ #libxrender1 \
libxss1 \ #libxss1 \
libxtst6 \ #libxtst6 \
ca-certificates \ #ca-certificates \
fonts-liberation \ #fonts-liberation \
libappindicator1 \ #libappindicator1 \
libnss3 \ #libnss3 \
lsb-release \ #lsb-release \
xdg-utils #xdg-utils
#
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ #RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
chromium-common \ # chromium-common \
chromium-driver \ # chromium-driver \
chromium-sandbox # chromium-sandbox
# Install general utilities # Install general utilities
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
@ -67,11 +67,6 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
curl \ curl \
libmagic-dev libmagic-dev
# Install image dependencies
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ffmpeg \
imagemagick
RUN python3 -m venv /app/venv RUN python3 -m venv /app/venv
RUN /app/venv/bin/pip install --upgrade --no-input pip wheel setuptools RUN /app/venv/bin/pip install --upgrade --no-input pip wheel setuptools

View file

@ -9,6 +9,11 @@ services:
- ./jimmy.log:/app/jimmy.log - ./jimmy.log:/app/jimmy.log
- /dev/dri:/dev/dri - /dev/dri:/dev/dri
- jimmy-data:/app/data - jimmy-data:/app/data
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- /usr/bin/nmap:/usr/bin/nmap:ro
- /usr/bin/ffmpeg:/usr/bin/ffmpeg:ro
- /usr/bin/ffprobe:/usr/bin/ffprobe:ro
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway
depends_on: depends_on:

View file

@ -1,5 +1,11 @@
# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin):
# - `ffmpeg`, usually `ffmpeg`, sometimes `ffmpeg-full`.
# - /dev/dri should be passed through to the container for hardware acceleration.
# - `ffprobe`, usually `ffmpeg`, sometimes `ffmpeg-full`.
# In docker, you can just bind these as volumes, and read-only too.
import asyncio import asyncio
import io import io
import shutil
import typing import typing
from collections.abc import Iterable from collections.abc import Iterable
import logging import logging
@ -105,6 +111,12 @@ class AutoResponder(commands.Cog):
:param uri: The URI to transcode :param uri: The URI to transcode
:return: A transcoded file :return: A transcoded file
""" """
if not shutil.which("ffmpeg"):
self.log.error("ffmpeg not installed")
return
if not shutil.which("ffprobe"):
self.log.error("ffprobe not installed")
return
last_reaction: str | None = None last_reaction: str | None = None
def update_reaction(new: str | None = None) -> None: def update_reaction(new: str | None = None) -> None:
@ -118,9 +130,7 @@ class AutoResponder(commands.Cog):
last_reaction = new last_reaction = new
self.log.info("Waiting for transcode lock to release") self.log.info("Waiting for transcode lock to release")
async with self.transcode_lock: async with self.transcode_lock:
cog: FFMeta = self.bot.get_cog("FFMeta") cog = FFMeta(self.bot)
if not cog:
raise RuntimeError("FFMeta cog not loaded")
if not isinstance(uri, str): if not isinstance(uri, str):
uri = str(uri) uri = str(uri)
@ -240,6 +250,12 @@ class AutoResponder(commands.Cog):
*domains: str, *domains: str,
additional: Iterable[str] = None additional: Iterable[str] = None
) -> None: ) -> None:
if not shutil.which("ffmpeg"):
self.log.error("ffmpeg not installed")
return
if not shutil.which("ffprobe"):
self.log.error("ffprobe not installed")
return
if self.allow_hevc_to_h264 is False: if self.allow_hevc_to_h264 is False:
self.log.debug("hevc_to_h264 is disabled, not engaging.") self.log.debug("hevc_to_h264 is disabled, not engaging.")
return return

View file

@ -2,7 +2,6 @@
This module is only meant to be loaded during election times. This module is only meant to be loaded during election times.
""" """
import asyncio import asyncio
import datetime
import logging import logging
import re import re

View file

@ -1,8 +1,14 @@
# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin):
# - `ffmpeg`, usually `ffmpeg`, sometimes `ffmpeg-full`.
# - /dev/dri should be passed through to the container for hardware acceleration.
# - `ffprobe`, usually `ffmpeg`, sometimes `ffmpeg-full`.
# In docker, you can just bind these as volumes, and read-only too.
import asyncio import asyncio
import io import io
import json import json
import logging import logging
import pathlib import pathlib
import shutil
import sys import sys
import tempfile import tempfile
import typing import typing
@ -56,6 +62,8 @@ class FFMeta(commands.Cog):
@commands.slash_command() @commands.slash_command()
async def ffprobe(self, ctx: discord.ApplicationContext, url: str = None, attachment: discord.Attachment = None): async def ffprobe(self, ctx: discord.ApplicationContext, url: str = None, attachment: discord.Attachment = None):
"""Runs ffprobe on a given URL or attachment""" """Runs ffprobe on a given URL or attachment"""
if not shutil.which("ffprobe"):
return await ctx.respond("ffprobe is not installed on this system.")
if url is None: if url is None:
if attachment is None: if attachment is None:
return await ctx.respond("No URL or attachment provided") return await ctx.respond("No URL or attachment provided")
@ -142,6 +150,10 @@ class FFMeta(commands.Cog):
] = False, ] = False,
): ):
"""Converts a given URL or attachment to an Opus file""" """Converts a given URL or attachment to an Opus file"""
if not shutil.which("ffmpeg"):
return await ctx.respond("ffmpeg is not installed on this system.")
if not shutil.which("ffprobe"):
return await ctx.respond("ffprobe is not installed on this system.")
if bitrate == 0: if bitrate == 0:
bitrate = 0.5 bitrate = 0.5
if mono: if mono:
@ -244,6 +256,8 @@ class FFMeta(commands.Cog):
rbh = Path("assets/right-behind-you.ogg").resolve() rbh = Path("assets/right-behind-you.ogg").resolve()
if not rbh.exists(): if not rbh.exists():
return await ctx.respond("The file `right-behind-you.ogg` is missing.") return await ctx.respond("The file `right-behind-you.ogg` is missing.")
if not shutil.which("ffmpeg"):
return await ctx.respond("ffmpeg is not installed on this system.")
if not image.content_type.startswith("image"): if not image.content_type.startswith("image"):
return await ctx.respond("That's not an image!") return await ctx.respond("That's not an image!")
with tempfile.NamedTemporaryFile(suffix=Path(image.filename).suffix) as temp: with tempfile.NamedTemporaryFile(suffix=Path(image.filename).suffix) as temp:

View file

@ -1,3 +1,9 @@
# In order to use commands in this system, the following things must be installed (ideally in /usr/local/bin):
# - `ping`, usually iputils-ping
# - `whois`, usually whois
# - `traceroute`, usually traceroute
# - `nmap`, usually nmap
# In docker, you can just bind these as volumes, and read-only too.
import asyncio import asyncio
import io import io
import json import json
@ -49,6 +55,8 @@ class NetworkCog(commands.Cog):
@commands.slash_command() @commands.slash_command()
async def ping(self, ctx: discord.ApplicationContext, target: str = None): async def ping(self, ctx: discord.ApplicationContext, target: str = None):
"""Get the bot's latency, or the network latency to a target.""" """Get the bot's latency, or the network latency to a target."""
if not shutil.which("ping"):
return await ctx.respond("Ping is not installed on this system.")
if target is None: if target is None:
return await ctx.respond(f"Pong! {round(self.bot.latency * 1000)}ms") return await ctx.respond(f"Pong! {round(self.bot.latency * 1000)}ms")
else: else:
@ -75,6 +83,8 @@ class NetworkCog(commands.Cog):
@commands.slash_command() @commands.slash_command()
async def whois(self, ctx: discord.ApplicationContext, target: str): async def whois(self, ctx: discord.ApplicationContext, target: str):
"""Get information about a user.""" """Get information about a user."""
if not shutil.which("whois"):
return await ctx.respond("Whois is not installed on this system.")
async def run_command(with_disclaimer: bool = False): async def run_command(with_disclaimer: bool = False):
args = [] if with_disclaimer else ["-H"] args = [] if with_disclaimer else ["-H"]
@ -221,6 +231,8 @@ class NetworkCog(commands.Cog):
await ctx.defer() await ctx.defer()
if re.search(r"\s+", url): if re.search(r"\s+", url):
return await ctx.respond("URL cannot contain spaces.") return await ctx.respond("URL cannot contain spaces.")
if not shutil.which("traceroute"):
return await ctx.respond("Traceroute is not installed on this system.")
args = ["sudo", "-E", "-n", "traceroute"] args = ["sudo", "-E", "-n", "traceroute"]
flags = { flags = {

View file

@ -3,6 +3,7 @@ import functools
import hashlib import hashlib
import logging import logging
import math import math
import shutil
import time import time
import datetime import datetime
@ -190,6 +191,8 @@ class YTDLCog(commands.Cog):
:param file: The file to convert :param file: The file to convert
:return: The converted file :return: The converted file
""" """
if not shutil.which("ffmpeg"):
raise RuntimeError("ffmpeg is not installed.")
new_file = file.with_suffix(".m4a") new_file = file.with_suffix(".m4a")
args = [ args = [
"-vn", "-vn",
@ -319,8 +322,9 @@ class YTDLCog(commands.Cog):
else: else:
chosen_format = user_format chosen_format = user_format
ffmpeg_installed = bool(shutil.which("ffmpeg"))
options.setdefault("postprocessors", []) options.setdefault("postprocessors", [])
if audio_only: if audio_only and ffmpeg_installed:
# Overwrite format here to be best audio under 25 megabytes. # Overwrite format here to be best audio under 25 megabytes.
chosen_format = "ba[filesize<20M]" chosen_format = "ba[filesize<20M]"
# Also force sorting by the best audio bitrate first. # Also force sorting by the best audio bitrate first.
@ -332,7 +336,7 @@ class YTDLCog(commands.Cog):
options["format"] = chosen_format options["format"] = chosen_format
options["paths"] = paths options["paths"] = paths
if subtitles: if subtitles and ffmpeg_installed:
subtitles, burn = subtitles.split("+", 1) if "+" in subtitles else (subtitles, "0") subtitles, burn = subtitles.split("+", 1) if "+" in subtitles else (subtitles, "0")
burn = burn[0].lower() in ("y", "1", "t") burn = burn[0].lower() in ("y", "1", "t")
if subtitles.lower() == "auto": if subtitles.lower() == "auto":