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

add book title metadata validation #3

Merged
merged 3 commits into from
Apr 28, 2023
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
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,12 @@ The following key/value pairs should be located in one or more `yaml` files moun
| `input.cleanup` | switch to control whether input files should be deleted after successful conversion | `true` |
| `output.path` | path to directory that converted files will be placed | `/app/output` |
| `output.extension` | file extension of output files | `.m4b` |
| `output.overwrite` | switch to control whether converted files should overwrite an existing file with the same name in the output location | `true` |
| `output.overwrite` | switch to control whether converted files should overwrite an existing file with the same name in the output location | `false` |
| `ffmpeg.run` | switch to control whether call to ffmpeg is allowed; useful to set `false` for debugging | `true` |
| `ffmpeg.path` | path to ffmpeg; if ffmpeg is in `PATH`, keep default value | `ffmpeg` |
| `ffmpeg.path` | path to `ffmpeg`; if `ffmpeg` is in `PATH`, keep default value | `ffmpeg` |
| `ffmpeg.additional_args` | list of extra args added to ffmpeg call; see [ffmpeg Additional Args](#ffmpeg-additional-args) for more details | `['-hide_banner', '-loglevel', 'error', '-nostats', '-y']` |
| `ffprobe.path` | path to `ffprobe`; if `ffprobe` is in `PATH`, keep default value | `ffprobe` |
| `ffprobe.additional_args` | list of extra args added to ffprobe call; these grab a specific value, so best not to change the default | `['-show_entries', 'format_tags=title', '-of', 'compact=p=0', '-v', '"0"']` |
| `logging.level` | controls logging verbosity; can be one of `info`, `warning`, `error`, `debug`; see [Debug Logging](#debug-logging) for important information on security concerns | `info` |
| `logging.format` | controls desired logging format | `'%(asctime)s - %(levelname)s - [%(name)s] %(message)s'` |

Expand Down
68 changes: 53 additions & 15 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os, yaml, logging, subprocess
from sys import exit as sys_exit
from pathlib import Path

config = {}
secret = {}
Expand Down Expand Up @@ -55,12 +56,16 @@ def app_setup():
log.error("(app_setup) Activation bytes not found in secrets nor in environment. Set activation_bytes and try again.")
sys_exit(1)

def output_exists(output_file) -> bool:
return os.path.isfile(output_file)

def cleanup(path):
if bool(config["input"]["cleanup"]):
os.remove(path)
log.info("(cleanup) Cleaned up " + path)
else:
if not bool(config["input"]["cleanup"]):
log.warning("(cleanup) Configuration 'input.cleanup: false' blocked cleanup. Set 'input.cleanup: true' to cleanup input files.")
return

os.remove(path)
log.info("(cleanup) Cleaned up " + path)

def ffmpeg_call(input_file, command):
log.info("(ffmpeg_call) Converting " + input_file)
Expand All @@ -69,6 +74,26 @@ def ffmpeg_call(input_file, command):
log.info("(ffmpeg_call) Successfully converted")
cleanup(input_file)

def get_book_title(path) -> str:
command = [config["ffprobe"]["path"], str(path)]
command[1:1] = config["ffprobe"]["additional_args"] # Splice additional args in
log.debug("(get_book_title) ffprobe command: " + str(command))

result = subprocess.check_output(command)
log.debug("(get_book_title) ffprobe result: " + result.decode("utf-8").replace("\n", ""))

return result.decode("utf-8").replace("\n", "")

def get_existing_book_titles() -> list:
existing_book_titles = []
existing_book_title_paths = list(Path(config["output"]["path"] + "/").rglob("*.[mM]4[bB]"))
log.debug("(get_existing_book_titles) existing_book_titles: " + str(existing_book_titles))
for file in existing_book_title_paths:
existing_book_titles.append(get_book_title(str(file)))

log.debug("(get_existing_book_titles) existing_book_titles: " + str(existing_book_titles))
return existing_book_titles

def convert(file):
log.info("(convert) Preparing to convert " + file)

Expand All @@ -85,17 +110,12 @@ def convert(file):
log.debug("(convert) Conversion command args: " + str(command))
log.debug("(convert) Conversion command string: " + command_str)

if bool(config["ffmpeg"]["run"]):
if not os.path.isfile(output_file):
ffmpeg_call(input_file, command)
else:
if bool(config["output"]["overwrite"]):
log.debug("(convert) Configuration 'output.overwrite: true'. Set 'output.overwrite: false' if overwriting is not desired.")
ffmpeg_call(input_file, command)
else:
log.warning("(convert) Detected converted file, skipping conversion. Note, set 'output.overwrite: true' to overwrite previously converted files.")
else:
if not bool(config["ffmpeg"]["run"]):
log.warning("(convert) Configuration 'ffmpeg.run: false' has blocked conversion. Set 'ffmpeg.run: true' to convert input files.")
return

log.debug("(convert) Configuration 'output.overwrite: true'. Set 'output.overwrite: false' if overwriting is not desired.")
ffmpeg_call(input_file, command)

if __name__ == "__main__":
app_setup()
Expand All @@ -106,7 +126,25 @@ def convert(file):
files = [f for f in files if f.endswith(config["input"]["extension"])]
log.debug("(__main__) Found files: " + str(files))

log.info("(__main__) Found " + str(len(files)) + " file(s) to convert.")
log.info("(__main__) Found " + str(len(files)) + " book(s) to convert.")

if len(files) == 0:
log.debug("(__main__) Exiting with no work to do.")
sys_exit()

existing_books = get_existing_book_titles()

log.info("(__main__) Found " + str(len(existing_books)) + " existing book(s).")

for file in files:
input_file_path = config["input"]["path"] + "/" + file
title = get_book_title(input_file_path)
if title in existing_books and not bool(config["output"]["overwrite"]):
log.debug("(__main__) Book already exists: " + str(file))
log.warning("(convert) Book with metadata `" + title + "` already exists; skipping conversion, but cleaning up. Note, set 'output.overwrite: true' to overwrite an existing book.")
cleanup(input_file_path)
break

log.debug("(__main__) converting: " + str(file))
convert(file)

12 changes: 11 additions & 1 deletion config/defaults.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ input:
output:
path: /app/output
extension: .m4b
overwrite: true
overwrite: false

ffmpeg:
run: true
Expand All @@ -18,6 +18,16 @@ ffmpeg:
- -nostats
- -y

ffprobe:
path: ffprobe
additional_args:
- -show_entries
- format_tags=title
- -of
- compact=p=0
- -v
- "0"

logging:
level: info
format: '%(asctime)s - %(levelname)s - [%(name)s] %(message)s'