عمت مساءً يا خبرافشيانس! مقال للمبتدئين ، نوع من الأفكار الجديدة التي لن تراها هنا. وهذه الوظيفة ، على الأرجح ، تم تنفيذها عشرات المرات بلغات مختلفة. الفكرة هي أنه إذا حصلت على رابط إلى منشور على Twitter يحتوي على مقطع فيديو ، فاختر هذا الفيديو وقم بتحويله إلى mkv.
لرجال الأعمال!
ما نحتاجه:
- اذهب
- ffmpeg
- عامل ميناء (على الرغم من أنه من الممكن بدونه. ومع ذلك ، أين بدونه هذه الأيام؟! ؛)
لدينا رابط إلى تغريدة من الفيديو:
https://twitter.com/FunnyVines/status/1101196533830041600
من الرابط بأكمله ، نحن مهتمون فقط بالمعرف الذي يتكون من أرقام ، لذلك نقوم بسحب السلسلة الفرعية بأكملها بانتظام منتظم:
var reurl = regexp.MustCompile(`\/(\d*)$`)
باستخدام المعرف المستلم ، انتقل إلى العنوان:
resp, err := client.Get("https://twitter.com/i/videos/tweet/" + id)
من أين نحصل على رابط رمز JS لمشغل الفيديو:
src="https://abs.twimg.com/web-video-player/TwitterVideoPlayerIframe.f52b5b572446290e.js"
من هذا الملف js نحتاج إلى شيء واحد مهم للغاية - حامل الترخيص للحصول على إذن في twitter api.
Regex'pee له!
re, _ := regexp.Compile(`(?m)authorization:\"Bearer (.*)\",\"x-csrf`)
هذا لا يكفي للوصول إلى واجهة برمجة التطبيقات ، ما زلت بحاجة إلى guest_token. يمكن الحصول عليها عن طريق تطبيق طلب POST على العنوان - " https://api.twitter.com/1.1/guest/activate.json " ، ويمر هناك: personalization_id و guest_id من ملف تعريف الارتباط (الذي تلقيناه في الرد من الخادم عند الوصول إلى الخادم السابق) عنوان 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_tokenيتغير بشكل دوري ، لم أفهم عدد مرات الاتصال به (بدلاً من ذلك ، يتغير في المؤقت - فاصل زمني مدته 15 دقيقة) ، لكن يبدو أن التنشيط المنتظم لـ /1.1/guest/activate.json يسمح لك بتجاوز حد api لـ 300 طلب.
الجواب هو gzip ، يمكن فكه في Go ، شيء مثل هذا:
res, err := gzip.NewReader(resp.Body) if err != nil { return "", err } defer res.Close() r, err := ioutil.ReadAll(res)
حسنا ، هذا كل شيء! الآن لدينا كل ما نحتاجه للاتصال بـ 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)
ستكون استجابة API هي Json مع وصف للفيديو ، والأهم من ذلك ، عنوان URL لاستلامه (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"
وأخيرًا ، لدينا عنوان الفيديو ، ونرسله إلى ffmpeg ، بينما نتحقق من تنسيق الفيديو الذي رأيته بتنسيقين محتملين ، الأول هو 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 }
والثاني هو ملف قائمة التشغيل m3u8 ، لهذا الخيار ، هناك حاجة إلى خطوة أخرى - لقد حصلنا على GET
له ، واتخاذ عنوان URL للمحتوى في الدقة المطلوبة:
#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
ولا يزال - ffmpeg.
والآن ، قليلا عن خادم HTTP
اعتدت:
المنطق على النحو التالي ، نبدأ الخادم:
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) }
ونقوم بمعالجة مسار / فيديو واحد فقط:
كيف يمكن جمع كل هذا؟
على سبيل المثال ، Makefile بسيط (إنشاء ، جعل تشغيل ...):
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
انتبه إلى العلم "-v / etc / ssl: / etc / ssl: ro" ، في الصورة الأساسية أوبونتو لم تكن هناك شهادات جذر ولم يتعرف عميل http على https twitter ، وألقاها من الجهاز المضيف عبر --volume (الآن ، مثل كيف ، فمن الأصح استخدام - جبل ).
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
لا شك في أنني لم أكتشف أمريكا في هذا المقال ، لكن فجأة سيكون مفيدًا لشخص ما.
المصادر متوفرة هنا .