Spaces:
No application file
No application file
| import os | |
| import json | |
| import tempfile | |
| from datetime import datetime | |
| from flask import Flask, request, render_template_string | |
| from huggingface_hub import HfApi, whoami | |
| app = Flask(__name__) | |
| api = HfApi() | |
| # ========================== | |
| # CONFIG | |
| # ========================== | |
| DATASET_REPO = "INDONESIA-AI/manga-dataset" | |
| OWNER_TOKEN = os.environ.get("HF_OWNER_TOKEN") | |
| # ========================== | |
| # VALIDASI USER TOKEN | |
| # ========================== | |
| def get_username(token): | |
| try: | |
| info = whoami(token=token) | |
| return info["name"] | |
| except: | |
| return None | |
| # ========================== | |
| # HTML UI | |
| # ========================== | |
| HTML = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Upload Komik</title> | |
| <style> | |
| body{ | |
| font-family:Arial; | |
| background:#111; | |
| color:white; | |
| padding:20px; | |
| } | |
| .container{ | |
| max-width:600px; | |
| margin:auto; | |
| background:#1e1e1e; | |
| padding:20px; | |
| border-radius:10px; | |
| } | |
| input{ | |
| width:100%; | |
| padding:12px; | |
| margin-top:5px; | |
| margin-bottom:15px; | |
| } | |
| button{ | |
| width:100%; | |
| padding:14px; | |
| background:#ff4757; | |
| border:none; | |
| color:white; | |
| font-size:18px; | |
| border-radius:8px; | |
| cursor:pointer; | |
| } | |
| button:hover{ | |
| background:#ff6b81; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h2>π Upload Komik Publik</h2> | |
| <form method="POST" action="/upload" enctype="multipart/form-data"> | |
| <label>HF API Key (identitas uploader)</label> | |
| <input type="text" name="user_token" required> | |
| <label>Judul Komik</label> | |
| <input type="text" name="title" required> | |
| <label>Comic ID (contoh: naruto)</label> | |
| <input type="text" name="comic_id" required> | |
| <label>Chapter (contoh: chapter_1)</label> | |
| <input type="text" name="chapter" required> | |
| <label>Cover Komik</label> | |
| <input type="file" name="cover" required> | |
| <label>Halaman Komik</label> | |
| <input type="file" name="pages" multiple required> | |
| <button type="submit">π Upload Komik</button> | |
| </form> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| def home(): | |
| return render_template_string(HTML) | |
| # ========================== | |
| # UPLOAD | |
| # ========================== | |
| def upload(): | |
| if not OWNER_TOKEN: | |
| return "β OWNER TOKEN belum diset di Space Secrets" | |
| user_token = request.form.get("user_token") | |
| username = get_username(user_token) | |
| if not username: | |
| return "β HF API Key tidak valid" | |
| title = request.form["title"] | |
| comic_id = request.form["comic_id"] | |
| chapter = request.form["chapter"] | |
| cover = request.files["cover"] | |
| pages = request.files.getlist("pages") | |
| base_path = f"comics/{comic_id}" | |
| chapter_path = f"{base_path}/{chapter}" | |
| # ========= Upload Cover ========= | |
| with tempfile.NamedTemporaryFile(delete=False) as tmp: | |
| cover.save(tmp.name) | |
| api.upload_file( | |
| path_or_fileobj=tmp.name, | |
| path_in_repo=f"{base_path}/cover.jpg", | |
| repo_id=DATASET_REPO, | |
| repo_type="dataset", | |
| token=OWNER_TOKEN, | |
| commit_message=f"Cover upload by {username}" | |
| ) | |
| os.remove(tmp.name) | |
| # ========= Upload Pages ========= | |
| for i, page in enumerate(pages): | |
| with tempfile.NamedTemporaryFile(delete=False) as tmp: | |
| page.save(tmp.name) | |
| api.upload_file( | |
| path_or_fileobj=tmp.name, | |
| path_in_repo=f"{chapter_path}/page_{i+1}.jpg", | |
| repo_id=DATASET_REPO, | |
| repo_type="dataset", | |
| token=OWNER_TOKEN, | |
| commit_message=f"{username} upload {chapter}" | |
| ) | |
| os.remove(tmp.name) | |
| # ========= Metadata ========= | |
| info = { | |
| "title": title, | |
| "uploaded_by": username, | |
| "created": datetime.now().isoformat() | |
| } | |
| with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp: | |
| json.dump(info, tmp) | |
| api.upload_file( | |
| path_or_fileobj=tmp.name, | |
| path_in_repo=f"{base_path}/info.json", | |
| repo_id=DATASET_REPO, | |
| repo_type="dataset", | |
| token=OWNER_TOKEN, | |
| commit_message="Update metadata" | |
| ) | |
| os.remove(tmp.name) | |
| return f""" | |
| <h3>β Upload berhasil!</h3> | |
| Uploader: {username}<br> | |
| Comic: {title}<br> | |
| Chapter: {chapter}<br><br> | |
| <a href="/">Upload lagi</a> | |
| """ | |
| # ========================== | |
| # RUN | |
| # ========================== | |
| if __name__ == "__main__": | |
| app.run(host="0.0.0.0", port=7860) | |