1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Captioning; |
4
|
|
|
|
5
|
|
|
abstract class File implements FileInterface, \Countable |
6
|
|
|
{ |
7
|
|
|
const DEFAULT_ENCODING = 'UTF-8'; |
8
|
|
|
|
9
|
|
|
const UNIX_LINE_ENDING = "\n"; |
10
|
|
|
const MAC_LINE_ENDING = "\r"; |
11
|
|
|
const WINDOWS_LINE_ENDING = "\r\n"; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* @var array |
15
|
|
|
*/ |
16
|
|
|
protected $cues; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @var string |
20
|
|
|
*/ |
21
|
|
|
protected $filename; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @var string |
25
|
|
|
*/ |
26
|
|
|
protected $encoding = self::DEFAULT_ENCODING; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var bool |
30
|
|
|
*/ |
31
|
|
|
protected $useIconv; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
protected $lineEnding; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $fileContent; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var array |
45
|
|
|
*/ |
46
|
|
|
protected $stats; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* File constructor. |
50
|
|
|
* @param null $_filename |
51
|
|
|
* @param null $_encoding |
52
|
|
|
* @param bool|false $_useIconv |
53
|
|
|
*/ |
54
|
|
|
public function __construct($_filename = null, $_encoding = null, $_useIconv = false) |
55
|
|
|
{ |
56
|
|
|
$this->lineEnding = self::UNIX_LINE_ENDING; |
57
|
|
|
|
58
|
|
|
if ($_filename !== null) { |
59
|
|
|
$this->setFilename($_filename); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
if ($_encoding !== null) { |
63
|
|
|
$this->setEncoding($_encoding); |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
$this->useIconv = $_useIconv; |
67
|
|
|
|
68
|
|
|
if ($this->getFilename() !== null) { |
69
|
|
|
$this->loadFromFile(); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
$this->stats = array( |
73
|
|
|
'tooSlow' => 0, |
74
|
|
|
'slowAcceptable' => 0, |
75
|
|
|
'aBitSlow' => 0, |
76
|
|
|
'goodSlow' => 0, |
77
|
|
|
'perfect' => 0, |
78
|
|
|
'goodFast' => 0, |
79
|
|
|
'aBitFast' => 0, |
80
|
|
|
'fastAcceptable' => 0, |
81
|
|
|
'tooFast' => 0 |
82
|
|
|
); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @param string $_filename The filename |
87
|
|
|
* @return $this |
88
|
|
|
*/ |
89
|
|
|
public function setFilename($_filename) |
90
|
|
|
{ |
91
|
|
|
$this->filename = file_exists($_filename) ? $_filename : null; |
92
|
|
|
|
93
|
|
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* @param string $_encoding |
98
|
|
|
* @return $this |
99
|
|
|
*/ |
100
|
|
|
public function setEncoding($_encoding) |
101
|
|
|
{ |
102
|
|
|
$this->encoding = $_encoding; |
103
|
|
|
|
104
|
|
|
return $this; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param bool $_useIconv |
109
|
|
|
* @return $this |
110
|
|
|
*/ |
111
|
|
|
public function setUseIconv($_useIconv) |
112
|
|
|
{ |
113
|
|
|
$this->useIconv = $_useIconv; |
114
|
|
|
|
115
|
|
|
return $this; |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @param string $_lineEnding |
120
|
|
|
*/ |
121
|
|
|
public function setLineEnding($_lineEnding) |
122
|
|
|
{ |
123
|
|
|
$lineEndings = array( |
124
|
|
|
self::UNIX_LINE_ENDING, |
125
|
|
|
self::MAC_LINE_ENDING, |
126
|
|
|
self::WINDOWS_LINE_ENDING |
127
|
|
|
); |
128
|
|
|
if (!in_array($_lineEnding, $lineEndings)) { |
129
|
|
|
return; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
$this->lineEnding = $_lineEnding; |
133
|
|
|
|
134
|
|
|
if ($this->getCuesCount() > 0) { |
135
|
|
|
foreach ($this->cues as $cue) { |
136
|
|
|
$cue->setLineEnding($this->lineEnding); |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* @return string |
143
|
|
|
*/ |
144
|
|
|
public function getFilename() |
145
|
|
|
{ |
146
|
|
|
return $this->filename; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @return string |
151
|
|
|
*/ |
152
|
|
|
public function getFileContent() |
153
|
|
|
{ |
154
|
|
|
return $this->fileContent; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @return string |
159
|
|
|
*/ |
160
|
|
|
public function getEncoding() |
161
|
|
|
{ |
162
|
|
|
return $this->encoding; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @return bool|false |
167
|
|
|
*/ |
168
|
|
|
public function getUseIconv() |
169
|
|
|
{ |
170
|
|
|
return $this->useIconv; |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* @param integer $_index |
175
|
|
|
* @return Cue|null |
176
|
|
|
*/ |
177
|
|
|
public function getCue($_index) |
178
|
|
|
{ |
179
|
|
|
return isset($this->cues[$_index]) ? $this->cues[$_index] : null; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* @return Cue|null |
184
|
|
|
*/ |
185
|
|
|
public function getFirstCue() |
186
|
|
|
{ |
187
|
|
|
return isset($this->cues[0]) ? $this->cues[0] : null; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return Cue|null |
192
|
|
|
*/ |
193
|
|
|
public function getLastCue() |
194
|
|
|
{ |
195
|
|
|
$count = count($this->cues); |
196
|
|
|
|
197
|
|
|
return ($count > 0) ? $this->cues[$count - 1] : null; |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @return array |
202
|
|
|
*/ |
203
|
|
|
public function getCues() |
204
|
|
|
{ |
205
|
|
|
return $this->cues; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @return integer |
210
|
|
|
*/ |
211
|
|
|
public function getCuesCount() |
212
|
|
|
{ |
213
|
|
|
return count($this->cues); |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param null $_filename |
218
|
|
|
* @return $this |
219
|
|
|
* @throws \Exception |
220
|
|
|
*/ |
221
|
|
|
public function loadFromFile($_filename = null) |
222
|
|
|
{ |
223
|
|
|
if ($_filename === null) { |
224
|
|
|
$_filename = $this->filename; |
225
|
|
|
} else { |
226
|
|
|
$this->filename = $_filename; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
if (!file_exists($_filename)) { |
230
|
|
|
throw new \Exception('File "'.$_filename.'" not found.'); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
if (!($content = file_get_contents($this->filename))) { |
234
|
|
|
throw new \Exception('Could not read file content ('.$_filename.').'); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
$this->loadFromString($content); |
238
|
|
|
|
239
|
|
|
return $this; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param string $_str |
244
|
|
|
* @return $this |
245
|
|
|
*/ |
246
|
|
|
public function loadFromString($_str) |
247
|
|
|
{ |
248
|
|
|
// Clear cues from previous runs |
249
|
|
|
$this->cues = array(); |
250
|
|
|
$this->fileContent = $_str; |
251
|
|
|
|
252
|
|
|
$this->encode(); |
253
|
|
|
$this->parse(); |
254
|
|
|
|
255
|
|
|
return $this; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Searches a word/expression and returns ids of the matched entries |
260
|
|
|
* |
261
|
|
|
* @param string $_word |
262
|
|
|
* @param boolean $_case_sensitive |
263
|
|
|
* @param boolean $_strict |
264
|
|
|
* @return array containing ids of entries |
265
|
|
|
*/ |
266
|
|
|
public function search($_word, $_case_sensitive = false, $_strict = false) |
267
|
|
|
{ |
268
|
|
|
$list = array(); |
269
|
|
|
$pattern = preg_quote($_word, '#'); |
270
|
|
|
|
271
|
|
|
$pattern = str_replace(' ', '( |\r\n|\r|\n)', $pattern); |
272
|
|
|
|
273
|
|
|
if ($_strict) { |
274
|
|
|
$pattern = '($| |\r\n|\r|\n|\?|\!|\.|, )'.$pattern.'(^| |\r\n|\r|\n|\?|\!|\.|,)'; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
$pattern = '#'.$pattern.'#'; |
278
|
|
|
|
279
|
|
|
if (!$_case_sensitive) { |
280
|
|
|
$pattern .= 'i'; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
$i = 0; |
284
|
|
|
foreach ($this->cues as $cue) { |
285
|
|
|
if (preg_match($pattern, $cue->getText())) { |
286
|
|
|
$list[] = $i; |
287
|
|
|
} |
288
|
|
|
$i++; |
289
|
|
|
} |
290
|
|
|
|
291
|
|
|
return (count($list) > 0) ? $list : -1; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
public function getCueFromStart($_start) |
295
|
|
|
{ |
296
|
|
|
$cueClass = self::getExpectedCueClass($this); |
297
|
|
|
$start = is_int($_start) ? $_start : $cueClass::tc2ms($_start); |
298
|
|
|
|
299
|
|
|
$prev_stop = 0; |
300
|
|
|
$i = 0; |
301
|
|
|
foreach ($this->cues as $cue) { |
302
|
|
|
if (($start > $prev_stop && $start < $cue->getStart()) || ($start >= $cue->getStart() && $start < $cue->getStop())) { |
303
|
|
|
break; |
304
|
|
|
} |
305
|
|
|
$prev_stop = $cue->getStop(); |
306
|
|
|
$i++; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
return $i; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* Add a cue |
314
|
|
|
* |
315
|
|
|
* @param mixed $_mixed An cue instance or a string representing the text |
316
|
|
|
* @param string $_start A timecode |
317
|
|
|
* @param string $_stop A timecode |
318
|
|
|
* |
319
|
|
|
* @throws \Exception |
320
|
|
|
* @return File |
321
|
|
|
*/ |
322
|
|
|
public function addCue($_mixed, $_start = null, $_stop = null) |
323
|
|
|
{ |
324
|
|
|
$fileFormat = self::getFormat($this); |
325
|
|
|
|
326
|
|
|
// if $_mixed is a Cue |
327
|
|
|
if (is_object($_mixed) && class_exists(get_class($_mixed)) && class_exists(__NAMESPACE__.'\Cue') && is_subclass_of($_mixed, __NAMESPACE__.'\Cue')) { |
328
|
|
|
$cueFormat = Cue::getFormat($_mixed); |
329
|
|
|
if ($cueFormat !== $fileFormat) { |
330
|
|
|
throw new \Exception("Can't add a $cueFormat cue in a $fileFormat file."); |
331
|
|
|
} |
332
|
|
|
$_mixed->setLineEnding($this->lineEnding); |
333
|
|
|
$this->cues[] = $_mixed; |
334
|
|
|
} else { |
335
|
|
|
$cueClass = self::getExpectedCueClass($this); |
336
|
|
|
$cue = new $cueClass($_start, $_stop, $_mixed); |
337
|
|
|
$cue->setLineEnding($this->lineEnding); |
338
|
|
|
$this->cues[] = $cue; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
return $this; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Removes a cue |
346
|
|
|
* |
347
|
|
|
* @param int $_index |
348
|
|
|
* @return File |
349
|
|
|
*/ |
350
|
|
|
public function removeCue($_index) |
351
|
|
|
{ |
352
|
|
|
if (isset($this->cues[$_index])) { |
353
|
|
|
unset($this->cues[$_index]); |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
return $this; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Sorts cues |
361
|
|
|
*/ |
362
|
|
|
public function sortCues() |
363
|
|
|
{ |
364
|
|
|
if (count($this->cues) === 0) { |
365
|
|
|
return $this; |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
$tmp = array(); |
369
|
|
|
|
370
|
|
|
$count = 0; // useful if 2 cues start at the same time code |
371
|
|
|
foreach ($this->cues as $cue) { |
372
|
|
|
$tmp[$cue->getStartMS().'.'.$count] = $cue; |
373
|
|
|
$count++; |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
ksort($tmp); |
377
|
|
|
|
378
|
|
|
$this->cues = array(); |
379
|
|
|
foreach ($tmp as $cue) { |
380
|
|
|
$this->cues[] = $cue; |
381
|
|
|
} |
382
|
|
|
|
383
|
|
|
return $this; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Converts timecodes based on the specified FPS ratio |
388
|
|
|
* |
389
|
|
|
* @param float $_old_fps |
390
|
|
|
* @param float $_new_fps |
391
|
|
|
* @return File |
392
|
|
|
*/ |
393
|
|
|
public function changeFPS($_old_fps, $_new_fps) |
394
|
|
|
{ |
395
|
|
|
$cuesCount = $this->getCuesCount(); |
396
|
|
|
for ($i = 0; $i < $cuesCount; $i++) { |
397
|
|
|
$cue = $this->getCue($i); |
398
|
|
|
|
399
|
|
|
$old_start = $cue->getStart(); |
400
|
|
|
$old_stop = $cue->getStop(); |
401
|
|
|
|
402
|
|
|
$new_start = $old_start * ($_new_fps / $_old_fps); |
403
|
|
|
$new_stop = $old_stop * ($_new_fps / $_old_fps); |
404
|
|
|
|
405
|
|
|
$cue->setStart($new_start); |
406
|
|
|
$cue->setStop($new_stop); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
return $this; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* @param FileInterface $_file |
414
|
|
|
* @return $this |
415
|
|
|
* @throws \Exception |
416
|
|
|
*/ |
417
|
|
|
public function merge(FileInterface $_file) |
418
|
|
|
{ |
419
|
|
|
if (!is_a($_file, get_class($this))) { |
420
|
|
|
throw new \Exception('Can\'t merge! Wrong format: '.$this->getFormat($_file)); |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
$this->cues = array_merge($this->cues, $_file->getCues()); |
424
|
|
|
$this->sortCues(); |
425
|
|
|
|
426
|
|
|
return $this; |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
/** |
430
|
|
|
* Shifts a range of subtitles a specified amount of time. |
431
|
|
|
* |
432
|
|
|
* @param int $_time The time to use (ms), which can be positive or negative. |
433
|
|
|
* @param int $_startIndex The subtitle index the range begins with. |
434
|
|
|
* @param int $_endIndex The subtitle index the range ends with. |
435
|
|
|
*/ |
436
|
|
|
public function shift($_time, $_startIndex = null, $_endIndex = null) |
437
|
|
|
{ |
438
|
|
|
if (!is_int($_time)) { |
439
|
|
|
return false; |
440
|
|
|
} |
441
|
|
|
if ($_time == 0) { |
442
|
|
|
return true; |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
if (null === $_startIndex) { |
446
|
|
|
$_startIndex = 0; |
447
|
|
|
} |
448
|
|
|
if (null === $_endIndex) { |
449
|
|
|
$_endIndex = $this->getCuesCount() - 1; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
$startCue = $this->getCue($_startIndex); |
453
|
|
|
$endCue = $this->getCue($_endIndex); |
454
|
|
|
|
455
|
|
|
//check subtitles do exist |
456
|
|
|
if (!$startCue || !$endCue) { |
457
|
|
|
return false; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
for ($i = $_startIndex; $i <= $_endIndex; $i++) { |
461
|
|
|
$cue = $this->getCue($i); |
462
|
|
|
$cue->shift($_time); |
463
|
|
|
} |
464
|
|
|
|
465
|
|
|
return true; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Auto syncs a range of subtitles given their first and last correct times. |
470
|
|
|
* The subtitles are first shifted to the first subtitle's correct time, and then proportionally |
471
|
|
|
* adjusted using the last subtitle's correct time. |
472
|
|
|
* |
473
|
|
|
* Based on gnome-subtitles (https://git.gnome.org/browse/gnome-subtitles/) |
474
|
|
|
* |
475
|
|
|
* @param int $_startIndex The subtitle index to start the adjustment with. |
476
|
|
|
* @param int $_startTime The correct start time for the first subtitle. |
477
|
|
|
* @param int $_endIndex The subtitle index to end the adjustment with. |
478
|
|
|
* @param int $_endTime The correct start time for the last subtitle. |
479
|
|
|
* @param bool $_syncLast Whether to sync the last subtitle. |
480
|
|
|
* @return bool Whether the subtitles could be adjusted |
481
|
|
|
*/ |
482
|
|
|
public function sync($_startIndex, $_startTime, $_endIndex, $_endTime, $_syncLast = true) |
483
|
|
|
{ |
484
|
|
|
//set first and last subtitles index |
485
|
|
|
if (!$_startIndex) { |
486
|
|
|
$_startIndex = 0; |
487
|
|
|
} |
488
|
|
|
if (!$_endIndex) { |
489
|
|
|
$_endIndex = $this->getCuesCount() - 1; |
490
|
|
|
} |
491
|
|
|
if (!$_syncLast) { |
492
|
|
|
$_endIndex--; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
//check subtitles do exist |
496
|
|
|
$startSubtitle = $this->getCue($_startIndex); |
497
|
|
|
$endSubtitle = $this->getCue($_endIndex); |
498
|
|
|
if (!$startSubtitle || !$endSubtitle || ($_startTime >= $_endTime)) { |
499
|
|
|
return false; |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
$shift = $_startTime - $startSubtitle->getStartMS(); |
503
|
|
|
$factor = ($_endTime - $_startTime) / ($endSubtitle->getStartMS() - $startSubtitle->getStartMS()); |
504
|
|
|
|
505
|
|
|
/* Shift subtitles to the start point */ |
506
|
|
|
if ($shift) { |
507
|
|
|
$this->shift($shift, $_startIndex, $_endIndex); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
/* Sync timings with proportion */ |
511
|
|
|
for ($index = $_startIndex; $index <= $_endIndex; $index++) { |
512
|
|
|
$cue = $this->getCue($index); |
513
|
|
|
$cue->scale($_startTime, $factor); |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
return true; |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
/** |
520
|
|
|
* @return $this |
521
|
|
|
*/ |
522
|
|
|
public function build() |
523
|
|
|
{ |
524
|
|
|
$this->buildPart(0, $this->getCuesCount() - 1); |
525
|
|
|
|
526
|
|
|
return $this; |
527
|
|
|
} |
528
|
|
|
|
529
|
|
|
/** |
530
|
|
|
* Saves the file |
531
|
|
|
* |
532
|
|
|
* @param string $filename |
533
|
|
|
* @param bool $writeBOM |
534
|
|
|
*/ |
535
|
|
|
public function save($filename = null, $writeBOM = false) |
536
|
|
|
{ |
537
|
|
|
if ($filename === null) { |
538
|
|
|
$filename = $this->filename; |
539
|
|
|
} |
540
|
|
|
|
541
|
|
|
if (trim($this->fileContent) == '') { |
542
|
|
|
$this->build(); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
$file_content = $this->fileContent; |
546
|
|
|
if (strtolower($this->encoding) != 'utf-8') { |
547
|
|
|
if ($this->useIconv) { |
548
|
|
|
$file_content = iconv('UTF-8', $this->encoding, $file_content); |
549
|
|
|
} else { |
550
|
|
|
$file_content = mb_convert_encoding($file_content, $this->encoding, 'UTF-8'); |
551
|
|
|
} |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
if ($writeBOM) { |
555
|
|
|
$file_content = "\xef\xbb\xbf".$file_content; |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
$res = file_put_contents($filename, $file_content); |
559
|
|
|
if (!$res) { |
560
|
|
|
throw new \Exception('Unable to save the file.'); |
561
|
|
|
} |
562
|
|
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* Computes reading speed statistics |
566
|
|
|
*/ |
567
|
|
|
public function getStats() |
568
|
|
|
{ |
569
|
|
|
$this->stats = array( |
570
|
|
|
'tooSlow' => 0, |
571
|
|
|
'slowAcceptable' => 0, |
572
|
|
|
'aBitSlow' => 0, |
573
|
|
|
'goodSlow' => 0, |
574
|
|
|
'perfect' => 0, |
575
|
|
|
'goodFast' => 0, |
576
|
|
|
'aBitFast' => 0, |
577
|
|
|
'fastAcceptable' => 0, |
578
|
|
|
'tooFast' => 0 |
579
|
|
|
); |
580
|
|
|
|
581
|
|
|
$cuesCount = $this->getCuesCount(); |
582
|
|
|
for ($i = 0; $i < $cuesCount; $i++) { |
583
|
|
|
$rs = $this->getCue($i)->getReadingSpeed(); |
584
|
|
|
|
585
|
|
|
if ($rs < 5) { |
586
|
|
|
$this->stats['tooSlow']++; |
587
|
|
|
} elseif ($rs < 10) { |
588
|
|
|
$this->stats['slowAcceptable']++; |
589
|
|
|
} elseif ($rs < 13) { |
590
|
|
|
$this->stats['aBitSlow']++; |
591
|
|
|
} elseif ($rs < 15) { |
592
|
|
|
$this->stats['goodSlow']++; |
593
|
|
|
} elseif ($rs < 23) { |
594
|
|
|
$this->stats['perfect']++; |
595
|
|
|
} elseif ($rs < 27) { |
596
|
|
|
$this->stats['goodFast']++; |
597
|
|
|
} elseif ($rs < 31) { |
598
|
|
|
$this->stats['aBitFast']++; |
599
|
|
|
} elseif ($rs < 35) { |
600
|
|
|
$this->stats['fastAcceptable']++; |
601
|
|
|
} else { |
602
|
|
|
$this->stats['tooFast']++; |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
return $this->stats; |
607
|
|
|
} |
608
|
|
|
|
609
|
|
|
/** |
610
|
|
|
* @param $_file |
611
|
|
|
* @return mixed |
612
|
|
|
*/ |
613
|
|
View Code Duplication |
public static function getFormat(FileInterface $_file) |
|
|
|
|
614
|
|
|
{ |
615
|
|
|
if (!is_subclass_of($_file, __NAMESPACE__.'\File')) { |
616
|
|
|
throw new \InvalidArgumentException('Expected subclass of File'); |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
$fullNamespace = explode('\\', get_class($_file)); |
620
|
|
|
$tmp = explode('File', end($fullNamespace)); |
621
|
|
|
|
622
|
|
|
return $tmp[0]; |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
/** |
626
|
|
|
* @param FileInterface $_file |
627
|
|
|
* @param bool|true $_full_namespace |
628
|
|
|
* @return string |
629
|
|
|
*/ |
630
|
|
|
public static function getExpectedCueClass(FileInterface $_file, $_full_namespace = true) |
631
|
|
|
{ |
632
|
|
|
$format = self::getFormat($_file).'Cue'; |
633
|
|
|
|
634
|
|
|
if ($_full_namespace) { |
635
|
|
|
$tmp = explode('\\', get_class($_file)); |
636
|
|
|
array_pop($tmp); |
637
|
|
|
$format = implode('\\', $tmp).'\\'.$format; |
638
|
|
|
} |
639
|
|
|
|
640
|
|
|
return $format; |
641
|
|
|
} |
642
|
|
|
|
643
|
|
|
/** |
644
|
|
|
* @param string $_output_format |
645
|
|
|
* @return mixed |
646
|
|
|
*/ |
647
|
|
|
public function convertTo($_output_format) |
648
|
|
|
{ |
649
|
|
|
$fileFormat = self::getFormat($this); |
650
|
|
|
$method = strtolower($fileFormat).'2'.strtolower(rtrim($_output_format, 'File')); |
651
|
|
|
|
652
|
|
|
if (method_exists(new Converter(), $method)) { |
653
|
|
|
return Converter::$method($this); |
654
|
|
|
} |
655
|
|
|
return Converter::defaultConverter($this, $_output_format); |
656
|
|
|
} |
657
|
|
|
|
658
|
|
|
/** |
659
|
|
|
* Encode file content |
660
|
|
|
*/ |
661
|
|
|
protected function encode() |
662
|
|
|
{ |
663
|
|
|
if ($this->useIconv) { |
664
|
|
|
$this->fileContent = iconv($this->encoding, 'UTF-8', $this->fileContent); |
665
|
|
|
} else { |
666
|
|
|
$this->fileContent = mb_convert_encoding($this->fileContent, 'UTF-8', $this->encoding); |
667
|
|
|
} |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
/** |
671
|
|
|
* @return array |
672
|
|
|
* @throws \Exception |
673
|
|
|
*/ |
674
|
|
|
protected function getFileContentAsArray() |
675
|
|
|
{ |
676
|
|
|
if (empty($this->fileContent)) { |
677
|
|
|
$this->loadFromFile($this->filename); |
678
|
|
|
} |
679
|
|
|
$fileContent = str_replace( // So we change line endings to one format |
680
|
|
|
array( |
681
|
|
|
self::WINDOWS_LINE_ENDING, |
682
|
|
|
self::MAC_LINE_ENDING, |
683
|
|
|
), |
684
|
|
|
self::UNIX_LINE_ENDING, |
685
|
|
|
$this->fileContent |
686
|
|
|
); |
687
|
|
|
$fileContentArray = explode(self::UNIX_LINE_ENDING, $fileContent); // Create array from file content |
688
|
|
|
|
689
|
|
|
return $fileContentArray; |
690
|
|
|
} |
691
|
|
|
|
692
|
|
|
/** |
693
|
|
|
* @param array $array |
694
|
|
|
* @return mixed |
695
|
|
|
*/ |
696
|
|
|
protected function getNextValueFromArray(array &$array) |
697
|
|
|
{ |
698
|
|
|
$element = each($array); |
699
|
|
|
if (is_array($element)) { |
700
|
|
|
return $element['value']; |
701
|
|
|
} |
702
|
|
|
return false; |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* @return int |
707
|
|
|
*/ |
708
|
|
|
public function count() |
709
|
|
|
{ |
710
|
|
|
return $this->getCuesCount(); |
711
|
|
|
} |
712
|
|
|
} |
713
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.