Major additions: - Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js) - Production Docker configuration (docker-compose.prod.yml, .env.production) - Database initialization SQL files (deploy/init_settings.sql) - Template builder system with drag-and-drop UI - Advanced features (OAuth, CDN, enhanced analytics, monetization) - Comprehensive documentation (deployment guides, quick start, feature docs) - Design system with accessibility and responsive layout - Deployment automation scripts (deploy.ps1, generate-secrets.ps1) Setup wizard allows customization of: - Platform name and branding - Domain configuration - Membership tiers and pricing - Admin credentials - Feature toggles Database includes 270+ tables for complete video streaming platform with advanced features for analytics, moderation, template building, and monetization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
188 lines
5.8 KiB
PHP
188 lines
5.8 KiB
PHP
<?php
|
|
/**
|
|
* EasyStream Advanced Search Engine
|
|
* Meilisearch Integration with MySQL Fallback
|
|
* Version: 1.0
|
|
*/
|
|
|
|
defined('_ISVALID') or header('Location: /error');
|
|
|
|
class VAdvancedSearch {
|
|
private static $db;
|
|
private static $meilisearch_host = 'http://localhost:7700';
|
|
private static $meilisearch_key = null;
|
|
|
|
public static function init() {
|
|
self::$db = VDatabase::getInstance();
|
|
|
|
// Load config if available
|
|
$config = VGenerate::getConfig('meilisearch_config');
|
|
if ($config) {
|
|
self::$meilisearch_host = $config['host'] ?? self::$meilisearch_host;
|
|
self::$meilisearch_key = $config['api_key'] ?? null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Perform advanced search
|
|
* @param string $query Search query
|
|
* @param array $filters Filters array
|
|
* @param array $options Pagination/sorting options
|
|
* @return array Search results
|
|
*/
|
|
public static function search($query, $filters = [], $options = []) {
|
|
self::init();
|
|
|
|
$usr_id = isset($_SESSION['USER_ID']) ? (int)$_SESSION['USER_ID'] : null;
|
|
|
|
// Track search
|
|
self::trackSearch($query, $filters, $usr_id);
|
|
|
|
// Use MySQL search (Meilisearch optional)
|
|
return self::searchMySQL($query, $filters, $options);
|
|
}
|
|
|
|
/**
|
|
* MySQL-based search with full-text
|
|
*/
|
|
private static function searchMySQL($query, $filters = [], $options = []) {
|
|
$page = $options['page'] ?? 1;
|
|
$limit = min(50, $options['limit'] ?? 20);
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
$query_safe = VDatabase::escape($query);
|
|
|
|
$where = ["vf.privacy = 'public'"];
|
|
|
|
// Full-text search
|
|
if (!empty($query)) {
|
|
$where[] = "(vf.file_title LIKE '%$query_safe%' OR vf.file_description LIKE '%$query_safe%' OR vf.file_tags LIKE '%$query_safe%')";
|
|
}
|
|
|
|
// Type filter
|
|
if (!empty($filters['type'])) {
|
|
$type = VDatabase::escape($filters['type']);
|
|
$where[] = "vf.file_type = '$type'";
|
|
}
|
|
|
|
// Duration filter
|
|
if (isset($filters['duration_min'])) {
|
|
$min = (int)$filters['duration_min'];
|
|
$where[] = "vf.file_duration >= $min";
|
|
}
|
|
if (isset($filters['duration_max'])) {
|
|
$max = (int)$filters['duration_max'];
|
|
$where[] = "vf.file_duration <= $max";
|
|
}
|
|
|
|
// Date filter
|
|
if (!empty($filters['date_from'])) {
|
|
$from = VDatabase::escape($filters['date_from']);
|
|
$where[] = "vf.upload_date >= '$from'";
|
|
}
|
|
|
|
// Category filter
|
|
if (!empty($filters['category'])) {
|
|
$cat = VDatabase::escape($filters['category']);
|
|
$where[] = "vf.file_category = '$cat'";
|
|
}
|
|
|
|
$whereClause = implode(' AND ', $where);
|
|
|
|
// Sorting
|
|
$sort = $options['sort'] ?? 'recent';
|
|
$orderBy = match($sort) {
|
|
'popular' => 'vf.file_views DESC',
|
|
'rating' => 'vf.file_rating DESC',
|
|
default => 'vf.upload_date DESC'
|
|
};
|
|
|
|
$sql = "SELECT vf.file_key, vf.file_title, vf.file_description, vf.file_type,
|
|
vf.file_views, vf.file_rating, vf.upload_date, vf.file_duration,
|
|
au.usr_user, au.usr_dname
|
|
FROM db_videofiles vf
|
|
JOIN db_accountuser au ON vf.usr_id = au.usr_id
|
|
WHERE $whereClause
|
|
ORDER BY $orderBy
|
|
LIMIT $limit OFFSET $offset";
|
|
|
|
$result = self::$db->execute($sql);
|
|
|
|
$results = [];
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$results[] = $row;
|
|
}
|
|
}
|
|
|
|
// Get total
|
|
$countSql = "SELECT COUNT(*) as total FROM db_videofiles vf WHERE $whereClause";
|
|
$countResult = self::$db->execute($countSql);
|
|
$total = 0;
|
|
if ($countResult) {
|
|
$row = $countResult->FetchRow();
|
|
$total = (int)$row['total'];
|
|
}
|
|
|
|
return [
|
|
'query' => $query,
|
|
'results' => $results,
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'limit' => $limit,
|
|
'total_pages' => ceil($total / $limit)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Track search for analytics
|
|
*/
|
|
private static function trackSearch($query, $filters, $usr_id) {
|
|
if (empty($query)) return;
|
|
|
|
$query_safe = VDatabase::escape($query);
|
|
$usr_id_val = $usr_id ? (int)$usr_id : 'NULL';
|
|
$session = session_id();
|
|
$session_safe = VDatabase::escape($session);
|
|
$filters_json = VDatabase::escape(json_encode($filters));
|
|
|
|
$sql = "INSERT INTO db_search_history
|
|
(usr_id, session_id, query, filters, created_at)
|
|
VALUES ($usr_id_val, '$session_safe', '$query_safe', '$filters_json', NOW())";
|
|
self::$db->execute($sql);
|
|
|
|
// Update suggestions
|
|
$sql = "INSERT INTO db_search_suggestions (query, search_count, last_searched)
|
|
VALUES ('$query_safe', 1, NOW())
|
|
ON DUPLICATE KEY UPDATE search_count = search_count + 1, last_searched = NOW()";
|
|
self::$db->execute($sql);
|
|
}
|
|
|
|
/**
|
|
* Get search suggestions
|
|
*/
|
|
public static function getSuggestions($query, $limit = 10) {
|
|
self::init();
|
|
|
|
$query_safe = VDatabase::escape($query);
|
|
$limit = (int)$limit;
|
|
|
|
$sql = "SELECT query, search_count
|
|
FROM db_search_suggestions
|
|
WHERE query LIKE '%$query_safe%'
|
|
ORDER BY search_count DESC
|
|
LIMIT $limit";
|
|
|
|
$result = self::$db->execute($sql);
|
|
$suggestions = [];
|
|
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$suggestions[] = $row['query'];
|
|
}
|
|
}
|
|
|
|
return $suggestions;
|
|
}
|
|
}
|