import os import sys import uuid import re import sqlite3 import numpy as np from datetime import datetime, timedelta import random import string import hashlib import json import time import threading import queue import logging import secrets import base64 from functools import wraps from contextlib import contextmanager from typing import Dict, List, Optional, Tuple, Any from dataclasses import dataclass from enum import Enum import hmac # استيراد Cloudinary import cloudinary import cloudinary.uploader import cloudinary.api # إنشاء المجلدات الضرورية os.makedirs('logs', exist_ok=True) os.makedirs('videos', exist_ok=True) os.makedirs('avatars', exist_ok=True) os.makedirs('thumbnails', exist_ok=True) os.makedirs('encrypted', exist_ok=True) os.makedirs('watermarked', exist_ok=True) os.makedirs('affiliate_logs', exist_ok=True) os.makedirs('cache', exist_ok=True) # إعداد التسجيل المتقدم logging.basicConfig( filename='logs/app.log', level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # محاولة استيراد المكتبات المتقدمة try: from PIL import Image, ImageDraw, ImageFont, ImageFilter HAS_PIL = True except ImportError: HAS_PIL = False logger.warning("PIL غير مثبت، ميزات الصور محدودة.") try: import cv2 import face_recognition HAS_CV2 = True HAS_FACE_RECOGNITION = True except ImportError: HAS_CV2 = False HAS_FACE_RECOGNITION = False logger.warning("OpenCV أو face_recognition غير مثبتين.") try: from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Random import get_random_bytes HAS_CRYPTO = True except ImportError: HAS_CRYPTO = False logger.warning("PyCryptodome غير مثبت، التشفير المتقدم لن يعمل.") try: import redis HAS_REDIS = True REDIS_CLIENT = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) except ImportError: HAS_REDIS = False REDIS_CLIENT = None logger.warning("Redis غير مثبت، سيتم استخدام الذاكرة المؤقتة المحلية.") try: import pyotp import qrcode from io import BytesIO HAS_2FA = True except ImportError: HAS_2FA = False logger.warning("pyotp أو qrcode غير مثبتين، المصادقة الثنائية غير متاحة.") try: from flask_limiter import Limiter from flask_limiter.util import get_remote_address HAS_LIMITER = True except ImportError: HAS_LIMITER = False logger.warning("Flask-Limiter غير مثبت، حماية Rate Limiting غير متاحة.") from flask import ( Flask, request, session, g, jsonify, render_template_string, redirect, url_for, abort, send_from_directory, Response, make_response, flash ) from werkzeug.utils import secure_filename from werkzeug.security import generate_password_hash, check_password_hash # ==================== التكوين الأساسي ==================== cloudinary.config( cloud_name="dpylnwrw0", api_key="631276857136451", api_secret="xpehguQcV_7nj0iBXsMNM5PssHE" ) app = Flask(__name__) app.secret_key = secrets.token_hex(64) app.config['SESSION_TYPE'] = 'filesystem' app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=30) app.config['JSON_AS_ASCII'] = False app.config['SESSION_COOKIE_SECURE'] = True app.config['SESSION_COOKIE_HTTPONLY'] = True app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' app.config['REMEMBER_COOKIE_SECURE'] = True app.config['REMEMBER_COOKIE_HTTPONLY'] = True # Rate Limiting if HAS_LIMITER: limiter = Limiter( app=app, key_func=get_remote_address, default_limits=["200 per day", "50 per hour"], storage_uri="memory://" ) else: # ديكور بديل بسيط class limiter: @staticmethod def limit(limit_string): def decorator(f): return f return decorator # إعدادات الملفات UPLOAD_FOLDER = os.path.join(os.getcwd(), 'videos') AVATAR_FOLDER = os.path.join(os.getcwd(), 'avatars') THUMBNAIL_FOLDER = os.path.join(os.getcwd(), 'thumbnails') ENCRYPTED_FOLDER = os.path.join(os.getcwd(), 'encrypted') WATERMARK_FOLDER = os.path.join(os.getcwd(), 'watermarked') CACHE_FOLDER = os.path.join(os.getcwd(), 'cache') ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'mov', 'avi', 'mkv', 'webm', '3gp', 'm4v'} ALLOWED_IMAGE_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'bmp'} ALLOWED_PLUGIN_EXTENSIONS = {'py'} app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['AVATAR_FOLDER'] = AVATAR_FOLDER app.config['THUMBNAIL_FOLDER'] = THUMBNAIL_FOLDER app.config['ENCRYPTED_FOLDER'] = ENCRYPTED_FOLDER app.config['WATERMARK_FOLDER'] = WATERMARK_FOLDER app.config['MAX_CONTENT_LENGTH'] = 1024 * 1024 * 1024 # 1GB للفيديوهات 4K # مفتاح التشفير الرئيسي MASTER_ENCRYPTION_KEY = hashlib.sha256(app.secret_key.encode()).digest() # قاعدة البيانات DATABASE = os.path.join(os.getcwd(), 'arc_video_pro.db') VECTOR_DIM = 64 # زيادة أبعاد المتجهات للذكاء الاصطناعي # مخازن مؤقتة notification_queues = {} active_streams = {} cache_storage = {} trending_cache = {'videos': [], 'hashtags': [], 'updated_at': None} recommendation_cache = {} # ==================== نماذج البيانات ==================== class UserRole(Enum): USER = 'user' CREATOR = 'creator' VIP = 'vip' VIP_GOLD = 'vip_gold' MODERATOR = 'moderator' ADMIN = 'admin' DEVELOPER = 'developer' class MembershipTier(Enum): FREE = 'free' PREMIUM = 'premium' VIP = 'vip' VIP_GOLD = 'vip_gold' @dataclass class MembershipBenefits: max_video_size: int = 500 * 1024 * 1024 # 500MB للعادي max_video_duration: int = 180 # 3 دقائق can_upload_4k: bool = False has_gold_badge: bool = False no_ads: bool = False can_live_stream: bool = False priority_support: bool = False monthly_coins: int = 0 MEMBERSHIP_BENEFITS = { MembershipTier.FREE: MembershipBenefits(), MembershipTier.PREMIUM: MembershipBenefits( max_video_size=1024 * 1024 * 1024, max_video_duration=600, can_upload_4k=True, no_ads=True, monthly_coins=100 ), MembershipTier.VIP: MembershipBenefits( max_video_size=2 * 1024 * 1024 * 1024, max_video_duration=1800, can_upload_4k=True, has_gold_badge=True, no_ads=True, can_live_stream=True, priority_support=True, monthly_coins=500 ), MembershipTier.VIP_GOLD: MembershipBenefits( max_video_size=5 * 1024 * 1024 * 1024, max_video_duration=3600, can_upload_4k=True, has_gold_badge=True, no_ads=True, can_live_stream=True, priority_support=True, monthly_coins=2000 ) } # ==================== دوال قاعدة البيانات المتقدمة ==================== def get_db(): db = getattr(g, '_database', None) if db is None: db = g._database = sqlite3.connect(DATABASE) db.row_factory = sqlite3.Row db.execute("PRAGMA foreign_keys = ON") db.execute("PRAGMA journal_mode = WAL") db.execute("PRAGMA synchronous = NORMAL") db.execute("PRAGMA cache_size = 10000") db.execute("PRAGMA temp_store = MEMORY") return db @app.teardown_appcontext def close_connection(exception): db = getattr(g, '_database', None) if db is not None: db.close() def query_db(query, args=(), one=False): """دالة مساعدة للاستعلامات مع التخزين المؤقت""" cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv def init_db(): """إنشاء قاعدة البيانات المتقدمة""" with app.app_context(): db = get_db() cursor = db.cursor() # جدول المستخدمين المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, avatar TEXT DEFAULT 'default.jpg', cover_image TEXT DEFAULT 'default_cover.jpg', bio TEXT, phone TEXT, gender TEXT, country TEXT, birth_date DATE, is_verified BOOLEAN DEFAULT 0, role TEXT DEFAULT 'user', membership_tier TEXT DEFAULT 'free', membership_expires TIMESTAMP, is_live BOOLEAN DEFAULT 0, coins INTEGER DEFAULT 100, diamonds INTEGER DEFAULT 0, xp INTEGER DEFAULT 0, level INTEGER DEFAULT 1, referral_code TEXT UNIQUE, referred_by INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, last_active TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- إحصائيات متقدمة total_views INTEGER DEFAULT 0, total_likes_received INTEGER DEFAULT 0, total_comments_received INTEGER DEFAULT 0, total_followers INTEGER DEFAULT 0, total_following INTEGER DEFAULT 0, -- أمان متقدم two_factor_secret TEXT, two_factor_enabled BOOLEAN DEFAULT 0, backup_codes TEXT, login_attempts INTEGER DEFAULT 0, locked_until TIMESTAMP, api_key TEXT UNIQUE, api_quota INTEGER DEFAULT 1000, -- إحالات affiliate_balance INTEGER DEFAULT 0, affiliate_clicks INTEGER DEFAULT 0, affiliate_conversions INTEGER DEFAULT 0, -- تطوير is_developer BOOLEAN DEFAULT 0, developer_api_key TEXT, FOREIGN KEY(referred_by) REFERENCES users(id) ) ''') # جدول الجلسات cursor.execute(''' CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, user_id INTEGER NOT NULL, ip_address TEXT, user_agent TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP NOT NULL, FOREIGN KEY(user_id) REFERENCES users(id) ) ''') # جدول إعدادات المستخدم المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS user_settings ( user_id INTEGER PRIMARY KEY, privacy_profile TEXT DEFAULT 'public', privacy_videos TEXT DEFAULT 'public', privacy_likes TEXT DEFAULT 'public', notifications_likes BOOLEAN DEFAULT 1, notifications_comments BOOLEAN DEFAULT 1, notifications_follows BOOLEAN DEFAULT 1, notifications_messages BOOLEAN DEFAULT 1, notifications_live BOOLEAN DEFAULT 1, notifications_gifts BOOLEAN DEFAULT 1, dark_mode BOOLEAN DEFAULT 1, language TEXT DEFAULT 'ar', content_language TEXT DEFAULT 'ar', autoplay BOOLEAN DEFAULT 1, save_data BOOLEAN DEFAULT 0, allow_download BOOLEAN DEFAULT 1, allow_duet BOOLEAN DEFAULT 1, allow_stitch BOOLEAN DEFAULT 1, FOREIGN KEY(user_id) REFERENCES users(id) ) ''') # جدول المتابعات cursor.execute(''' CREATE TABLE IF NOT EXISTS follows ( user_id INTEGER, follower_id INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(follower_id) REFERENCES users(id) ON DELETE CASCADE, PRIMARY KEY (user_id, follower_id) ) ''') # جدول الفيديوهات المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS videos ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, -- الملفات filename TEXT NOT NULL, filepath TEXT NOT NULL, filesize INTEGER DEFAULT 0, duration INTEGER DEFAULT 0, -- Cloudinary cloudinary_url TEXT, cloudinary_public_id TEXT, thumbnail TEXT, -- محتوى title TEXT, description TEXT, music TEXT, music_id INTEGER, -- إعدادات allow_comments BOOLEAN DEFAULT 1, allow_duet BOOLEAN DEFAULT 1, allow_stitch BOOLEAN DEFAULT 1, visibility TEXT DEFAULT 'public', -- إحصائيات upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, views INTEGER DEFAULT 0, likes_count INTEGER DEFAULT 0, shares_count INTEGER DEFAULT 0, saves_count INTEGER DEFAULT 0, comments_count INTEGER DEFAULT 0, -- تحليلات متقدمة avg_watch_time REAL DEFAULT 0, completion_rate REAL DEFAULT 0, -- بيانات AI vector BLOB, nsfw_score REAL DEFAULT 0.0, ai_tags TEXT, processed_for_ai BOOLEAN DEFAULT 0, -- أمان encrypted_path TEXT, encrypted_key BLOB, watermarked_path TEXT, -- تحديات challenge_id INTEGER, -- تقارير is_reported BOOLEAN DEFAULT 0, report_count INTEGER DEFAULT 0, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(challenge_id) REFERENCES challenges(id) ) ''') # جدول تفاعلات المستخدمين المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS interactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, video_id INTEGER NOT NULL, liked BOOLEAN DEFAULT 0, watched BOOLEAN DEFAULT 0, shared BOOLEAN DEFAULT 0, saved BOOLEAN DEFAULT 0, reported BOOLEAN DEFAULT 0, watch_time INTEGER DEFAULT 0, watch_percentage REAL DEFAULT 0, completed BOOLEAN DEFAULT 0, voted_challenge BOOLEAN DEFAULT 0, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE, UNIQUE(user_id, video_id) ) ''') # جدول التعليقات المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, video_id INTEGER NOT NULL, parent_id INTEGER, comment_text TEXT NOT NULL, likes_count INTEGER DEFAULT 0, is_pinned BOOLEAN DEFAULT 0, is_edited BOOLEAN DEFAULT 0, edit_history TEXT, is_reported BOOLEAN DEFAULT 0, report_count INTEGER DEFAULT 0, is_moderated BOOLEAN DEFAULT 0, moderation_action TEXT, timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE, FOREIGN KEY(parent_id) REFERENCES comments(id) ON DELETE CASCADE ) ''') # جدول مشاهدات الفيديو cursor.execute(''' CREATE TABLE IF NOT EXISTS video_views ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, video_id INTEGER NOT NULL, watch_time INTEGER DEFAULT 0, watch_percentage REAL DEFAULT 0, completed BOOLEAN DEFAULT 0, ip_address TEXT, user_agent TEXT, viewed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE ) ''') # جدول الهاشتاغات cursor.execute(''' CREATE TABLE IF NOT EXISTS hashtags ( id INTEGER PRIMARY KEY AUTOINCREMENT, tag TEXT UNIQUE NOT NULL, usage_count INTEGER DEFAULT 1, total_views INTEGER DEFAULT 0, total_likes INTEGER DEFAULT 0, trending_score REAL DEFAULT 0, last_trending_update TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # جدول ربط الفيديوهات بالهاشتاغات cursor.execute(''' CREATE TABLE IF NOT EXISTS video_hashtags ( video_id INTEGER NOT NULL, hashtag_id INTEGER NOT NULL, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE, FOREIGN KEY(hashtag_id) REFERENCES hashtags(id) ON DELETE CASCADE, PRIMARY KEY (video_id, hashtag_id) ) ''') # جدول الإشعارات المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS notifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, from_user_id INTEGER, type TEXT NOT NULL, content TEXT, video_id INTEGER, comment_id INTEGER, gift_id INTEGER, is_read BOOLEAN DEFAULT 0, is_seen BOOLEAN DEFAULT 0, priority INTEGER DEFAULT 0, action_url TEXT, image_url TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(from_user_id) REFERENCES users(id) ON DELETE SET NULL, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE, FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE ) ''') # جدول الرسائل الخاصة cursor.execute(''' CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, sender_id INTEGER NOT NULL, receiver_id INTEGER NOT NULL, message TEXT NOT NULL, is_read BOOLEAN DEFAULT 0, is_delivered BOOLEAN DEFAULT 0, is_deleted BOOLEAN DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(sender_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(receiver_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول المحادثات cursor.execute(''' CREATE TABLE IF NOT EXISTS conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, user1_id INTEGER NOT NULL, user2_id INTEGER NOT NULL, last_message TEXT, last_message_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, unread_count_user1 INTEGER DEFAULT 0, unread_count_user2 INTEGER DEFAULT 0, FOREIGN KEY(user1_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(user2_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE(user1_id, user2_id) ) ''') # جدول التحديات cursor.execute(''' CREATE TABLE IF NOT EXISTS challenges ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT, prize_coins INTEGER DEFAULT 50, prize_diamonds INTEGER DEFAULT 0, start_date DATE, end_date DATE, is_active BOOLEAN DEFAULT 1, winner_id INTEGER, total_participants INTEGER DEFAULT 0, total_votes INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(winner_id) REFERENCES users(id) ON DELETE SET NULL ) ''') # جدول المشاركات في التحديات cursor.execute(''' CREATE TABLE IF NOT EXISTS challenge_participants ( id INTEGER PRIMARY KEY AUTOINCREMENT, challenge_id INTEGER NOT NULL, user_id INTEGER NOT NULL, video_id INTEGER NOT NULL, votes INTEGER DEFAULT 0, rank INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(challenge_id) REFERENCES challenges(id) ON DELETE CASCADE, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(video_id) REFERENCES videos(id) ON DELETE CASCADE ) ''') # جدول التصويت في التحديات cursor.execute(''' CREATE TABLE IF NOT EXISTS challenge_votes ( user_id INTEGER NOT NULL, participant_id INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(participant_id) REFERENCES challenge_participants(id) ON DELETE CASCADE, PRIMARY KEY (user_id, participant_id) ) ''') # جدول البث المباشر المتقدم cursor.execute(''' CREATE TABLE IF NOT EXISTS live_streams ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, stream_key TEXT UNIQUE NOT NULL, title TEXT, description TEXT, thumbnail TEXT, viewers INTEGER DEFAULT 0, peak_viewers INTEGER DEFAULT 0, total_watch_time INTEGER DEFAULT 0, likes INTEGER DEFAULT 0, gifts_value INTEGER DEFAULT 0, started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ended_at TIMESTAMP, is_active BOOLEAN DEFAULT 1, recording_url TEXT, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول هدايا البث المباشر cursor.execute(''' CREATE TABLE IF NOT EXISTS live_gifts ( id INTEGER PRIMARY KEY AUTOINCREMENT, stream_id INTEGER NOT NULL, user_id INTEGER NOT NULL, gift_type TEXT NOT NULL, gift_value INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(stream_id) REFERENCES live_streams(id) ON DELETE CASCADE, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول الهدايا الافتراضية cursor.execute(''' CREATE TABLE IF NOT EXISTS virtual_gifts ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, price INTEGER NOT NULL, animation_url TEXT, image_url TEXT, is_active BOOLEAN DEFAULT 1 ) ''') # جدول نقاط الخبرة cursor.execute(''' CREATE TABLE IF NOT EXISTS user_xp ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, action TEXT NOT NULL, xp_gained INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول المكافآت اليومية cursor.execute(''' CREATE TABLE IF NOT EXISTS daily_rewards ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, reward_date DATE NOT NULL, reward_coins INTEGER NOT NULL, reward_xp INTEGER DEFAULT 0, streak INTEGER DEFAULT 1, claimed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, reward_date) ) ''') # جدول المعاملات المالية cursor.execute(''' CREATE TABLE IF NOT EXISTS transactions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, type TEXT NOT NULL, amount INTEGER NOT NULL, currency TEXT DEFAULT 'coins', balance_after INTEGER, description TEXT, reference_id TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول الإحالات cursor.execute(''' CREATE TABLE IF NOT EXISTS referrals ( id INTEGER PRIMARY KEY AUTOINCREMENT, referrer_id INTEGER NOT NULL, referred_id INTEGER NOT NULL, reward_coins INTEGER DEFAULT 50, reward_xp INTEGER DEFAULT 20, status TEXT DEFAULT 'completed', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(referrer_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(referred_id) REFERENCES users(id) ON DELETE CASCADE, UNIQUE(referred_id) ) ''') # جدول نقرات الإحالة cursor.execute(''' CREATE TABLE IF NOT EXISTS affiliate_clicks ( id INTEGER PRIMARY KEY AUTOINCREMENT, referrer_id INTEGER NOT NULL, ip TEXT, user_agent TEXT, clicked_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, converted BOOLEAN DEFAULT 0, FOREIGN KEY(referrer_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول البلاغات cursor.execute(''' CREATE TABLE IF NOT EXISTS reports ( id INTEGER PRIMARY KEY AUTOINCREMENT, reporter_id INTEGER NOT NULL, reported_user_id INTEGER, reported_video_id INTEGER, reported_comment_id INTEGER, reason TEXT NOT NULL, details TEXT, status TEXT DEFAULT 'pending', action_taken TEXT, handled_by INTEGER, handled_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(reporter_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(reported_user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY(reported_video_id) REFERENCES videos(id) ON DELETE CASCADE, FOREIGN KEY(reported_comment_id) REFERENCES comments(id) ON DELETE CASCADE, FOREIGN KEY(handled_by) REFERENCES users(id) ON DELETE SET NULL ) ''') # جدول إضافات المطورين cursor.execute(''' CREATE TABLE IF NOT EXISTS developer_plugins ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, name TEXT NOT NULL, filename TEXT NOT NULL, code TEXT NOT NULL, is_active BOOLEAN DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id, name) ) ''') # جدول جلسات المطورين cursor.execute(''' CREATE TABLE IF NOT EXISTS developer_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, session_token TEXT UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP NOT NULL, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE ) ''') # جدول أحداث التحليلات cursor.execute(''' CREATE TABLE IF NOT EXISTS analytics_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, session_id TEXT, event_name TEXT NOT NULL, event_data TEXT, page_url TEXT, ip_address TEXT, user_agent TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # جدول الموسيقى cursor.execute(''' CREATE TABLE IF NOT EXISTS music_tracks ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, artist TEXT, album TEXT, duration INTEGER, url TEXT, cloudinary_public_id TEXT, cover_image TEXT, genre TEXT, usage_count INTEGER DEFAULT 0, is_active BOOLEAN DEFAULT 1, uploaded_by INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # جدول كلمات البحث cursor.execute(''' CREATE TABLE IF NOT EXISTS search_queries ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, query TEXT NOT NULL, results_count INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE SET NULL ) ''') # إنشاء الفهارس لتحسين الأداء cursor.execute('CREATE INDEX IF NOT EXISTS idx_videos_user_id ON videos(user_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_videos_upload_time ON videos(upload_time DESC)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_videos_views ON videos(views DESC)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_videos_nsfw ON videos(nsfw_score)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_comments_video_id ON comments(video_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_comments_user_id ON comments(user_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_interactions_user_id ON interactions(user_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_interactions_video_id ON interactions(video_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id, is_read, created_at DESC)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_follows_user_id ON follows(user_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_follows_follower_id ON follows(follower_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_hashtags_tag ON hashtags(tag)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_hashtags_trending ON hashtags(trending_score DESC)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_video_views_video_id ON video_views(video_id)') cursor.execute('CREATE INDEX IF NOT EXISTS idx_transactions_user_id ON transactions(user_id, created_at DESC)') db.commit() # إنشاء البيانات الافتراضية create_default_data() logger.info("✅ تم تهيئة قاعدة البيانات بنجاح") def create_default_data(): """إنشاء البيانات الافتراضية""" db = get_db() # إنشاء مستخدم admin admin = db.execute("SELECT id FROM users WHERE username = 'admin'").fetchone() if not admin: hashed = generate_password_hash('Admin@123456') referral = generate_referral_code() cursor = db.execute(''' INSERT INTO users ( username, email, password_hash, role, referral_code, is_developer, is_verified, coins, diamonds, xp, level ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ('admin', 'admin@arc.com', hashed, 'admin', referral, 1, 1, 10000, 1000, 10000, 50)) admin_id = cursor.lastrowid db.execute('INSERT INTO user_settings (user_id) VALUES (?)', (admin_id,)) db.commit() # إنشاء مستخدم VIP تجريبي vip = db.execute("SELECT id FROM users WHERE username = 'vip'").fetchone() if not vip: hashed = generate_password_hash('Vip@123456') referral = generate_referral_code() cursor = db.execute(''' INSERT INTO users ( username, email, password_hash, role, membership_tier, referral_code, is_verified, coins, diamonds, xp, level ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ('vip', 'vip@arc.com', hashed, 'vip', 'vip_gold', referral, 1, 5000, 500, 5000, 30)) vip_id = cursor.lastrowid db.execute('INSERT INTO user_settings (user_id) VALUES (?)', (vip_id,)) db.commit() # إنشاء مستخدم creator creator = db.execute("SELECT id FROM users WHERE username = 'creator'").fetchone() if not creator: hashed = generate_password_hash('Creator@123456') referral = generate_referral_code() cursor = db.execute(''' INSERT INTO users ( username, email, password_hash, role, referral_code, is_verified, coins, diamonds, xp, level ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', ('creator', 'creator@arc.com', hashed, 'creator', referral, 1, 2000, 100, 2000, 15)) creator_id = cursor.lastrowid db.execute('INSERT INTO user_settings (user_id) VALUES (?)', (creator_id,)) db.commit() # إنشاء هدايا افتراضية gifts = db.execute("SELECT id FROM virtual_gifts").fetchall() if not gifts: default_gifts = [ ('وردة', 10, '🌹', '/static/gifts/rose.gif'), ('تاج', 50, '👑', '/static/gifts/crown.gif'), ('سيارة', 100, '🚗', '/static/gifts/car.gif'), ('يخت', 500, '🛥️', '/static/gifts/yacht.gif'), ('قصر', 1000, '🏰', '/static/gifts/castle.gif'), ('صاروخ', 2000, '🚀', '/static/gifts/rocket.gif'), ('مجرة', 5000, '🌌', '/static/gifts/galaxy.gif'), ] for name, price, emoji, url in default_gifts: db.execute(''' INSERT INTO virtual_gifts (name, price, animation_url, image_url) VALUES (?, ?, ?, ?) ''', (f'{emoji} {name}', price, url, url)) db.commit() # إنشاء تحديات افتراضية challenges = db.execute("SELECT id FROM challenges").fetchall() if not challenges: today = datetime.now().strftime('%Y-%m-%d') next_week = (datetime.now() + timedelta(days=7)).strftime('%Y-%m-%d') next_month = (datetime.now() + timedelta(days=30)).strftime('%Y-%m-%d') challenges_data = [ ('تحدي الرقص', 'ارقص أفضل رقصة واربح 1000 عملة', 1000, 50, today, next_week), ('تحدي الطبخ', 'اطبخ ألذ طبق في 60 ثانية', 1500, 100, today, next_week), ('تحدي الضحك', 'أضحكنا من قلبك', 500, 20, today, next_week), ('تحدي التقليد', 'قلد أي شخصية مشهورة', 2000, 150, today, next_month), ('تحدي المواهب', 'أظهر موهبتك الفريدة', 3000, 200, today, next_month), ('تحدي الجمال', 'شاركنا روتين جمالك', 800, 30, today, next_week), ('تحدي اللياقة', 'تمرين سريع في 5 دقائق', 1200, 60, today, next_week), ] for c in challenges_data: db.execute(''' INSERT INTO challenges (title, description, prize_coins, prize_diamonds, start_date, end_date) VALUES (?, ?, ?, ?, ?, ?) ''', c) db.commit() # إنشاء موسيقى افتراضية music = db.execute("SELECT id FROM music_tracks").fetchall() if not music: music_data = [ ('صوت الطبيعة', 'ARC', 'أصوات', 180, None), ('إيقاعات حماسية', 'ARC', 'إلكتروني', 210, None), ('لحن هادئ', 'ARC', 'موسيقى تصويرية', 240, None), ('تحدي الرقص', 'ARC', 'بوب', 195, None), ('مقدمة فيديو', 'ARC', 'إعلاني', 90, None), ] for title, artist, genre, duration, url in music_data: db.execute(''' INSERT INTO music_tracks (title, artist, genre, duration, url, is_active) VALUES (?, ?, ?, ?, ?, 1) ''', (title, artist, genre, duration, url)) db.commit() # ==================== دوال مساعدة متقدمة ==================== def generate_referral_code(): """توليد كود إحالة فريد""" return secrets.token_hex(6).upper() def random_vector(dim=VECTOR_DIM): """توليد متجه عشوائي للفيديوهات الجديدة""" vec = np.random.randn(dim).astype(np.float32) return vec.tobytes() def bytes_to_vector(b): """تحويل البيانات الثنائية إلى متجه numpy""" if b is None: return np.zeros(VECTOR_DIM, dtype=np.float32) return np.frombuffer(b, dtype=np.float32) def allowed_file(filename, allowed_extensions): """التحقق من امتداد الملف""" return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions def allowed_video(filename): return allowed_file(filename, ALLOWED_VIDEO_EXTENSIONS) def allowed_image(filename): return allowed_file(filename, ALLOWED_IMAGE_EXTENSIONS) def allowed_plugin(filename): return allowed_file(filename, ALLOWED_PLUGIN_EXTENSIONS) def get_client_ip(): """الحصول على عنوان IP العميل""" if request.headers.get('X-Forwarded-For'): return request.headers.get('X-Forwarded-For').split(',')[0].strip() return request.remote_addr def get_user_membership_tier(user_id): """الحصول على مستوى عضوية المستخدم""" user = query_db('SELECT membership_tier, membership_expires FROM users WHERE id = ?', (user_id,), one=True) if not user: return MembershipTier.FREE # التحقق من انتهاء الصلاحية if user['membership_expires']: expires = datetime.strptime(user['membership_expires'], '%Y-%m-%d %H:%M:%S') if expires < datetime.now(): return MembershipTier.FREE try: return MembershipTier(user['membership_tier']) except: return MembershipTier.FREE def get_membership_benefits(user_id): """الحصول على مزايا العضوية للمستخدم""" tier = get_user_membership_tier(user_id) return MEMBERSHIP_BENEFITS[tier] def cache_get(key): """الحصول من التخزين المؤقت""" if HAS_REDIS and REDIS_CLIENT: return REDIS_CLIENT.get(key) return cache_storage.get(key) def cache_set(key, value, timeout=300): """تخزين في الذاكرة المؤقتة""" if HAS_REDIS and REDIS_CLIENT: REDIS_CLIENT.setex(key, timeout, value) else: cache_storage[key] = { 'value': value, 'expires': time.time() + timeout } def cache_delete(key): """حذف من التخزين المؤقت""" if HAS_REDIS and REDIS_CLIENT: REDIS_CLIENT.delete(key) else: cache_storage.pop(key, None) # ==================== دوال الذكاء الاصطناعي المتقدمة ==================== def analyze_video_content(video_path): """تحليل متقدم لمحتوى الفيديو""" results = { 'nsfw_score': 0.0, 'faces_count': 0, 'has_text': False, 'brightness': 0.5, 'motion_score': 0.0, 'scene_type': 'unknown', 'tags': [] } if not HAS_CV2: return results try: cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return results total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) if total_frames == 0: cap.release() return results # أخذ عينات من الفيديو sample_frames = min(20, total_frames) frame_indices = [int(i * total_frames / sample_frames) for i in range(sample_frames)] skin_tone_frames = 0 face_frames = 0 brightness_sum = 0 motion_sum = 0 prev_frame = None for idx in frame_indices: cap.set(cv2.CAP_PROP_POS_FRAMES, idx) ret, frame = cap.read() if not ret: continue # تحليل البشرة hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_skin = np.array([0, 20, 70], dtype=np.uint8) upper_skin = np.array([20, 255, 255], dtype=np.uint8) skin_mask = cv2.inRange(hsv, lower_skin, upper_skin) skin_percentage = (cv2.countNonZero(skin_mask) / (frame.shape[0] * frame.shape[1])) * 100 if skin_percentage > 30: skin_tone_frames += 1 # اكتشاف الوجوه if HAS_FACE_RECOGNITION: gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) face_locations = face_recognition.face_locations(gray) if face_locations: face_frames += len(face_locations) # تحليل السطوع gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) brightness_sum += np.mean(gray) / 255.0 # تحليل الحركة if prev_frame is not None: flow = cv2.calcOpticalFlowFarneback(prev_frame, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0) motion = np.mean(np.sqrt(flow[...,0]**2 + flow[...,1]**2)) motion_sum += motion prev_frame = gray cap.release() # حساب النتائج results['nsfw_score'] = skin_tone_frames / sample_frames results['faces_count'] = face_frames // sample_frames if sample_frames > 0 else 0 results['brightness'] = brightness_sum / sample_frames if sample_frames > 0 else 0.5 results['motion_score'] = motion_sum / (sample_frames - 1) if sample_frames > 1 else 0 # تحديد نوع المشهد if results['faces_count'] > 2: results['scene_type'] = 'group' elif results['faces_count'] > 0: results['scene_type'] = 'portrait' elif results['motion_score'] > 10: results['scene_type'] = 'action' elif results['motion_score'] < 2: results['scene_type'] = 'static' else: results['scene_type'] = 'normal' # توليد وسوم تلقائية tags = [] if results['faces_count'] > 0: tags.append('بشر') tags.append('people') if results['scene_type'] == 'action': tags.append('حركة') tags.append('action') if results['brightness'] > 0.7: tags.append('مشرق') tags.append('bright') elif results['brightness'] < 0.3: tags.append('داكن') tags.append('dark') results['tags'] = tags[:10] except Exception as e: logger.error(f"خطأ في تحليل الفيديو: {e}") return results def generate_ai_tags(text, video_analysis=None): """توليد وسوم ذكية من النص وتحليل الفيديو""" tags = set() # استخراج الهاشتاغات hashtags = re.findall(r'#(\w+)', text) tags.update(hashtags) # استخراج الكلمات المهمة words = re.findall(r'[\w]+', text) important_words = [w for w in words if len(w) > 2 and w.isalpha()] common_tags = ['فيديو', 'ترند', 'مشاهير', 'جديد', 'حلويات', 'طبخ', 'comedy', 'funny', 'music', 'dance', 'tiktok', 'fyp', 'trending', 'viral', 'love', 'happy', 'sad', 'emotional'] for word in important_words: word_lower = word.lower() if word_lower in common_tags: tags.add(word_lower) # إضافة وسوم من تحليل الفيديو if video_analysis: tags.update(video_analysis.get('tags', [])) return json.dumps(list(tags)[:20]) def moderate_comment(comment_text): """فحص التعليقات تلقائياً""" # كلمات ممنوعة banned_words = ['سب', 'شتم', 'سخاف', 'قذر', 'fuck', 'shit', 'ass', 'sex', 'porn'] comment_lower = comment_text.lower() # فحص الكلمات الممنوعة for word in banned_words: if word in comment_lower: return { 'is_appropriate': False, 'reason': f'يحتوي على كلمة ممنوعة: {word}', 'action': 'block' } # فحص الروابط المشبوهة suspicious_patterns = [r'bit\.ly', r'tinyurl', r'goo\.gl', r'http://', r'https://'] for pattern in suspicious_patterns: if re.search(pattern, comment_lower): return { 'is_appropriate': False, 'reason': 'يحتوي على رابط خارجي', 'action': 'flag' } # فحص التكرار (سبام) words = comment_lower.split() if len(set(words)) < len(words) * 0.5 and len(words) > 10: return { 'is_appropriate': False, 'reason': 'نسبة تكرار عالية (احتمال سبام)', 'action': 'flag' } return { 'is_appropriate': True, 'reason': None, 'action': 'allow' } # ==================== نظام التوصيات المتقدم ==================== def get_user_embedding(user_id): """توليد تضمين متقدم للمستخدم""" db = get_db() # جمع تفاعلات المستخدم interactions = db.execute(''' SELECT v.vector, i.liked, i.watch_time, i.completed, i.shared, i.saved FROM videos v JOIN interactions i ON v.id = i.video_id WHERE i.user_id = ? AND v.vector IS NOT NULL ORDER BY i.timestamp DESC LIMIT 100 ''', (user_id,)).fetchall() if not interactions: return np.zeros(VECTOR_DIM, dtype=np.float32) # حساب التضمين المرجح vectors = [] weights = [] for interaction in interactions: vec = bytes_to_vector(interaction['vector']) vectors.append(vec) # حساب الوزن بناءً على التفاعل weight = 1.0 if interaction['liked']: weight += 2.0 if interaction['completed']: weight += 1.5 if interaction['shared']: weight += 2.5 if interaction['saved']: weight += 3.0 # وزن إضافي للمشاهدة الطويلة watch_time = interaction['watch_time'] or 0 weight += min(watch_time / 60, 2.0) # كل دقيقة مشاهدة تزيد الوزن حتى 2 weights.append(weight) if not vectors: return np.zeros(VECTOR_DIM, dtype=np.float32) # المتوسط المرجح vectors_array = np.array(vectors) weights_array = np.array(weights).reshape(-1, 1) weighted_avg = np.average(vectors_array, axis=0, weights=weights_array.flatten()) return weighted_avg def get_trending_score(video_id): """حساب درجة الشعبية للفيديو""" db = get_db() video = db.execute(''' SELECT views, likes_count, comments_count, shares_count, saves_count, upload_time FROM videos WHERE id = ? ''', (video_id,)).fetchone() if not video: return 0 # معاملات الوزن weights = { 'view': 1, 'like': 3, 'comment': 5, 'share': 8, 'save': 4 } # حساب النقاط الأساسية base_score = ( video['views'] * weights['view'] + video['likes_count'] * weights['like'] + video['comments_count'] * weights['comment'] + video['shares_count'] * weights['share'] + video['saves_count'] * weights['save'] ) # عامل الحداثة (كلما كان الفيديو أحدث، كلما زادت النقاط) upload_time = datetime.strptime(video['upload_time'], '%Y-%m-%d %H:%M:%S') hours_ago = (datetime.now() - upload_time).total_seconds() / 3600 freshness = max(0, 100 / (hours_ago + 1)) # عامل الانتشار total_interactions = video['views'] + video['likes_count'] + video['comments_count'] spread_factor = 1 + (video['shares_count'] / (total_interactions + 1)) final_score = base_score * freshness * spread_factor return final_score def update_trending(): """تحديث المحتوى الرائج""" global trending_cache db = get_db() # تحديث الفيديوهات الرائجة videos = db.execute(''' SELECT v.*, u.username, u.avatar, u.is_verified FROM videos v JOIN users u ON v.user_id = u.id WHERE v.visibility = 'public' AND v.nsfw_score < 0.7 ORDER BY (v.views * 1 + v.likes_count * 3 + v.comments_count * 5 + v.shares_count * 8 + v.saves_count * 4) * (1.0 / (julianday('now') - julianday(v.upload_time))) DESC LIMIT 50 ''').fetchall() # تحديث الهاشتاغات الرائجة hashtags = db.execute(''' SELECT h.*, (h.usage_count * 1 + h.total_views * 0.1 + h.total_likes * 0.3) as trending_score FROM hashtags h ORDER BY trending_score DESC LIMIT 20 ''').fetchall() trending_cache = { 'videos': [dict(v) for v in videos], 'hashtags': [dict(h) for h in hashtags], 'updated_at': datetime.now() } return trending_cache def get_trending(): """الحصول على المحتوى الرائج""" global trending_cache # تحديث كل ساعة if (trending_cache['updated_at'] and datetime.now() - trending_cache['updated_at'] < timedelta(hours=1)): return trending_cache return update_trending() def recommend_videos_advanced(user_id, limit=10, offset=0, include_explore=False): """نظام توصيات متقدم""" db = get_db() try: # الحصول على الفيديوهات التي شاهدها المستخدم watched = db.execute('SELECT video_id FROM interactions WHERE user_id = ? AND watched = 1', (user_id,)).fetchall() watched_ids = [w['video_id'] for w in watched] if watched else [-1] # الحصول على المستخدمين الذين يتابعهم following = db.execute('SELECT user_id FROM follows WHERE follower_id = ?', (user_id,)).fetchall() following_ids = [f['user_id'] for f in following] if following else [-1] # الحصول على تضمين المستخدم user_embedding = get_user_embedding(user_id) # الحصول على الفيديوهات المتاحة placeholders = ','.join(['?'] * len(watched_ids)) # استعلام متقدم مع عوامل متعددة query = f''' SELECT v.*, u.username, u.avatar, u.is_verified, u.role, (SELECT COUNT(*) FROM follows WHERE user_id = v.user_id) as creator_followers, (SELECT COUNT(*) FROM interactions WHERE video_id = v.id AND liked = 1) as likes, (SELECT COUNT(*) FROM comments WHERE video_id = v.id) as comments, julianday('now') - julianday(v.upload_time) as days_ago FROM videos v JOIN users u ON v.user_id = u.id WHERE v.id NOT IN ({placeholders}) AND v.visibility = 'public' AND v.nsfw_score < 0.7 ''' if not include_explore: # في الوضع العادي، نفضل الفيديوهات من المستخدمين المشهورين أو المتبعين query += ' AND (u.role IN ("creator", "vip") OR v.user_id IN ({follow_ph}))'.format( follow_ph=','.join(['?'] * len(following_ids)) if following_ids else 'NULL' ) query += ' ORDER BY v.upload_time DESC LIMIT ? OFFSET ?' params = watched_ids + (following_ids if following_ids else []) + [limit * 3, offset] rows = db.execute(query, params).fetchall() video_scores = [] for row in rows: score = 0 # 1. تشابه المحتوى (30% من النتيجة) if not np.all(user_embedding == 0): vec_row = db.execute('SELECT vector FROM videos WHERE id = ?', (row['id'],)).fetchone() if vec_row and vec_row['vector']: vec = bytes_to_vector(vec_row['vector']) similarity = 1.0 / (1.0 + np.linalg.norm(user_embedding - vec)) score += similarity * 30 # 2. الشعبية (25% من النتيجة) popularity = ( row['views'] * 1 + row['likes_count'] * 3 + row['comments_count'] * 5 + row['shares_count'] * 8 ) / 1000 score += min(popularity, 25) # 3. الحداثة (20% من النتيجة) days_ago = row['days_ago'] or 0 recency = max(0, 20 - (days_ago * 2)) score += recency # 4. المتابعة (15% من النتيجة) if row['user_id'] in following_ids: score += 15 elif row['creator_followers'] > 1000: # منشئ محتوى مشهور score += 5 # 5. نسبة التفاعل (10% من النتيجة) if row['views'] > 0: engagement = (row['likes_count'] + row['comments_count']) / row['views'] * 100 score += min(engagement, 10) video_scores.append((score, dict(row))) video_scores.sort(key=lambda x: x[0], reverse=True) return [v for _, v in video_scores[:limit]] except Exception as e: logger.error(f"خطأ في نظام التوصيات: {e}") # fallback إلى فيديوهات عشوائية rows = db.execute(''' SELECT v.*, u.username, u.avatar, u.is_verified FROM videos v JOIN users u ON v.user_id = u.id WHERE v.visibility = 'public' AND v.nsfw_score < 0.7 ORDER BY RANDOM() LIMIT ? OFFSET ? ''', (limit, offset)).fetchall() return [dict(row) for row in rows] # ==================== نظام النقاط والمستويات المتقدم ==================== def calculate_level_from_xp(xp): """حساب المستوى من نقاط الخبرة (منحنى أسي)""" if xp < 100: return 1 return int((xp / 100) ** 0.5) + 1 def calculate_xp_for_level(level): """حساب النقاط المطلوبة لمستوى معين""" return 100 * (level - 1) ** 2 def add_xp(user_id, action, base_xp, multiplier=1.0): """إضافة نقاط خبرة مع مضاعفات""" db = get_db() try: # حساب النقاط الفعلية مع المضاعفات xp_gained = int(base_xp * multiplier) db.execute(''' INSERT INTO user_xp (user_id, action, xp_gained) VALUES (?, ?, ?) ''', (user_id, action, xp_gained)) db.execute('UPDATE users SET xp = xp + ? WHERE id = ?', (xp_gained, user_id)) # تحديث المستوى user = db.execute('SELECT xp, level FROM users WHERE id = ?', (user_id,)).fetchone() if user: new_level = calculate_level_from_xp(user['xp']) if new_level > user['level']: db.execute('UPDATE users SET level = ? WHERE id = ?', (new_level, user_id)) # مكافأة عند زيادة المستوى add_coins(user_id, new_level * 10, f'مكافأة وصول للمستوى {new_level}') # إشعار بزيادة المستوى add_notification( user_id, None, 'level_up', f'تهانينا! وصلت إلى المستوى {new_level}', priority=2 ) db.commit() return True except Exception as e: logger.error(f"خطأ في إضافة XP: {e}") return False def add_coins(user_id, amount, description, currency='coins'): """إضافة عملات مع تسجيل المعاملة""" db = get_db() try: # الحصول على الرصيد الحالي user = db.execute(f'SELECT {currency} FROM users WHERE id = ?', (user_id,)).fetchone() balance_before = user[currency] if user else 0 # تحديث الرصيد db.execute(f'UPDATE users SET {currency} = {currency} + ? WHERE id = ?', (amount, user_id)) # تسجيل المعاملة db.execute(''' INSERT INTO transactions (user_id, type, amount, currency, balance_after, description) VALUES (?, 'earn', ?, ?, ?, ?) ''', (user_id, amount, currency, balance_before + amount, description)) db.commit() return True except Exception as e: logger.error(f"خطأ في إضافة العملات: {e}") return False def deduct_coins(user_id, amount, description, currency='coins'): """خصم عملات مع تسجيل المعاملة""" db = get_db() try: # التحقق من الرصيد user = db.execute(f'SELECT {currency} FROM users WHERE id = ?', (user_id,)).fetchone() if not user or user[currency] < amount: return False balance_before = user[currency] # تحديث الرصيد db.execute(f'UPDATE users SET {currency} = {currency} - ? WHERE id = ?', (amount, user_id)) # تسجيل المعاملة db.execute(''' INSERT INTO transactions (user_id, type, amount, currency, balance_after, description) VALUES (?, 'spend', ?, ?, ?, ?) ''', (user_id, amount, currency, balance_before - amount, description)) db.commit() return True except Exception as e: logger.error(f"خطأ في خصم العملات: {e}") return False def claim_daily_reward(user_id): """المطالبة بالمكافأة اليومية المتقدمة""" db = get_db() today = datetime.now().date() try: # التحقق من استلام المكافأة اليوم existing = db.execute(''' SELECT id, streak FROM daily_rewards WHERE user_id = ? AND reward_date = ? ''', (user_id, today.isoformat())).fetchone() if existing: return False, "تم استلام المكافأة اليوم مسبقاً" # حساب السلسلة المتتالية yesterday = (today - timedelta(days=1)).isoformat() last = db.execute(''' SELECT streak FROM daily_rewards WHERE user_id = ? AND reward_date = ? ''', (user_id, yesterday)).fetchone() streak = (last['streak'] + 1) if last else 1 # حساب المكافآت (تزيد مع السلسلة) base_coins = 50 base_xp = 20 coins = base_coins * streak xp = base_xp * streak # مكافآت إضافية للسلاسل الطويلة bonus_coins = 0 if streak >= 7: bonus_coins += 100 # مكافأة أسبوعية if streak >= 30: bonus_coins += 500 # مكافأة شهرية total_coins = coins + bonus_coins # تسجيل المكافأة db.execute(''' INSERT INTO daily_rewards (user_id, reward_date, reward_coins, reward_xp, streak) VALUES (?, ?, ?, ?, ?) ''', (user_id, today.isoformat(), total_coins, xp, streak)) # إضافة العملات والنقاط add_coins(user_id, total_coins, f'مكافأة يومية (السلسلة: {streak})') add_xp(user_id, 'daily_reward', xp) db.commit() return True, total_coins, xp, streak except Exception as e: logger.error(f"خطأ في المكافأة اليومية: {e}") return False, str(e) # ==================== الأمن المتقدم ==================== def generate_2fa_secret(): """توليد سر للمصادقة الثنائية""" if not HAS_2FA: return None return pyotp.random_base32() def generate_2fa_qr(username, secret): """توليد رمز QR للمصادقة الثنائية""" if not HAS_2FA: return None totp = pyotp.TOTP(secret) provisioning_uri = totp.provisioning_uri(name=username, issuer_name="ARC Video") # توليد QR code qr = qrcode.QRCode(version=1, box_size=10, border=5) qr.add_data(provisioning_uri) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") # تحويل إلى base64 buffered = BytesIO() img.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode() return img_str def verify_2fa_code(secret, code): """التحقق من رمز المصادقة الثنائية""" if not HAS_2FA or not secret: return False totp = pyotp.TOTP(secret) return totp.verify(code) def generate_backup_codes(): """توليد رموز احتياطية""" codes = [] for _ in range(8): code = secrets.token_hex(4).upper() codes.append(code) return json.dumps(codes) def encrypt_sensitive_data(data, key=None): """تشفير البيانات الحساسة""" if not HAS_CRYPTO: return data if key is None: key = MASTER_ENCRYPTION_KEY try: if isinstance(data, str): data = data.encode('utf-8') cipher = AES.new(key, AES.MODE_GCM) ciphertext, tag = cipher.encrypt_and_digest(data) return { 'ciphertext': base64.b64encode(ciphertext).decode('utf-8'), 'nonce': base64.b64encode(cipher.nonce).decode('utf-8'), 'tag': base64.b64encode(tag).decode('utf-8') } except Exception as e: logger.error(f"خطأ في التشفير: {e}") return data def decrypt_sensitive_data(encrypted_data, key=None): """فك تشفير البيانات الحساسة""" if not HAS_CRYPTO: return encrypted_data if key is None: key = MASTER_ENCRYPTION_KEY try: if isinstance(encrypted_data, dict) and 'ciphertext' in encrypted_data: ciphertext = base64.b64decode(encrypted_data['ciphertext']) nonce = base64.b64decode(encrypted_data['nonce']) tag = base64.b64decode(encrypted_data['tag']) cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) data = cipher.decrypt_and_verify(ciphertext, tag) return data.decode('utf-8') except Exception as e: logger.error(f"خطأ في فك التشفير: {e}") return encrypted_data # ==================== نظام الإشعارات المتقدم ==================== def add_notification(user_id, from_user_id, n_type, content, video_id=None, comment_id=None, gift_id=None, priority=0, action_url=None, image_url=None): """إضافة إشعار متقدم""" if user_id == from_user_id: return db = get_db() # تحديد تاريخ انتهاء الصلاحية (30 يوم) expires_at = datetime.now() + timedelta(days=30) cursor = db.execute(''' INSERT INTO notifications ( user_id, from_user_id, type, content, video_id, comment_id, gift_id, priority, action_url, image_url, expires_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', (user_id, from_user_id, n_type, content, video_id, comment_id, gift_id, priority, action_url, image_url, expires_at)) db.commit() # إرسال إشعار فوري إذا كان متصلاً if user_id in notification_queues: notification_queues[user_id].put({ 'id': cursor.lastrowid, 'type': n_type, 'content': content, 'from_user': from_user_id, 'video_id': video_id, 'priority': priority, 'image_url': image_url, 'timestamp': datetime.now().isoformat() }) return cursor.lastrowid def send_bulk_notifications(user_ids, from_user_id, n_type, content, **kwargs): """إرسال إشعارات جماعية""" for user_id in user_ids: add_notification(user_id, from_user_id, n_type, content, **kwargs) # ==================== دوال المطورين ==================== def create_developer_session(user_id): """إنشاء جلسة مطور جديدة""" token = secrets.token_urlsafe(48) expires = datetime.now() + timedelta(hours=2) db = get_db() db.execute(''' INSERT INTO developer_sessions (user_id, session_token, expires_at) VALUES (?, ?, ?) ''', (user_id, token, expires.isoformat())) db.commit() return token def validate_developer_session(token): """التحقق من صحة جلسة المطور""" db = get_db() session = db.execute(''' SELECT user_id, expires_at FROM developer_sessions WHERE session_token = ? AND expires_at > datetime('now') ''', (token,)).fetchone() if session: return session['user_id'] return None # ==================== الديكوراتورات ==================== def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({'error': 'unauthorized', 'redirect': '/login'}), 401 return redirect(url_for('login')) return f(*args, **kwargs) return decorated_function def admin_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: return redirect(url_for('login')) user = query_db('SELECT role FROM users WHERE id = ?', (session['user_id'],), one=True) if not user or user['role'] not in ['admin', 'moderator']: abort(403) return f(*args, **kwargs) return decorated_function def developer_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: return redirect(url_for('login')) user = query_db('SELECT is_developer FROM users WHERE id = ?', (session['user_id'],), one=True) if not user or not user['is_developer']: abort(403) return f(*args, **kwargs) return decorated_function def vip_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'user_id' not in session: return redirect(url_for('login')) tier = get_user_membership_tier(session['user_id']) if tier in [MembershipTier.VIP, MembershipTier.VIP_GOLD]: return f(*args, **kwargs) abort(403) return decorated_function # ==================== صفحات المصادقة ==================== @app.route('/') def index(): if 'user_id' not in session: return redirect(url_for('login')) return render_template_string(MAIN_TEMPLATE, session=session) @app.route('/login', methods=['GET', 'POST']) @limiter.limit("10 per minute") def login(): if request.method == 'POST': username = request.form.get('username', '').strip() password = request.form.get('password', '') remember = request.form.get('remember', False) twofa_code = request.form.get('twofa_code', '') db = get_db() user = db.execute('SELECT * FROM users WHERE username = ? OR email = ?', (username, username)).fetchone() if not user: return render_template_string(LOGIN_PAGE, error='بيانات الدخول غير صحيحة') # التحقق من قفل الحساب if user['locked_until']: locked_until = datetime.strptime(user['locked_until'], '%Y-%m-%d %H:%M:%S') if locked_until > datetime.now(): return render_template_string(LOGIN_PAGE, error=f'الحساب مقفل حتى {locked_until.strftime("%H:%M")}') if check_password_hash(user['password_hash'], password): # التحقق من المصادقة الثنائية if user['two_factor_enabled'] and not twofa_code: return render_template_string(TWOFA_PAGE, user_id=user['id']) if user['two_factor_enabled']: if not verify_2fa_code(user['two_factor_secret'], twofa_code): return render_template_string(LOGIN_PAGE, error='رمز المصادقة غير صحيح') # إعادة تعيين محاولات الدخول db.execute('UPDATE users SET login_attempts = 0, locked_until = NULL WHERE id = ?', (user['id'],)) # إنشاء جلسة session_id = secrets.token_urlsafe(32) session['user_id'] = user['id'] session['username'] = user['username'] session['role'] = user['role'] session['session_id'] = session_id if remember: session.permanent = True # تسجيل الجلسة في قاعدة البيانات db.execute(''' INSERT INTO sessions (id, user_id, ip_address, user_agent, expires_at) VALUES (?, ?, ?, ?, datetime("now", "+30 days")) ''', (session_id, user['id'], get_client_ip(), request.headers.get('User-Agent', ''))) db.execute('UPDATE users SET last_active = CURRENT_TIMESTAMP WHERE id = ?', (user['id'],)) db.commit() # XP لتسجيل الدخول اليومي cache_key = f"login_{user['id']}_{datetime.now().date()}" if not cache_get(cache_key): add_xp(user['id'], 'daily_login', 10) cache_set(cache_key, '1', 86400) # تخزين لمدة 24 ساعة track_event(user['id'], 'login', {}) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({'success': True, 'redirect': '/'}) return redirect(url_for('index')) # تسجيل محاولة فاشلة attempts = user['login_attempts'] + 1 locked_until = None if attempts >= 5: locked_until = datetime.now() + timedelta(minutes=15) db.execute('UPDATE users SET login_attempts = ?, locked_until = ? WHERE id = ?', (attempts, locked_until, user['id'])) db.commit() if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return jsonify({'success': False, 'error': 'بيانات الدخول غير صحيحة'}), 401 return render_template_string(LOGIN_PAGE, error='اسم المستخدم أو كلمة المرور غير صحيحة') return render_template_string(LOGIN_PAGE) @app.route('/register', methods=['GET', 'POST']) @limiter.limit("5 per hour") def register(): if request.method == 'POST': username = request.form.get('username', '').strip() email = request.form.get('email', '').strip().lower() password = request.form.get('password', '') confirm = request.form.get('confirm_password', '') referral = request.form.get('referral_code', '').strip() agree_terms = request.form.get('agree_terms') == 'on' if not username or not email or not password: return render_template_string(REGISTER_PAGE, error='جميع الحقول مطلوبة') if not agree_terms: return render_template_string(REGISTER_PAGE, error='يجب الموافقة على الشروط والأحكام') if password != confirm: return render_template_string(REGISTER_PAGE, error='كلمات المرور غير متطابقة') if len(password) < 8: return render_template_string(REGISTER_PAGE, error='كلمة المرور يجب أن تكون 8 أحرف على الأقل') if not re.search(r'[A-Z]', password): return render_template_string(REGISTER_PAGE, error='كلمة المرور يجب أن تحتوي على حرف كبير واحد على الأقل') if not re.search(r'[a-z]', password): return render_template_string(REGISTER_PAGE, error='كلمة المرور يجب أن تحتوي على حرف صغير واحد على الأقل') if not re.search(r'[0-9]', password): return render_template_string(REGISTER_PAGE, error='كلمة المرور يجب أن تحتوي على رقم واحد على الأقل') db = get_db() existing = db.execute('SELECT id FROM users WHERE username = ? OR email = ?', (username, email)).fetchone() if existing: return render_template_string(REGISTER_PAGE, error='اسم المستخدم أو البريد الإلكتروني موجود بالفعل') hashed = generate_password_hash(password) referral_code = generate_referral_code() cursor = db.execute(''' INSERT INTO users (username, email, password_hash, referral_code) VALUES (?, ?, ?, ?) ''', (username, email, hashed, referral_code)) user_id = cursor.lastrowid db.execute('INSERT INTO user_settings (user_id) VALUES (?)', (user_id,)) # معالجة الإحالة if referral: referrer = db.execute('SELECT id FROM users WHERE referral_code = ?', (referral,)).fetchone() if referrer: process_affiliate_conversion(referral, user_id) # XP للمستخدم الجديد add_xp(user_id, 'register', 50) # عملات ترحيبية add_coins(user_id, 100, 'هدية ترحيبية') track_event(user_id, 'register', {'referral': referral if referral else None}) db.commit() # إرسال بريد ترحيبي (محاكاة) logger.info(f"مستخدم جديد: {username} (ID: {user_id})") return redirect(url_for('login')) return render_template_string(REGISTER_PAGE) @app.route('/logout') def logout(): if 'user_id' in session and 'session_id' in session: db = get_db() db.execute('DELETE FROM sessions WHERE id = ?', (session['session_id'],)) db.commit() track_event(session['user_id'], 'logout', {}) session.clear() return redirect(url_for('login')) @app.route('/2fa/setup', methods=['GET', 'POST']) @login_required def setup_2fa(): """إعداد المصادقة الثنائية""" if not HAS_2FA: return "المصادقة الثنائية غير متاحة", 501 db = get_db() user = db.execute('SELECT username, two_factor_secret FROM users WHERE id = ?', (session['user_id'],)).fetchone() if request.method == 'POST': code = request.form.get('code', '') if verify_2fa_code(user['two_factor_secret'], code): db.execute('UPDATE users SET two_factor_enabled = 1 WHERE id = ?', (session['user_id'],)) db.commit() return redirect(url_for('settings')) else: return render_template_string(SETUP_2FA_PAGE, error='رمز غير صحيح') # توليد سر جديد إذا لم يكن موجوداً secret = user['two_factor_secret'] if not secret: secret = generate_2fa_secret() db.execute('UPDATE users SET two_factor_secret = ? WHERE id = ?', (secret, session['user_id'])) db.commit() qr_code = generate_2fa_qr(user['username'], secret) backup_codes = generate_backup_codes() db.execute('UPDATE users SET backup_codes = ? WHERE id = ?', (backup_codes, session['user_id'])) db.commit() return render_template_string(SETUP_2FA_PAGE, secret=secret, qr_code=qr_code, backup_codes=json.loads(backup_codes)) # ==================== ملف المستخدم ==================== @app.route('/profile/') @login_required def profile(user_id): db = get_db() user = db.execute(''' SELECT u.*, s.* FROM users u LEFT JOIN user_settings s ON u.id = s.user_id WHERE u.id = ? ''', (user_id,)).fetchone() if not user: abort(404) # الحصول على إحصائيات متقدمة stats = get_user_stats(user_id) # فيديوهات المستخدم videos = db.execute(''' SELECT id, filename, title, thumbnail, cloudinary_url, views, likes_count, comments_count, upload_time FROM videos WHERE user_id = ? AND visibility = 'public' ORDER BY upload_time DESC LIMIT 30 ''', (user_id,)).fetchall() # متابعون ومتابعات followers_count = db.execute('SELECT COUNT(*) FROM follows WHERE user_id = ?', (user_id,)).fetchone()[0] following_count = db.execute('SELECT COUNT(*) FROM follows WHERE follower_id = ?', (user_id,)).fetchone()[0] # هل يتابع المستخدم الحالي هذا الحساب is_following = False if session['user_id'] != user_id: is_following = db.execute('SELECT * FROM follows WHERE user_id = ? AND follower_id = ?', (user_id, session['user_id'])).fetchone() is not None # تجهيز قائمة الفيديوهات videos_list = [] for v in videos: video_dict = dict(v) video_dict['url'] = v['cloudinary_url'] if v['cloudinary_url'] else f'/videos/{v["filename"]}' videos_list.append(video_dict) # مزايا العضوية benefits = get_membership_benefits(user_id) return render_template_string(PROFILE_TEMPLATE, user=dict(user), videos=videos_list, stats=stats, followers_count=followers_count, following_count=following_count, is_following=is_following, benefits=benefits, session=session) @app.route('/settings', methods=['GET', 'POST']) @login_required def settings(): db = get_db() if request.method == 'POST': # المعلومات الشخصية bio = request.form.get('bio', '') phone = request.form.get('phone', '') gender = request.form.get('gender', '') country = request.form.get('country', '') birth_date = request.form.get('birth_date', '') # الصور avatar = request.files.get('avatar') cover = request.files.get('cover') if avatar and avatar.filename and allowed_image(avatar.filename): ext = avatar.filename.rsplit('.', 1)[1].lower() filename = f"avatar_{session['user_id']}_{uuid.uuid4().hex[:8]}.{ext}" filepath = os.path.join(AVATAR_FOLDER, filename) avatar.save(filepath) # تحسين الصورة إذا كان PIL متاحاً if HAS_PIL: try: img = Image.open(filepath) img = img.resize((400, 400), Image.Resampling.LANCZOS) img.save(filepath, optimize=True, quality=85) except: pass db.execute('UPDATE users SET avatar = ? WHERE id = ?', (filename, session['user_id'])) if cover and cover.filename and allowed_image(cover.filename): ext = cover.filename.rsplit('.', 1)[1].lower() filename = f"cover_{session['user_id']}_{uuid.uuid4().hex[:8]}.{ext}" filepath = os.path.join(AVATAR_FOLDER, filename) cover.save(filepath) if HAS_PIL: try: img = Image.open(filepath) img = img.resize((1500, 500), Image.Resampling.LANCZOS) img.save(filepath, optimize=True, quality=85) except: pass db.execute('UPDATE users SET cover_image = ? WHERE id = ?', (filename, session['user_id'])) db.execute(''' UPDATE users SET bio = ?, phone = ?, gender = ?, country = ?, birth_date = ? WHERE id = ? ''', (bio, phone, gender, country, birth_date, session['user_id'])) # إعدادات الخصوصية privacy_profile = request.form.get('privacy_profile', 'public') privacy_videos = request.form.get('privacy_videos', 'public') privacy_likes = request.form.get('privacy_likes', 'public') # إعدادات الإشعارات notifications_likes = request.form.get('notifications_likes') == 'on' notifications_comments = request.form.get('notifications_comments') == 'on' notifications_follows = request.form.get('notifications_follows') == 'on' notifications_messages = request.form.get('notifications_messages') == 'on' notifications_live = request.form.get('notifications_live') == 'on' notifications_gifts = request.form.get('notifications_gifts') == 'on' # تفضيلات dark_mode = request.form.get('dark_mode') == 'on' language = request.form.get('language', 'ar') content_language = request.form.get('content_language', 'ar') autoplay = request.form.get('autoplay') == 'on' save_data = request.form.get('save_data') == 'on' allow_download = request.form.get('allow_download') == 'on' allow_duet = request.form.get('allow_duet') == 'on' allow_stitch = request.form.get('allow_stitch') == 'on' db.execute(''' UPDATE user_settings SET privacy_profile = ?, privacy_videos = ?, privacy_likes = ?, notifications_likes = ?, notifications_comments = ?, notifications_follows = ?, notifications_messages = ?, notifications_live = ?, notifications_gifts = ?, dark_mode = ?, language = ?, content_language = ?, autoplay = ?, save_data = ?, allow_download = ?, allow_duet = ?, allow_stitch = ? WHERE user_id = ? ''', (privacy_profile, privacy_videos, privacy_likes, notifications_likes, notifications_comments, notifications_follows, notifications_messages, notifications_live, notifications_gifts, dark_mode, language, content_language, autoplay, save_data, allow_download, allow_duet, allow_stitch, session['user_id'])) db.commit() track_event(session['user_id'], 'update_settings', {}) return redirect(url_for('profile', user_id=session['user_id'])) user = db.execute('SELECT * FROM users WHERE id = ?', (session['user_id'],)).fetchone() settings = db.execute('SELECT * FROM user_settings WHERE user_id = ?', (session['user_id'],)).fetchone() return render_template_string(SETTINGS_TEMPLATE, user=dict(user), settings=dict(settings) if settings else {}) def get_user_stats(user_id): """الحصول على إحصائيات متقدمة للمستخدم""" db = get_db() try: # إحصائيات الفيديوهات videos = db.execute('SELECT COUNT(*) FROM videos WHERE user_id = ?', (user_id,)).fetchone()[0] total_views = db.execute('SELECT SUM(views) FROM videos WHERE user_id = ?', (user_id,)).fetchone()[0] or 0 total_likes = db.execute('SELECT SUM(likes_count) FROM videos WHERE user_id = ?', (user_id,)).fetchone()[0] or 0 total_comments = db.execute('SELECT SUM(comments_count) FROM videos WHERE user_id = ?', (user_id,)).fetchone()[0] or 0 total_shares = db.execute('SELECT SUM(shares_count) FROM videos WHERE user_id = ?', (user_id,)).fetchone()[0] or 0 # متابعون followers = db.execute('SELECT COUNT(*) FROM follows WHERE user_id = ?', (user_id,)).fetchone()[0] following = db.execute('SELECT COUNT(*) FROM follows WHERE follower_id = ?', (user_id,)).fetchone()[0] # معلومات المستخدم user = db.execute(''' SELECT xp, level, coins, diamonds, affiliate_balance, affiliate_clicks, affiliate_conversions FROM users WHERE id = ? ''', (user_id,)).fetchone() # إحصائيات آخر 7 أيام weekly_views = db.execute(''' SELECT SUM(watch_time) FROM video_views WHERE user_id = ? AND viewed_at > datetime("now", "-7 days") ''', (user_id,)).fetchone()[0] or 0 # إنجازات achievements = [] if videos >= 10: achievements.append('منشئ محتوى') if followers >= 1000: achievements.append('مؤثر') if total_views >= 100000: achievements.append('نجم') return { 'videos': videos, 'total_views': total_views, 'total_likes': total_likes, 'total_comments': total_comments, 'total_shares': total_shares, 'followers': followers, 'following': following, 'xp': user['xp'], 'level': user['level'], 'coins': user['coins'], 'diamonds': user['diamonds'], 'affiliate_balance': user['affiliate_balance'], 'affiliate_clicks': user['affiliate_clicks'], 'affiliate_conversions': user['affiliate_conversions'], 'weekly_views': weekly_views, 'achievements': achievements } except Exception as e: logger.error(f"خطأ في إحصائيات المستخدم: {e}") return {} # ==================== نظام المتابعة ==================== @app.route('/follow/', methods=['POST']) @login_required @limiter.limit("30 per minute") def follow(user_id): if user_id == session['user_id']: return jsonify({'error': 'لا يمكنك متابعة نفسك'}), 400 db = get_db() # التحقق من الخصوصية target_user = db.execute('SELECT username FROM users WHERE id = ?', (user_id,)).fetchone() if not target_user: return jsonify({'error': 'المستخدم غير موجود'}), 404 db.execute('INSERT OR IGNORE INTO follows (user_id, follower_id) VALUES (?, ?)', (user_id, session['user_id'])) # تحديث إحصائيات المتابعين db.execute(''' UPDATE users SET total_followers = ( SELECT COUNT(*) FROM follows WHERE user_id = ? ) WHERE id = ? ''', (user_id, user_id)) db.execute(''' UPDATE users SET total_following = ( SELECT COUNT(*) FROM follows WHERE follower_id = ? ) WHERE id = ? ''', (session['user_id'], session['user_id'])) db.commit() # إرسال إشعار follower = db.execute('SELECT username FROM users WHERE id = ?', (session['user_id'],)).fetchone() add_notification(user_id, session['user_id'], 'follow', f'بدأ {follower["username"]} بمتابعتك', priority=1) # XP للمتابعة add_xp(session['user_id'], 'follow', 5) # إحصائيات track_event(session['user_id'], 'follow', {'followed_user_id': user_id}) count = db.execute('SELECT COUNT(*) FROM follows WHERE user_id = ?', (user_id,)).fetchone()[0] return jsonify({'status': 'ok', 'followers_count': count}) @app.route('/unfollow/', methods=['POST']) @login_required def unfollow(user_id): db = get_db() db.execute('DELETE FROM follows WHERE user_id = ? AND follower_id = ?', (user_id, session['user_id'])) # تحديث الإحصائيات db.execute(''' UPDATE users SET total_followers = ( SELECT COUNT(*) FROM follows WHERE user_id = ? ) WHERE id = ? ''', (user_id, user_id)) db.execute(''' UPDATE users SET total_following = ( SELECT COUNT(*) FROM follows WHERE follower_id = ? ) WHERE id = ? ''', (session['user_id'], session['user_id'])) db.commit() track_event(session['user_id'], 'unfollow', {'unfollowed_user_id': user_id}) count = db.execute('SELECT COUNT(*) FROM follows WHERE user_id = ?', (user_id,)).fetchone()[0] return jsonify({'status': 'ok', 'followers_count': count}) @app.route('/api/followers/') @login_required def get_followers(user_id): page = int(request.args.get('page', 1)) limit = 20 offset = (page - 1) * limit db = get_db() followers = db.execute(''' SELECT u.id, u.username, u.avatar, u.is_verified, u.role, f.created_at FROM follows f JOIN users u ON f.follower_id = u.id WHERE f.user_id = ? ORDER BY f.created_at DESC LIMIT ? OFFSET ? ''', (user_id, limit, offset)).fetchall() result = [] for f in followers: follower = dict(f) follower['avatar_url'] = f'/avatars/{f["avatar"]}' # هل يتابع المستخدم الحالي هذا الشخص if session['user_id'] != f['id']: is_following_back = db.execute(''' SELECT * FROM follows WHERE user_id = ? AND follower_id = ? ''', (f['id'], session['user_id'])).fetchone() follower['is_following_back'] = is_following_back is not None else: follower['is_following_back'] = False result.append(follower) return jsonify({ 'followers': result, 'has_more': len(result) == limit }) @app.route('/api/following/') @login_required def get_following(user_id): page = int(request.args.get('page', 1)) limit = 20 offset = (page - 1) * limit db = get_db() following = db.execute(''' SELECT u.id, u.username, u.avatar, u.is_verified, u.role, f.created_at FROM follows f JOIN users u ON f.user_id = u.id WHERE f.follower_id = ? ORDER BY f.created_at DESC LIMIT ? OFFSET ? ''', (user_id, limit, offset)).fetchall() result = [] for f in following: follow = dict(f) follow['avatar_url'] = f'/avatars/{f["avatar"]}' result.append(follow) return jsonify({ 'following': result, 'has_more': len(result) == limit }) # ==================== نظام الفيديوهات المتقدم ==================== @app.route('/upload', methods=['GET', 'POST']) @login_required def upload_video(): if request.method == 'GET': db = get_db() challenges = db.execute(''' SELECT * FROM challenges WHERE is_active = 1 AND end_date > DATE() ORDER BY prize_coins DESC ''').fetchall() music = db.execute(''' SELECT * FROM music_tracks WHERE is_active = 1 ORDER BY usage_count DESC LIMIT 50 ''').fetchall() return render_template_string(UPLOAD_TEMPLATE, challenges=[dict(c) for c in challenges], music=[dict(m) for m in music]) # معالجة رفع الفيديو if 'video' not in request.files: return jsonify({'error': 'لم يتم اختيار فيديو'}), 400 file = request.files['video'] if file.filename == '': return jsonify({'error': 'لم يتم اختيار فيديو'}), 400 # التحقق من مزايا العضوية benefits = get_membership_benefits(session['user_id']) # التحقق من حجم الملف file.seek(0, os.SEEK_END) file_size = file.tell() file.seek(0) if file_size > benefits.max_video_size: max_size_mb = benefits.max_video_size / (1024 * 1024) return jsonify({'error': f'حجم الملف يتجاوز الحد المسموح ({max_size_mb}MB)'}), 400 title = request.form.get('title', '') description = request.form.get('description', '') music_id = request.form.get('music_id') challenge_id = request.form.get('challenge_id') allow_comments = request.form.get('allow_comments') == 'on' allow_duet = request.form.get('allow_duet') == 'on' allow_stitch = request.form.get('allow_stitch') == 'on' visibility = request.form.get('visibility', 'public') if file and allowed_video(file.filename): filename = secure_filename(file.filename) unique_name = f"{uuid.uuid4().hex}_{filename}" filepath = os.path.join(app.config['UPLOAD_FOLDER'], unique_name) file.save(filepath) # رفع إلى Cloudinary cloudinary_url, cloudinary_public_id = upload_video_to_cloudinary(filepath) thumbnail_url = generate_video_thumbnail(cloudinary_public_id) if cloudinary_public_id else None # تحليل متقدم للفيديو video_analysis = analyze_video_content(filepath) # استخراج الخصائص if HAS_CV2: vec_bytes = extract_video_features(filepath) else: vec_bytes = random_vector() # توليد وسوم ذكية ai_tags = generate_ai_tags(f"{title} {description}", video_analysis) # تشفير الفيديو إذا كان المستخدم VIP encrypted_path, enc_key = None, None if HAS_CRYPTO and benefits.priority_support: encrypted_path, enc_key = encrypt_video_file(filepath) # إضافة علامة مائية watermarked_path = None if HAS_CV2 and HAS_PIL: watermark_text = f"@{session['username']}" watermarked_path = add_watermark_to_video(filepath, watermark_text=watermark_text) db = get_db() cursor = db.execute(''' INSERT INTO videos ( user_id, filename, filepath, filesize, cloudinary_url, cloudinary_public_id, thumbnail, title, description, music_id, allow_comments, allow_duet, allow_stitch, visibility, vector, challenge_id, nsfw_score, ai_tags, encrypted_path, encrypted_key, watermarked_path ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ''', (session['user_id'], unique_name, filepath, file_size, cloudinary_url, cloudinary_public_id, thumbnail_url, title, description, music_id, allow_comments, allow_duet, allow_stitch, visibility, vec_bytes, challenge_id, video_analysis['nsfw_score'], ai_tags, encrypted_path, enc_key, watermarked_path)) video_id = cursor.lastrowid # معالجة الهاشتاغات hashtags = re.findall(r'#(\w+)', description) for tag in hashtags: tag_lower = tag.lower() db.execute('INSERT OR IGNORE INTO hashtags (tag) VALUES (?)', (tag_lower,)) tag_row = db.execute('SELECT id FROM hashtags WHERE tag = ?', (tag_lower,)).fetchone() if tag_row: db.execute('INSERT OR IGNORE INTO video_hashtags (video_id, hashtag_id) VALUES (?, ?)', (video_id, tag_row['id'])) db.execute('UPDATE hashtags SET usage_count = usage_count + 1 WHERE id = ?', (tag_row['id'],)) # معالجة التحدي if challenge_id and challenge_id.isdigit(): db.execute(''' INSERT INTO challenge_participants (challenge_id, user_id, video_id) VALUES (?, ?, ?) ''', (int(challenge_id), session['user_id'], video_id)) db.commit() # مكافآت add_coins(session['user_id'], 20, 'مكافأة نشر فيديو') add_xp(session['user_id'], 'upload_video', 50, multiplier=2 if benefits.priority_support else 1) track_event(session['user_id'], 'upload_video', { 'video_id': video_id, 'size': file_size, 'duration': video_analysis.get('duration', 0) }) return redirect(url_for('index')) return jsonify({'error': 'نوع الملف غير مدعوم'}), 400 @app.route('/api/for-you') @login_required def for_you(): """الصفحة الرئيسية مع توصيات متقدمة""" try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) offset = (page - 1) * limit # استخدام نظام التوصيات المتقدم videos = recommend_videos_advanced(session['user_id'], limit, offset) result = [] for v in videos: video = {} for key, value in v.items(): if key not in ['vector', 'encrypted_key'] and not isinstance(value, bytes): video[key] = value video['url'] = v['cloudinary_url'] if v['cloudinary_url'] else f'/videos/{v["filename"]}' video['avatar_url'] = f'/avatars/{v["avatar"]}' # هل أعجب المستخدم هذا الفيديو db = get_db() interaction = db.execute(''' SELECT liked, saved FROM interactions WHERE user_id = ? AND video_id = ? ''', (session['user_id'], v['id'])).fetchone() video['liked_by_user'] = bool(interaction['liked']) if interaction else False video['saved_by_user'] = bool(interaction['saved']) if interaction else False result.append(video) # تسجيل حدث المشاهدة track_event(session['user_id'], 'view_for_you', {'page': page}) return jsonify({ 'videos': result, 'next_page': page + 1 if len(result) == limit else None, 'has_more': len(result) == limit }) except Exception as e: logger.error(f"خطأ في for_you: {e}") return jsonify({'videos': [], 'next_page': None, 'error': str(e)}), 500 @app.route('/api/trending') @login_required def trending(): """الفيديوهات الرائجة""" try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 10)) offset = (page - 1) * limit trending_data = get_trending() videos = trending_data['videos'][offset:offset+limit] result = [] for v in videos: video = dict(v) video['url'] = v['cloudinary_url'] if v['cloudinary_url'] else f'/videos/{v["filename"]}' video['avatar_url'] = f'/avatars/{v["avatar"]}' video.pop('vector', None) video.pop('encrypted_key', None) result.append(video) return jsonify({ 'videos': result, 'next_page': page + 1 if len(result) == limit else None, 'has_more': len(videos) == limit }) except Exception as e: logger.error(f"خطأ في trending: {e}") return jsonify({'videos': [], 'next_page': None}), 500 @app.route('/api/trending/hashtags') @login_required def trending_hashtags(): """الهاشتاغات الرائجة""" try: trending_data = get_trending() return jsonify({'hashtags': trending_data['hashtags']}) except Exception as e: logger.error(f"خطأ في trending_hashtags: {e}") return jsonify({'hashtags': []}), 500 @app.route('/api/search') @login_required def search(): """بحث متقدم""" try: q = request.args.get('q', '').strip() page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 20)) offset = (page - 1) * limit content_type = request.args.get('type', 'videos') # videos, users, hashtags db = get_db() # تسجيل عملية البحث if q: db.execute('INSERT INTO search_queries (user_id, query) VALUES (?, ?)', (session['user_id'], q)) db.commit() if content_type == 'videos': # بحث في الفيديوهات videos = db.execute(''' SELECT v.*, u.username, u.avatar, u.is_verified, (SELECT COUNT(*) FROM interactions WHERE video_id = v.id AND liked = 1) as likes FROM videos v JOIN users u ON v.user_id = u.id WHERE v.visibility = 'public' AND (v.title LIKE ? OR v.description LIKE ?) ORDER BY v.views DESC, v.likes_count DESC LIMIT ? OFFSET ? ''', ('%'+q+'%', '%'+q+'%', limit, offset)).fetchall() # بحث في الهاشتاغات hashtag_videos = [] hashtag = db.execute('SELECT id FROM hashtags WHERE tag LIKE ?', ('%'+q+'%',)).fetchone() if hashtag: hashtag_videos = db.execute(''' SELECT v.*, u.username, u.avatar, u.is_verified FROM videos v JOIN users u ON v.user_id = u.id JOIN video_hashtags vh ON v.id = vh.video_id WHERE vh.hashtag_id = ? AND v.visibility = 'public' ORDER BY v.views DESC LIMIT ? ''', (hashtag['id'], limit)).fetchall() # دمج النتائج وإزالة التكرار all_videos = list(videos) + list(hashtag_videos) seen = set() unique_videos = [] for v in all_videos: if v['id'] not in seen: seen.add(v['id']) unique_videos.append(v) result = [] for v in unique_videos[:limit]: video = dict(v) video['url'] = v['cloudinary_url'] if v['cloudinary_url'] else f'/videos/{v["filename"]}' video['avatar_url'] = f'/avatars/{v["avatar"]}' video.pop('vector', None) result.append(video) track_event(session['user_id'], 'search', {'query': q, 'results': len(result)}) return jsonify({ 'videos': result, 'next_page': page + 1 if len(result) == limit else None, 'has_more': len(result) == limit }) elif content_type == 'users': # بحث عن المستخدمين users = db.execute(''' SELECT id, username, avatar, bio, is_verified, role, total_followers FROM users WHERE username LIKE ? OR bio LIKE ? ORDER BY total_followers DESC LIMIT ? OFFSET ? ''', ('%'+q+'%', '%'+q+'%', limit, offset)).fetchall() result = [] for u in users: user = dict(u) user['avatar_url'] = f'/avatars/{u["avatar"]}' # هل يتابع المستخدم الحالي هذا الحساب if session['user_id'] != u['id']: is_following = db.execute(''' SELECT * FROM follows WHERE user_id = ? AND follower_id = ? ''', (u['id'], session['user_id'])).fetchone() user['is_following'] = is_following is not None result.append(user) return jsonify({ 'users': result, 'next_page': page + 1 if len(result) == limit else None }) elif content_type == 'hashtags': # بحث عن الهاشتاغات hashtags = db.execute(''' SELECT tag, usage_count, total_views FROM hashtags WHERE tag LIKE ? ORDER BY usage_count DESC, total_views DESC LIMIT ? OFFSET ? ''', ('%'+q+'%', limit, offset)).fetchall() return jsonify({ 'hashtags': [dict(h) for h in hashtags], 'next_page': page + 1 if len(hashtags) == limit else None }) return jsonify({'error': 'نوع بحث غير صحيح'}), 400 except Exception as e: logger.error(f"خطأ في البحث: {e}") return jsonify({'videos': []}), 500 @app.route('/api/like/', methods=['POST']) @login_required @limiter.limit("30 per minute") def like_video(video_id): try: user_id = session['user_id'] db = get_db() interaction = db.execute('SELECT liked FROM interactions WHERE user_id = ? AND video_id = ?', (user_id, video_id)).fetchone() if interaction and interaction['liked']: # إلغاء الإعجاب db.execute('UPDATE interactions SET liked = 0 WHERE user_id = ? AND video_id = ?', (user_id, video_id)) db.execute('UPDATE videos SET likes_count = likes_count - 1 WHERE id = ?', (video_id,)) liked = False else: # إعجاب db.execute(''' INSERT INTO interactions (user_id, video_id, liked, watched) VALUES (?, ?, 1, 1) ON CONFLICT(user_id, video_id) DO UPDATE SET liked = 1 ''', (user_id, video_id)) db.execute('UPDATE videos SET likes_count = likes_count + 1 WHERE id = ?', (video_id,)) liked = True # إرسال إشعار لصاحب الفيديو video = db.execute('SELECT user_id FROM videos WHERE id = ?', (video_id,)).fetchone() if video and video['user_id'] != user_id: user = db.execute('SELECT username FROM users WHERE id = ?', (user_id,)).fetchone() if user: add_notification(video['user_id'], user_id, 'like', f'أعجب {user["username"]} بفيديو لك', video_id=video_id, priority=1) db.commit() likes_count = db.execute('SELECT likes_count FROM videos WHERE id = ?', (video_id,)).fetchone()['likes_count'] if liked: add_xp(user_id, 'like', 2) track_event(user_id, 'like', {'video_id': video_id, 'liked': liked}) return jsonify({'status': 'ok', 'liked': liked, 'likes_count': likes_count}) except Exception as e: logger.error(f"خطأ في like_video: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/view/', methods=['POST']) @login_required def view_video(video_id): """تسجيل مشاهدة متقدمة""" try: user_id = session['user_id'] data = request.get_json() or {} watch_time = data.get('watch_time', 1) watch_percentage = data.get('watch_percentage', 0) completed = data.get('completed', False) ip = get_client_ip() db = get_db() # تسجيل المشاهدة existing = db.execute('SELECT watched FROM interactions WHERE user_id = ? AND video_id = ?', (user_id, video_id)).fetchone() if not existing or not existing['watched']: db.execute(''' INSERT INTO interactions (user_id, video_id, watched, watch_time, watch_percentage, completed) VALUES (?, ?, 1, ?, ?, ?) ON CONFLICT(user_id, video_id) DO UPDATE SET watched = 1, watch_time = watch_time + ?, watch_percentage = MAX(watch_percentage, ?), completed = MAX(completed, ?) ''', (user_id, video_id, watch_time, watch_percentage, completed, watch_time, watch_percentage, completed)) db.execute('UPDATE videos SET views = views + 1 WHERE id = ?', (video_id,)) else: db.execute(''' UPDATE interactions SET watch_time = watch_time + ?, watch_percentage = MAX(watch_percentage, ?), completed = MAX(completed, ?) WHERE user_id = ? AND video_id = ? ''', (watch_time, watch_percentage, completed, user_id, video_id)) # تسجيل في جدول المشاهدات المفصل db.execute(''' INSERT INTO video_views (user_id, video_id, watch_time, watch_percentage, completed, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (user_id, video_id, watch_time, watch_percentage, completed, ip, request.headers.get('User-Agent', ''))) # تحديث متوسط وقت المشاهدة للفيديو db.execute(''' UPDATE videos SET avg_watch_time = ( SELECT AVG(watch_time) FROM video_views WHERE video_id = ? ), completion_rate = ( SELECT AVG(watch_percentage) FROM video_views WHERE video_id = ? ) WHERE id = ? ''', (video_id, video_id, video_id)) db.commit() # XP للمشاهدة if completed: add_xp(user_id, 'complete_view', 5) elif watch_percentage > 50: add_xp(user_id, 'partial_view', 2) track_event(user_id, 'view_video', { 'video_id': video_id, 'watch_time': watch_time, 'watch_percentage': watch_percentage, 'completed': completed }) return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في view_video: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/share/', methods=['POST']) @login_required def share_video(video_id): try: user_id = session['user_id'] platform = request.get_json().get('platform', 'internal') db = get_db() db.execute(''' INSERT INTO interactions (user_id, video_id, shared, watched) VALUES (?, ?, 1, 1) ON CONFLICT(user_id, video_id) DO UPDATE SET shared = 1 ''', (user_id, video_id)) db.execute('UPDATE videos SET shares_count = shares_count + 1 WHERE id = ?', (video_id,)) db.commit() shares = db.execute('SELECT shares_count FROM videos WHERE id = ?', (video_id,)).fetchone()['shares_count'] # مكافآت add_coins(user_id, 10, 'مكافأة مشاركة فيديو') add_xp(user_id, 'share', 5) track_event(user_id, 'share_video', {'video_id': video_id, 'platform': platform}) return jsonify({'status': 'ok', 'shares_count': shares}) except Exception as e: logger.error(f"خطأ في share_video: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/save/', methods=['POST']) @login_required def save_video(video_id): try: user_id = session['user_id'] db = get_db() saved = db.execute('SELECT saved FROM interactions WHERE user_id = ? AND video_id = ?', (user_id, video_id)).fetchone() if saved and saved['saved']: db.execute('UPDATE interactions SET saved = 0 WHERE user_id = ? AND video_id = ?', (user_id, video_id)) db.execute('UPDATE videos SET saves_count = saves_count - 1 WHERE id = ?', (video_id,)) saved_status = False else: db.execute(''' INSERT INTO interactions (user_id, video_id, saved, watched) VALUES (?, ?, 1, 1) ON CONFLICT(user_id, video_id) DO UPDATE SET saved = 1 ''', (user_id, video_id)) db.execute('UPDATE videos SET saves_count = saves_count + 1 WHERE id = ?', (video_id,)) saved_status = True db.commit() saves = db.execute('SELECT saves_count FROM videos WHERE id = ?', (video_id,)).fetchone()['saves_count'] track_event(user_id, 'save', {'video_id': video_id, 'saved': saved_status}) return jsonify({'status': 'ok', 'saved': saved_status, 'saves_count': saves}) except Exception as e: logger.error(f"خطأ في save_video: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/saved') @login_required def get_saved_videos(): """الحصول على الفيديوهات المحفوظة""" try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 20)) offset = (page - 1) * limit db = get_db() videos = db.execute(''' SELECT v.*, u.username, u.avatar, u.is_verified FROM interactions i JOIN videos v ON i.video_id = v.id JOIN users u ON v.user_id = u.id WHERE i.user_id = ? AND i.saved = 1 ORDER BY i.timestamp DESC LIMIT ? OFFSET ? ''', (session['user_id'], limit, offset)).fetchall() result = [] for v in videos: video = dict(v) video['url'] = v['cloudinary_url'] if v['cloudinary_url'] else f'/videos/{v["filename"]}' video['avatar_url'] = f'/avatars/{v["avatar"]}' video.pop('vector', None) result.append(video) return jsonify({ 'videos': result, 'next_page': page + 1 if len(result) == limit else None }) except Exception as e: logger.error(f"خطأ في get_saved_videos: {e}") return jsonify([]), 500 # ==================== نظام التعليقات المتقدم ==================== @app.route('/api/comments/') @login_required def get_comments(video_id): try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 20)) offset = (page - 1) * limit db = get_db() # التعليقات الرئيسية comments = db.execute(''' SELECT c.*, u.username, u.avatar, u.is_verified, u.role, (SELECT COUNT(*) FROM comment_likes WHERE comment_id = c.id) as likes_count, (SELECT COUNT(*) FROM comments WHERE parent_id = c.id) as replies_count FROM comments c JOIN users u ON c.user_id = u.id WHERE c.video_id = ? AND c.parent_id IS NULL ORDER BY c.is_pinned DESC, c.likes_count DESC, c.timestamp DESC LIMIT ? OFFSET ? ''', (video_id, limit, offset)).fetchall() result = [] for comment in comments: c = dict(comment) # الردود على هذا التعليق replies = db.execute(''' SELECT c.*, u.username, u.avatar, u.is_verified, u.role, (SELECT COUNT(*) FROM comment_likes WHERE comment_id = c.id) as likes_count FROM comments c JOIN users u ON c.user_id = u.id WHERE c.parent_id = ? ORDER BY c.timestamp ASC LIMIT 5 ''', (comment['id'],)).fetchall() c['replies'] = [] for r in replies: reply = dict(r) liked = db.execute('SELECT * FROM comment_likes WHERE user_id = ? AND comment_id = ?', (session['user_id'], r['id'])).fetchone() reply['liked_by_user'] = liked is not None reply['avatar_url'] = f'/avatars/{r["avatar"]}' c['replies'].append(reply) liked = db.execute('SELECT * FROM comment_likes WHERE user_id = ? AND comment_id = ?', (session['user_id'], comment['id'])).fetchone() c['liked_by_user'] = liked is not None c['avatar_url'] = f'/avatars/{comment["avatar"]}' result.append(c) total = db.execute('SELECT COUNT(*) FROM comments WHERE video_id = ? AND parent_id IS NULL', (video_id,)).fetchone()[0] return jsonify({ 'comments': result, 'total': total, 'page': page, 'has_more': len(result) == limit }) except Exception as e: logger.error(f"خطأ في get_comments: {e}") return jsonify({'comments': [], 'error': str(e)}), 500 @app.route('/api/comment/', methods=['POST']) @login_required @limiter.limit("20 per minute") def add_comment(video_id): try: user_id = session['user_id'] data = request.get_json() comment_text = data.get('comment', '').strip() parent_id = data.get('parent_id') if not comment_text: return jsonify({'error': 'التعليق لا يمكن أن يكون فارغاً'}), 400 if len(comment_text) > 1000: return jsonify({'error': 'التعليق طويل جداً'}), 400 # فحص التعليق تلقائياً moderation = moderate_comment(comment_text) if not moderation['is_appropriate']: if moderation['action'] == 'block': return jsonify({'error': 'التعليق غير مناسب'}), 400 elif moderation['action'] == 'flag': # سيتم مراجعة التعليق pass db = get_db() cursor = db.execute(''' INSERT INTO comments (user_id, video_id, parent_id, comment_text, is_moderated) VALUES (?, ?, ?, ?, ?) ''', (user_id, video_id, parent_id, comment_text, not moderation['is_appropriate'])) comment_id = cursor.lastrowid db.execute('UPDATE videos SET comments_count = comments_count + 1 WHERE id = ?', (video_id,)) # إشعارات video = db.execute('SELECT user_id FROM videos WHERE id = ?', (video_id,)).fetchone() if video and video['user_id'] != user_id: user = db.execute('SELECT username FROM users WHERE id = ?', (user_id,)).fetchone() add_notification(video['user_id'], user_id, 'comment', f'علق {user["username"]} على فيديو لك', video_id=video_id, comment_id=comment_id, priority=1) if parent_id: parent = db.execute('SELECT user_id FROM comments WHERE id = ?', (parent_id,)).fetchone() if parent and parent['user_id'] != user_id: user = db.execute('SELECT username FROM users WHERE id = ?', (user_id,)).fetchone() add_notification(parent['user_id'], user_id, 'reply', f'رد {user["username"]} على تعليقك', video_id=video_id, comment_id=comment_id, priority=1) db.commit() add_xp(user_id, 'comment', 3) track_event(user_id, 'add_comment', {'video_id': video_id, 'comment_id': comment_id}) return jsonify({'status': 'ok', 'comment_id': comment_id}) except Exception as e: logger.error(f"خطأ في add_comment: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/comment/like/', methods=['POST']) @login_required def like_comment(comment_id): try: user_id = session['user_id'] db = get_db() liked = db.execute('SELECT * FROM comment_likes WHERE user_id = ? AND comment_id = ?', (user_id, comment_id)).fetchone() if liked: db.execute('DELETE FROM comment_likes WHERE user_id = ? AND comment_id = ?', (user_id, comment_id)) db.execute('UPDATE comments SET likes_count = likes_count - 1 WHERE id = ?', (comment_id,)) liked_status = False else: db.execute('INSERT INTO comment_likes (user_id, comment_id) VALUES (?, ?)', (user_id, comment_id)) db.execute('UPDATE comments SET likes_count = likes_count + 1 WHERE id = ?', (comment_id,)) liked_status = True # إشعار لصاحب التعليق comment = db.execute('SELECT user_id FROM comments WHERE id = ?', (comment_id,)).fetchone() if comment and comment['user_id'] != user_id: user = db.execute('SELECT username FROM users WHERE id = ?', (user_id,)).fetchone() add_notification(comment['user_id'], user_id, 'like_comment', f'أعجب {user["username"]} بتعليقك', priority=0) db.commit() likes_count = db.execute('SELECT likes_count FROM comments WHERE id = ?', (comment_id,)).fetchone()['likes_count'] track_event(user_id, 'like_comment', {'comment_id': comment_id, 'liked': liked_status}) return jsonify({'status': 'ok', 'liked': liked_status, 'likes_count': likes_count}) except Exception as e: logger.error(f"خطأ في like_comment: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/comment/pin/', methods=['POST']) @login_required def pin_comment(comment_id): """تثبيت تعليق (لصاحب الفيديو فقط)""" try: user_id = session['user_id'] db = get_db() comment = db.execute(''' SELECT c.video_id, v.user_id FROM comments c JOIN videos v ON c.video_id = v.id WHERE c.id = ? ''', (comment_id,)).fetchone() if not comment or comment['user_id'] != user_id: return jsonify({'error': 'غير مصرح'}), 403 db.execute('UPDATE comments SET is_pinned = 0 WHERE video_id = ?', (comment['video_id'],)) db.execute('UPDATE comments SET is_pinned = 1 WHERE id = ?', (comment_id,)) db.commit() track_event(user_id, 'pin_comment', {'comment_id': comment_id}) return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في pin_comment: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/comment/delete/', methods=['POST']) @login_required def delete_comment(comment_id): try: user_id = session['user_id'] db = get_db() comment = db.execute(''' SELECT c.user_id, c.video_id, v.user_id as video_owner FROM comments c JOIN videos v ON c.video_id = v.id WHERE c.id = ? ''', (comment_id,)).fetchone() if not comment: return jsonify({'error': 'التعليق غير موجود'}), 404 is_owner = comment['user_id'] == user_id is_video_owner = comment['video_owner'] == user_id is_admin = session.get('role') in ['admin', 'moderator'] if not (is_owner or is_video_owner or is_admin): return jsonify({'error': 'غير مصرح'}), 403 db.execute('DELETE FROM comments WHERE id = ? OR parent_id = ?', (comment_id, comment_id)) db.execute('UPDATE videos SET comments_count = comments_count - 1 WHERE id = ?', (comment['video_id'],)) db.commit() track_event(user_id, 'delete_comment', {'comment_id': comment_id}) return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في delete_comment: {e}") return jsonify({'error': str(e)}), 500 # ==================== نظام الإشعارات المباشر ==================== @app.route('/api/notifications/stream') @login_required def notification_stream(): """بث الإشعارات المباشر باستخدام Server-Sent Events""" user_id = session['user_id'] def event_stream(): q = queue.Queue() notification_queues[user_id] = q try: while True: try: notification = q.get(timeout=30) yield f"event: notification\ndata: {json.dumps(notification, ensure_ascii=False)}\n\n" except queue.Empty: yield "event: ping\ndata: {}\n\n" except GeneratorExit: notification_queues.pop(user_id, None) return Response( event_stream(), mimetype="text/event-stream", headers={ 'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no', 'Connection': 'keep-alive' } ) @app.route('/api/notifications') @login_required def get_notifications(): """الحصول على الإشعارات مع ترقيم الصفحات""" try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 20)) offset = (page - 1) * limit db = get_db() notifications = db.execute(''' SELECT n.*, u.username, u.avatar FROM notifications n LEFT JOIN users u ON n.from_user_id = u.id WHERE n.user_id = ? ORDER BY n.priority DESC, n.created_at DESC LIMIT ? OFFSET ? ''', (session['user_id'], limit, offset)).fetchall() # تحديث حالة القراءة if page == 1: db.execute('UPDATE notifications SET is_read = 1 WHERE user_id = ?', (session['user_id'],)) db.commit() result = [] for n in notifications: notif = dict(n) notif['avatar_url'] = f'/avatars/{n["avatar"]}' if n['avatar'] else '/avatars/default.jpg' result.append(notif) unread_count = db.execute(''' SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = 0 ''', (session['user_id'],)).fetchone()[0] return jsonify({ 'notifications': result, 'unread_count': unread_count, 'next_page': page + 1 if len(result) == limit else None }) except Exception as e: logger.error(f"خطأ في get_notifications: {e}") return jsonify({'notifications': [], 'unread_count': 0}), 500 @app.route('/api/notifications/count') @login_required def notification_count(): """عدد الإشعارات غير المقروءة""" try: db = get_db() count = db.execute('SELECT COUNT(*) FROM notifications WHERE user_id = ? AND is_read = 0', (session['user_id'],)).fetchone()[0] return jsonify({'count': count}) except Exception as e: logger.error(f"خطأ في notification_count: {e}") return jsonify({'count': 0}), 500 @app.route('/api/notifications/mark-read', methods=['POST']) @login_required def mark_notifications_read(): """تحديد الإشعارات كمقروءة""" try: notification_id = request.get_json().get('notification_id') db = get_db() if notification_id: db.execute('UPDATE notifications SET is_read = 1 WHERE id = ? AND user_id = ?', (notification_id, session['user_id'])) else: db.execute('UPDATE notifications SET is_read = 1 WHERE user_id = ?', (session['user_id'],)) db.commit() return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في mark_notifications_read: {e}") return jsonify({'error': str(e)}), 500 # ==================== نظام البث المباشر المتقدم ==================== @app.route('/live') @login_required def live_page(): """صفحة البث المباشر""" return render_template_string(LIVE_TEMPLATE, session=session) @app.route('/api/live/start', methods=['POST']) @login_required def start_live(): """بدء بث مباشر (يتطلب عضوية VIP)""" try: # التحقق من صلاحية البث benefits = get_membership_benefits(session['user_id']) if not benefits.can_live_stream: return jsonify({'error': 'البث المباشر متاح فقط لأعضاء VIP'}), 403 data = request.get_json() title = data.get('title', 'بث مباشر جديد') description = data.get('description', '') db = get_db() stream_key = hashlib.sha256(f"{session['user_id']}_{uuid.uuid4()}_{time.time()}".encode()).hexdigest() # إنهاء أي بث سابق db.execute('UPDATE live_streams SET is_active = 0, ended_at = CURRENT_TIMESTAMP WHERE user_id = ? AND is_active = 1', (session['user_id'],)) cursor = db.execute(''' INSERT INTO live_streams (user_id, stream_key, title, description) VALUES (?, ?, ?, ?) ''', (session['user_id'], stream_key, title, description)) stream_id = cursor.lastrowid db.execute('UPDATE users SET is_live = 1 WHERE id = ?', (session['user_id'],)) db.commit() # تخزين في الذاكرة active_streams[stream_id] = { 'user_id': session['user_id'], 'viewers': 0, 'started_at': datetime.now() } # إشعار للمتابعين followers = db.execute('SELECT follower_id FROM follows WHERE user_id = ?', (session['user_id'],)).fetchall() follower_ids = [f['follower_id'] for f in followers] send_bulk_notifications( follower_ids, session['user_id'], 'live_start', f'{session["username"]} بدأ بثاً مباشراً!', action_url=f'/live/{stream_id}', priority=2 ) track_event(session['user_id'], 'start_live', {'stream_id': stream_id, 'title': title}) return jsonify({ 'status': 'ok', 'stream_id': stream_id, 'stream_key': stream_key, 'rtmp_url': 'rtmp://your-server.com/live' }) except Exception as e: logger.error(f"خطأ في start_live: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/live/stop', methods=['POST']) @login_required def stop_live(): """إنهاء بث مباشر""" try: db = get_db() stream = db.execute('SELECT id FROM live_streams WHERE user_id = ? AND is_active = 1', (session['user_id'],)).fetchone() if stream: db.execute('UPDATE live_streams SET is_active = 0, ended_at = CURRENT_TIMESTAMP WHERE id = ?', (stream['id'],)) if stream['id'] in active_streams: del active_streams[stream['id']] db.execute('UPDATE users SET is_live = 0 WHERE id = ?', (session['user_id'],)) db.commit() track_event(session['user_id'], 'stop_live', {}) return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في stop_live: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/live/active') @login_required def get_active_streams(): """الحصول على البثوث النشطة""" try: db = get_db() streams = db.execute(''' SELECT ls.*, u.username, u.avatar, u.is_verified FROM live_streams ls JOIN users u ON ls.user_id = u.id WHERE ls.is_active = 1 ORDER BY ls.viewers DESC LIMIT 50 ''').fetchall() result = [] for s in streams: stream = dict(s) stream['avatar_url'] = f'/avatars/{s["avatar"]}' result.append(stream) return jsonify(result) except Exception as e: logger.error(f"خطأ في get_active_streams: {e}") return jsonify([]), 500 @app.route('/api/live//view', methods=['POST']) @login_required def view_stream(stream_id): """تسجيل مشاهدة بث""" try: db = get_db() db.execute('UPDATE live_streams SET viewers = viewers + 1, peak_viewers = MAX(peak_viewers, viewers + 1) WHERE id = ?', (stream_id,)) db.commit() if stream_id in active_streams: active_streams[stream_id]['viewers'] += 1 return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في view_stream: {e}") return jsonify({'error': str(e)}), 500 @app.route('/api/live/gift/', methods=['POST']) @login_required def send_gift(stream_id): """إرسال هدية في البث""" try: data = request.get_json() gift_id = data.get('gift_id') db = get_db() # الحصول على معلومات الهدية gift = db.execute('SELECT * FROM virtual_gifts WHERE id = ?', (gift_id,)).fetchone() if not gift: return jsonify({'error': 'الهدية غير موجودة'}), 404 price = gift['price'] # التحقق من الرصيد user = db.execute('SELECT coins FROM users WHERE id = ?', (session['user_id'],)).fetchone() if user['coins'] < price: return jsonify({'error': 'رصيد غير كافٍ'}), 400 # خصم العملات if deduct_coins(session['user_id'], price, f'إرسال هدية {gift["name"]}'): # تسجيل الهدية cursor = db.execute(''' INSERT INTO live_gifts (stream_id, user_id, gift_type, gift_value) VALUES (?, ?, ?, ?) ''', (stream_id, session['user_id'], gift['name'], price)) gift_record_id = cursor.lastrowid # إضافة قيمة الهدية لصاحب البث stream = db.execute('SELECT user_id FROM live_streams WHERE id = ?', (stream_id,)).fetchone() if stream: stream_owner = stream['user_id'] owner_share = price // 2 # نصف قيمة الهدية لصاحب البث add_coins(stream_owner, owner_share, f'هدية {gift["name"]} من {session["username"]}') # تحديث إجمالي الهدايا في البث db.execute('UPDATE live_streams SET gifts_value = gifts_value + ? WHERE id = ?', (price, stream_id)) # إرسال إشعار لصاحب البث add_notification(stream_owner, session['user_id'], 'gift', f'أرسل {session["username"]} هدية {gift["name"]} في بثك', gift_id=gift_record_id, priority=2, image_url=gift['image_url']) db.commit() track_event(session['user_id'], 'send_gift', {'stream_id': stream_id, 'gift_id': gift_id, 'price': price}) return jsonify({'status': 'ok'}) else: return jsonify({'error': 'فشل في خصم العملات'}), 400 except Exception as e: logger.error(f"خطأ في send_gift: {e}") return jsonify({'error': str(e)}), 500 # ==================== نظام النقاط والمكافآت ==================== @app.route('/api/gamification/xp') @login_required def get_xp(): """الحصول على نقاط الخبرة والتاريخ""" db = get_db() user = db.execute('SELECT xp, level FROM users WHERE id = ?', (session['user_id'],)).fetchone() # تاريخ النقاط history = db.execute(''' SELECT action, xp_gained, created_at FROM user_xp WHERE user_id = ? ORDER BY created_at DESC LIMIT 50 ''', (session['user_id'],)).fetchall() # النقاط المطلوبة للمستوى التالي current_level_xp = calculate_xp_for_level(user['level']) next_level_xp = calculate_xp_for_level(user['level'] + 1) progress = ((user['xp'] - current_level_xp) / (next_level_xp - current_level_xp)) * 100 if next_level_xp > current_level_xp else 100 return jsonify({ 'xp': user['xp'], 'level': user['level'], 'next_level_xp': next_level_xp, 'progress': min(progress, 100), 'history': [dict(h) for h in history] }) @app.route('/api/gamification/daily', methods=['POST']) @login_required def claim_daily(): """المطالبة بالمكافأة اليومية""" success, *rest = claim_daily_reward(session['user_id']) if success: coins, xp, streak = rest track_event(session['user_id'], 'claim_daily', {'coins': coins, 'xp': xp, 'streak': streak}) return jsonify({'status': 'ok', 'coins': coins, 'xp': xp, 'streak': streak}) else: return jsonify({'error': rest[0]}), 400 @app.route('/api/gamification/leaderboard') @login_required def leaderboard(): """لوحة المتصدرين""" period = request.args.get('period', 'all') # all, weekly, daily limit = int(request.args.get('limit', 50)) db = get_db() if period == 'daily': # المتصدرين اليومي (بناءً على XP اليوم) users = db.execute(''' SELECT u.username, u.avatar, u.is_verified, SUM(x.xp_gained) as daily_xp, u.level FROM user_xp x JOIN users u ON x.user_id = u.id WHERE DATE(x.created_at) = DATE('now') GROUP BY x.user_id ORDER BY daily_xp DESC LIMIT ? ''', (limit,)).fetchall() elif period == 'weekly': # المتصدرين الأسبوعي users = db.execute(''' SELECT u.username, u.avatar, u.is_verified, SUM(x.xp_gained) as weekly_xp, u.level FROM user_xp x JOIN users u ON x.user_id = u.id WHERE x.created_at > datetime('now', '-7 days') GROUP BY x.user_id ORDER BY weekly_xp DESC LIMIT ? ''', (limit,)).fetchall() else: # المتصدرين الشامل users = db.execute(''' SELECT username, avatar, xp, level, is_verified FROM users ORDER BY xp DESC LIMIT ? ''', (limit,)).fetchall() result = [] for u in users: user = dict(u) user['avatar_url'] = f'/avatars/{u["avatar"]}' result.append(user) # موقع المستخدم الحالي if period == 'all': rank = db.execute(''' SELECT COUNT(*) + 1 as rank FROM users WHERE xp > (SELECT xp FROM users WHERE id = ?) ''', (session['user_id'],)).fetchone()[0] else: rank = None return jsonify({ 'leaderboard': result, 'user_rank': rank }) @app.route('/api/coins/balance') @login_required def coins_balance(): """رصيد العملات""" try: db = get_db() user = db.execute('SELECT coins, diamonds FROM users WHERE id = ?', (session['user_id'],)).fetchone() return jsonify({'coins': user['coins'], 'diamonds': user['diamonds']}) except Exception as e: logger.error(f"خطأ في coins_balance: {e}") return jsonify({'coins': 0, 'diamonds': 0}), 500 @app.route('/api/transactions') @login_required def get_transactions(): """سجل المعاملات""" try: page = int(request.args.get('page', 1)) limit = int(request.args.get('limit', 20)) offset = (page - 1) * limit db = get_db() transactions = db.execute(''' SELECT * FROM transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT ? OFFSET ? ''', (session['user_id'], limit, offset)).fetchall() return jsonify({ 'transactions': [dict(t) for t in transactions], 'next_page': page + 1 if len(transactions) == limit else None }) except Exception as e: logger.error(f"خطأ في get_transactions: {e}") return jsonify({'transactions': []}), 500 @app.route('/api/gifts') @login_required def get_gifts(): """الحصول على قائمة الهدايا المتاحة""" try: db = get_db() gifts = db.execute('SELECT * FROM virtual_gifts WHERE is_active = 1 ORDER BY price').fetchall() return jsonify([dict(g) for g in gifts]) except Exception as e: logger.error(f"خطأ في get_gifts: {e}") return jsonify([]), 500 # ==================== نظام التحليلات ==================== def track_event(user_id, event_name, event_data=None): """تسجيل حدث تحليلي""" db = get_db() try: db.execute(''' INSERT INTO analytics_events (user_id, session_id, event_name, event_data, ip_address, user_agent, page_url) VALUES (?, ?, ?, ?, ?, ?, ?) ''', (user_id, session.get('session_id'), event_name, json.dumps(event_data) if event_data else None, get_client_ip(), request.headers.get('User-Agent', ''), request.path)) db.commit() except Exception as e: logger.error(f"خطأ في track_event: {e}") @app.route('/api/analytics/stats') @login_required def analytics_stats(): """إحصائيات المستخدم""" stats = get_user_stats(session['user_id']) return jsonify(stats) @app.route('/api/analytics/track', methods=['POST']) @login_required def track_analytics(): """تتبع حدث مخصص""" data = request.get_json() event_name = data.get('event_name') event_data = data.get('event_data') if not event_name: return jsonify({'error': 'event_name مطلوب'}), 400 track_event(session['user_id'], event_name, event_data) return jsonify({'status': 'ok'}) # ==================== نظام الإحالات والتسويق ==================== def generate_affiliate_link(user_id): """توليد رابط إحالة فريد""" code = secrets.token_urlsafe(10) db = get_db() db.execute('UPDATE users SET referral_code = ? WHERE id = ?', (code, user_id)) db.commit() return f"/register?ref={code}" def track_affiliate_click(referral_code, ip, user_agent): """تسجيل نقرة على رابط الإحالة""" db = get_db() try: referrer = db.execute('SELECT id FROM users WHERE referral_code = ?', (referral_code,)).fetchone() if not referrer: return False db.execute(''' INSERT INTO affiliate_clicks (referrer_id, ip, user_agent) VALUES (?, ?, ?) ''', (referrer['id'], ip, user_agent)) db.execute('UPDATE users SET affiliate_clicks = affiliate_clicks + 1 WHERE id = ?', (referrer['id'],)) db.commit() # تسجيل في ملف log_file = os.path.join('affiliate_logs', f"clicks_{datetime.now().strftime('%Y%m')}.log") with open(log_file, 'a', encoding='utf-8') as f: f.write(f"{datetime.now().isoformat()}|{referrer['id']}|{ip}|{user_agent}\n") return True except Exception as e: logger.error(f"خطأ في track_affiliate_click: {e}") return False def process_affiliate_conversion(referral_code, new_user_id): """معالجة تحويل الإحالة""" db = get_db() try: referrer = db.execute('SELECT id FROM users WHERE referral_code = ?', (referral_code,)).fetchone() if not referrer: return # تحديث آخر نقرة db.execute(''' UPDATE affiliate_clicks SET converted = 1 WHERE referrer_id = ? AND converted = 0 ORDER BY clicked_at DESC LIMIT 1 ''', (referrer['id'],)) db.execute('UPDATE users SET affiliate_conversions = affiliate_conversions + 1 WHERE id = ?', (referrer['id'],)) # مكافآت coins_referrer = 200 coins_referred = 100 xp_referrer = 50 add_coins(referrer['id'], coins_referrer, 'مكافأة إحالة') add_coins(new_user_id, coins_referred, 'مكافأة تسجيل عن طريق رابط') add_xp(referrer['id'], 'referral', xp_referrer) db.execute(''' INSERT INTO referrals (referrer_id, referred_id, reward_coins) VALUES (?, ?, ?) ''', (referrer['id'], new_user_id, coins_referrer)) db.commit() # تسجيل في ملف log_file = os.path.join('affiliate_logs', f"conversions_{datetime.now().strftime('%Y%m')}.log") with open(log_file, 'a', encoding='utf-8') as f: f.write(f"{datetime.now().isoformat()}|{referrer['id']}|{new_user_id}\n") except Exception as e: logger.error(f"خطأ في process_affiliate_conversion: {e}") @app.route('/api/affiliate/link') @login_required def get_affiliate_link(): """الحصول على رابط الإحالة""" link = generate_affiliate_link(session['user_id']) return jsonify({'link': request.host_url.rstrip('/') + link}) @app.route('/api/affiliate/stats') @login_required def affiliate_stats(): """إحصائيات الإحالات""" db = get_db() user = db.execute(''' SELECT affiliate_clicks, affiliate_conversions, affiliate_balance FROM users WHERE id = ? ''', (session['user_id'],)).fetchone() clicks = db.execute(''' SELECT clicked_at, converted FROM affiliate_clicks WHERE referrer_id = ? ORDER BY clicked_at DESC LIMIT 50 ''', (session['user_id'],)).fetchall() return jsonify({ 'clicks': user['affiliate_clicks'], 'conversions': user['affiliate_conversions'], 'balance': user['affiliate_balance'], 'recent_clicks': [dict(c) for c in clicks] }) @app.route('/r/') def referral_redirect(referral_code): """تسجيل نقرة وإعادة توجيه للتسجيل""" ip = get_client_ip() user_agent = request.headers.get('User-Agent', '') track_affiliate_click(referral_code, ip, user_agent) return redirect(url_for('register', ref=referral_code)) # ==================== نظام البلاغات ==================== @app.route('/api/report', methods=['POST']) @login_required def create_report(): """إنشاء بلاغ""" try: data = request.get_json() report_type = data.get('type') # user, video, comment reported_id = data.get('id') reason = data.get('reason') details = data.get('details', '') if not reason: return jsonify({'error': 'يرجى ذكر سبب البلاغ'}), 400 if len(reason) < 10: return jsonify({'error': 'الرجاء كتابة سبب أكثر تفصيلاً'}), 400 db = get_db() if report_type == 'user': db.execute(''' INSERT INTO reports (reporter_id, reported_user_id, reason, details) VALUES (?, ?, ?, ?) ''', (session['user_id'], reported_id, reason, details)) elif report_type == 'video': db.execute(''' INSERT INTO reports (reporter_id, reported_video_id, reason, details) VALUES (?, ?, ?, ?) ''', (session['user_id'], reported_id, reason, details)) db.execute('UPDATE videos SET report_count = report_count + 1 WHERE id = ?', (reported_id,)) video = db.execute('SELECT report_count FROM videos WHERE id = ?', (reported_id,)).fetchone() if video and video['report_count'] >= 5: db.execute('UPDATE videos SET is_reported = 1 WHERE id = ?', (reported_id,)) elif report_type == 'comment': db.execute(''' INSERT INTO reports (reporter_id, reported_comment_id, reason, details) VALUES (?, ?, ?, ?) ''', (session['user_id'], reported_id, reason, details)) db.execute('UPDATE comments SET report_count = report_count + 1 WHERE id = ?', (reported_id,)) comment = db.execute('SELECT report_count FROM comments WHERE id = ?', (reported_id,)).fetchone() if comment and comment['report_count'] >= 3: db.execute('UPDATE comments SET is_reported = 1 WHERE id = ?', (reported_id,)) else: return jsonify({'error': 'نوع البلاغ غير صحيح'}), 400 db.commit() # مكافأة صغيرة للإبلاغ add_coins(session['user_id'], 5, 'مكافأة الإبلاغ عن محتوى غير لائق') track_event(session['user_id'], 'create_report', {'type': report_type, 'id': reported_id}) return jsonify({'status': 'ok'}) except Exception as e: logger.error(f"خطأ في create_report: {e}") return jsonify({'error': str(e)}), 500 # ==================== دوال الملفات ==================== @app.route('/videos/') def serve_video(filename): """تقديم ملفات الفيديو""" return send_from_directory(app.config['UPLOAD_FOLDER'], filename) @app.route('/avatars/') def serve_avatar(filename): """تقديم الصور الشخصية""" return send_from_directory(app.config['AVATAR_FOLDER'], filename) @app.route('/thumbnails/') def serve_thumbnail(filename): """تقديم الصور المصغرة""" return send_from_directory(app.config['THUMBNAIL_FOLDER'], filename) @app.route('/encrypted/') @login_required def serve_encrypted(filename): """تقديم الفيديوهات المشفرة""" return send_from_directory(app.config['ENCRYPTED_FOLDER'], filename) @app.route('/watermarked/') def serve_watermarked(filename): """تقديم الفيديوهات ذات العلامة المائية""" return send_from_directory(app.config['WATERMARK_FOLDER'], filename) # ==================== مسارات إضافية ==================== @app.route('/search') @login_required def search_page(): """صفحة البحث""" return render_template_string(SEARCH_TEMPLATE, session=session) @app.route('/leaderboard') @login_required def leaderboard_page(): """صفحة المتصدرين""" return render_template_string(LEADERBOARD_TEMPLATE, session=session) @app.route('/gifts') @login_required def gifts_page(): """صفحة الهدايا""" return render_template_string(GIFTS_TEMPLATE, session=session) # ==================== قوالب HTML المتقدمة ==================== LOGIN_PAGE = ''' تسجيل الدخول - ARC Video

ARC

منصة الفيديو الأكثر تطوراً

{% if error %}
{{ error }}
{% endif %}

ليس لديك حساب؟ سجل الآن

بالتسجيل أنت توافق على الشروط والأحكام و سياسة الخصوصية

''' REGISTER_PAGE = ''' تسجيل جديد - ARC Video

انضم إلينا

أنشئ حسابك وابدأ الإبداع

8 أحرف على الأقل، حرف كبير وصغير ورقم

{% if error %}
{{ error }}
{% endif %}

لديك حساب بالفعل؟ سجل دخول

''' TWOFA_PAGE = ''' المصادقة الثنائية - ARC Video
🔐

المصادقة الثنائية

أدخل الرمز من تطبيق المصادقة

''' SETUP_2FA_PAGE = ''' إعداد المصادقة الثنائية - ARC Video

إعداد المصادقة الثنائية

📱

1. ثبّت تطبيق مصادقة

حمّل Google Authenticator أو Microsoft Authenticator

2. امسح رمز QR

{% if qr_code %} {% endif %}

أو أدخل المفتاح يدوياً

{{ secret }}

3. رموز الاسترداد الاحتياطية

{% for code in backup_codes %}
{{ code }}
{% endfor %}

احفظ هذه الرموز في مكان آمن، يمكنك استخدامها مرة واحدة إذا فقدت هاتفك

4. تحقق من الإعداد

{% if error %}

{{ error }}

{% endif %}
''' MAIN_TEMPLATE = ''' ARC Video - الصفحة الرئيسية
📸

💬 التعليقات

''' PROFILE_TEMPLATE = ''' @{{ user.username }} - ARC Video

@{{ user.username }}

{% if user.is_verified %} ✓ متحقق {% endif %} {% if user.role == 'vip' %} VIP {% elif user.role == 'vip_gold' %} VIP GOLD {% endif %}

{{ user.bio or 'لا توجد سيرة ذاتية' }}

{{ stats.videos or 0 }}
فيديو
{{ followers_count }}
متابع
{{ following_count }}
يتابع
{{ stats.total_views or 0 }}
مشاهدة
المستوى {{ stats.level }}
💎 {{ stats.xp }} XP
🪙 {{ stats.coins }} عملة
{% if session.user_id == user.id %} ⚙️ الإعدادات 📁 المحفوظة {% else %} {% endif %}
{% if stats.achievements %}

🏆 الإنجازات

{% for achievement in stats.achievements %} {{ achievement }} {% endfor %}
{% endif %}

📹 فيديوهات @{{ user.username }}

{% if videos %} {% else %}

لا توجد فيديوهات بعد

{% endif %}
''' SETTINGS_TEMPLATE = ''' الإعدادات - ARC Video
← العودة للملف الشخصي

الإعدادات

🔒 الخصوصية

🔔 الإشعارات

⚙️ التفضيلات

''' UPLOAD_TEMPLATE = ''' رفع فيديو - ARC Video
← العودة للرئيسية

رفع فيديو جديد

📸

اضغط لفتح الكاميرا أو اسحب الفيديو

🎬

اسحب الفيديو هنا أو

''' SEARCH_TEMPLATE = ''' بحث - ARC Video
← العودة للرئيسية
''' LIVE_TEMPLATE = ''' البث المباشر - ARC Video
← العودة للرئيسية

البث المباشر

''' LEADERBOARD_TEMPLATE = ''' المتصدرين - ARC Video
← العودة للرئيسية

🏆 المتصدرين

''' GIFTS_TEMPLATE = ''' الهدايا - ARC Video
← العودة للرئيسية

🎁 الهدايا الافتراضية

رصيدك الحالي

🪙 0 💎 0
''' # ==================== دوال Cloudinary ==================== def upload_video_to_cloudinary(file_path, public_id=None): """رفع فيديو إلى Cloudinary مع تحسينات""" try: response = cloudinary.uploader.upload( file_path, resource_type="video", public_id=public_id, folder="arc_videos", eager=[ {"width": 300, "height": 300, "crop": "pad", "audio_codec": "none"}, {"width": 600, "height": 600, "crop": "pad", "audio_codec": "none"}, {"width": 1080, "height": 1920, "crop": "pad", "quality": "auto"} ], eager_async=True, chunk_size=6000000, timeout=120 ) return response.get('secure_url'), response.get('public_id') except Exception as e: logger.error(f"خطأ في رفع Cloudinary: {e}") return None, None def generate_video_thumbnail(video_public_id): """توليد رابط الصورة المصغرة من Cloudinary""" return cloudinary.CloudinaryVideo(video_public_id).video_thumbnail_url( width=400, height=400, crop="fill", quality="auto" ) def add_watermark_to_cloudinary_video(public_id, text="ARC"): """إنشاء رابط فيديو مع علامة مائية نصية""" transformed_url = cloudinary.CloudinaryVideo(public_id).video_url( transformation=[ {"width": 1080, "height": 1920, "crop": "limit"}, {"overlay": {"font_family": "Arial", "font_size": 60, "text": text}, "gravity": "south_east", "opacity": 50, "x": 20, "y": 20} ] ) return transformed_url # ==================== دوال التشفير المتقدمة ==================== def encrypt_video_file(input_path, output_path=None, key=None): """تشفير ملف فيديو باستخدام AES-256-GCM""" if not HAS_CRYPTO: return None, None if output_path is None: filename = os.path.basename(input_path) output_path = os.path.join(ENCRYPTED_FOLDER, f"{filename}.enc") if key is None: key = get_random_bytes(32) try: with open(input_path, 'rb') as f: data = f.read() cipher = AES.new(key, AES.MODE_GCM) ciphertext, tag = cipher.encrypt_and_digest(data) # تخزين IV و tag مع البيانات المشفرة with open(output_path, 'wb') as f: f.write(cipher.nonce) f.write(tag) f.write(ciphertext) return output_path, key except Exception as e: logger.error(f"خطأ في التشفير: {e}") return None, None def decrypt_video_file(encrypted_path, key, output_path=None): """فك تشفير فيديو""" if not HAS_CRYPTO: return None if output_path is None: output_path = encrypted_path.replace('.enc', '') try: with open(encrypted_path, 'rb') as f: nonce = f.read(16) tag = f.read(16) ciphertext = f.read() cipher = AES.new(key, AES.MODE_GCM, nonce=nonce) data = cipher.decrypt_and_verify(ciphertext, tag) with open(output_path, 'wb') as f: f.write(data) return output_path except Exception as e: logger.error(f"خطأ في فك التشفير: {e}") return None def add_watermark_to_video(input_path, output_path=None, watermark_text="ARC"): """إضافة علامة مائية نصية على الفيديو""" if not HAS_CV2 or not HAS_PIL: return None if output_path is None: filename = os.path.basename(input_path) output_path = os.path.join(WATERMARK_FOLDER, f"watermarked_{filename}") try: cap = cv2.VideoCapture(input_path) if not cap.isOpened(): return None fps = int(cap.get(cv2.CAP_PROP_FPS)) width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fourcc = cv2.VideoWriter_fourcc(*'mp4v') out = cv2.VideoWriter(output_path, fourcc, fps, (width, height)) font = cv2.FONT_HERSHEY_SIMPLEX text_size = cv2.getTextSize(watermark_text, font, 1, 2)[0] text_x = width - text_size[0] - 20 text_y = height - 20 frame_count = 0 while True: ret, frame = cap.read() if not ret: break # إضافة العلامة المائية الشفافة overlay = frame.copy() cv2.putText(overlay, watermark_text, (text_x, text_y), font, 1, (255, 255, 255), 2, cv2.LINE_AA) cv2.putText(overlay, f"ARC {datetime.now().year}", (20, 40), font, 0.7, (255, 45, 85), 2) # دمج مع الشفافية cv2.addWeighted(overlay, 0.7, frame, 0.3, 0, frame) out.write(frame) frame_count += 1 if frame_count % 100 == 0: logger.info(f"تقدم العلامة المائية: {frame_count}/{total_frames}") cap.release() out.release() cv2.destroyAllWindows() return output_path except Exception as e: logger.error(f"خطأ في إضافة العلامة المائية: {e}") return None def extract_video_features(video_path): """استخراج خصائص من الفيديو لتوليد متجه""" if not HAS_CV2: return random_vector() try: cap = cv2.VideoCapture(video_path) if not cap.isOpened(): return random_vector() # استخراج عدة إطارات frames = [] total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) sample_indices = [int(i * total_frames / 5) for i in range(5)] for idx in sample_indices: cap.set(cv2.CAP_PROP_POS_FRAMES, idx) ret, frame = cap.read() if ret: # تحويل إلى تدرج رمادي وتصغير gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) resized = cv2.resize(gray, (32, 32)) frames.append(resized.flatten()) cap.release() if not frames: return random_vector() # متوسط الإطارات features = np.mean(frames, axis=0).astype(np.float32) # تطبيع features = features / 255.0 # تقليل الأبعاد if len(features) > VECTOR_DIM: step = len(features) // VECTOR_DIM features = features[::step][:VECTOR_DIM] elif len(features) < VECTOR_DIM: features = np.pad(features, (0, VECTOR_DIM - len(features))) return features.tobytes() except Exception as e: logger.error(f"خطأ في استخراج الخصائص: {e}") return random_vector() # ==================== التشغيل ==================== if __name__ == '__main__': with app.app_context(): try: init_db() logger.info("✅ تم تهيئة قاعدة البيانات بنجاح") print("✅ ARC Video Global - النسخة المتقدمة جاهزة للتشغيل") print("=" * 60) print("🚀 منصة ARC Video - إمبراطورية الفيديو العالمية") print("📱 Glassmorphism UI - نظام توصيات ذكي - أمان متقدم") print("💎 عضوية VIP - هدايا افتراضية - بث مباشر") print("=" * 60) except Exception as e: logger.error(f"❌ خطأ أثناء التهيئة: {e}") print(f"❌ خطأ: {e}") app.run(host='0.0.0.0', port=7860, debug=True, threaded=True)