from flask import Flask, render_template, request, redirect, url_for, jsonify, session import requests import os import json from datetime import timedelta app = Flask(__name__) app.secret_key = os.urandom(24) # 세션 암호화를 위한 비밀 키 app.permanent_session_lifetime = timedelta(days=7) # 세션 유지 기간 설정 # 허깅페이스 URL 목록 HUGGINGFACE_URLS = [ "https://huggingface.co/spaces/ginipick/Tech_Hangman_Game", "https://huggingface.co/spaces/openfree/deepseek_r1_API", "https://huggingface.co/spaces/ginipick/open_Deep-Research", "https://huggingface.co/spaces/aiqmaster/open-deep-research", "https://huggingface.co/spaces/seawolf2357/DeepSeek-R1-32b-search", "https://huggingface.co/spaces/ginigen/LLaDA", "https://huggingface.co/spaces/VIDraft/PHI4-Multimodal", "https://huggingface.co/spaces/ginigen/Ovis2-8B", "https://huggingface.co/spaces/ginigen/Graph-Mind", "https://huggingface.co/spaces/ginigen/Workflow-Canvas", "https://huggingface.co/spaces/ginigen/Design", "https://huggingface.co/spaces/ginigen/Diagram", "https://huggingface.co/spaces/ginigen/Mockup", "https://huggingface.co/spaces/ginigen/Infographic", "https://huggingface.co/spaces/ginigen/Flowchart", "https://huggingface.co/spaces/aiqcamp/FLUX-Vision", "https://huggingface.co/spaces/ginigen/VoiceClone-TTS", "https://huggingface.co/spaces/openfree/Perceptron-Network", "https://huggingface.co/spaces/openfree/Article-Generator", ] # URL에서 모델/스페이스 정보 추출 def extract_model_info(url): parts = url.split('/') if len(parts) < 6: return None if parts[3] == 'spaces' or parts[3] == 'models': return { 'type': parts[3], 'owner': parts[4], 'repo': parts[5], 'full_id': f"{parts[4]}/{parts[5]}" } elif len(parts) >= 5: # 다른 형식의 URL return { 'type': 'models', # 기본값 'owner': parts[3], 'repo': parts[4], 'full_id': f"{parts[3]}/{parts[4]}" } return None # URL의 마지막 부분을 제목으로 추출 def extract_title(url): parts = url.split("/") title = parts[-1] if parts else "" return title.replace("_", " ").replace("-", " ") # 허깅페이스 인증 확인 def validate_token(token): headers = {"Authorization": f"Bearer {token}"} try: response = requests.get("https://huggingface.co/api/whoami", headers=headers) return response.ok, response.json() if response.ok else None except Exception as e: print(f"토큰 검증 오류: {e}") return False, None # 좋아요 목록 가져오기 def get_liked_repos(token): headers = {"Authorization": f"Bearer {token}"} endpoints = [ "/api/me/likes", "/api/me/liked-repos", "/api/me/favorites" ] liked_models = {} for endpoint in endpoints: try: response = requests.get(f"https://huggingface.co{endpoint}", headers=headers) if response.ok: data = response.json() if isinstance(data, list): for model in data: if isinstance(model, dict): if 'owner' in model and 'name' in model: liked_models[f"{model['owner']}/{model['name']}"] = True elif 'id' in model: liked_models[model['id']] = True elif isinstance(data, dict): for key in data: liked_models[key] = True print(f"좋아요 목록 가져오기 성공 (엔드포인트: {endpoint})") return liked_models except Exception as e: print(f"좋아요 목록 가져오기 오류 (엔드포인트: {endpoint}): {e}") return liked_models # 좋아요 토글 API 호출 def toggle_like(token, type_name, owner, repo, is_liked): headers = {"Authorization": f"Bearer {token}"} normalized_type = "spaces" if type_name == "spaces" else "models" method = "DELETE" if is_liked else "POST" url = f"https://huggingface.co/api/{normalized_type}/{owner}/{repo}/like" try: if method == "DELETE": response = requests.delete(url, headers=headers) else: response = requests.post(url, headers=headers) return response.ok, response.status_code except Exception as e: print(f"좋아요 토글 오류: {e}") return False, 500 # 홈페이지 라우트 @app.route('/') def home(): return render_template('index.html') # 로그인 처리 API @app.route('/api/login', methods=['POST']) def login(): token = request.form.get('token', '') if not token: return jsonify({'success': False, 'message': '토큰을 입력해주세요.'}) is_valid, user_info = validate_token(token) if not is_valid or not user_info: return jsonify({'success': False, 'message': '유효하지 않은 토큰입니다.'}) # 사용자 이름 찾기 username = None if 'name' in user_info: username = user_info['name'] elif 'user' in user_info and 'username' in user_info['user']: username = user_info['user']['username'] elif 'username' in user_info: username = user_info['username'] else: username = '인증된 사용자' # 좋아요 목록 가져오기 liked_models = get_liked_repos(token) # 세션에 저장 session['token'] = token session['username'] = username session['liked_models'] = liked_models return jsonify({ 'success': True, 'username': username, 'liked_models': liked_models }) # 로그아웃 처리 API @app.route('/api/logout', methods=['POST']) def logout(): session.pop('token', None) session.pop('username', None) session.pop('liked_models', None) return jsonify({'success': True}) # URL 목록 가져오기 API @app.route('/api/urls', methods=['GET']) def get_urls(): search_query = request.args.get('search', '').lower() show_only_liked = request.args.get('liked', 'false').lower() == 'true' liked_models = session.get('liked_models', {}) results = [] for url in HUGGINGFACE_URLS: title = extract_title(url) model_info = extract_model_info(url) if not model_info: continue is_liked = model_info['full_id'] in liked_models # 필터링 적용 if show_only_liked and not is_liked: continue if search_query and search_query not in url.lower() and search_query not in title.lower(): continue results.append({ 'url': url, 'title': title, 'model_info': model_info, 'is_liked': is_liked }) return jsonify(results) # 좋아요 토글 API @app.route('/api/toggle-like', methods=['POST']) def api_toggle_like(): if 'token' not in session: return jsonify({'success': False, 'message': '로그인이 필요합니다.'}) data = request.json url = data.get('url') current_liked = data.get('currentLiked', False) if not url: return jsonify({'success': False, 'message': 'URL이 필요합니다.'}) model_info = extract_model_info(url) if not model_info: return jsonify({'success': False, 'message': '유효하지 않은 URL입니다.'}) success, status_code = toggle_like( session['token'], model_info['type'], model_info['owner'], model_info['repo'], current_liked ) liked_models = session.get('liked_models', {}) if success: # 세션의 좋아요 상태 업데이트 if current_liked: if model_info['full_id'] in liked_models: del liked_models[model_info['full_id']] else: liked_models[model_info['full_id']] = True session['liked_models'] = liked_models return jsonify({ 'success': True, 'new_state': not current_liked, 'message': f"{'좋아요 취소' if current_liked else '좋아요'} 성공" }) else: return jsonify({ 'success': False, 'message': f"API 오류 (상태 코드: {status_code})" }) # 세션 상태 확인 API @app.route('/api/session-status', methods=['GET']) def session_status(): return jsonify({ 'logged_in': 'token' in session, 'username': session.get('username'), 'liked_count': len(session.get('liked_models', {})) }) if __name__ == '__main__': # templates 폴더 생성 os.makedirs('templates', exist_ok=True) # index.html 파일 생성 with open('templates/index.html', 'w', encoding='utf-8') as f: f.write(''' Hugging Face URL 카드 리스트
허깅페이스 계정: 로그인되지 않음
처리 중...
''') # 서버 실행 app.run(debug=True)