Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use PyAV 14 #599

Merged
merged 1 commit into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,9 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install FFmpeg
run: |
sudo apt update
sudo apt install ffmpeg
- name: Install Auto-Editor
run: pip install -e .
- name: Run Debug
run: auto-editor --debug
- name: Test
run: |
auto-editor --debug
auto-editor test all
run: auto-editor test all
14 changes: 13 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
# 26.1.0

## Features
- Use PyAV 14.

## Fixes
- Remove `--ffmpeg-location` arg.
- Remove help text for recently removed args.

**Full Changelog**: https://github.com/WyattBlue/auto-editor/compare/26.0.1...26.1.0


# 26.0.1

## Fixes
Expand All @@ -7,7 +19,7 @@
- Remove the `ae-ffmpeg` package dependency.
- Remove unused args, functions.

**Full Changelog**: https://github.com/WyattBlue/auto-editor/compare/26.0.0...26.0.1
**Full Changelog**: https://github.com/WyattBlue/auto-editor/compare/26.0.0...26.0.1


# 26.0.0
Expand Down
2 changes: 1 addition & 1 deletion auto_editor/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "26.0.1"
__version__ = "26.1.0"
18 changes: 4 additions & 14 deletions auto_editor/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,6 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
metavar="PATH",
help="Set where the temporary directory is located",
)
parser.add_argument(
"--ffmpeg-location",
metavar="PATH",
help="Set a custom path to the ffmpeg location",
)
parser.add_text("Display Options:")
parser.add_argument(
"--progress",
Expand Down Expand Up @@ -251,7 +246,7 @@ def main_options(parser: ArgumentParser) -> ArgumentParser:
return parser


def download_video(my_input: str, args: Args, ffmpeg: FFmpeg, log: Log) -> str:
def download_video(my_input: str, args: Args, log: Log) -> str:
log.conwrite("Downloading video...")

def get_domain(url: str) -> str:
Expand All @@ -267,18 +262,14 @@ def get_domain(url: str) -> str:
else:
output_format = args.output_format

yt_dlp_path = args.yt_dlp_location

cmd = ["--ffmpeg-location", ffmpeg.get_path("yt-dlp", log)]

if download_format is not None:
cmd.extend(["-f", download_format])

cmd.extend(["-o", output_format, my_input])

if args.yt_dlp_extras is not None:
cmd.extend(args.yt_dlp_extras.split(" "))

yt_dlp_path = args.yt_dlp_location
try:
location = get_stdout(
[yt_dlp_path, "--get-filename", "--no-warnings"] + cmd
Expand Down Expand Up @@ -347,11 +338,10 @@ def main() -> None:
is_machine = args.progress == "machine"
log = Log(args.debug, args.quiet, args.temp_dir, is_machine, no_color)

ffmpeg = FFmpeg(args.ffmpeg_location)
paths = []
for my_input in args.input:
if my_input.startswith("http://") or my_input.startswith("https://"):
paths.append(download_video(my_input, args, ffmpeg, log))
paths.append(download_video(my_input, args, log))
else:
if not splitext(my_input)[1]:
if isdir(my_input):
Expand All @@ -365,7 +355,7 @@ def main() -> None:
paths.append(my_input)

try:
edit_media(paths, ffmpeg, args, log)
edit_media(paths, args, log)
except KeyboardInterrupt:
log.error("Keyboard Interrupt")
log.cleanup()
Expand Down
10 changes: 5 additions & 5 deletions auto_editor/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import av
from av import AudioResampler

from auto_editor.ffwrapper import FFmpeg, FileInfo, initFileInfo
from auto_editor.ffwrapper import FileInfo, initFileInfo
from auto_editor.lib.contracts import is_int, is_str
from auto_editor.make_layers import clipify, make_av, make_timeline
from auto_editor.output import Ensure, parse_bitrate
Expand Down Expand Up @@ -160,7 +160,7 @@ def parse_export(export: str, log: Log) -> dict[str, Any]:
log.error(f"'{name}': Export must be [{', '.join([s for s in parsing.keys()])}]")


def edit_media(paths: list[str], ffmpeg: FFmpeg, args: Args, log: Log) -> None:
def edit_media(paths: list[str], args: Args, log: Log) -> None:
bar = initBar(args.progress)
tl = None

Expand Down Expand Up @@ -294,7 +294,7 @@ def make_media(tl: v3, output_path: str) -> None:

if ctr.default_aud != "none":
ensure = Ensure(bar, samplerate, log)
audio_paths = make_new_audio(tl, ctr, ensure, args, ffmpeg, bar, log)
audio_paths = make_new_audio(tl, ctr, ensure, args, bar, log)
else:
audio_paths = []

Expand Down Expand Up @@ -343,8 +343,8 @@ def make_media(tl: v3, output_path: str) -> None:
for i, sub_path in enumerate(sub_paths):
subtitle_input = av.open(sub_path)
subtitle_inputs.append(subtitle_input)
subtitle_stream = output.add_stream(
template=subtitle_input.streams.subtitles[0]
subtitle_stream = output.add_stream_from_template(
subtitle_input.streams.subtitles[0]
)
if i < len(src.subtitles) and src.subtitles[i].lang is not None:
subtitle_stream.metadata["language"] = src.subtitles[i].lang # type: ignore
Expand Down
26 changes: 0 additions & 26 deletions auto_editor/ffwrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,6 @@
from auto_editor.utils.log import Log


def _get_ffmpeg(reason: str, ffloc: str | None, log: Log) -> str:
program = "ffmpeg" if ffloc is None else ffloc
if (path := which(program)) is None:
log.error(f"{reason} needs ffmpeg cli but couldn't find ffmpeg on PATH.")
return path


@dataclass(slots=True)
class FFmpeg:
ffmpeg_location: str | None
path: str | None = None

def get_path(self, reason: str, log: Log) -> str:
if self.path is not None:
return self.path

self.path = _get_ffmpeg(reason, self.ffmpeg_location, log)
return self.path

def Popen(self, reason: str, cmd: list[str], log: Log) -> Popen:
if self.path is None:
self.path = _get_ffmpeg(reason, self.ffmpeg_location, log)

return Popen([self.path] + cmd, stdout=PIPE, stderr=PIPE)


def mux(input: Path, output: Path, stream: int) -> None:
input_container = av.open(input, "r")
output_container = av.open(output, "w")
Expand Down
58 changes: 13 additions & 45 deletions auto_editor/render/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import io
from pathlib import Path
from platform import system

import av
import numpy as np
from av.filter.loudnorm import stats

from auto_editor.ffwrapper import FFmpeg, FileInfo
from auto_editor.ffwrapper import FileInfo
from auto_editor.lang.json import Lexer, Parser
from auto_editor.lang.palet import env
from auto_editor.lib.contracts import andc, between_c, is_int_or_float
Expand Down Expand Up @@ -56,25 +56,11 @@ def parse_norm(norm: str, log: Log) -> dict | None:
log.error(e)


def parse_ebu_bytes(norm: dict, stderr: bytes, log: Log) -> tuple[str, str]:
start = end = 0
lines = stderr.splitlines()

for index, line in enumerate(lines):
if line.startswith(b"[Parsed_loudnorm"):
start = index + 1
continue
if start != 0 and line.startswith(b"}"):
end = index + 1
break

if start == 0 or end == 0:
log.error(f"Invalid loudnorm stats.\n{stderr!r}")

def parse_ebu_bytes(norm: dict, stat: bytes, log: Log) -> tuple[str, str]:
try:
parsed = Parser(Lexer("loudnorm", b"\n".join(lines[start:end]))).expr()
parsed = Parser(Lexer("loudnorm", stat)).expr()
except MyError:
log.error(f"Invalid loudnorm stats.\n{start=},{end=}\n{stderr!r}")
log.error(f"Invalid loudnorm stats.\n{stat!r}")

for key in ("input_i", "input_tp", "input_lra", "input_thresh", "target_offset"):
val = float(parsed[key])
Expand All @@ -101,29 +87,17 @@ def parse_ebu_bytes(norm: dict, stderr: bytes, log: Log) -> tuple[str, str]:


def apply_audio_normalization(
ffmpeg: FFmpeg, norm: dict, pre_master: Path, path: Path, log: Log
norm: dict, pre_master: Path, path: Path, log: Log
) -> None:
if norm["tag"] == "ebu":
first_pass = (
f"loudnorm=i={norm['i']}:lra={norm['lra']}:tp={norm['tp']}:"
f"offset={norm['gain']}:print_format=json"
f"i={norm['i']}:lra={norm['lra']}:tp={norm['tp']}:" f"offset={norm['gain']}"
)
log.debug(f"audio norm first pass: {first_pass}")
file_null = "NUL" if system() in ("Windows", "cli") else "/dev/null"
cmd = [
"-hide_banner",
"-i",
f"{pre_master}",
"-af",
first_pass,
"-vn",
"-sn",
"-f",
"null",
file_null,
]
stderr = ffmpeg.Popen("EBU", cmd, log).communicate()[1]
name, filter_args = parse_ebu_bytes(norm, stderr, log)
with av.open(f"{pre_master}") as container:
stats_ = stats(first_pass, container.streams.audio[0])
av.logging.set_level(None)
name, filter_args = parse_ebu_bytes(norm, stats_, log)
else:
assert "t" in norm

Expand Down Expand Up @@ -310,13 +284,7 @@ def mix_audio_files(sr: int, audio_paths: list[str], output_path: str) -> None:


def make_new_audio(
tl: v3,
ctr: Container,
ensure: Ensure,
args: Args,
ffmpeg: FFmpeg,
bar: Bar,
log: Log,
tl: v3, ctr: Container, ensure: Ensure, args: Args, bar: Bar, log: Log
) -> list[str]:
sr = tl.sr
tb = tl.tb
Expand Down Expand Up @@ -390,7 +358,7 @@ def make_new_audio(
with open(pre_master, "wb") as fid:
write(fid, sr, arr)

apply_audio_normalization(ffmpeg, norm, pre_master, path, log)
apply_audio_normalization(norm, pre_master, path, log)

bar.end()

Expand Down
2 changes: 1 addition & 1 deletion auto_editor/render/subtitle.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def _ensure(input_: Input, format: str, stream: int) -> str:
output = av.open(output_bytes, "w", format=format)

in_stream = input_.streams.subtitles[stream]
out_stream = output.add_stream(template=in_stream)
out_stream = output.add_stream_from_template(in_stream)

for packet in input_.demux(in_stream):
if packet.dts is None:
Expand Down
1 change: 0 additions & 1 deletion auto_editor/utils/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ class Args:
player: str | None = None
no_open: bool = False
temp_dir: str | None = None
ffmpeg_location: str | None = None
progress: str = "modern"
version: bool = False
debug: bool = False
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ license = { text = "Unlicense" }
authors = [{ name = "WyattBlue", email = "[email protected]" }]
requires-python = ">=3.10,<3.14"
dependencies = [
"numpy>=1.23.0,<3.0",
"pyav==13.1.*",
"numpy>=1.24,<3.0",
"pyav==14.0.0rc4",
]
keywords = [
"video", "audio", "media", "editor", "editing",
Expand Down
Loading