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