Skip to content

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsubashi committed Oct 26, 2022
0 parents commit 465ad78
Show file tree
Hide file tree
Showing 8 changed files with 871 additions and 0 deletions.
186 changes: 186 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
## Project Specific ##
version_generator/_my_version.py

## MacOS ##
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

## Vi ##
# Swap
[._]*.s[a-v][a-z]
!*.svg # comment out if you don't need vector files
[._]*.sw[a-p]
[._]s[a-rt-v][a-z]
[._]ss[a-gi-z]
[._]sw[a-p]

# Session
Session.vim
Sessionx.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags
# Persistent undo
[._]*.un~

## Python ##
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyCharm
.idea

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
coverage_html/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
m4b-utils
---------

A collection of command line utilities for creating, editing, and generally working with m4b files.



52 changes: 52 additions & 0 deletions audacity-chapter-labels-to-ffmpeg-metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
"""Convert an audacity label file to an ffmpeg compatible metadata file.
Remember to put a throwaway label at the very end to mark the extent of the file.
"""
import argparse
import re

if __name__ == "__main__":
# Parse arguments
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('inputFile', type=str,
help="The label file from audacity.")

args = parser.parse_args()

# Read in all the chapters
chapters = list()
with open(args.inputFile, 'r') as f:
for line in f:
x = re.match(r"(\d+\.?\d*)\s+\d+\.?\d*\s+(.*)", line)
if not x:
continue
seconds = float(x.group(1))
title = x.group(2)

timestamp = int(seconds * 1000)
chap = {
"title": title,
"startTime": timestamp
}
chapters.append(chap)

# Format the data we read
text = ""
for i in range(len(chapters)-1):
chap = chapters[i]
title = chap['title']
start = chap['startTime']
end = chapters[i+1]['startTime']-1
text += ("[CHAPTER]\n"
"TIMEBASE=1/1000\n"
f"START={start}\n"
f"END={end}\n"
f"title={title}\n"
)

# Send it to the user
print(text)


# vim: syntax=python
52 changes: 52 additions & 0 deletions edit-av-metadata
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/bin/bash

# exit when any command fails
set -e

# Declare colors, if available
if test -n "$ncolors" && test $ncolors -ge 8; then
# Modifiers
BOLD="$(tput bold)"
RESET="$(tput sgr0)"

# Colors
RED="$(tput setaf 1)"
WHITE="$(tput setaf 7)"
YELLOW="$(tput setaf 226)"
fi

# Ensure we have at least 1 arguments
if (( $# < 1 )); then
EXE_NAME=$(basename -- "$0")
echo "Usage: ${EXE_NAME} path/to/file"
exit 1
fi

# Set up a temporary directory
TMPDIR=$(mktemp -d -t edit-av-metadata)

# Do it!
for ENTRY in "$@"; do
FILENAME="$(basename -- "${ENTRY}")"
TEMP_FILE="${TMPDIR}/${FILENAME}"
METADATA_FILE="${TMPDIR}/metadata.txt"

# Alert the User
echo "Editing '${FILENAME}'"

# Extract the Metadata
ffmpeg -i "${ENTRY}" -f ffmetadata "${METADATA_FILE}"

# Open the metadata file for editing and wait for close
gvim -f "${METADATA_FILE}"

# Write the metadata to a new file
ffmpeg -i "${ENTRY}" -i "${METADATA_FILE}" -map_metadata 1 -map_chapters 1 -codec copy "${TEMP_FILE}"

# Replace the old file
mv "${TEMP_FILE}" "${ENTRY}"
done

echo "Done!"

# vim: syntax=sh
55 changes: 55 additions & 0 deletions m4b-chapter-split
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash

# Declare colors, if available
if test -n "$ncolors" && test $ncolors -ge 8; then
# Modifiers
BOLD="$(tput bold)"
RESET="$(tput sgr0)"

# Colors
RED="$(tput setaf 1)"
WHITE="$(tput setaf 7)"
YELLOW="$(tput setaf 226)"
fi


# Ensure we have at least 1 arguments
if (( $# < 1 )); then
EXE_NAME=$(basename -- "$0")
echo "Usage: ${EXE_NAME} path/to/m4b"
exit 1
fi

# Ensure all subsequent arguments end in m4b
for ENTRY in "$@"; do
if [[ ! "$ENTRY" == *.m4b ]]; then
echo "${RED}'${WHITE}${ENTRY}${RED}' does not have an m4b extension.${RESET}"
exit 1
fi
done

set -efu

for inputFile in "$@"; do
# Dump the cover art
ffmpeg -i ${inputFile} ${inputFile%%.m4b}.png

# Dump the chapters
ffprobe -hide_banner "$inputFile" -print_format json -show_chapters -loglevel error |
jq -r '.chapters[] | [ .id, .start_time, .end_time, .tags.title | tostring ] | join(" ")' |
while read ID START END CHAPTER; do
TEMP="000$ID"
ID_PADDED="${TEMP:(-3)}"
ffmpeg -nostdin \
-ss "$START" -to "$END" \
-i "$inputFile" \
-map 0:a \
-map_chapters -1 \
-c copy \
-metadata title="$CHAPTER" \
"${inputFile%.*}-$ID_PADDED.m4a";
done
done
echo "DONE!"

# vim: syntax=sh
Loading

0 comments on commit 465ad78

Please sign in to comment.