123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- import os
- import shutil
- import re
- import glob
- import subprocess
- import json
- import sys
- try:
- import pil
- import logger
- import helpers
- from constants import Constants
- except ImportError:
- from . import pil
- from . import logger
- from . import helpers
- from .constants import Constants
- """
- The content of this file was originally written by https://github.com/taengstagram
- The code has been edited for use in PyInstaLive.
- """
- def _get_file_index(filename):
- """ Extract the numbered index in filename for sorting """
- mobj = re.match(r'.+\-(?P<idx>[0-9]+)\.[a-z]+', filename)
- if mobj:
- return int(mobj.group('idx'))
- return -1
- def assemble(user_called=True, retry_with_zero_m4v=False):
- try:
- ass_json_file = pil.assemble_arg if pil.assemble_arg.endswith(".json") else pil.assemble_arg + ".json"
- ass_mp4_file = os.path.join(pil.dl_path, os.path.basename(ass_json_file).replace("_downloads", "").replace(".json", ".mp4"))
- ass_segment_dir = pil.assemble_arg if not pil.assemble_arg.endswith(".json") else pil.assemble_arg.replace(".json", "")
-
- broadcast_info = {}
- if not os.path.isdir(ass_segment_dir) or not os.listdir(ass_segment_dir):
- logger.error('The segment directory does not exist or does not contain any files: %s' % ass_segment_dir)
- logger.separator()
- return
- if not os.path.isfile(ass_json_file):
- logger.warn("No matching json file found for the segment directory, trying to continue without it.")
- ass_stream_id = os.listdir(ass_segment_dir)[0].split('-')[0]
- broadcast_info['id'] = ass_stream_id
- broadcast_info['broadcast_status'] = "active"
- broadcast_info['segments'] = {}
- else:
- with open(ass_json_file) as info_file:
- try:
- broadcast_info = json.load(info_file)
- except Exception as e:
- logger.warn("Could not decode json file, trying to continue without it.")
- ass_stream_id = os.listdir(ass_segment_dir)[0].split('-')[0]
- broadcast_info['id'] = ass_stream_id
- broadcast_info['broadcast_status'] = "active"
- broadcast_info['segments'] = {}
- if broadcast_info.get('broadcast_status', '') == 'post_live':
- logger.error('Video segment files from replay downloads cannot be assembled.')
- return
- stream_id = str(broadcast_info['id'])
- segment_meta = broadcast_info.get('segments', {})
- if segment_meta:
- all_segments = [
- os.path.join(ass_segment_dir, k)
- for k in broadcast_info['segments'].keys()]
- else:
- all_segments = list(filter(
- os.path.isfile,
- glob.glob(os.path.join(ass_segment_dir, '%s-*.m4v' % stream_id))))
- all_segments = sorted(all_segments, key=lambda x: _get_file_index(x))
- sources = []
- audio_stream_format = 'assembled_source_{0}_{1}_mp4.tmp'
- video_stream_format = 'assembled_source_{0}_{1}_m4a.tmp'
- video_stream = ''
- audio_stream = ''
- has_skipped_zero_m4v = False
- if not all_segments:
- logger.error("No video segment files have been found in the specified folder.")
- logger.separator()
- return
- else:
- logger.info("Assembling video segment files from specified folder: {}".format(ass_segment_dir))
- for segment in all_segments:
- segment = re.sub('\?.*$', '', segment)
- if not os.path.isfile(segment.replace('.m4v', '.m4a')):
- logger.warn('Audio segment not found: {0!s}'.format(segment.replace('.m4v', '.m4a')))
- continue
- if segment.endswith('-init.m4v'):
- logger.info('Replacing %s' % segment)
- segment = os.path.join(
- os.path.dirname(os.path.realpath(__file__)), 'repair', 'init.m4v')
- if segment.endswith('-0.m4v') and not retry_with_zero_m4v:
- has_skipped_zero_m4v = True
- continue
- video_stream = os.path.join(
- ass_segment_dir, video_stream_format.format(stream_id, len(sources)))
- audio_stream = os.path.join(
- ass_segment_dir, audio_stream_format.format(stream_id, len(sources)))
- file_mode = 'ab'
- with open(video_stream, file_mode) as outfile, open(segment, 'rb') as readfile:
- shutil.copyfileobj(readfile, outfile)
- with open(audio_stream, file_mode) as outfile, open(segment.replace('.m4v', '.m4a'), 'rb') as readfile:
- shutil.copyfileobj(readfile, outfile)
- if audio_stream and video_stream:
- sources.append({'video': video_stream, 'audio': audio_stream})
- for n, source in enumerate(sources):
- ffmpeg_binary = os.getenv('FFMPEG_BINARY', 'ffmpeg')
- cmd = [
- ffmpeg_binary, '-loglevel', 'warning', '-y',
- '-i', source['audio'],
- '-i', source['video'],
- '-c:v', 'copy', '-c:a', 'copy', ass_mp4_file]
- #fnull = open(os.devnull, 'w')
- fnull = None
- exit_code = subprocess.call(cmd, stdout=fnull, stderr=subprocess.STDOUT)
- if exit_code != 0:
- logger.warn("FFmpeg exit code not '0' but '{:d}'.".format(exit_code))
- if has_skipped_zero_m4v and not retry_with_zero_m4v:
- logger.binfo("*-0.m4v segment was detected but skipped, retrying to assemble video without "
- "skipping it.")
- os.remove(source['audio'])
- os.remove(source['video'])
- logger.separator()
- assemble(user_called, retry_with_zero_m4v=True)
- return
- else:
- logger.info('The video file has been generated: %s' % os.path.basename(ass_mp4_file))
- os.remove(source['audio'])
- os.remove(source['video'])
- if user_called:
- logger.separator()
- except Exception as e:
- logger.error("An error occurred: {:s}".format(str(e)))
|