選挙でのカメラからの大量録画-2

Habr-政治用ではありません。 この記事では、特定のソフトウェアソリューションを実装する技術的側面のみを扱います。 共通の利益のために、コメントでの政治的議論、スピーチ、動揺などを放棄してください。 また、取得した知識を破壊的な目的に使用したり、特別な必要がない限りビデオアーカイブ全体のバックアップを開始したりしないでください。 ありがとう



9月8日、単一投票日。 今年、一般市民は、インターネットを介して首都の市長の選挙を監視するために招待されています。 多くの市民は、カメラから写真を記録することを興味深いと感じています。誰かは政治的に動機付けられた興味を持ち、ほとんどは自分自身とインターネットの目をよく知っている人を見ることに単に平凡です。 この記事は、現在のシステムの原理を実証し、実用的な概念を提供することを目的としています。





前回の選挙以来、システムは少し変更されました(そうでなければ記事はありません)。最初 、すべてが以前どのように機能し、どのように機能し始めたかを思い出します。 そのため、各カメラには、ビデオのストリーミング元となる一意のuidとサーバープールがあります。 このデータを使用して特別なリクエストを作成すると、選択したカメラで録画されたビデオへのリンクを取得できます。



まず、既存のすべてのカメラでデータを見つけます。 次の方法が最も簡単に思えました。1〜3800のサイト番号で検索を開始します。これを行うには、GET vybory.mos.ru/json/id_search aaa / bbb .jsonを送信します。bbbはuid、 aaaはlen( bbb )です。 たとえば、 vybory.mos.ru / json / id_search / 1 / 3.json



このセクションに関する情報を含むjsonを取得します。次のようなものです。

[{"id":7933,"name":"   №3","num":"3","location_id":1162,"address":" , 36/9","raw_address":".,   .,  36/9","is_standalone":false,"size":null,"location":{"id":1162,"address":", ,   , 36/9","raw_address":".,   .,  36/9","district_id":1,"area_id":null,"sub_area_id":null,"locality_id":1,"street_id":1590,"lat":55.753266,"lon":37.577301,"max_zoom":17}}]
      
      







ここで特に興味深いのはidです。 vybory.mos.ru/account/channels?station_id= idという形式のGETを送信します。この場合、 vybory.mos.ru / account / channels?station_id = 7933



それに応じて、編集者が誓う詐欺師の行を取得しますが、内部にはカメラのハッシュとサーバーのアドレスが含まれています。 そこから通常の形式でハッシュを取り出します

\ $([0-9a-h] {8}-[0-9a-h] {4}-[0-9a-h] {4}-[0-9a-h] {4}-[0- 9a-h] {12})および正規形式のIPアドレス*?(\ D {1,3} \。\ D {1,3} \。\ D {1,3} \。\ D {1,3 })



その結果、現在のセクションのカメラに関する必要な情報を取得します。

2e9dd8dc-edd4-11e2-9a6b-f0def1c0f84c 188.254.112.2 188.254.112.3 188.254.112.4

2ea32990-edd4-11e2-9a6b-f0def1c0f84c 188.254.112.2 188.254.112.3 188.254.112.4



次に、ニュアンスが始まります。 カメラには、古い、新しい、不在の3つのタイプがあります。 それらがどのように異なるか、少し後で説明しますが、最初にそれらを区別する方法を見つけますが、それらを区別するのは非常に簡単です-http:// SERVER /master.m3u8?cid= UIDの形式のGETを送信する必要があります

新しいカメラは次のようなものを返します



#EXTM3U

#EXT-X-VERSION:2

#EXT-X-STREAM-INF:プログラムID = 777、帯域幅= 3145728

/variant.m3u8?cid=e1164950-0c19-11e3-803b-00163ebf8df9&var=orig





古いカメラは次のようなものを返します。

#EXTM3U

#EXT-X-MEDIA-SEQUENCE:136

#EXT-X-TARGETDURATION:15

#EXT-X-ALLOW-CACHE:いいえ

#EXT-X-PROGRAM-DATE-TIME:2013-09-04T12:05:40Z

#EXTINF:15、

/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296340.93-1378296355.93

#EXTINF:15、

/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296355.93-1378296370.93

#EXTINF:15、

/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296370.93-1378296385.93

#EXTINF:15、

/segment.ts?cid=2ea32990-edd4-11e2-9a6b-f0def1c0f84c&var=orig&ts=1378296385.93-1378296400.93





見つからないカメラは404 CIDが見つかりませんでした以外は何も返しません :)



特定のエリアのカメラに関する情報を取得できるようになったので、必要なすべての情報を収集するマルチスレッドパーセルを作成します。 私は無料のMongolabにデータを保存することを好みますが、通常の棚でうまくいくことができます。 モスクワには3500以上のサイトがあることを知って、1から3800のサイクルを繰り返します。以下は私の膝の上でスケッチされていますが、それでも動作するコードです。 もちろん、mongaサーバーからCookieとパスワードを入力する必要があります。



 # -*- coding: utf-8 -*- import json, re import httplib import threading from time import sleep import Queue from pymongo import MongoClient client = MongoClient('mongodb://admin:@.mongolab.com:43368/elections') db = client['elections'] data = db['data'] data.drop() def get_data(uid): print uid headers = {'Origin': 'vybory.mos.ru', 'X-Requested-With': 'XMLHttpRequest', 'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0);', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': '*/*', 'Referer': 'http://vybory.mos.ru/', 'Accept-Encoding': 'deflate,sdch', 'Accept-Language': 'ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4', 'Accept-Charset': 'windows-1251,utf-8;q=0.7,*;q=0.3', 'Cookie': 'rack.session=' } try: conn = httplib.HTTPConnection('vybory.mos.ru') conn.request('GET', '/json/id_search/%d/%d.json'%(len(str(uid)), uid), None,headers) resp = conn.getresponse() try: content = json.loads(resp.read())[0] conn.request('GET', '/account/channels?station_id=%s'%content['id'], None,headers) resp = conn.getresponse() cont = resp.read() cnt=0 for i in cont.split('\x00')[1:]: cnt+=1 uid=re.findall(r'\$([0-9a-h]{8}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{4}-[0-9a-h]{12})', i)[0] ip=re.findall(r'.*?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})', i) conn2 = httplib.HTTPConnection('%s'%ip[0]) conn2.request('GET', '/master.m3u8?cid=%s'%(uid), None,headers) info = conn2.getresponse().read() conn2.close() if '/segment.ts' in info: camtype='old' elif '/variant.m3u8' in info: camtype='new' else: camtype='nil' #print content data.insert({ 'name':content['name'], 'num':content['num'], 'addr':content['address'], 'uid':uid, 'ip':ip, 'cnt':str(cnt), 'type':camtype }) except Exception,e: pass except Exception,e: print e conn.close() queue = Queue.Queue() def repeat(): while True: try: item = queue.get_nowait() except Queue.Empty: break get_data(item) sleep(0.01) queue.task_done() for i in xrange(1, 3800): queue.put(i) for i in xrange(10): t = threading.Thread(target=repeat) t.start() queue.join() print data.find().count(),'all cams' print data.find({'type':'nil'}).count(),'offline cams' print data.find({'type':'old'}).count(),'old cams' print data.find({'type':'new'}).count(),'new cams'
      
      







これで、カメラの完全に組み立てられたベースができました。 この記事の執筆時点では、残念ながら544台の古いカメラがありましたが、それらは古い方法でしか使用できません。

しかし、今では5778の新しいカメラがあり、1つの機能があります。 古いカメラからのチャンクは非常に短い時間でフェードアウトします-常に新しいプレイリストをダウンロードし、そこからチャンクへのリンクを切り取り、腐敗するまでダウンロードする必要があります。 新しいカメラにはこの欠陥がありません。 http:// SERVER /segment.ts?cid= UID &var = orig&ts = BEGIN - ENDの間にGETを送信することにより、任意の期間に任意のサイズのチャンクをダウンロードできます。 5分間続くチャンクに落ち着きました。 実際、少なくとも1時間は指定できますが、場合によっては、チャンクの制限中にブロードキャストが中断された場合、チャンク全体がダウンロードされないことがあります。 大まかに言えば、アーカイブから8時間を1時間単位でチャンクでダウンロードしようとすると、同時に数分以内にブロードキャストチャンクが実質的になくなり、1時間チャンク全体がダウンロードされなくなります。 したがって、小さいチャンクを選択するのが賢明です。 アルゴリズムの達人(覚えているように、10%)は、ビデオの1秒が失われないようにバイナリ検索を記述できます=)

ところで、質問を閉じるために、カメラは不在と呼ばれ、ポータルに登録されていますが、実際には機能しません。



ダウンロードプロセスを自動化します。 ここでは、Pythonでマルチスレッドバイクをブロックできますが、サードパーティのソフトウェアを使用することにしました。 aria2cのチャンク、 tsmuxerのメタファイルへのリンクを持つメタファイルを生成し、それらを順次実行します。



たとえば、次のようなもの:

 # -*- coding: utf-8 -*- from time import sleep, time from pymongo import MongoClient import os import subprocess import shutil # ,     directory='e:/dumps' #  delta=300 #   num='666' client = MongoClient('mongodb://:@.mongolab.com:43368/elections') db = client['elections'] data = db['data'] #    8  start=int(time())-3600*8 #       try: os.mkdir('%s/%s'%(directory,num)) except: pass #           for i in data.find({'num':num}): if i['type']=='nil': print 'Offline camera',i['uid'] elif i['type']=='old': print 'Old camera',i['uid'] else: print 'New camera',i['uid'] f=open('links-%s-%s.txt'%(num, i['cnt']),'w') #     try: os.mkdir('%s/%s/%s'%(directory,num,i['cnt'])) except: pass cur=start files='' #      while True: if cur+delta>time(): for ip in i['ip']: url = 'http://{0}/segment.ts?cid={1}&var=orig&ts={2}.00-{3}'.format(ip, i['uid'], cur, time()) f.write('%s\t'%url) f.write('\n dir={0}/{1}/{2}\n out={3}.ts\n'.format(directory,num,i['cnt'],url[-27:])) files += '"{0}/{1}/{2}/{3}.ts"+'.format(directory,num,i['cnt'],url[-27:]) break else: for ip in i['ip']: url = 'http://{0}/segment.ts?cid={1}&var=orig&ts={2}.00-{3}.00'.format(ip, i['uid'], cur, cur+delta) f.write('%s\t'%url) f.write('\n dir={0}/{1}/{2}\n out={3}.ts\n'.format(directory,num,i['cnt'],url[-27:])) files += '"{0}/{1}/{2}/{3}.ts"+'.format(directory,num,i['cnt'],url[-27:]) cur+=delta #        . m=open('%s-%s.meta'%(num,i['cnt']),'w') m.write('MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr --vbv-len=500\n') m.write('V_MPEG4/ISO/AVC, %s, fps=23.976, insertSEI, contSPS, track=3300\n'%files[:-1]) m.write('A_AAC, %s, timeshift=-20ms, track=3301\n'%files[:-1]) m.close() f.close() subprocess.Popen('aria2c.exe -i links-%s-%s.txt -d %s -x 16'%(num, i['cnt'], directory), shell=True).communicate() subprocess.Popen('tsMuxeR.exe %s-%s.meta %s/%s-%s.ts\n'%(num, i['cnt'], directory, num,i['cnt']), shell=True).communicate() shutil.rmtree('%s/%s'%(directory,num)) os.remove('%s-%s.meta'%(num, i['cnt'])) os.remove('links-%s-%s.txt'%(num, i['cnt']))
      
      







繰り返しになりますが、このコードは概念を確認するためだけに書かれたものであり、PEP8への準拠の例ではありませんが、機能します。 明らかな理由によるダウンロード速度は、多くの要因に依存します。



UPD古いカメラは体系的に新しいカメラに交換されているという意見があります。 昨夜は、今朝337古い5776新しい-273古い、5811新しいがありました。



UPD webvybory2013.ruもあり、他の選挙からの写真もあることがわかりました 。 この記事に書かれていることはすべてそれらに適用され、ドメインのみを変更する必要があります。



UPDカメラのステータスは常に変化しています。これに注意してください。 古いシステムは新しいものに置き換えられます。



All Articles