O script a seguir usa duas entradas sourceFile="${1}"
e arquivos trimFile="${2}"
. O $sourceFile
é um arquivo de vídeo que deve ser cortado em vários novos arquivos com base nos dados do arquivo $trimFile
. O $trimFile
é um arquivo de texto com a seguinte estrutura (arquivo real relacionado a este problema):
00:49:30,00:53:00 DescriptionA
03:33:30,03:38:40 DescriptionB
04:54:32,04:55:37 DescriptionC
O $trimFile
usa vírgulas e espaços como delimitadores no script abaixo. Embora bastante óbvio, cada linha representa um clipe a ser criado a partir do arquivo $sourceFile
. Além disso, o primeiro campo é a hora inicial do clipe a ser cortado, o segundo campo é a hora final do clipe a ser cortado e o último campo é uma descrição do clipe.
Meu problema não é com o corte do arquivo $sourceFile
. Estou tentando renomear os novos clipes com data e hora em relação à data dos arquivos de vídeo originais. Cada um $sourceFile
e $trimFile
é nomeado como o exemplo a seguir (que são os nomes de arquivo reais que estou usando atualmente): 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).mp4
e 2017-05-15_14-17-22 (2017-05-16 00-45-41.151674100 -0400) (HEVC27).txt
, respectivamente. Novamente, embora bastante óbvio, os componentes da data são Year: 2017
, Month: 05
, Day: 16
, Hour: 14
, Minute: 17
e Second:22
(ignore a data entre parênteses, pois é uma referência antiga com a data ajustada errada do UTC).
Deve ficar evidente no script abaixo como a data/hora no arquivo é extraída, bem como a data/hora dentro do arquivo é $trimFile
extraída. Para ilustrar o problema, preciso mostrá-lo com e sem algumas linhas comentadas. Aqui está com algumas linhas comentadas (fará sentido quando eu discutir isso a seguir).
01: sourceFile="${1}"
02: trimFile="${2}"
03:
04: IFS=$'\n'
05:
06: dos2unix "$trimFile"
07: numberOfSegments=`cat "$trimFile" | wc -l`
08: numberOfSegments=$((numberOfSegments + 1))
09: extension=`echo "$sourceFile" | awk -F'.' '{print $NF}'`
10:
11: base=`echo "$sourceFile" | sed -e "s|.$extension||g"`
12:
13: #~/bin/ffmpeg -i "${sourceFile}" -c:v copy "/dev/shm/$base.${extension}"
14: #sourceFile="/dev/shm/$base.${extension}"
15:
16: # File date/time information
17: origYear="${sourceFile:0:4}"
18: origMonth="${sourceFile:5:2}"
19: origDay="${sourceFile:8:2}"
20: origHour="${sourceFile:11:2}"
21: origMinute="${sourceFile:14:2}"
22: origSecond="${sourceFile:17:2}"
23:
24: origDate="${origYear}-${origMonth}-${origDay} ${origHour}:${origMinute}:${origSecond}"
25:
26: for (( i=1;i<="$numberOfSegments";i++ ))
27: do
28:
29: lineEntry=`cat "$trimFile" | head -"$i" | tail -1`
30:
31: startHour=`echo "$lineEntry" | awk -F'[:,]' '{print $1}'`
32: startMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $2}'`
33: startSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $3}'`
34:
35: endHour=`echo "$lineEntry" | awk -F'[:,]' '{print $4}'`
36: endMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $5}'`
37: endSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $6}'`
38:
39: description=`echo "$lineEntry" | awk -F'[:, ]' '{print $7}'`
40:
41: beginSeconds=`awk "BEGIN {print ($startHour*3600+$startMinute*60+$startSecond)}"`
42: stopSeconds=`awk "BEGIN {print ($endHour*3600+$endMinute*60+$endSecond)}"`
43: duration=`awk "BEGIN {print $stopSeconds-$beginSeconds}"`
44:
45: newDate=$(date -d "@$(( $(date -d "${origDate}" +%s) + ${beginSeconds}))" +'%Y-%m-%d_%H-%M-%S')
46:
47: new="${newDate}_${description}"
48: echo "${lineEntry}"
49: echo "${origDate}"
50: echo "${beginSeconds}"
51: echo "${new}"
52: echo ""
53:
54: #~/bin/ffmpeg -n -vsync drop -fflags +genpts -i "$sourceFile" -ss "$beginSeconds" -t "$duration" -c:v libx265 -crf 27 -preset slow "$new.mkv"
55:
56:
57: done
58:
59: #rm "/dev/shm/${base}.${extension}"
Quando executo o script neste formulário, obtenho a seguinte saída correspondente às linhas 48-52 do script:
01: 00:49:30,00:53:00 DescriptionA
02: 2017-05-15 14:17:22
03: 2970
04: 2017-05-15_15-06-52_DescriptionA
05:
06: 03:33:30,03:38:40 DescriptionB
07: 2017-05-15 14:17:22
08: 12810
09: 2017-05-15_17-50-52_DescriptionB
10:
11: 04:54:32,04:55:37 DescriptionC
12: 2017-05-15 14:17:22
13: 17672
14: 2017-05-15_19-11-54_DescriptionC
15:
16: 04:54:32,04:55:37 DescriptionC
17: 2017-05-15 14:17:22
18: 17672
19: 2017-05-15_19-11-54_DescriptionC
Como você pode ver, a nova data/hora esperada é exibida corretamente nas linhas 4,9,14 (não tenho certeza porque a última linha do $trimFile
é exibida duas vezes, mas isso não é minha preocupação no momento).
O problema está na renomeação real do arquivo quando removo as linhas comentadas (linhas 13, 14, 54, 59) do script para que o script agora fique assim:
01: sourceFile="${1}"
02: trimFile="${2}"
03:
04: IFS=$'\n'
05:
06: dos2unix "$trimFile"
07: numberOfSegments=`cat "$trimFile" | wc -l`
08: numberOfSegments=$((numberOfSegments + 1))
09: extension=`echo "$sourceFile" | awk -F'.' '{print $NF}'`
10:
11: base=`echo "$sourceFile" | sed -e "s|.$extension||g"`
12:
13: ~/bin/ffmpeg -i "${sourceFile}" -c:v copy "/dev/shm/$base.${extension}"
14: sourceFile="/dev/shm/$base.${extension}"
15:
16: # File date/time information
17: origYear="${sourceFile:0:4}"
18: origMonth="${sourceFile:5:2}"
19: origDay="${sourceFile:8:2}"
20: origHour="${sourceFile:11:2}"
21: origMinute="${sourceFile:14:2}"
22: origSecond="${sourceFile:17:2}"
23:
24: origDate="${origYear}-${origMonth}-${origDay} ${origHour}:${origMinute}:${origSecond}"
25:
26: for (( i=1;i<="$numberOfSegments";i++ ))
27: do
28:
29: lineEntry=`cat "$trimFile" | head -"$i" | tail -1`
30:
31: startHour=`echo "$lineEntry" | awk -F'[:,]' '{print $1}'`
32: startMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $2}'`
33: startSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $3}'`
34:
35: endHour=`echo "$lineEntry" | awk -F'[:,]' '{print $4}'`
36: endMinute=`echo "$lineEntry" | awk -F'[:,]' '{print $5}'`
37: endSecond=`echo "$lineEntry" | awk -F'[:,]' '{print $6}'`
38:
39: description=`echo "$lineEntry" | awk -F'[:, ]' '{print $7}'`
40:
41: beginSeconds=`awk "BEGIN {print ($startHour*3600+$startMinute*60+$startSecond)}"`
42: stopSeconds=`awk "BEGIN {print ($endHour*3600+$endMinute*60+$endSecond)}"`
43: duration=`awk "BEGIN {print $stopSeconds-$beginSeconds}"`
44:
45: newDate=$(date -d "@$(( $(date -d "${origDate}" +%s) + ${beginSeconds}))" +'%Y-%m-%d_%H-%M-%S')
46:
47: new="${newDate}_${description}"
48: echo "${lineEntry}"
49: echo "${origDate}"
50: echo "${beginSeconds}"
51: echo "${new}"
52: echo ""
53:
54: ~/bin/ffmpeg -n -vsync drop -fflags +genpts -i "$sourceFile" -ss "$beginSeconds" -t "$duration" -c:v libx265 -crf 27 -preset slow "$new.mkv"
55:
56:
57: done
58:
59: rm "/dev/shm/${base}.${extension}"
60:
61:
Os arquivos criados pelo script são nomeados da seguinte forma:
1969-12-31_19-49-30_DescriptionA.mkv
1969-12-31_22-33-30_DescriptionB.mkv
1969-12-31_23-54-32_DescriptionC.mkv
Obviamente, as datas usadas para nomear o arquivo não são nada parecidas com a saída para stout criada quando eu não recodifiquei o vídeo, mas apenas testei se as novas variáveis de data estavam sendo calculadas corretamente.
Então, minha pergunta se resume a isso, por que, depois de fazer algumas contas de data/hora, a data/hora está correta quando ecoada para robusta, mas totalmente errada quando usada para nomear os arquivos uma vez codificados.
Obrigado!
Ao descomentar
você fez as linhas a seguir extrair fragmentos errados.
Por exemplo, agora
$origYear
se expande para/dev
.Eu esperaria
date -d
lançar erros semelhantes adate: invalid date '/dev-sh-/2 17:05:15'
. Talvez o seudate -d
não valide sua entrada (ou você ignorou os erros?).Você pode corrigir esse problema específico atribuindo substrings a
origYear
,origMonth
etc. antes de alterar o valor desourceFile
.Caso alguém faça referência a esta postagem no futuro, abaixo está o script de trabalho corrigido. Houve erros adicionais além do que foi abordado nesta postagem que também foram corrigidos (ou seja, o comando ffmpeg e a localização de
-ss
e-t
dentro do comando.