added ffmpeg progressbar

This commit is contained in:
Hellow2 2023-06-15 09:58:48 +02:00
parent 5c31d6f7a8
commit 805017fea5
3 changed files with 49 additions and 64 deletions

View File

@ -1,47 +1,18 @@
from typing import List, Tuple from typing import List, Tuple
import ffmpeg from tqdm import tqdm
from ffmpeg_progress_yield import FfmpegProgress
import subprocess import subprocess
from ..utils.shared import BITRATE, AUDIO_FORMAT, CODEX_LOGGER as LOGGER from ..utils.shared import BITRATE, AUDIO_FORMAT, CODEX_LOGGER as LOGGER
from ..objects import Target from ..objects import Target
def remove_intervals(target: Target, interval_list: List[Tuple[float, float]]): def correct_codec(target: Target, bitrate_kb: int = BITRATE, audio_format: str = AUDIO_FORMAT, interval_list: List[Tuple[float, float]] = None):
if not target.exists: if not target.exists:
LOGGER.warning(f"Target doesn't exist: {target.file_path}") LOGGER.warning(f"Target doesn't exist: {target.file_path}")
return return
output_target = Target( interval_list = interval_list or []
path=target._path,
file=str(target._file) + "." + AUDIO_FORMAT
)
# https://stackoverflow.com/questions/50594412/cut-multiple-parts-of-a-video-with-ffmpeg
aselect_list: List[str] = []
start = 0
next_start = 0
for end, next_start in interval_list:
aselect_list.append(f"between(t,{start},{end})")
start = next_start
aselect_list.append(f"gte(t,{next_start})")
select = f"aselect='{'+'.join(aselect_list)}',asetpts=N/SR/TB"
r = subprocess.run(["ffmpeg", "-i", str(target.file_path), "-af", select, str(output_target.file_path)])
if r.stderr != "":
LOGGER.debug(r.stderr)
output_target.copy_content(target)
output_target.delete()
def correct_codec(target: Target, bitrate_kb: int = BITRATE, audio_format: str = AUDIO_FORMAT):
if not target.exists:
LOGGER.warning(f"Target doesn't exist: {target.file_path}")
return
bitrate_b = int(bitrate_kb / 1024) bitrate_b = int(bitrate_kb / 1024)
@ -50,17 +21,35 @@ def correct_codec(target: Target, bitrate_kb: int = BITRATE, audio_format: str =
file=str(target._file) + "." + audio_format file=str(target._file) + "." + audio_format
) )
stream = ffmpeg.input(target.file_path) # get the select thingie
stream = stream.audio # https://stackoverflow.com/questions/50594412/cut-multiple-parts-of-a-video-with-ffmpeg
stream = ffmpeg.output( aselect_list: List[str] = []
stream,
str(output_target.file_path), start = 0
audio_bitrate=bitrate_b, next_start = 0
format=audio_format for end, next_start in interval_list:
) aselect_list.append(f"between(t,{start},{end})")
out, err = ffmpeg.run(stream, quiet=True, overwrite_output=True) start = next_start
if err != "": aselect_list.append(f"gte(t,{next_start})")
LOGGER.debug(err)
select = f"aselect='{'+'.join(aselect_list)}',asetpts=N/SR/TB"
# build the ffmpeg command
ffmpeg_command = [
"ffmpeg",
"-i", str(target.file_path),
"-af", select,
"-b", str(bitrate_b),
str(output_target.file_path)
]
# run the ffmpeg command with a progressbar
ff = FfmpegProgress(ffmpeg_command)
with tqdm(total=100, position=1, desc="ffmpeg") as pbar:
for progress in ff.run_command_with_progress():
pbar.update(progress - pbar.n)
LOGGER.debug(ff.stderr)
output_target.copy_content(target) output_target.copy_content(target)
output_target.delete() output_target.delete()

View File

@ -1,7 +1,7 @@
import logging import logging
import random import random
from copy import copy from copy import copy
from typing import Optional, Union, Type, Dict, Set, List from typing import Optional, Union, Type, Dict, Set, List, Tuple
import threading import threading
from queue import Queue from queue import Queue
@ -345,15 +345,16 @@ class Page:
file=str(random.randint(0, 999999)) file=str(random.randint(0, 999999))
) )
r = self.download_song_to_target(source=sources[0], target=temp_target, desc=song.title) source = sources[0]
r = self.download_song_to_target(source=source, target=temp_target, desc=song.title)
if not r.is_fatal_error: if not r.is_fatal_error:
r.merge(self._post_process_targets(song, temp_target)) r.merge(self._post_process_targets(song, temp_target, source))
return r return r
def _post_process_targets(self, song: Song, temp_target: Target) -> DownloadResult: def _post_process_targets(self, song: Song, temp_target: Target, source: Source) -> DownloadResult:
correct_codec(temp_target) correct_codec(temp_target, interval_list=self.get_skip_intervals(song, source))
self.post_process_hook(song, temp_target) self.post_process_hook(song, temp_target)
@ -371,6 +372,9 @@ class Page:
return r return r
def get_skip_intervals(self, song: Song, source: Source) -> List[Tuple[float, float]]:
return []
def post_process_hook(self, song: Song, temp_target: Target, **kwargs): def post_process_hook(self, song: Song, temp_target: Target, **kwargs):
pass pass

View File

@ -5,6 +5,8 @@ from enum import Enum
import sponsorblock import sponsorblock
from sponsorblock.errors import HTTPException, NotFoundException from sponsorblock.errors import HTTPException, NotFoundException
from music_kraken.objects import Song, Source
from ..objects import Source, DatabaseObject, Song, Target from ..objects import Source, DatabaseObject, Song, Target
from .abstract import Page from .abstract import Page
from ..objects import ( from ..objects import (
@ -18,7 +20,6 @@ from ..objects import (
FormattedText, FormattedText,
ID3Timestamp ID3Timestamp
) )
from ..audio.codec import remove_intervals
from ..connection import Connection from ..connection import Connection
from ..utils.support_classes import DownloadResult from ..utils.support_classes import DownloadResult
from ..utils.shared import YOUTUBE_LOGGER, INVIDIOUS_INSTANCE, BITRATE, ENABLE_SPONSOR_BLOCK from ..utils.shared import YOUTUBE_LOGGER, INVIDIOUS_INSTANCE, BITRATE, ENABLE_SPONSOR_BLOCK
@ -384,23 +385,15 @@ class YouTube(Page):
return DownloadResult(total=1) return DownloadResult(total=1)
return DownloadResult(error_message=f"Streaming to the file went wrong: {endpoint}, {str(target.file_path)}") return DownloadResult(error_message=f"Streaming to the file went wrong: {endpoint}, {str(target.file_path)}")
def post_process_hook(self, song: Song, temp_target: Target, **kwargs): def get_skip_intervals(self, song: Song, source: Source) -> List[Tuple[float, float]]:
if not ENABLE_SPONSOR_BLOCK: if not ENABLE_SPONSOR_BLOCK:
return return []
# find the YouTube id
source_list = song.source_collection.get_sources_from_page(self.SOURCE_TYPE)
if len(source_list) <= 0:
self.LOGGER.warning(f"Couldn't find a youtube source in the post_processing_hook for {song.option_string}")
return
source = source_list[0]
parsed = YouTubeUrl(source.url) parsed = YouTubeUrl(source.url)
if parsed.url_type != YouTubeUrlType.VIDEO: if parsed.url_type != YouTubeUrlType.VIDEO:
self.LOGGER.warning(f"{source.url} is no video url.") self.LOGGER.warning(f"{source.url} is no video url.")
return return []
# call the sponsorblock api, and remove the segments from the audio
segments = [] segments = []
try: try:
segments = self.sponsorblock_client.get_skip_segments(parsed.id) segments = self.sponsorblock_client.get_skip_segments(parsed.id)
@ -409,5 +402,4 @@ class YouTube(Page):
except HTTPException as e: except HTTPException as e:
self.LOGGER.warning(f"{e}") self.LOGGER.warning(f"{e}")
if len(segments) <= 0: return [(segment.start, segment.end) for segment in segments]
remove_intervals(temp_target, [(segment.start, segment.end) for segment in segments])