Skip to content

Commit c9e41d3

Browse files
authored
Update generate_srt.php
1 parent 09a7fd2 commit c9e41d3

File tree

1 file changed

+121
-174
lines changed

1 file changed

+121
-174
lines changed

generate_srt.php

Lines changed: 121 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -1,184 +1,131 @@
1-
<!DOCTYPE html><html lang="en">
2-
<head>
3-
<meta charset="UTF-8">
4-
<title>CapCut SRT Generator - Convert your script to SRT format for easy subtitles in CapCut</title>
5-
<style>
6-
* { box-sizing: border-box; }
7-
body {
8-
background-color: #1e1e2f;
9-
color: #f1f1f1;
10-
font-family: 'Segoe UI', sans-serif;
11-
margin: 0;
12-
padding: 20px;
13-
height: 100vh;
14-
display: flex;
15-
flex-direction: column;
16-
}
17-
h2 { margin-top: 0; }
18-
.container {
19-
flex: 1;
20-
display: flex;
21-
gap: 20px;
22-
}
23-
.left, .right {
24-
display: flex;
25-
flex-direction: column;
26-
height: 100%;
27-
}
28-
.left { flex: 0 0 60%; }
29-
.right { flex: 0 0 40%; }
30-
.controls {
31-
display: flex;
32-
gap: 10px;
33-
margin-bottom: 10px;
34-
flex-wrap: wrap;
35-
}
36-
.controls label {
37-
flex: 1;
38-
display: flex;
39-
flex-direction: column;
40-
font-size: 0.8rem;
41-
}
42-
input[type=number] {
43-
background-color: #2e2e40;
44-
color: #e9e9e9;
45-
border: 1px solid #444;
46-
border-radius: 5px;
47-
padding: 6px 8px;
48-
font-size: 0.9rem;
49-
}
50-
textarea {
51-
background-color: #2e2e40;
52-
color: #e9e9e9;
53-
border: 1px solid #444;
54-
border-radius: 5px;
55-
padding: 10px;
56-
font-family: monospace;
57-
flex: 1;
58-
resize: none;
59-
max-height: 300px;
60-
overflow-y: auto;
61-
}
62-
.preview-box {
63-
background-color: #2e2e40;
64-
color: #e9e9e9;
65-
border: 1px solid #444;
66-
border-radius: 5px;
67-
padding: 10px;
68-
font-family: monospace;
69-
flex: 1;
70-
white-space: pre-wrap;
71-
overflow-y: auto;
72-
}
73-
.copy-icon {
74-
align-self: flex-end;
75-
margin-bottom: 5px;
76-
cursor: pointer;
77-
font-size: 1rem;
78-
background: #3e3e5c;
79-
padding: 5px 10px;
80-
border-radius: 4px;
81-
}
82-
.copy-icon:hover {
83-
background: #4a90e2;
1+
<?php
2+
header('Content-Type: application/json; charset=UTF-8');
3+
header('Cache-Control: no-cache, no-store, must-revalidate');
4+
5+
ini_set('display_errors', 0);
6+
ini_set('display_startup_errors', 0);
7+
error_reporting(0);
8+
9+
function cleanOutput($data) {
10+
return json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
11+
}
12+
13+
try {
14+
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
15+
$input = file_get_contents('php://input');
16+
$data = json_decode($input, true) ?: $_POST;
17+
18+
if (empty($data['script'])) {
19+
throw new Exception('Script text is required');
20+
}
21+
22+
$result = processScript(
23+
$data['script'],
24+
floatval($data['wpm'] ?? 3),
25+
floatval($data['min_time'] ?? 1.5),
26+
floatval($data['punctuation_pad'] ?? 0.5),
27+
intval($data['max_length'] ?? 450)
28+
);
29+
30+
echo cleanOutput([
31+
'success' => true,
32+
'preview' => $result['srt'],
33+
'filename' => $result['filename']
34+
]);
35+
exit;
36+
} elseif ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['download'])) {
37+
handleDownload($_GET['download']);
38+
exit;
8439
}
85-
.bottom-bar {
86-
display: flex;
87-
justify-content: space-between;
88-
gap: 10px;
89-
margin-top: 10px;
40+
41+
throw new Exception('Invalid request method');
42+
43+
} catch (Exception $e) {
44+
http_response_code(400);
45+
echo cleanOutput([
46+
'success' => false,
47+
'error' => $e->getMessage()
48+
]);
49+
exit;
50+
}
51+
52+
function processScript($script, $wpm, $minTime, $punctuationPad, $maxLength) {
53+
if ($wpm < 0.5 || $wpm > 10) throw new Exception('Invalid words per second value');
54+
if ($minTime < 0.5 || $minTime > 10) throw new Exception('Invalid minimum duration');
55+
if ($punctuationPad < 0 || $punctuationPad > 2) throw new Exception('Invalid punctuation padding');
56+
57+
$chunks = splitIntoChunks($script, $maxLength);
58+
if (empty($chunks)) throw new Exception('No valid chunks found in script');
59+
60+
$srt = "";
61+
$index = 1;
62+
$currentTime = 0.0;
63+
64+
foreach ($chunks as $line) {
65+
$wordCount = str_word_count($line);
66+
$duration = max($minTime, $wordCount / $wpm);
67+
if (preg_match('/[.!?]$/', $line)) {
68+
$duration += $punctuationPad;
69+
}
70+
71+
$start = formatTime($currentTime);
72+
$end = formatTime($currentTime + $duration);
73+
74+
$srt .= "$index\n$start --> $end\n$line\n\n";
75+
$currentTime += $duration;
76+
$index++;
9077
}
91-
button {
92-
background-color: #4a90e2;
93-
color: white;
94-
padding: 8px 16px;
95-
border: none;
96-
border-radius: 5px;
97-
font-weight: bold;
98-
cursor: pointer;
78+
79+
$filename = 'generated_' . time() . '_' . md5($srt) . '.srt';
80+
$dir = __DIR__ . '/srt_files';
81+
if (!is_dir($dir) && !mkdir($dir, 0755, true)) {
82+
throw new Exception('Could not create output directory');
9983
}
100-
button:disabled {
101-
background-color: #555;
84+
85+
$path = "$dir/$filename";
86+
if (file_put_contents($path, $srt) === false) {
87+
throw new Exception('Could not save SRT file');
10288
}
103-
.status {
104-
margin-top: 5px;
105-
font-size: 0.9rem;
106-
color: #99ffbb;
107-
display: none;
89+
90+
return ['srt' => $srt, 'filename' => $filename];
91+
}
92+
93+
function splitIntoChunks($text, $maxLength = 450) {
94+
$chunks = [];
95+
$buffer = '';
96+
$sentences = preg_split('/(?<=[.!?])\s+/', $text);
97+
98+
foreach ($sentences as $sentence) {
99+
if (strlen($buffer . ' ' . $sentence) <= $maxLength) {
100+
$buffer .= ' ' . $sentence;
101+
} else {
102+
if (trim($buffer)) $chunks[] = trim($buffer);
103+
$buffer = $sentence;
104+
}
108105
}
109-
</style>
110-
<link rel="icon" type="image/x-icon" href="favicon.png">
111-
</head>
112-
<body>
113-
<h2>🎬 CapCut SRT Generator<br>
114-
<small style="font-size: 0.8rem; font-weight: normal;font-family: 'Poppins', sans-serif;">
115-
Convert your script to SRT format for easy subtitles in CapCut
116-
</small>
117-
</h2>
118-
<div class="container">
119-
<div class="left">
120-
<div class="controls">
121-
<label><small>Words per Second:</small>
122-
<input type="number" id="wpm" value="3" step="0.1" min="0.5">
123-
</label>
124-
<label><small>Minimum Duration (s):</small>
125-
<input type="number" id="min_time" value="1.5" step="0.1" min="0.5">
126-
</label>
127-
<label><small>Pause Padding (s):</small>
128-
<input type="number" id="punctuation_pad" value="0.5" step="0.1" min="0">
129-
</label>
130-
</div>
131-
<textarea id="script" placeholder="Paste your script here..."></textarea>
132-
<div class="bottom-bar">
133-
<div>
134-
<button id="processBtn">⚙️ Process</button>
135-
<button onclick="location.reload()">🔄 New Script</button>
136-
</div>
137-
<div>
138-
<button id="copyBtn" disabled>📋 Copy All</button>
139-
<button id="downloadBtn" disabled>⬇️ Download</button>
140-
</div>
141-
</div>
142-
<div id="copyStatus" class="status">✅ Copied!</div>
143-
</div>
144-
<div class="right">
145-
<h3>🔍 SRT Preview</h3>
146-
<div class="copy-icon" onclick="copyToClipboard()">📋 Copy Preview</div>
147-
<div id="previewBox" class="preview-box">(Click "Process" to preview)</div>
148-
</div>
149-
</div>
150-
<script>
151-
const previewBox = document.getElementById("previewBox");
152-
const processBtn = document.getElementById("processBtn");
153-
const downloadBtn = document.getElementById("downloadBtn");
154-
const copyBtn = document.getElementById("copyBtn");
155-
const copyStatus = document.getElementById("copyStatus");
156-
let latestFile = "";function processScript() { const script = document.getElementById("script").value; const wpm = document.getElementById("wpm").value; const min_time = document.getElementById("min_time").value; const punctuation_pad = document.getElementById("punctuation_pad").value;
157-
158-
const formData = new FormData();
159-
formData.append("script", script);
160-
formData.append("wpm", wpm);
161-
formData.append("min_time", min_time);
162-
formData.append("punctuation_pad", punctuation_pad);
163-
formData.append("preview_only", "1");
164-
165-
fetch("generate_srt.php", {
166-
method: "POST",
167-
body: formData
168-
})
169-
.then(response => response.json())
170-
.then(data => {
171-
previewBox.textContent = data.preview;
172-
latestFile = data.filename;
173-
downloadBtn.disabled = false;
174-
copyBtn.disabled = false;
175-
});
106+
if (trim($buffer)) $chunks[] = trim($buffer);
107+
return $chunks;
108+
}
176109

110+
function formatTime($seconds) {
111+
$h = floor($seconds / 3600);
112+
$m = floor(($seconds % 3600) / 60);
113+
$s = floor($seconds % 60);
114+
$ms = ($seconds - floor($seconds)) * 1000;
115+
return sprintf("%02d:%02d:%02d,%03d", $h, $m, $s, $ms);
177116
}
178117

179-
function copyToClipboard() { navigator.clipboard.writeText(previewBox.textContent).then(() => { copyStatus.style.display = "inline"; setTimeout(() => { copyStatus.style.display = "none"; }, 1500); }); }
118+
function handleDownload($filename) {
119+
$dir = __DIR__ . '/srt_files';
120+
$path = "$dir/" . basename($filename);
180121

181-
processBtn.addEventListener("click", processScript); copyBtn.addEventListener("click", copyToClipboard); downloadBtn.addEventListener("click", () => { if (latestFile) { window.location.href = "generate_srt.php?download=" + latestFile; } }); </script>
122+
if (!file_exists($path)) {
123+
throw new Exception('File not found');
124+
}
182125

183-
</body>
184-
</html>
126+
header('Content-Type: text/plain');
127+
header('Content-Disposition: attachment; filename="' . basename($path) . '"');
128+
header('Content-Length: ' . filesize($path));
129+
readfile($path);
130+
exit;
131+
}

0 commit comments

Comments
 (0)