diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3b5efb4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,39 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/go/build-context-dockerignore/ + +**/.DS_Store +**/__pycache__ +**/.venv +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.github +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose.y*ml +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +.github +.git +.docker-compose.yml +.gitignore diff --git a/.env.example b/.env.example index 291d0db..1936bba 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -REPLICATE_API_TOKEN=yourtokenhere \ No newline at end of file +REPLICATE_API_TOKEN=your_replicate_api_token +HOST_OUTPUT_DIR=/path/to/your/output/directory \ No newline at end of file diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..2320075 --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,42 @@ +name: Docker Build and Push + +on: + push: + branches: + - main + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml deleted file mode 100644 index c73e032..0000000 --- a/.github/workflows/pylint.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Pylint - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.8", "3.9", "3.10"] - steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pylint - - name: Analysing the code with pylint - run: | - pylint $(git ls-files '*.py') diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..b6690e0 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,8 @@ +name: Ruff +on: [push, pull_request] +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..52f5206 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 + +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser + +COPY requirements.txt . + +RUN --mount=type=cache,target=/root/.cache/pip \ + --mount=type=bind,source=requirements.txt,target=requirements.txt \ + python -m pip install -r requirements.txt + +USER appuser + +COPY src/ . + +EXPOSE 8080 + +CMD [ "python", "main.py" ] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..27aa432 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ + +services: + flux-replicate-gui: + image: ghcr.io/rtuszik: + container_name: flux-replicate-gui + environment: + - REPLICATE_API_TOKEN=${REPLICATE_API_TOKEN} + ports: + - "8080:8080" + volumes: + - ${HOST_OUTPUT_DIR}:/app/output + restart: unless-stopped \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 73be64b..e7314f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ token_count==0.2.1 pillow==10.4.0 loguru==0.7.2 nicegui==1.4.36 -httpx \ No newline at end of file +httpx==0.27.0 diff --git a/src/gui.py b/src/gui.py index 7100542..2332caa 100644 --- a/src/gui.py +++ b/src/gui.py @@ -63,7 +63,7 @@ def setup_ui(self): with ui.column().classes("w-full max-w-7xl mx-auto p-4 space-y-4"): with ui.card().classes("w-full"): ui.label("Image Generator").classes("text-2xl font-bold mb-4") - with ui.row().classes("w-full justify-between"): + with ui.row().classes("w-full"): with ui.column().classes("w-1/2 pr-2"): self.setup_left_panel() with ui.column().classes("w-1/2 pl-2"): diff --git a/src/utils.py b/src/utils.py deleted file mode 100644 index fd01ba4..0000000 --- a/src/utils.py +++ /dev/null @@ -1,80 +0,0 @@ -import os - -from PyQt6.QtCore import QObject, QRunnable, QThread, pyqtSignal -from PyQt6.QtWidgets import QLabel, QVBoxLayout, QWidget - - -class ImageLoaderSignals(QObject): - finished = pyqtSignal(list) - - -class ImageLoader(QRunnable): - def __init__(self, folder_path): - super().__init__() - self.folder_path = folder_path - self.signals = ImageLoaderSignals() - - def run(self): - images = [] - for filename in os.listdir(self.folder_path): - if filename.lower().endswith((".png", ".jpg", ".jpeg", ".webp")): - file_path = os.path.join(self.folder_path, filename) - mod_time = os.path.getmtime(file_path) - images.append((file_path, mod_time)) - images.sort(key=lambda x: x[1], reverse=True) - self.signals.finished.emit([img[0] for img in images]) - - -class ImageGeneratorThread(QThread): - finished = pyqtSignal(list) - error = pyqtSignal(str) - - def __init__(self, image_generator, params): - super().__init__() - self.image_generator = image_generator - self.params = params - - def run(self): - try: - output = self.image_generator.generate_images(self.params) - self.finished.emit(output) - except Exception as e: - self.error.emit(str(e)) - - -class TokenCount: - def __init__(self, model_name): - self.model_name = model_name - - def num_tokens_from_string(self, string: str) -> int: - # This is a simplified implementation. You might want to use a proper tokenizer here. - return len(string.split()) - - -class TokenCounter(QWidget): - def __init__(self, text_edit, *args, **kwargs): - super().__init__(*args, **kwargs) - self.text_edit = text_edit - self.tc = TokenCount(model_name="gpt-3.5-turbo") - - layout = QVBoxLayout(self) - self.token_count_label = QLabel("Tokens: 0") - self.warning_label = QLabel() - self.warning_label.setStyleSheet("color: orange;") - self.warning_label.hide() - - layout.addWidget(self.token_count_label) - layout.addWidget(self.warning_label) - - self.text_edit.textChanged.connect(self.update_count) - - def update_count(self): - text = self.text_edit.toPlainText() - token_count = self.tc.num_tokens_from_string(text) - self.token_count_label.setText(f"Tokens: {token_count}") - - if token_count > 77: - self.warning_label.setText("Warning: Tokens beyond 77 will be ignored") - self.warning_label.show() - else: - self.warning_label.hide()