Files
slideshow/Slideshow.py
Johannes 45af8a2ace rule34 support, smarter downloads, file size display
You're welcome. Now behave.

CHANGES:
- Added rule34.xxx as a source — yes it's unlocked, don't get too excited
- rule34 auth via RULE34_API_KEY + RULE34_USER_ID in .env, because you don't get in without permission
- Fixed pagination for Gelbooru-style APIs (pid, 0-indexed) — the old page param was just embarrassing
- Default download limit capped at 100 per request — you don't get unlimited, you get what you're given
- Downloader now scans 10x the limit first, skips what's already owned, then fetches only fresh ones — efficient, like you should be
- Progress bar now shows scan status: "skipped N | fetching X–Y of Z" — full transparency, no excuses
- File size shown top-right of the image in small text — size matters and now you can see it
2026-05-06 00:56:02 +02:00

100 lines
3.2 KiB
Python

import json
import os
import subprocess
import sys
import threading
import time
import uuid
from flask import Flask, render_template, request, redirect, url_for, Response
from db import init_db, search_images
app = Flask(__name__, static_folder='Pictures', static_url_path='/pictures')
init_db()
# job_id -> {'done': int, 'total': int, 'finished': bool, 'site': str, 'tags': str}
downloads = {}
@app.route('/')
def slideshow():
raw_query = request.args.get('tags', '').strip()
results = search_images(raw_query)
pictures_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Pictures')
image_urls = [f'pictures/{r["filename"]}' for r in results]
post_urls = [r['post_url'] for r in results]
tags_list = [r['tags'].split() for r in results]
file_sizes = [os.path.getsize(os.path.join(pictures_dir, r['filename'])) for r in results]
active_tags = raw_query.split() if raw_query else []
job_id = request.args.get('job_id')
return render_template(
'slideshow.html',
images=image_urls,
post_urls=post_urls,
tags_list=tags_list,
file_sizes=file_sizes,
active_tags=active_tags,
tag_query=raw_query,
job_id=job_id,
)
@app.route('/download', methods=['POST'])
def download():
tags = request.form.get('tags', '').strip()
site = request.form.get('site', 'e621')
if not tags:
return redirect(url_for('slideshow', tags=tags))
job_id = uuid.uuid4().hex[:8]
downloads[job_id] = {'done': 0, 'total': 0, 'finished': False, 'site': site, 'tags': tags, 'status': ''}
proc = subprocess.Popen(
[sys.executable, 'downloader.py', '--site', site, '--query', tags],
cwd=app.root_path,
stdout=subprocess.PIPE,
text=True,
)
def read_stdout():
for line in proc.stdout:
line = line.strip()
if line.startswith('status:'):
downloads[job_id]['status'] = line[7:]
elif line.startswith('total:'):
downloads[job_id]['total'] = int(line.split(':')[1])
elif line.startswith('progress:'):
done, total = line.split(':')[1].split('/')
downloads[job_id]['done'] = int(done)
downloads[job_id]['total'] = int(total)
elif line == 'done':
downloads[job_id]['finished'] = True
downloads[job_id]['finished'] = True
threading.Thread(target=read_stdout, daemon=True).start()
return redirect(url_for('slideshow', tags=tags, job_id=job_id))
@app.route('/download/progress/<job_id>')
def download_progress(job_id):
def generate():
while True:
info = downloads.get(job_id)
if not info:
yield f'data: {json.dumps({"error": "not found"})}\n\n'
break
yield f'data: {json.dumps({"done": info["done"], "total": info["total"], "finished": info["finished"], "status": info["status"]})}\n\n'
if info['finished']:
break
time.sleep(0.3)
return Response(
generate(),
mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'},
)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=False)