bool, 'message' => string, 'sub_id' => int] */ public static function uploadSubtitle($file_id, $file_type, $subtitle_file, $language = 'en', $label = 'English', $is_default = false) { global $cfg, $class_database; // Get configuration $config = $class_database->getConfigurations('subtitles_enabled,subtitles_max_size,subtitles_allowed_formats,subtitles_auto_convert,subtitles_max_per_video,subtitles_require_approval'); // Check if subtitles are enabled if ($config['subtitles_enabled'] != 1) { return ['success' => false, 'message' => 'Subtitles are not enabled on this platform']; } // Validate file upload if (!isset($subtitle_file['tmp_name']) || $subtitle_file['error'] !== UPLOAD_ERR_OK) { return ['success' => false, 'message' => 'No file uploaded or upload error occurred']; } // Validate file size if ($subtitle_file['size'] > $config['subtitles_max_size']) { $max_mb = round($config['subtitles_max_size'] / 1048576, 2); return ['success' => false, 'message' => "Subtitle file too large. Maximum size: {$max_mb}MB"]; } // Validate file format $file_ext = strtolower(pathinfo($subtitle_file['name'], PATHINFO_EXTENSION)); $allowed_formats = explode(',', $config['subtitles_allowed_formats']); if (!in_array($file_ext, $allowed_formats)) { return ['success' => false, 'message' => 'Invalid subtitle format. Allowed: ' . implode(', ', $allowed_formats)]; } // Check subtitle count limit $current_count = self::getSubtitleCount($file_id, $file_type); if ($current_count >= $config['subtitles_max_per_video']) { return ['success' => false, 'message' => "Maximum {$config['subtitles_max_per_video']} subtitle tracks allowed per video"]; } // Get user ID $usr_id = isset($_SESSION['USER_ID']) ? (int) $_SESSION['USER_ID'] : 0; if ($usr_id == 0) { return ['success' => false, 'message' => 'You must be logged in to upload subtitles']; } // Verify file ownership if (!self::verifyFileOwnership($file_id, $file_type, $usr_id)) { return ['success' => false, 'message' => 'You do not have permission to add subtitles to this file']; } // Generate unique filename $unique_filename = self::generateSubtitleFilename($file_id, $file_type, $language, $file_ext); $subtitle_dir = $cfg['main_dir'] . '/f_data/data_subtitles/'; $destination = $subtitle_dir . $unique_filename; // Ensure directory exists if (!file_exists($subtitle_dir)) { mkdir($subtitle_dir, 0755, true); } // Move uploaded file if (!move_uploaded_file($subtitle_file['tmp_name'], $destination)) { return ['success' => false, 'message' => 'Failed to save subtitle file']; } // Convert SRT to VTT if needed $final_format = $file_ext; if ($file_ext === 'srt' && $config['subtitles_auto_convert'] == 1) { $vtt_filename = self::convertSrtToVtt($destination, $unique_filename); if ($vtt_filename) { unlink($destination); // Remove SRT file $unique_filename = $vtt_filename; $final_format = 'vtt'; } } // If this is set as default, unset other defaults if ($is_default) { self::unsetDefaultSubtitles($file_id, $file_type); } // Insert into database $active = ($config['subtitles_require_approval'] == 1 && !self::isAdmin()) ? 0 : 1; $sql = "INSERT INTO `db_subtitles` (`file_id`, `file_type`, `usr_id`, `sub_language`, `sub_label`, `sub_filename`, `sub_format`, `sub_kind`, `sub_default`, `sub_filesize`, `active`) VALUES (%d, '%s', %d, '%s', '%s', '%s', '%s', 'subtitles', %d, %d, %d)"; $class_database->doQuery($sql, $file_id, $class_database->safe_input($file_type), $usr_id, $class_database->safe_input($language), $class_database->safe_input($label), $class_database->safe_input($unique_filename), $final_format, $is_default ? 1 : 0, filesize($subtitle_dir . $unique_filename), $active ); $sub_id = $class_database->insert_id(); // Update subtitle count in file table self::updateSubtitleCount($file_id, $file_type); $message = $active ? 'Subtitle uploaded successfully' : 'Subtitle uploaded and pending approval'; return ['success' => true, 'message' => $message, 'sub_id' => $sub_id]; } /** * Get all subtitles for a file * * @param int $file_id File ID * @param string $file_type File type * @param bool $active_only Only return active subtitles * @return array Array of subtitle records */ public static function getSubtitles($file_id, $file_type, $active_only = true) { global $class_database; $active_clause = $active_only ? "AND `active` = 1" : ""; $sql = "SELECT * FROM `db_subtitles` WHERE `file_id` = %d AND `file_type` = '%s' $active_clause ORDER BY `sub_default` DESC, `sub_language` ASC"; $result = $class_database->doQuery($sql, $file_id, $class_database->safe_input($file_type)); $subtitles = []; while ($row = $result->fetch_assoc()) { $subtitles[] = $row; } return $subtitles; } /** * Get subtitle track URL for player * * @param int $file_id File ID * @param string $file_type File type * @return array Array of subtitle tracks for video player */ public static function getSubtitleTracksForPlayer($file_id, $file_type) { global $cfg; $subtitles = self::getSubtitles($file_id, $file_type, true); $tracks = []; foreach ($subtitles as $sub) { $tracks[] = [ 'kind' => $sub['sub_kind'], 'label' => $sub['sub_label'], 'srclang' => $sub['sub_language'], 'src' => $cfg['main_url'] . '/f_data/data_subtitles/' . $sub['sub_filename'], 'default' => $sub['sub_default'] == 1 ]; } return $tracks; } /** * Delete subtitle track * * @param int $sub_id Subtitle ID * @return bool Success */ public static function deleteSubtitle($sub_id) { global $cfg, $class_database; // Get subtitle info $sql = "SELECT * FROM `db_subtitles` WHERE `sub_id` = %d LIMIT 1"; $result = $class_database->doQuery($sql, $sub_id); $subtitle = $result->fetch_assoc(); if (!$subtitle) { return false; } // Verify ownership $usr_id = isset($_SESSION['USER_ID']) ? (int) $_SESSION['USER_ID'] : 0; if ($subtitle['usr_id'] != $usr_id && !self::isAdmin()) { return false; } // Delete file $file_path = $cfg['main_dir'] . '/f_data/data_subtitles/' . $subtitle['sub_filename']; if (file_exists($file_path)) { unlink($file_path); } // Delete from database $sql = "DELETE FROM `db_subtitles` WHERE `sub_id` = %d"; $class_database->doQuery($sql, $sub_id); // Update count self::updateSubtitleCount($subtitle['file_id'], $subtitle['file_type']); return true; } /** * Convert SRT to VTT format * * @param string $srt_path Path to SRT file * @param string $original_filename Original filename * @return string|false VTT filename or false on failure */ private static function convertSrtToVtt($srt_path, $original_filename) { global $cfg; $srt_content = file_get_contents($srt_path); if ($srt_content === false) { return false; } // Add WEBVTT header $vtt_content = "WEBVTT\n\n"; // Convert timestamps from SRT format (00:00:00,000) to VTT format (00:00:00.000) $vtt_content .= str_replace(',', '.', $srt_content); // Generate VTT filename $vtt_filename = str_replace('.srt', '.vtt', $original_filename); $vtt_path = $cfg['main_dir'] . '/f_data/data_subtitles/' . $vtt_filename; // Write VTT file if (file_put_contents($vtt_path, $vtt_content) === false) { return false; } return $vtt_filename; } /** * Generate unique subtitle filename */ private static function generateSubtitleFilename($file_id, $file_type, $language, $ext) { return $file_type . '_' . $file_id . '_' . $language . '_' . time() . '.' . $ext; } /** * Unset default flag for all other subtitles */ private static function unsetDefaultSubtitles($file_id, $file_type) { global $class_database; $sql = "UPDATE `db_subtitles` SET `sub_default` = 0 WHERE `file_id` = %d AND `file_type` = '%s'"; $class_database->doQuery($sql, $file_id, $class_database->safe_input($file_type)); } /** * Get subtitle count for a file */ private static function getSubtitleCount($file_id, $file_type) { global $class_database; $sql = "SELECT COUNT(*) as count FROM `db_subtitles` WHERE `file_id` = %d AND `file_type` = '%s' AND `active` = 1"; $result = $class_database->doQuery($sql, $file_id, $class_database->safe_input($file_type)); $row = $result->fetch_assoc(); return (int) $row['count']; } /** * Update subtitle count in file table */ private static function updateSubtitleCount($file_id, $file_type) { global $class_database; $count = self::getSubtitleCount($file_id, $file_type); $table_map = [ 'video' => 'db_videofiles', 'short' => 'db_shortfiles', 'live' => 'db_livefiles', 'audio' => 'db_audiofiles' ]; if (isset($table_map[$file_type])) { $sql = "UPDATE `{$table_map[$file_type]}` SET `subtitle_count` = %d WHERE `db_id` = %d"; $class_database->doQuery($sql, $count, $file_id); } } /** * Verify file ownership */ private static function verifyFileOwnership($file_id, $file_type, $usr_id) { global $class_database; $table_map = [ 'video' => 'db_videofiles', 'short' => 'db_shortfiles', 'live' => 'db_livefiles', 'audio' => 'db_audiofiles' ]; if (!isset($table_map[$file_type])) { return false; } $sql = "SELECT `usr_id` FROM `{$table_map[$file_type]}` WHERE `db_id` = %d LIMIT 1"; $result = $class_database->doQuery($sql, $file_id); $row = $result->fetch_assoc(); return $row && $row['usr_id'] == $usr_id; } /** * Check if current user is admin */ private static function isAdmin() { return isset($_SESSION['USER_ADMIN']) && $_SESSION['USER_ADMIN'] == 1; } }