Skip to content

Commit

Permalink
v0.1.3
Browse files Browse the repository at this point in the history
- Improved logging and printing messages
- Logging logs to a file in ~/.baet (or equiv on Windows)
- Fixed bugs preventing jobs from running correctly and some command line options from working as expected
  • Loading branch information
TimeTravelPenguin authored and TimeTravelPenguin committed Jan 5, 2024
1 parent b389460 commit ec4717c
Show file tree
Hide file tree
Showing 11 changed files with 503 additions and 94 deletions.
65 changes: 56 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,71 @@
# Bulk Audio Extract Tool

<img src="./images/help-preview.svg" alt="baet help screen" style="display: block; margin: auto; max-height: 500px">

## About

Bulk Audio Extract Tool (BAET) is a commandline tool to bulk export audio tracks from within a single directory.

## Install

### Requirements

BAET will run on Windows, macOS, and Linux. Listed below the pre-installation requirements:

- FFmpeg ([website](https://ffmpeg.org))
- Python v3.11+ ([website](https://www.python.org))

### Installing BAET

Installation is done via `pip`.
Depending on your platform, to call python, you may need to use the command `python` or `python3`.
Typing `python3 --version` or `python --version` should display the currently installed python environment your PATH.
For the remainder of this document, replace instances of `python3` with the appropriate alias on your machine.
For the remainder of this document, replace instances of `python` with the appropriate alias on your machine.

To install the most recent stable release, use:

```bash
python -m pip install baet
```

For pre-releases, use:

```bash
python -m pip install baet --pre
```

To update/upgrade to a new version, use:

```bash
python -m pip install baet -U [--pre]
```

To verify your install, call

To install the requirements:
```bash
python3 -m pip install -r requirements.txt
baet --version
```

Then, call `./main.py --help` to see application information.
## Usage

## Add to Path
Once installed, calling `baet --help` will display the general help screen, showing a list of options you can use.

To simply extract the audio tracks of all videos in a directory `~/inputs`,
and extract each into a subdirectory of `~/outputs`, call

On Linux/MacOS, locate your `~/.bashrc` or `~/.zshrc` file and edit it, adding:
```bash
BAET_PATH="/path/to/BulkAudioExtractTool/main.py"
alias baet="python3 $(BAET_PATH)"
baet -i "~/inputs" -o "~/outputs"
```
Restart your terminal and enter `baet --help`. The application's help screen should now show from any directory.

Unless you add the option `--no-subdirs`, a video `~/inputs/my_video.mp4` will have each audio track individually
exported to an audio file located in `./outputs/my_video/`.

### Note on the help screen

Currently, the help screen contains descriptions starting with `[TODO]`.
This indicates that the associated option may or may not be implemented fully or at all.

## Known issues

- The option `--no-subdirs` may cause BAET to misbehave if two files are generated with the same name,
unless the option `--overwrite` is given, in which case one file will be overwritten.
280 changes: 280 additions & 0 deletions images/help-preview.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
[tool.poetry]
name = "BAET"
version = "0.1.2.post4.dev0"
description = ""
version = "0.1.3.dev11"
description = "A tool to bulk extract audio tracks from video using FFmpeg"
authors = ["TimeTravelPenguin <TimeTravelPenguin@gmail.com>"]
readme = "README.md"
packages = [{ include = "BAET", from = "src" }]
classifiers = [
"Environment :: Console",
"Topic :: Multimedia :: Sound/Audio",
"Topic :: Multimedia :: Sound/Audio :: Conversion",
"Topic :: Multimedia :: Video :: Conversion",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
"Operating System :: POSIX :: Linux",
Expand All @@ -16,7 +19,7 @@ classifiers = [
]

[project.urls]
Homepage = "https://github.com/TimeTravelPenguin/BulkAudioExtractTool"
homepage = "https://github.com/TimeTravelPenguin/BulkAudioExtractTool"

[tool.poetry.scripts]
baet = "BAET.__main__:main"
Expand Down
4 changes: 2 additions & 2 deletions src/BAET/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from ._console import app_console
from ._logging import create_logger
from ._logging import configure_logging, create_logger
from ._theme import app_theme

__all__ = ["app_console", "create_logger", "app_theme"]
__all__ = ["app_console", "configure_logging", "create_logger", "app_theme"]
17 changes: 12 additions & 5 deletions src/BAET/__main__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import logging
import sys
from datetime import datetime
from pathlib import Path

import rich
from rich.live import Live
from rich.traceback import install

from BAET import app_console, create_logger
from BAET import app_console, configure_logging, create_logger
from BAET.app_args import get_args
from BAET.extract import MultiTrackAudioBulkExtractor

Expand All @@ -19,11 +20,17 @@ def main() -> None:
rich.print(args)
sys.exit(0)

if not args.debug_options.logging:
logging.disable(logging.CRITICAL)
if args.debug_options.logging:
log_path = Path("~/.baet").expanduser()
log_path.mkdir(parents=True, exist_ok=True)
log_file = log_path / f"logs_{datetime.now()}.txt"
log_file.touch()

configure_logging(enable_logging=True, file_out=log_file)
else:
configure_logging(enable_logging=False, file_out=None)

logger = create_logger()
logger.info("Building extractor jobs")
extractor = MultiTrackAudioBulkExtractor(args)

with Live(extractor, console=app_console):
Expand Down
13 changes: 12 additions & 1 deletion src/BAET/_logging.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import inspect
import logging
from logging import Logger
from logging import FileHandler, Logger
from pathlib import Path

from rich.logging import RichHandler

Expand Down Expand Up @@ -28,3 +29,13 @@ def create_logger() -> Logger:

logger = app_logger.getChild(module_name)
return logger


def configure_logging(*, enable_logging: bool = True, file_out: Path | None = None) -> None:
if not enable_logging:
logging.disable(logging.CRITICAL)

if file_out is not None:
handler = FileHandler(filename=file_out)
handler.setFormatter(rich_handler.formatter)
app_logger.addHandler(handler)
57 changes: 57 additions & 0 deletions src/BAET/_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import subprocess
from os import PathLike

from ._console import error_console
from ._logging import create_logger

logger = create_logger()


def which_ffmpeg() -> str | PathLike[str] | None:
from shutil import which

return which("ffmpeg")


def get_ffmpeg_version() -> str | None:
try:
ffmpeg = which_ffmpeg()

if not ffmpeg:
return None

proc = subprocess.run([ffmpeg, "-version"], capture_output=True)

if proc.returncode != 0:
err = proc.stderr.decode("utf-8")
raise RuntimeError(f"FFmpeg returned non-zero exit code when getting version:\n{err}")

output = proc.stdout.decode("utf-8")
return output[14 : output.find("Copyright")].strip()

except RuntimeError as e:
logger.critical("%s: %s", type(e).__name__, e)
error_console.print_exception()
raise e


class FFmpegVersionInfo:
def __init__(self) -> None:
self._version: str | None = None

@property
def version(self) -> str:
if not self._version:
self._version = get_ffmpeg_version()

return self._version or "None"

def __str__(self) -> str:
return self.version


ffmpeg_version_info = FFmpegVersionInfo()

if __name__ == "__main__":
version_info = FFmpegVersionInfo()
print("Version:", version_info)
40 changes: 16 additions & 24 deletions src/BAET/app_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
from rich_argparse import HelpPreviewAction, RichHelpFormatter
from typing_extensions import Annotated

from BAET._console import app_console
from BAET._metadata import app_version
from ._console import app_console
from ._metadata import app_version
from ._path import ffmpeg_version_info

file_type_pattern = re.compile(r"^\.?(\w+)$")

Expand All @@ -40,7 +41,6 @@ def validate_exclude_nonempty(cls, v: str) -> Pattern[str] | None:


class OutputConfigurationOptions(BaseModel):
output_streams_separately: bool = Field(...)
overwrite_existing: bool = Field(...)
no_output_subdirs: bool = Field(...)
acodec: str = Field(...)
Expand Down Expand Up @@ -68,29 +68,33 @@ class DebugOptions(BaseModel):
class AppDescription:
@staticmethod
def __rich_console__(console: Console, options: ConsoleOptions) -> RenderResult:
yield Markdown("# Bulk Audio Extract Tool (src)")
yield Padding(Markdown("# Bulk Audio Extract Tool"), pad=(1, 0))
yield "Extract audio from a directory of videos using FFMPEG.\n"

website_link = "https://github.com/TimeTravelPenguin/BulkAudioExtractTool"
desc_kvps = [
(
Padding(Text("App name:", justify="right"), (0, 5, 0, 0)),
Text("App name:", justify="right"),
Text(
"Bulk Audio Extract Tool (src)",
"Bulk Audio Extract Tool (BAET)",
style="argparse.prog",
justify="left",
),
),
(
Padding(Text("Version:", justify="right"), (0, 5, 0, 0)),
Text("App Version:", justify="right"),
Text(app_version(), style="app.version", justify="left"),
),
(
Padding(Text("Author:", justify="right"), (0, 5, 0, 0)),
Text("FFmpeg Version:", justify="right"),
Text(ffmpeg_version_info.version, style="app.version", justify="left"),
),
(
Text("Author:", justify="right"),
Text("Phillip Smith", style="bright_yellow", justify="left"),
),
(
Padding(Text("Website:", justify="right"), (0, 5, 0, 0)),
Text("Website:", justify="right"),
Text(
website_link,
style=f"underline blue link {website_link}",
Expand All @@ -103,7 +107,7 @@ def __rich_console__(console: Console, options: ConsoleOptions) -> RenderResult:
grid.add_column(justify="left")
grid.add_column(justify="right")
for key, value in desc_kvps:
grid.add_row(key, value)
grid.add_row(Padding(key, (0, 3, 0, 0)), value)

yield grid

Expand All @@ -125,8 +129,8 @@ def get_formatter(prog: str) -> RichHelpFormatter:
return argparse.ArgumentParser(
prog="Bulk Audio Extract Tool (src)",
description=description, # type: ignore
epilog=Markdown( # type: ignore
"Phillip Smith, 2023",
epilog=Markdown(
"Phillip Smith, 2024",
justify="right",
style="argparse.prog",
),
Expand Down Expand Up @@ -211,17 +215,6 @@ def get_args() -> AppArgs:
description="Override the default output behavior of the application.",
)

output_group.add_argument(
"--output-streams-separately",
"--sep",
default=False,
action="store_true",
help="[TODO] When set, individual commands are given to [blue]ffmpeg[/] to export each stream. Otherwise, "
"a single command is given to ffmpeg to export all streams. This latter option will result in all files "
"appearing in the directory at once, and so any errors may result in a loss of data. Setting this flag "
"may be useful when experiencing errors. (Default: False)",
)

output_group.add_argument(
"--overwrite-existing",
"--overwrite",
Expand Down Expand Up @@ -324,7 +317,6 @@ def get_args() -> AppArgs:
input_filters = InputFilters(include=args.include, exclude=args.exclude)

output_config = OutputConfigurationOptions(
output_streams_separately=args.output_streams_separately,
overwrite_existing=args.overwrite_existing,
no_output_subdirs=args.no_output_subdirs,
acodec=args.acodec,
Expand Down
Loading

0 comments on commit ec4717c

Please sign in to comment.