commit d2626aa6913a989133922927591aa313f2ee9a96 Author: Mike Johnston Date: Mon Feb 9 16:52:27 2026 -0500 initial commit diff --git a/.gitea/workflows/rebuild-prod.yaml b/.gitea/workflows/rebuild-prod.yaml new file mode 100644 index 0000000..660e9a9 --- /dev/null +++ b/.gitea/workflows/rebuild-prod.yaml @@ -0,0 +1,89 @@ +# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions +name: Build Images and Deploy +run-name: ${{ gitea.actor }} is building new PROD images and redeploying the existing stack 🚀 +on: + push: + # not working right now https://github.com/actions/runner/issues/2324 + # paths-ignore: + # - **.yml + branches: + - main + +env: + STACK_NAME: video-editor + DOT_ENV: ${{ secrets.PROD_ENV }} + PORTAINER_TOKEN: ${{ vars.PORTAINER_TOKEN }} + PORTAINER_API_URL: https://portainer.dev.nervesocket.com/api + ENDPOINT_NAME: "mini" #sometimes "primary" + IMAGE_TAG: "reg.dev.nervesocket.com/video-editor:latest" + +jobs: + Update-PROD-Stack: + runs-on: ubuntu-latest + steps: + # if: contains(github.event.pull_request.head.ref, 'init-stack') + - name: Checkout + uses: actions/checkout@v4 + with: + ref: main + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push PROD Docker image + run: | + echo $DOT_ENV | base64 -d > .env + docker buildx build --push -f Dockerfile -t $IMAGE_TAG . + + - name: Get the endpoint ID + # Usually ID is 1, but you can get it from the API. Only skip this if you are VERY sure. + run: | + ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/endpoints" | jq -r ".[] | select(.Name==\"$ENDPOINT_NAME\") | .Id") + echo "ENDPOINT_ID=$ENDPOINT_ID" >> $GITHUB_ENV + echo "Got stack Endpoint ID: $ENDPOINT_ID" + + - name: Fetch stack ID from Portainer + run: | + STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks" | jq -r ".[] | select(.Name==\"$STACK_NAME\" and .EndpointId==$ENDPOINT_ID) | .Id") + + echo "STACK_ID=$STACK_ID" >> $GITHUB_ENV + echo "Got stack ID: $STACK_ID matched with Endpoint ID: $ENDPOINT_ID" + + - name: Fetch Stack + run: | + # Get the stack details (including env vars) + STACK_DETAILS=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks/$STACK_ID") + + # Extract environment variables from the stack + echo "$STACK_DETAILS" | jq -r '.Env' > stack_env.json + + echo "Existing stack environment variables:" + cat stack_env.json + + - name: Redeploy stack in Portainer + run: | + # Read stack file content + STACK_FILE_CONTENT=$(echo "$( 0: + cmd.extend(['-ss', str(start_time)]) + if end_time is not None: + duration = end_time - (start_time if start_time else 0) + if duration > 0: + cmd.extend(['-t', str(duration)]) + + # Build filter complex for crop and scale + filters = [] + + # Add crop filter if specified + crop = data.get('crop') + if crop: + x = crop.get('x', 0) + y = crop.get('y', 0) + width = crop.get('width') + height = crop.get('height') + if width and height: + filters.append(f"crop={width}:{height}:{x}:{y}") + + # Add scale filter if resolution specified + resolution = data.get('resolution') + if resolution: + width = resolution.get('width') + height = resolution.get('height') + if width and height: + filters.append(f"scale={width}:{height}") + + # Apply filters if any + if filters: + cmd.extend(['-vf', ','.join(filters)]) + + # Output settings for H.264 MP4 + cmd.extend([ + '-c:v', 'libx264', + '-preset', 'medium', + '-crf', '23', + '-c:a', 'aac', + '-b:a', '128k', + '-movflags', '+faststart', + '-y', # Overwrite output file + output_path + ]) + + # Execute ffmpeg + print(f"Executing: {' '.join(cmd)}") + result = subprocess.run(cmd, capture_output=True, text=True) + + if result.returncode != 0: + print(f"FFmpeg error: {result.stderr}") + return jsonify({'error': 'Video processing failed', 'details': result.stderr}), 500 + + return jsonify({ + 'success': True, + 'output_file': output_filename, + 'file_id': file_id + }) + + except Exception as e: + print(f"Processing error: {e}") + return jsonify({'error': str(e)}), 500 + +@app.route('/download/') +def download_video(file_id): + try: + output_filename = f"{file_id}_processed.mp4" + output_path = os.path.join(app.config['OUTPUT_FOLDER'], output_filename) + + if not os.path.exists(output_path): + return jsonify({'error': 'File not found'}), 404 + + return send_file(output_path, as_attachment=True, download_name='processed_video.mp4') + except Exception as e: + return jsonify({'error': str(e)}), 500 + +@app.route('/cleanup/', methods=['DELETE']) +def cleanup_files(file_id): + """Clean up uploaded and processed files""" + try: + # Clean up upload folder + for f in os.listdir(app.config['UPLOAD_FOLDER']): + if f.startswith(file_id): + os.remove(os.path.join(app.config['UPLOAD_FOLDER'], f)) + + # Clean up output folder + for f in os.listdir(app.config['OUTPUT_FOLDER']): + if f.startswith(file_id): + os.remove(os.path.join(app.config['OUTPUT_FOLDER'], f)) + + return jsonify({'success': True}) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000, debug=True) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d30adc0 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + video-editor: + build: . + ports: + - "5000:5000" + volumes: + - ./uploads:/app/uploads + - ./outputs:/app/outputs + environment: + - FLASK_ENV=production + restart: unless-stopped diff --git a/prod-compose.yml b/prod-compose.yml new file mode 100644 index 0000000..ad205c5 --- /dev/null +++ b/prod-compose.yml @@ -0,0 +1,13 @@ +version: '3.8' + +services: + video-editor: + image: reg.dev.nervesocket.com/video-editor:latest + ports: + - "5000:5000" + volumes: + - ./uploads:/app/uploads + - ./outputs:/app/outputs + environment: + - FLASK_ENV=production + restart: unless-stopped diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..53ea104 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.0 +Werkzeug==3.0.1 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..7d54342 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,589 @@ + + + + + + Simple Video Editor + + + +
+
+

🎬 Simple Video Editor

+

Trim, crop, and convert your videos with ease

+
+ +
+ +
+

1. Select Video

+
+
📹
+

Drag and drop your video here

+

or click to browse

+

+ Supported formats: MP4, AVI, MOV, MKV, WMV, FLV, WebM +

+ +
+
+
+ + + + + + +
+
+ + + +