Boa tarde, Khabravchians! Artigo para iniciantes, algum tipo de novas idéias que você não verá aqui. E essa funcionalidade, provavelmente, foi implementada dezenas de vezes em vários idiomas. A idéia é que, se você receber um link para uma postagem no Twitter que contenha um vídeo, pegue esse vídeo e converta-o para mkv.
Para negócios!
Do que precisamos:
- GO
- ffmpeg
- janela de encaixe (embora seja possível sem ela. No entanto, onde sem ela atualmente?!;)
Temos um link para um tweet do vídeo:
https://twitter.com/FunnyVines/status/1101196533830041600
De todo o link, estamos interessados apenas no ID que consiste em números; portanto, extraímos toda a substring digital por regularidade elementar:
var reurl = regexp.MustCompile(`\/(\d*)$`)
Com o ID recebido, vá para o endereço:
resp, err := client.Get("https://twitter.com/i/videos/tweet/" + id)
Onde podemos obter o link para o código JS do player de vídeo:
src="https://abs.twimg.com/web-video-player/TwitterVideoPlayerIframe.f52b5b572446290e.js"
A partir deste arquivo js, precisamos de uma coisa muito importante - portador de autorização na API do Twitter.
Regex faz xixi nele!
re, _ := regexp.Compile(`(?m)authorization:\"Bearer (.*)\",\"x-csrf`)
Isso não é suficiente para acessar a API, você ainda precisa do guest_token. Pode ser obtido aplicando uma solicitação POST ao endereço - " https://api.twitter.com/1.1/guest/activate.json ", passando lá: personalization_id e guest_id do cookie (que recebemos na resposta do servidor ao acessar o anterior) URL):
var personalization_id, guest_id string cookies := resp.Cookies() for _, cookie := range cookies { if cookie.Name == "personalization_id" { personalization_id = cookie.Value } if cookie.Name == "guest_id" { guest_id = cookie.Value } }
guest_tokenEle muda periodicamente, eu não entendi com que frequência chamá-lo (em vez disso, muda no timer - um intervalo de 15 minutos), mas parece que a ativação regular de /1.1/guest/activate.json permite ignorar o limite de API para 300 solicitações.
A resposta é gzip, pode ser descompactada no Go, algo como isto:
res, err := gzip.NewReader(resp.Body) if err != nil { return "", err } defer res.Close() r, err := ioutil.ReadAll(res)
Bem, é isso! Agora temos tudo o que precisamos para chamar a API:
url, _ = url.Parse("https://api.twitter.com/1.1/videos/tweet/config/" + id + ".json") request = &http.Request{ Method: "GET", URL: url, Header: http.Header{ "user-agent": []string{"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"}, "accept-encoding": []string{"gzip", "deflate", "br"}, "origin": []string{"https://twitter.com"}, "x-guest-token": []string{gt.GuestToken}, "referer": []string{"https://twitter.com/i/videos/tweet/" + id}, "authorization": []string{"Bearer " + bearer}}, } resp, err = client.Do(request)
A resposta da API será Json com uma descrição do vídeo e, mais importante, o URL para recebê-lo (playbackUrl):
{"contentType":"media_entity","publisherId":"4888096512","contentId":"1096941371649347584","durationMs":11201,"playbackUrl":"https:\/\/video.twimg.com\/ext_tw_video\/1096941371649347584\/pu\/pl\/xcBvPmwAmKckck-F.m3u8?tag=6","playbackType"
E finalmente, temos o endereço do vídeo, enviamos para o ffmpeg, enquanto verificamos em qual formato de vídeo eu vi 2 formatos possíveis, o primeiro é o mp4:
if strings.Contains(videoURL.Track.PlaybackURL, ".mp4") { convert := exec.Command("ffmpeg", "-i", videoURL.Track.PlaybackURL, "-c", "copy", "./videos/"+id+".mkv") convert.Stdout = os.Stdout convert.Stderr = os.Stderr if convert.Run() != nil { return "", err } return id, nil }
E o segundo é o arquivo da lista de reprodução m3u8 . Para esta opção, é necessário mais um passo - obtemos GET
e pegue o URL do conteúdo na resolução desejada:
#EXT-X-INDEPENDENT-SEGMENTS #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=256000,RESOLUTION=180x316,CODECS="mp4a.40.2,avc1.4d0015" /ext_tw_video/1039516210948333568/pu/pl/180x316/x0HWMgnbSJ9y6NFL.m3u8 #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=832000,RESOLUTION=464x816,CODECS="mp4a.40.2,avc1.4d001f" /ext_tw_video/1039516210948333568/pu/pl/464x816/Z58__ptq1xBk8CIV.m3u8
E ainda - ffmpeg.
E agora, um pouco sobre o servidor HTTP
Eu usei:
A lógica é a seguinte, iniciamos o servidor:
cfg := tcplisten.Config{ ReusePort: true, FastOpen: true, DeferAccept: true, Backlog: 1024, } ln, err := cfg.NewListener("tcp4", ":8080") if err != nil { log.Fatalf("error in reuseport listener: %s\n", err) } serv := fasthttp.Server{Handler: e.Handler, ReduceMemoryUsage: false, Name: "highload", Concurrency: 2 * 1024, DisableHeaderNamesNormalizing: true} if err := serv.Serve(ln); err != nil { log.Fatalf("error in fasthttp Server: %s", err) }
E processamos apenas uma rota / * vídeo:
Como tudo isso pode ser coletado?
Por exemplo, um Makefile simples (make build, make run ...):
build: go build -o main docker build -t tvideo . run: go build -o main docker build -t tvideo . docker kill tvideo docker run -d --rm --name tvideo -v /etc/ssl:/etc/ssl:ro -v videos:/opt/videos -p 8080:8080 tvideo docker logs -f tvideo
Preste atenção no sinalizador "-v / etc / ssl: / etc / ssl: ro", na imagem base ubuntu não havia certificados raiz e o cliente http não reconheceu https twitter, lançou-o da máquina host por meio de --volume (agora, como como, é mais correto usar --mount ).
Dockerfile
FROM ubuntu // docker image COPY main /opt/app RUN apt-get update && \ // apt-get install -y ffmpeg && \ chmod +x /opt/app EXPOSE 8080 WORKDIR /opt CMD ./app
Sem dúvida, não descobri a América neste artigo, mas de repente isso será útil para alguém.
As fontes estão disponíveis aqui .