upload_video.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. # -*- coding: utf-8 -*-
  2. import copy
  3. import json
  4. import os
  5. import re
  6. import shutil
  7. import subprocess
  8. import time
  9. from requests_toolbelt import MultipartEncoder
  10. from . import config
  11. def download_video(self, media_id, filename, media=False, folder='videos'):
  12. if not media:
  13. self.media_info(media_id)
  14. media = self.last_json['items'][0]
  15. filename = '{0}_{1}.mp4'.format(media['user']['username'], media_id) if not filename else '{0}.mp4'.format(filename)
  16. try:
  17. clips = media['video_versions']
  18. except Exception:
  19. return False
  20. fname = os.path.join(folder, filename)
  21. if os.path.exists(fname):
  22. return os.path.abspath(fname)
  23. response = self.session.get(clips[0]['url'], stream=True)
  24. if response.status_code == 200:
  25. with open(fname, 'wb') as f:
  26. response.raw.decode_content = True
  27. shutil.copyfileobj(response.raw, f)
  28. return os.path.abspath(fname)
  29. def get_video_info(filename):
  30. res = {}
  31. try:
  32. terminalResult = subprocess.Popen(["ffprobe", filename],
  33. stdout=subprocess.PIPE,
  34. stderr=subprocess.STDOUT)
  35. for x in terminalResult.stdout.readlines():
  36. # Duration: 00:00:59.51, start: 0.000000, bitrate: 435 kb/s
  37. m = re.search(r'duration: (\d\d:\d\d:\d\d\.\d\d),', str(x), flags=re.IGNORECASE)
  38. if m is not None:
  39. res['duration'] = m.group(1)
  40. # Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 480x268
  41. m = re.search(r'video:\s.*\s(\d+)x(\d+)\s', str(x), flags=re.IGNORECASE)
  42. if m is not None:
  43. res['width'] = m.group(1)
  44. res['height'] = m.group(2)
  45. finally:
  46. if 'width' not in res:
  47. print(("ERROR: 'ffprobe' not found, please install "
  48. "'ffprobe' with one of following methods:"))
  49. print(" sudo apt-get install ffmpeg")
  50. print("or sudo apt-get install -y libav-tools")
  51. return res
  52. def upload_video(self, video, thumbnail, caption=None, upload_id=None):
  53. if upload_id is None:
  54. upload_id = str(int(time.time() * 1000))
  55. data = {
  56. 'upload_id': upload_id,
  57. '_csrftoken': self.token,
  58. 'media_type': '2',
  59. '_uuid': self.uuid,
  60. }
  61. m = MultipartEncoder(data, boundary=self.uuid)
  62. self.session.headers.update({'X-IG-Capabilities': '3Q4=',
  63. 'X-IG-Connection-Type': 'WIFI',
  64. 'Host': 'i.instagram.com',
  65. 'Cookie2': '$Version=1',
  66. 'Accept-Language': 'en-US',
  67. 'Accept-Encoding': 'gzip, deflate',
  68. 'Content-type': m.content_type,
  69. 'Connection': 'keep-alive',
  70. 'User-Agent': self.user_agent})
  71. response = self.session.post(config.API_URL + "upload/video/", data=m.to_string())
  72. if response.status_code == 200:
  73. body = json.loads(response.text)
  74. upload_url = body['video_upload_urls'][3]['url']
  75. upload_job = body['video_upload_urls'][3]['job']
  76. with open(video, 'rb') as video_bytes:
  77. video_data = video_bytes.read()
  78. # solve issue #85 TypeError: slice indices must be integers or None or have an __index__ method
  79. request_size = len(video_data) // 4
  80. last_request_extra = len(video_data) - 3 * request_size
  81. headers = copy.deepcopy(self.session.headers)
  82. self.session.headers.update({
  83. 'X-IG-Capabilities': '3Q4=',
  84. 'X-IG-Connection-Type': 'WIFI',
  85. 'Cookie2': '$Version=1',
  86. 'Accept-Language': 'en-US',
  87. 'Accept-Encoding': 'gzip, deflate',
  88. 'Content-type': 'application/octet-stream',
  89. 'Session-ID': upload_id,
  90. 'Connection': 'keep-alive',
  91. 'Content-Disposition': 'attachment; filename="video.mov"',
  92. 'job': upload_job,
  93. 'Host': 'upload.instagram.com',
  94. 'User-Agent': self.user_agent
  95. })
  96. for i in range(4):
  97. start = i * request_size
  98. if i == 3:
  99. end = i * request_size + last_request_extra
  100. else:
  101. end = (i + 1) * request_size
  102. length = last_request_extra if i == 3 else request_size
  103. content_range = "bytes {start}-{end}/{len_video}".format(
  104. start=start, end=end - 1, len_video=len(video_data)).encode('utf-8')
  105. self.session.headers.update({'Content-Length': str(end - start), 'Content-Range': content_range})
  106. response = self.session.post(upload_url, data=video_data[start:start + length])
  107. self.session.headers = headers
  108. if response.status_code == 200:
  109. if self.configure_video(upload_id, video, thumbnail, caption):
  110. self.expose()
  111. return True
  112. return False
  113. def configure_video(self, upload_id, video, thumbnail, caption=''):
  114. clipInfo = get_video_info(video)
  115. self.upload_photo(photo=thumbnail, caption=caption, upload_id=upload_id)
  116. data = self.json_data({
  117. 'upload_id': upload_id,
  118. 'source_type': 3,
  119. 'poster_frame_index': 0,
  120. 'length': 0.00,
  121. 'audio_muted': False,
  122. 'filter_type': 0,
  123. 'video_result': 'deprecated',
  124. 'clips': {
  125. 'length': clipInfo['duration'],
  126. 'source_type': '3',
  127. 'camera_position': 'back',
  128. },
  129. 'extra': {
  130. 'source_width': clipInfo['width'],
  131. 'source_height': clipInfo['height'],
  132. },
  133. 'device': self.device_settings,
  134. 'caption': caption,
  135. })
  136. return self.send_request('media/configure/?video=1', data)