Landstalker
Lenda da internet
- Mensagens
- 19.355
- Reações
- 39.658
- Pontos
- 1.584
A grana tá apertada, Netflix há tempos cancelada, e o torresmo comendo solto aqui em casa. Praticamente só me alimento de torresmo. Devido a isso, acabo que muitas das coisas que baixo vem com legendas sem sincronia com o vídeo em questão, então, ou eu fico igual um corno procurando novas legendas internet afora ou uso alguma ferramenta para alterar a sincronia delas.
Há um bom tempo que eu uso o site http://subshifter.bitsnbites.eu para sincronizar os meus arquivos SRT. O problema que ele é um pouco chato pra fazer isso, então, há dois domingos resolvi eu mesmo criar a minha própria ferramenta de sincronia de legendas. Fui na velha e boa wiki para ver como era o formato padrão dos arquivos SRT e começar a programar a minha própria ferramenta em C/C++, mas no meio do processo, meu de uma epifania e me perguntei: "Por que não dou uma olhadinha naquela linguagem da Google e ver como ela funciona, quem sabe fique legal fazer esse programinha nela."
Então o motivo de criar um pequeno programa para ajustar a legenda seria já suficiente para eu começar a estudar Go. O que eu sabia sobre a linguagem eram apenas duas coisas: que ela era compilada (igual a C/C++) e que ela era feita pela Google, fora isso nunca tinha visto nada sobre a linguagem, o que seria um bom desafio em começar a estudá-la e no mesmo dia já criar alguma coisa útil nela.
O.o Go me ameaçou com uma metralhadora na mão
Depois de uns 30 minutos da instalação, estava eu todo pimpão começando a estudar e escrever o meu projeto quando de repente ao testá-lo o Go me dá uma facada no peito, tipo a que Bolsonaro tomou na barriga. Eu olhei o código e não tinha nenhum erro aparente, até que ao rodá-lo no terminal novamente, percebo que a linguagem reclamou e gerou um erro de compilação de uma variável que declarei e não utilizei, fiquei chocado com isso, ao mesmo tempo que fiquei bastante intrigado porque nunca tinha visto uma linguagem de programação reclamar disso.
Na Golang você só declara aquilo que você for usar, se colocar alguma variável e não utilizá-la, um erro de compilação é gerado. Isso é de fato legal porque você sabe que seu programa nunca terá variáveis inutilizadas, força na marra uma boa prática de programação que até mesmo coders veteranos deixa passar.
Toolchain super simples de mexer, sem o stress da compilação em C/C++
Calma, não vou aqui abandonar C/C++, na verdade, nem posso fazer isso. E eu adoro C/C++. Mas como a Golang é uma linguagem compilada, comparações com a linguagens clássicas não poderia deixar de serem feitas.
Logo de cara uma coisa que percebi e me animou demais foi a facilidade de se compilar um projeto na Go. Depois de seu projeto estar configurado na workspace, o resto fica super fácil de mexer. Quem trabalhou com C/C++ além do nível trivial, precisando adicionar várias libs e ferramentas internas e externas, sabe que é uma bagunça desgraça fazer o seu projeto funcionar com tudo interligado. Ainda mais se você estiver usando o CMake para isso. Na Go é tudo bem simples e intuitivo, depois de mais de 1h, eu já estava adicionando libs thirds usando apenas o comando go get -u -v <endereço GitHub da lib> em qualquer lugar do terminal e o toolchain da Go já baixava no lugar certo e já deixava pronto para a minha IDE enxergar e eu conseguir adicionar no meu projeto sem problema nenhum.
Onde diabos estão as classes?
Nesse aspecto a Go se assemelha mais a C que a C++. Não há classes, não há hierarquia como conhecemos, não há necessidade de encapsulamento, não há overloading e overriding de métodos e membros, enfim, a linguagem é rápida e caceteira eliminando muita coisa que linguagens como Java, C# e C++ usam e abusam.
Afinal, isso é bom ou ruim? Depende. Depende do que você for fazer e de como você for programar. Eu tive que adaptar algumas lógicas para funcionar da forma como a linguagem Go funciona. Há, no entanto, uma estrutura de de armazenamento de informações que se assemelha ao struct da linguagem C que, inclusive, se chama struct também na Go.
Você tem métodos e funções, e ambas funcionam um pouco diferente. Assim como ocorre no C++ que há as duas coisas, na Go também há, porém, na C apenas há functions.
Então, landinho, se a Go não é uma linguagem orientada a objetos, então, POR QUE diabos ela usa método e não fica apenas com funções como é em C?
A resposta é simples, é porque ela não é C :D
Go herda sim algumas características híbridas de outras linguagens, ela não consegue ser uma linguagem puramente procedural como é a C, e no caso dos seus métodos são usados em conjunto com seus Structs e até onde vi apenas restritos a eles.
Podemos usar o conceito de interface na Go (funciona de uma forma um pouco diferente do conceito de interface na linguagem Java). Já que a linguagem não tem generics as interfaces podem fazer um pouco dessa papel, mas nada perto dos templates que podemos usar no C++ que é extremamente poderoso e versátil, porém, extremamente complicado de se masterizar.
Landinho, já que a Go é uma linguagem que compila, então tem acesso ao baixo nível como em C/C++? Posso usar ponteiros e referência de memória? Como fica a questão de gerenciamento de memória?
Mesmo a linguagem possuindo ponteiros e referência, o uso deles é descorajado pela documentação oficial da Google. Até onde onde eu vi, a linguagem não dá acesso total ao baixo nível como C/C++ dá. Prova disso é que eu não tenho controle onde devo gravar os valores das minhas variáveis. C/C++ são tão poderosas que eu escolho onde e como gravar meus próprios dados (por isso também há uma avalanche de coisas para se fazer uma mesma coisa nessas duas linguagens O.o).
Em C/C++ eu posso escolher se uma variável ficará armazenada na stack ou na heap ou jogá-la até na VRAM da minha placa de vídeo se assim eu quiser. Posso gravar os dados aonde eu quiser. Na Go existe um GC (Garbage Collection), assim como há em linguagens como C# e Java, portanto o gerenciamento das variáveis e suas memórias ficam ao encargo do próprio controlador da linguagem, isso gera vantagens e desvantagens. As instâncias criadas pelo operador new terão controle pela GC automaticamente e não dá pra saber onde e como a Go tá gerenciando isso.
Uma curiosidade: Toda a arquitetura do gerenciador de memória da Go (o seu GC) foi escrita estritamente na linguagem C. Já a parte de frontend da linguagem foi feita, por sua vez, na linguagem C++.
Calma, amiguinho, não veremos os códigos doidão em Go como existem em C/C++ :D ex:
C++:
//socorro, man!!!
void(*change)(int&) = varDaEmossaum;
void forEach(const std::vector<int>& array, void(*func)(const int&) = nullptr)
{
if (func)
for (const int& value : array)
{
func(value);
}
}
C++:
//socorro, help, me tire daqui!!!
char array[size+1];
template <int I, int ... indices>
constexpr string(char const (&str) [I], detail_::indices<Indices...>): array{str[indices]..., '\0'}{}
Se eu não posso manipular a memória do jeito como eu quiser, onde fica o poder da Go?
A Go veio para facilitar o uso da criação de aplicações multithreading, sendo uma linguagem voltado para isso. Então, a Go se torna uma ótima opção para criação de programas de servidor e daqueles que fazem uso excessivo de vários processadores.
Na Go não existe threads da mesma forma como em outras linguagens. Aqui existem as goroutines que funcionam para isso e são bem versáteis e poderosas.
Bom, chega de rodeios e falar apenas da linguagem e mostrar um pouquinho da prática dela.
NOTA: Codificação do fórum colocada como linguagem C, mas o código é Go. Fiz isso porque o fórum não tem suporte para Golang.
C:
package main
/**
Autor: LandStalker (inocencio)
Data: 02/09/2018
Descricao: Programa para adicionar ou subtrair tempo nas legendas em formato SRT
Uso: ./submanager -file:<arquivo_legenda.srt> -time=<tempo_em_ms>
Ex.: ./submanager -file:"lawrence of arabia.srt" -time=-2000
EN Version: This is a short program to increment or reduce srt subtitle files. To use it call this program passing
these arguments ./submanager -file:<file.srt> -time=<time in ms>
Make sure you entered time argument as millesecond. Positive value will increase the time to subtitle shows up and
negative value will decrese this time instead.
`:+shhso+/
`` ./hddy+..:/+o.
`/osyyso+/:` -odNdhy+ .yNd-+:
.oddhhdddhysooso/-` `-smmhs+//: -oh-/+:
/hdysdhs+/. `-yo+smhyyhmNNmdhhhhyso+/-` ohh:
`-mhyyhmo/// -dMo--/////+syyss+///:/+osyydmNo.
````-ddysyms+///.` .-.`-/+osys/:-:.``:so.```..-:+yso/.
`:sdddhyNysyhN//////:.````-/syo/-```:so.``.:.`````````-+sh+`
.ymdyyhmNmsshdy///////////+sy+-.``````..``````````.-:+oooosys:
hmhyddhdmdsshd+//////////ys+-``````````........-/oys+/-..``.so:
Mdhhm-.+ddsyhh////////+sh+-```.-/+ooooooooossso+/-.`````````:ss
Mhhdd``+dhshhy///////oss-``.-/oo+:--------....``````````````.oy.
Ndhhm-.+dhsdho/////+ys/.`.+ss/-.`````````````````````````````+y/
omdhhmdmmhsdy+////oh+--/os/-.````````````````````````````````+h+
+hmddmNNdsdh+//+sh/:oso:..``````````````````````````````````+y-
`-+oosymshhy/oyyoso/-`````````````````````````````````````.oy
`:Nyyhdhddy+-.```````````````````````````````````````:so
.dhysymms:.````````````````````````````````````````.sy+
.Nhyssyhhho:..`````````````````````````.--:://+osshdmy
+hdyssssyyyyo+-..````````````...-:+osyyyyhhyyyyhddyo-
:sddhyysssyyyhys+:.`````-:/oshdddhhhysssssyhhdh+`
`.-oyyhhhhhyyyyhhhdhhhdhhhhhhhyyyyyhhhhhhyy/-`
`-:/+oyhddddddddddddhhyysso+////:-.
*/
import (
"bufio"
"bytes"
"flag"
"fmt"
"os"
"path/filepath"
"strconv"
s "strings"
"time"
"github.com/logrusorgru/aurora"
)
var srtfilepath string
type TextPart struct {
num string
time string
lines []string
}
func main() {
isNotFlag := false
timePtr := flag.Int("time", 0, "O tempo é dado em millisegundos (1000ms = 1s) "+
"Valores acima de 0 atrasa a legenda, abaixo de 0, adianta.")
filePtr := flag.String("file", "", "Nome do arquivo SRT.")
flag.Parse()
//fmt.Print(aurora.Green(">> SubStr Manager v0.1 <<\n\n").Bold())
fmt.Println(aurora.Colorize(">> SubStr Manager v0.1 <<", aurora.GrayFg|aurora.GreenBg|aurora.BoldFm))
if *timePtr == 0 {
fmt.Println(aurora.Red("Informe um 'time'. Ex: -time=-1000"))
isNotFlag = true
}
if *filePtr == "" {
fmt.Println(aurora.Red("Informe um 'file'. Ex: -file=\"minha legenda.srt\""))
isNotFlag = true
}
if isNotFlag {
os.Exit(1)
}
ex, err := os.Executable()
checkError(err)
fmt.Println("CDir: ", aurora.Cyan(filepath.Dir(ex)))
fmt.Println("Time: ", aurora.Cyan(*timePtr), aurora.Cyan("ms"))
fmt.Println("File: ", aurora.Cyan(*filePtr))
srtfilepath = *filePtr
if s.HasSuffix(srtfilepath, ".srt") {
//todos os parametros OK? Entao converte o tempo do arquivo SRT.
strShifter(srtfilepath, *timePtr)
} else {
//arquivo invalido
fmt.Println(aurora.Red("Error: O arquivo "), srtfilepath, aurora.Red(" não é uma extensão srt."))
}
}
func (p *TextPart) convertTime(timestamp int) {
times := s.Split(p.time, "-->")
p.time = ""
for counter, e := range times {
timesdiv := s.Split(s.TrimSpace(e), ",")
var hours int
var minutes int
var seconds int
var ms int
var err error
for i, _e := range timesdiv {
if i == 0 {
//time completo
t := s.Split(_e, ":")
for j, v := range t {
if j == 0 {
//hora
hours, err = convertStrToInt(v)
} else if j == 1 {
//minuto
minutes, err = convertStrToInt(v)
} else {
//segundo
seconds, err = convertStrToInt(v)
}
checkError(err)
}
} else {
//restante em millisegundos
ms, err = convertStrToInt(_e)
//cria uma data no padrao Go Lang
result := time.Date(2018, 9, 7, hours, minutes, seconds, 0, time.UTC)
//adiciona os millisegundos restantes a data criada
result = result.Add(time.Duration(ms) * time.Millisecond)
result = result.Add(time.Duration(timestamp) * time.Millisecond)
//formata o tempo ja convertido para o padrao do formato SRT com base num padrao definido
//ref: https://golang.org/pkg/time/#example_Time_Format
formattedTime := s.Replace(result.Format("15:04:05.000"), ".", ",", 1)
if counter > 0 {
p.time += " --> "
}
//adiciona o tempo convertido no lugar do anterior
p.time += formattedTime
}
}
}
}
/**
Converte uma string em inteiro.
@param srt
*/
func convertStrToInt(str string) (int, error) {
return strconv.Atoi(str)
}
/**
Ajusta o tempo em millesegundos da legenda. Valores positivos, aumenta o tempo da legenda em relação ao
vídeo enquanto valores negativos reduz o tempo da legenda.
*/
func strShifter(filename string, timestamp int) {
file, err := os.Open(filename)
checkError(err)
defer file.Close()
//variaveis
scanner := bufio.NewScanner(file)
var line string
var text []string
var parts []TextPart
partCounter := 0
i := 0
isNewPart := true
textPart := new(TextPart)
//le o arquivo linha a linha (para leitura direta usa-se ioutil.ReadFile(filename) no lugar)
for scanner.Scan() {
//pega a linha corrente do arquivo
line = scanner.Text()
//cria um novo trecho
if isNewPart {
i++
isNewPart = false
partCounter = 0
textPart = new(TextPart)
}
//novo trecho do arquivo?
if len(line) == 0 {
textPart.lines = text
text = nil
//salva o trecho atual no slice
parts = append(parts, *textPart)
isNewPart = true
continue
}
if partCounter == 0 {
//0 = numeracao da legenda
textPart.num = line
partCounter = 1
} else if partCounter == 1 {
//1 = tempo da legenda
textPart.time = line
partCounter = 2
} else if partCounter == 2 {
//2 = linha(s)
text = append(text, line)
}
}
fmt.Println("Parts: ", aurora.Cyan(i))
//formata o texto de saida para o arquivo de saida
var buffer bytes.Buffer
br := "\n"
for _, e := range parts {
e.convertTime(timestamp)
buffer.WriteString(e.num + br)
buffer.WriteString(e.time + br)
fmt.Println("entrada")
for _, _e := range e.lines {
buffer.WriteString(_e + br)
}
buffer.WriteString(br)
}
//cria o arquivo
file, err = os.Create(srtfilepath)
//escreve no arquivo criado
n, err := file.Write(buffer.Bytes())
file.Sync()
fmt.Println("Written Bytes: ", aurora.Cyan(n))
}
func checkError(e error) {
if e != nil {
panic(e)
}
}
Ultima Edição: