Beispiel #1
0
def download_process_hook(event: dict):
    """
    Allows to handle processes of downloading episode's file.
    It is called by `youtube_dl.YoutubeDL`
    """
    total_bytes = event.get("total_bytes") or event.get(
        "total_bytes_estimate", 0)
    episode_process_hook(
        status=EpisodeStatus.DL_EPISODE_DOWNLOADING,
        filename=event["filename"],
        total_bytes=total_bytes,
        processed_bytes=event.get("downloaded_bytes", total_bytes),
    )
Beispiel #2
0
def ffmpeg_preparation(filename: str):
    """
    Ffmpeg allows to fix problem with length of audio track
    (in metadata value for this is incorrect, but fact length is fully correct)
    """

    logger.info(f"Start FFMPEG preparations for {filename} === ")
    src_path = os.path.join(settings.TMP_AUDIO_PATH, filename)
    total_bytes = get_file_size(src_path)
    episode_process_hook(
        status=EpisodeStatus.DL_EPISODE_POSTPROCESSING,
        filename=filename,
        total_bytes=total_bytes,
        processed_bytes=0,
    )
    tmp_path = os.path.join(settings.TMP_AUDIO_PATH, f"tmp_{filename}")

    logger.info(f"Start SUBPROCESS (filesize watching) for {filename} === ")
    p = Process(
        target=post_processing_process_hook,
        kwargs={
            "filename": filename,
            "target_path": tmp_path,
            "total_bytes": total_bytes
        },
    )
    p.start()
    try:
        completed_proc = subprocess.run(
            [
                "ffmpeg", "-y", "-i", src_path, "-vn", "-acodec", "libmp3lame",
                "-q:a", "5", tmp_path
            ],
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            check=True,
            timeout=settings.FFMPEG_TIMEOUT,
        )
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as err:
        episode_process_hook(status=EpisodeStatus.ERROR, filename=filename)
        with suppress(IOError):
            os.remove(tmp_path)

        err_details = f"FFMPEG failed with errors: {err}"
        if stdout := getattr(err, "stdout", ""):
            err_details += f"\n{str(stdout, encoding='utf-8')}"

        p.terminate()
        raise FFMPegPreparationError(err_details)
Beispiel #3
0
 def test_call_hook__ok(self, mocked_redis):
     mocked_redis.get.return_value = {"total_bytes": 1024}
     episode_process_hook(
         EpisodeStatus.DL_EPISODE_DOWNLOADING,
         "test-episode.mp3",
         total_bytes=1024,
         processed_bytes=124,
     )
     mocked_redis.set.assert_called_with(
         "test-episode",
         {
             "event_key": "test-episode",
             "status": str(EpisodeStatus.DL_EPISODE_DOWNLOADING),
             "processed_bytes": 124,
             "total_bytes": 1024,
         },
         ttl=settings.DOWNLOAD_EVENT_REDIS_TTL,
     )
Beispiel #4
0
def ffmpeg_preparation(filename: str):
    """ Ffmpeg allows to fix problem with length of audio track
        (in metadata value for this is incorrect, but fact length is fully correct)
    """

    logger.info(f"Start FFMPEG preparations for {filename} === ")
    src_path = os.path.join(settings.TMP_AUDIO_PATH, filename)
    episode_process_hook(
        status=EpisodeStatuses.episode_postprocessing,
        filename=filename,
        total_bytes=get_file_size(src_path),
        processed_bytes=0,
    )
    tmp_filename = os.path.join(settings.TMP_AUDIO_PATH, f"tmp_{filename}")
    proc = subprocess.Popen(
        ["ffmpeg", "-i", src_path, "-strict", "-2", "-y", tmp_filename])
    outs, errs = proc.communicate(timeout=settings.FFMPEG_TIMEOUT)
    if outs:
        logger.info(outs)
    if errs:
        logger.error(errs)
        episode_process_hook(status=EpisodeStatuses.error, filename=filename)

    try:
        os.remove(src_path)
        os.rename(tmp_filename, src_path)
    except IOError as err:
        logger.exception(
            "Failed to rename/remove tmp file after ffmpeg preparation")
        episode_process_hook(status=EpisodeStatuses.error, filename=filename)
        raise FFMPegPreparationError(err)

    total_file_size = get_file_size(src_path)
    episode_process_hook(
        status=EpisodeStatuses.episode_postprocessing,
        filename=filename,
        total_bytes=total_file_size,
        processed_bytes=total_file_size,
    )
    logger.info("FFMPEG Preparation for %s was done", filename)
Beispiel #5
0
            err_details += f"\n{str(stdout, encoding='utf-8')}"

        p.terminate()
        raise FFMPegPreparationError(err_details)

    p.terminate()
    logger.info(
        "FFMPEG success done preparation for file %s:\n%s",
        filename,
        str(completed_proc.stdout, encoding="utf-8"),
    )

    try:
        assert os.path.exists(
            tmp_path), f"Prepared file {tmp_path} wasn't created"
        os.remove(src_path)
        os.rename(tmp_path, src_path)
    except (IOError, AssertionError) as err:
        episode_process_hook(status=EpisodeStatus.ERROR, filename=filename)
        raise FFMPegPreparationError(
            f"Failed to rename/remove tmp file: {err}")

    total_file_size = get_file_size(src_path)
    episode_process_hook(
        status=EpisodeStatus.DL_EPISODE_POSTPROCESSING,
        filename=filename,
        total_bytes=total_file_size,
        processed_bytes=total_file_size,
    )
    logger.info("FFMPEG Preparation for %s was done", filename)