added ffmpeg progressbar
This commit is contained in:
parent
5c31d6f7a8
commit
805017fea5
@ -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()
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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])
|
|
||||||
|
Loading…
Reference in New Issue
Block a user