Skip to content

Commit ff48f77

Browse files
committed
Add bitrate option
1 parent a8ac7d7 commit ff48f77

File tree

5 files changed

+27
-11
lines changed

5 files changed

+27
-11
lines changed

src/m4b_util/helpers/audiobook.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
class Audiobook:
2323
"""Store all the info pertaining to an audiobook."""
2424
author: str = None
25-
# Set duration to a big number, (hopefully larger than our actual duration)
26-
# just in case we try to write chapters without knowing the final duration.
25+
bitrate: str = "128k"
2726
chapters: list = field(default_factory=lambda: [])
2827
cover: str = None
2928
date: str = None
@@ -284,7 +283,7 @@ def _bind_multiple_segments(self, output_path):
284283
if segment.title:
285284
cmd.extend(["-metadata", f"title={segment.title}"])
286285
cmd.extend(["-filter_complex", "[0:a]asetpts=N/SR/TB[s0]", "-map", "[s0]",
287-
"-c:a", "aac", out_m4a, "-y"])
286+
"-c:a", "aac", "-b:a", self.bitrate, out_m4a, "-y"])
288287
tasks.append({
289288
"name": file.stem,
290289
"command": cmd

src/m4b_util/subcommands/bind.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def _parse_args():
1616
)
1717
parser.add_argument('input_folder', nargs="?", type=str, help="The folder to scan for input files.")
1818
parser.add_argument('-a', "--author", type=str, help="Name of the author.")
19+
parser.add_argument('-b', "--bitrate", type=str, default="128k", help="Bitrate to use for the audiobook. (e.g. 128k)")
1920
parser.add_argument('-c', "--cover", type=str, help="Image file to use as cover")
2021
parser.add_argument('-f', "--files", nargs='+', type=str, action='extend',
2122
help="Specific files to use for the audiobook. Overrides `input_folder`.")
@@ -40,7 +41,7 @@ def check_args(input_folder, files, output_dir):
4041
# We either need an input folder or a list of files.
4142
if not input_folder and not files:
4243
print("[bold red]Error:[/] You must provide either an input folder or a list of files.")
43-
return -1
44+
return False
4445

4546
# Warn the user if they specified both an input folder and a list of files.
4647
if input_folder and files:
@@ -50,18 +51,22 @@ def check_args(input_folder, files, output_dir):
5051
# Make sure the output directory exists, if it was specified.
5152
if output_dir and not Path(output_dir).is_dir():
5253
print(f"[bold red]Error:[/] '{output_dir}' is not a directory.")
53-
return -1
54+
return False
55+
56+
return True
5457

5558

5659
def run():
5760
"""Entrypoint for bind subcommand."""
5861
args = _parse_args()
5962

60-
check_args(args.input_folder, args.files, args.output_dir)
63+
if not check_args(args.input_folder, args.files, args.output_dir):
64+
return -1
6165

6266
# Set info from args
6367
book = Audiobook(
6468
author=args.author,
69+
bitrate=args.bitrate,
6570
cover=args.cover,
6671
output_name=args.output_name,
6772
title=args.title,
@@ -95,9 +100,6 @@ def run():
95100
return 0
96101

97102
# Add the files to the binder
98-
if not args.input_folder:
99-
print("[red]Error:[/] No input folder specified.")
100-
return -1
101103
book.add_chapters_from_directory(
102104
input_dir=args.input_folder,
103105
use_filenames=args.use_filename,

src/m4b_util/subcommands/slide.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def _parse_args():
1313
description='Slide chapter segments up or down.'
1414
)
1515
parser.add_argument('input_file', help='Input filename')
16+
parser.add_argument('-b', "--bitrate", type=str, default="128k", help='Bitrate to use for the audiobook. (e.g. 128k)')
1617
parser.add_argument('-d', "--duration", type=float, help='Duration to shift (seconds). Negative values allowed.')
1718
parser.add_argument("--trim-start", type=float, help='Duration to trim from beginning (seconds).')
1819

@@ -99,7 +100,7 @@ def run():
99100
if args.trim_start:
100101
slide_dur = slide_dur - args.trim_start
101102

102-
book = Audiobook()
103+
book = Audiobook(bitrate=args.bitrate)
103104
book.add_chapters_from_chaptered_file(input_path)
104105
if book.chapters:
105106
# Find initial end times, in case we need to modify it.

tests/helpers/test_audiobook.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,3 +638,18 @@ def test_set_metadata():
638638
assert b.chapters[1].title == "Part I"
639639
assert b.chapters[1].start_time == 330.987
640640
assert b.chapters[1].end_time == 335.943
641+
642+
643+
def test_custom_bitrate(mp3_path, tmp_path):
644+
"""Bind an audiobook with a custom bitrate."""
645+
out_file_path = tmp_path / "Book.m4b"
646+
b = Audiobook(bitrate="64k")
647+
b.add_chapters_from_directory(mp3_path)
648+
b.bind(out_file_path)
649+
650+
# Verify output
651+
probe = ffprobe.run_probe(out_file_path)
652+
assert probe
653+
654+
# Check bitrate
655+
assert probe.format['bit_rate'] == "35380"

tests/subcommands/test_cover.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Cover Subcommand Tests."""
22
import filecmp
3-
import platform
43
import shutil
54
from unittest import mock
65

0 commit comments

Comments
 (0)