Microservice sur GO pour saisir des vidéos à partir de tweets

Bonjour, Khabravchians! Article pour débutants, une sorte de nouvelles idées que vous ne verrez pas ici. Et cette fonctionnalité, très probablement, a été mise en œuvre des dizaines de fois dans différentes langues. L'idée est que si vous obtenez un lien vers une publication sur Twitter contenant une vidéo, prenez cette vidéo et convertissez-la en mkv.


Pour les affaires!


Ce dont nous avons besoin:


  • Allez
  • ffmpeg
  • docker (bien qu'il soit possible sans lui. Cependant, oĂą sans lui de nos jours?!;)

Nous avons un lien vers un tweet de la vidéo:


https://twitter.com/FunnyVines/status/1101196533830041600 

De tout le lien, nous ne sommes intéressés que par l'ID composé de nombres, nous retirons donc la sous-chaîne numérique entière par régularité élémentaire:


 var reurl = regexp.MustCompile(`\/(\d*)$`) //   -  e.GET("/*video", func(c *fasthttp.RequestCtx) { //      url := reurl.FindSubmatch([]byte(c.UserValue("video").(string))) 

Avec l'ID reçu, allez à l'adresse:


 resp, err := client.Get("https://twitter.com/i/videos/tweet/" + id) 

Où obtient-on le lien vers le code JS du lecteur vidéo:


src="https://abs.twimg.com/web-video-player/TwitterVideoPlayerIframe.f52b5b572446290e.js"
À partir de ce fichier js, nous avons besoin d'une chose très importante - porteur de l'autorisation dans twitter api.


Regex'pee lui!


 re, _ := regexp.Compile(`(?m)authorization:\"Bearer (.*)\",\"x-csrf`) 

Cela ne suffit pas pour accéder à l'API, vous avez toujours besoin de guest_token. Il peut être obtenu en appliquant une demande POST à ​​l'adresse - " https://api.twitter.com/1.1/guest/activate.json ", en y passant: personalization_id et guest_id du cookie (que nous avons reçu dans la réponse du serveur lors de l'accès au précédent) 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 } } // // Get Activation url, _ := url.Parse("https://api.twitter.com/1.1/guest/activate.json") request := &http.Request{ Method: "POST", 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"}, "authorization": []string{"Bearer " + bearer}, "cookie": []string{"personalization_id=\"" + personalization_id + "\"; guest_id=" + guest_id} }, } resp, err = client.Do(request) 

guest_token

Il change périodiquement, je ne comprenais pas à quelle fréquence l'appeler (plutôt, il change dans la minuterie - un intervalle de 15 minutes), mais il semble que l'activation régulière de /1.1/guest/activate.json vous permette de contourner la limite d'api pour 300 requêtes.


La réponse est gzip, il peut être décompressé dans Go, quelque chose comme ceci:


 res, err := gzip.NewReader(resp.Body) if err != nil { return "", err } defer res.Close() r, err := ioutil.ReadAll(res) 

Et bien c'est tout! Nous avons maintenant tout ce dont nous avons besoin pour appeler l'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) 

La réponse de l'API sera Json avec une description de la vidéo, et surtout, l'URL pour la recevoir (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" 

Et enfin, nous avons l'adresse vidéo, nous l'envoyons à ffmpeg, tout en vérifiant dans quel format vidéo j'ai vu 2 formats possibles, le premier est 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 } 

Et le second est le fichier de liste de lecture m3u8 , pour cette option, une étape supplémentaire est nécessaire - nous obtenons GET
lui, et prenez l'URL du contenu dans la résolution souhaitée:


 #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 

Et encore - ffmpeg.


Et maintenant, un peu sur le serveur HTTP


J'ai utilisé:



La logique est la suivante, on démarre le serveur:


 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) } 

Et nous ne traitons qu'une seule route / * vidéo:


 //  : //  http://localhost:8080/https://twitter.com/FunnyVines/status/1101196533830041600 //   http://localhost:8080/1101196533830041600 e.GET("/*video", func(c *fasthttp.RequestCtx) { 

Comment tout cela peut-il être collecté?


Par exemple, un simple Makefile (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 

Faites attention au drapeau "-v / etc / ssl: / etc / ssl: ro", il n'y avait pas de certificat racine dans l'image de base ubuntu et le client http n'a pas reconnu twitter https, l'a jeté de la machine hôte via --volume (maintenant, comme comment, il est plus correct d'utiliser --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 

Sans aucun doute, je n'ai pas découvert l'Amérique dans cet article, mais tout à coup, cela sera utile pour quelqu'un.


Les sources sont disponibles ici .

Source: https://habr.com/ru/post/fr442096/


All Articles