import os import json import time from flask import Flask, request, jsonify, render_template from flask_cors import CORS import logging import threading from huggingface_hub import HfApi, hf_hub_download from huggingface_hub.utils import RepositoryNotFoundError, EntryNotFoundError # --- تنظیمات اصلی --- DATASET_REPO = "Ezmary/Karbaran-rayegan-tedad" DATASET_FILENAME = "video2ta_usage_data.json" USAGE_LIMIT = 5 HF_TOKEN = os.environ.get("HF_TOKEN") # --- راه‌اندازی Flask و لاگ‌ها --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) CORS(app) # --- مدیریت داده‌های کاربران --- usage_data_cache = [] cache_lock = threading.Lock() data_changed = threading.Event() api = None if not HF_TOKEN: logging.error("CRITICAL: Secret 'HF_TOKEN' not found. Cannot access the private dataset.") else: api = HfApi(token=HF_TOKEN) logging.info("HfApi initialized successfully.") def load_initial_data(): global usage_data_cache with cache_lock: if not api: return try: logging.info(f"Attempting to load data from '{DATASET_REPO}'...") local_path = hf_hub_download( repo_id=DATASET_REPO, filename=DATASET_FILENAME, repo_type="dataset", token=HF_TOKEN, force_download=True ) with open(local_path, 'r', encoding='utf-8') as f: content = f.read() usage_data_cache = json.loads(content) if content else [] logging.info(f"Loaded {len(usage_data_cache)} records.") except (RepositoryNotFoundError, EntryNotFoundError): logging.warning("Dataset file not found. A new one will be created.") usage_data_cache = [] except Exception as e: logging.warning(f"Could not load initial data (this is often normal in Spaces due to cache permissions): {e}") usage_data_cache = [] def persist_data_to_hub(): with cache_lock: if not data_changed.is_set() or not api: return logging.info("Change detected, preparing to write to Hub...") try: data_to_write = list(usage_data_cache) temp_filepath = "/tmp/temp_usage_data.json" with open(temp_filepath, 'w', encoding='utf-8') as f: json.dump(data_to_write, f, ensure_ascii=False, indent=2) api.upload_file( path_or_fileobj=temp_filepath, path_in_repo=DATASET_FILENAME, repo_id=DATASET_REPO, repo_type="dataset", commit_message="Update video usage data" ) os.remove(temp_filepath) data_changed.clear() logging.info(f"Successfully persisted {len(data_to_write)} records to Hub.") except Exception as e: logging.error(f"CRITICAL: Failed to persist data to Hub: {e}") def background_persister(): while True: time.sleep(30) persist_data_to_hub() # --- روت‌های API --- @app.route('/') def index(): return render_template('index.html') def get_user_identifier(data): fingerprint = data.get('fingerprint') if fingerprint: return str(fingerprint) if request.headers.getlist("X-Forwarded-For"): return request.headers.getlist("X-Forwarded-For")[0].split(',')[0].strip() return request.remote_addr @app.route('/api/check-credit', methods=['POST']) def check_credit(): data = request.get_json() if not data: return jsonify({"error": "Invalid request"}), 400 user_id = get_user_identifier(data) if not user_id: return jsonify({"error": "User identifier is required."}), 400 with cache_lock: now = time.time() one_week_seconds = 7 * 24 * 60 * 60 user_record = next((user for user in usage_data_cache if user.get('id') == user_id), None) credits_remaining = USAGE_LIMIT limit_reached = False reset_timestamp = 0 if user_record: if user_record.get('week_start', 0) < (now - one_week_seconds): user_record['count'] = 0 user_record['week_start'] = now data_changed.set() credits_remaining = max(0, USAGE_LIMIT - user_record.get('count', 0)) if credits_remaining == 0: limit_reached = True reset_timestamp = user_record.get('week_start', now) + one_week_seconds return jsonify({ "credits_remaining": credits_remaining, "limit_reached": limit_reached, "reset_timestamp": reset_timestamp }) @app.route('/api/use-credit', methods=['POST']) def use_credit(): data = request.get_json() if not data: return jsonify({"error": "Invalid request"}), 400 user_id = get_user_identifier(data) if not user_id: return jsonify({"error": "User identifier is required."}), 400 with cache_lock: now = time.time() one_week_seconds = 7 * 24 * 60 * 60 user_record = next((user for user in usage_data_cache if user.get('id') == user_id), None) if user_record: if user_record.get('week_start', 0) < (now - one_week_seconds): user_record['count'] = 0 user_record['week_start'] = now if user_record.get('count', 0) >= USAGE_LIMIT: reset_timestamp = user_record.get('week_start', now) + one_week_seconds return jsonify({ "status": "limit_reached", "credits_remaining": 0, "reset_timestamp": reset_timestamp }), 429 user_record['count'] += 1 else: user_record = {"id": user_id, "count": 1, "week_start": now} usage_data_cache.append(user_record) credits_remaining = USAGE_LIMIT - user_record['count'] data_changed.set() return jsonify({"status": "success", "credits_remaining": credits_remaining}) # --- اجرای برنامه --- if __name__ != '__main__': load_initial_data() persister_thread = threading.Thread(target=background_persister, daemon=True) persister_thread.start() if __name__ == '__main__': port = int(os.environ.get('PORT', 7860)) app.run(host='0.0.0.0', port=port)