assembler.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import os
  2. import shutil
  3. import re
  4. import glob
  5. import subprocess
  6. import json
  7. import sys
  8. try:
  9. import pil
  10. import logger
  11. import helpers
  12. from constants import Constants
  13. except ImportError:
  14. from . import pil
  15. from . import logger
  16. from . import helpers
  17. from .constants import Constants
  18. """
  19. The content of this file was originally written by https://github.com/taengstagram
  20. The code has been edited for use in PyInstaLive.
  21. """
  22. def _get_file_index(filename):
  23. """ Extract the numbered index in filename for sorting """
  24. mobj = re.match(r'.+\-(?P<idx>[0-9]+)\.[a-z]+', filename)
  25. if mobj:
  26. return int(mobj.group('idx'))
  27. return -1
  28. def assemble(user_called=True, retry_with_zero_m4v=False):
  29. try:
  30. ass_json_file = pil.assemble_arg if pil.assemble_arg.endswith(".json") else pil.assemble_arg + ".json"
  31. ass_mp4_file = os.path.join(pil.dl_path, os.path.basename(ass_json_file).replace("_downloads", "").replace(".json", ".mp4"))
  32. ass_segment_dir = pil.assemble_arg if not pil.assemble_arg.endswith(".json") else pil.assemble_arg.replace(".json", "")
  33. if pil.verbose:
  34. logger.plain("{}\n{}\n{}".format(ass_json_file, ass_mp4_file, ass_segment_dir))
  35. broadcast_info = {}
  36. if not os.path.isdir(ass_segment_dir) or not os.listdir(ass_segment_dir):
  37. logger.error('The segment directory does not exist or does not contain any files: %s' % ass_segment_dir)
  38. logger.separator()
  39. return
  40. if not os.path.isfile(ass_json_file):
  41. logger.warn("No matching json file found for the segment directory, trying to continue without it.")
  42. ass_stream_id = os.listdir(ass_segment_dir)[0].split('-')[0]
  43. broadcast_info['id'] = ass_stream_id
  44. broadcast_info['broadcast_status'] = "active"
  45. broadcast_info['segments'] = {}
  46. else:
  47. with open(ass_json_file) as info_file:
  48. try:
  49. broadcast_info = json.load(info_file)
  50. except Exception as e:
  51. logger.warn("Could not decode json file, trying to continue without it.")
  52. ass_stream_id = os.listdir(ass_segment_dir)[0].split('-')[0]
  53. broadcast_info['id'] = ass_stream_id
  54. broadcast_info['broadcast_status'] = "active"
  55. broadcast_info['segments'] = {}
  56. if broadcast_info.get('broadcast_status', '') == 'post_live':
  57. logger.error('Video segment files from replay downloads cannot be assembled.')
  58. return
  59. stream_id = str(broadcast_info['id'])
  60. segment_meta = broadcast_info.get('segments', {})
  61. if segment_meta:
  62. all_segments = [
  63. os.path.join(ass_segment_dir, k)
  64. for k in broadcast_info['segments'].keys()]
  65. else:
  66. all_segments = list(filter(
  67. os.path.isfile,
  68. glob.glob(os.path.join(ass_segment_dir, '%s-*.m4v' % stream_id))))
  69. all_segments = sorted(all_segments, key=lambda x: _get_file_index(x))
  70. sources = []
  71. audio_stream_format = 'assembled_source_{0}_{1}_mp4.tmp'
  72. video_stream_format = 'assembled_source_{0}_{1}_m4a.tmp'
  73. video_stream = ''
  74. audio_stream = ''
  75. has_skipped_zero_m4v = False
  76. if not all_segments:
  77. logger.error("No video segment files have been found in the specified folder.")
  78. logger.separator()
  79. return
  80. else:
  81. logger.info("Assembling video segment files from specified folder: {}".format(ass_segment_dir))
  82. for segment in all_segments:
  83. if not os.path.isfile(segment.replace('.m4v', '.m4a')):
  84. logger.warn('Audio segment not found: {0!s}'.format(segment.replace('.m4v', '.m4a')))
  85. continue
  86. if segment.endswith('-init.m4v'):
  87. logger.info('Replacing %s' % segment)
  88. segment = os.path.join(
  89. os.path.dirname(os.path.realpath(__file__)), 'repair', 'init.m4v')
  90. if segment.endswith('-0.m4v') and not retry_with_zero_m4v:
  91. has_skipped_zero_m4v = True
  92. continue
  93. video_stream = os.path.join(
  94. ass_segment_dir, video_stream_format.format(stream_id, len(sources)))
  95. audio_stream = os.path.join(
  96. ass_segment_dir, audio_stream_format.format(stream_id, len(sources)))
  97. file_mode = 'ab'
  98. with open(video_stream, file_mode) as outfile, open(segment, 'rb') as readfile:
  99. shutil.copyfileobj(readfile, outfile)
  100. with open(audio_stream, file_mode) as outfile, open(segment.replace('.m4v', '.m4a'), 'rb') as readfile:
  101. shutil.copyfileobj(readfile, outfile)
  102. if audio_stream and video_stream:
  103. sources.append({'video': video_stream, 'audio': audio_stream})
  104. for n, source in enumerate(sources):
  105. ffmpeg_binary = os.getenv('FFMPEG_BINARY', 'ffmpeg')
  106. cmd = [
  107. ffmpeg_binary, '-loglevel', 'warning', '-y',
  108. '-i', source['audio'],
  109. '-i', source['video'],
  110. '-c:v', 'copy', '-c:a', 'copy', ass_mp4_file]
  111. #fnull = open(os.devnull, 'w')
  112. fnull = None
  113. exit_code = subprocess.call(cmd, stdout=fnull, stderr=subprocess.STDOUT)
  114. if exit_code != 0:
  115. logger.warn("FFmpeg exit code not '0' but '{:d}'.".format(exit_code))
  116. if has_skipped_zero_m4v and not retry_with_zero_m4v:
  117. logger.binfo("*-0.m4v segment was detected but skipped, retrying to assemble video without "
  118. "skipping it.")
  119. os.remove(source['audio'])
  120. os.remove(source['video'])
  121. logger.separator()
  122. assemble(user_called, retry_with_zero_m4v=True)
  123. return
  124. else:
  125. logger.info('The video file has been generated: %s' % os.path.basename(ass_mp4_file))
  126. os.remove(source['audio'])
  127. os.remove(source['video'])
  128. if user_called:
  129. logger.separator()
  130. except Exception as e:
  131. logger.error("An error occurred: {:s}".format(str(e)))
  132. def organize_videos():
  133. # Find the files in the current directory.
  134. current_dir_files = os.listdir()
  135. # The Instagram videos are in MP4 format.
  136. video_format = ['mp4']
  137. # Find the MP4 files and save them in a variable called 'filenames'.
  138. filenames = [filename for filename in current_dir_files if filename.split('.')[-1] in video_format]
  139. if len(filenames) == 0:
  140. print('Detected no valid files in current directory.')
  141. return
  142. # We want a dictionary where the filenames are the keys and the usernames are the values.
  143. filenames_to_usernames = {}
  144. # Populate the dictionary by going through each filename and removing the undesired characters, leaving just the usernames.
  145. for filename in filenames:
  146. filename_parts = filename.split('_')[1:-3]
  147. usernames = '_'.join(filename_parts)
  148. filenames_to_usernames[filename] = usernames
  149. # The usernames are the values of the filenames_to_usernames dictionary.
  150. usernames = set(filenames_to_usernames.values())
  151. # Make a folder for each username.
  152. for username in usernames:
  153. if not os.path.isdir(username):
  154. os.mkdir(username)
  155. # Move the videos into the folders
  156. for filename, username in filenames_to_usernames.items():
  157. shutil.move(filename, username)
  158. # Can also do os.rename(filename, '{}/{}'.format(username, filename))
  159. num_videos_moved = len(filenames_to_usernames.keys())
  160. print("{} videos moved successfully.".format(num_videos_moved))