22 Commits

Author SHA1 Message Date
Jamie Hardt
66ac136270 autopep 2025-05-20 15:23:14 -07:00
Jamie Hardt
aa64d5e183 Fixed a bug in the increment code
Not sure how that happened I thought this used to work!
2025-05-20 15:19:46 -07:00
Jamie Hardt
6766e81b23 Commenting this out for now 2024-10-17 21:32:34 -07:00
Jamie Hardt
a2ce03a259 Added default lines to the top of a new list 2024-10-17 12:19:08 -07:00
Jamie Hardt
0ba40893df Adding some blank picture methods...
...to be implemented later if ever a need.
2024-10-17 09:50:27 -07:00
Jamie Hardt
e2b93f5183 Fixed obvious bug in --help-commands 2024-10-16 14:39:41 -07:00
Jamie Hardt
7015e80cf9 Merge pull request #1 from iluvcapra/iluvcapra-patch-py313
Add support for Python 3.13
2024-10-16 14:33:43 -07:00
Jamie Hardt
042f3116dd Update pyproject.toml 2024-10-16 14:33:02 -07:00
Jamie Hardt
20518fa31c Update pyproject.toml 2024-10-16 14:32:31 -07:00
Jamie Hardt
c4a2e380de Update pylint.yml
Adding 3.13 to test matrix
2024-10-16 14:30:09 -07:00
Jamie Hardt
4ec028b51b Pylints 2024-10-16 14:19:15 -07:00
Jamie Hardt
ac00f93a8d Update pyproject.toml
Nudged version to 0.5.0
2024-10-16 14:10:49 -07:00
Jamie Hardt
8bcfd2ee54 Added .venv to gitignore 2024-10-16 14:09:57 -07:00
Jamie Hardt
63ec226be1 Implemented sorting on creation 2024-10-16 14:09:18 -07:00
Jamie Hardt
69cdb6dec1 Amended error line 2024-10-16 13:37:59 -07:00
Jamie Hardt
720eacc2a4 Added possible sort modes for --create 2024-10-15 22:09:55 -07:00
Jamie Hardt
afa02e4c96 Twiddle 2024-10-15 20:59:13 -07:00
Jamie Hardt
b986a36281 lint 2024-10-15 11:34:32 -07:00
Jamie Hardt
fb90b5db3c Online doc changes 2024-10-15 11:33:21 -07:00
Jamie Hardt
e69573b2a4 Made some online doc tweaks 2024-10-15 11:09:31 -07:00
Jamie Hardt
55cf591690 Added some commentary 2024-10-15 11:04:13 -07:00
Jamie Hardt
f0e05a9609 Paramaterized output stream
for reporting messages from BatchfileParser.
2024-07-07 22:44:52 -07:00
5 changed files with 78 additions and 22 deletions

View File

@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"] python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }} - name: Set up Python ${{ matrix.python-version }}

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
dist/ dist/
*.pyc *.pyc
poetry.lock poetry.lock
.venv/

View File

@@ -4,7 +4,7 @@ mfbatch main - Command entrypoint for mfbatch
import os import os
from glob import glob from glob import glob
from subprocess import run from subprocess import CalledProcessError, run
import sys import sys
from argparse import ArgumentParser from argparse import ArgumentParser
import shlex import shlex
@@ -29,18 +29,51 @@ def execute_batch_list(batch_list_path: str, dry_run: bool, interactive: bool):
parser.eval(line, line_no, interactive) parser.eval(line, line_no, interactive)
def create_batch_list(command_file: str, recursive=True): def create_batch_list(command_file: str, recursive=True, sort_mode='path'):
""" """
Read all FLAC files in the cwd and create a batchfile that re-creates all Read all FLAC files in the cwd and create a batchfile that re-creates all
of their metadata. of their metadata.
:param recursive: Recursively enter directories
:param sort_mode: Order of paths in the batch list. Either 'path',
'mtime', 'ctime', 'name'
""" """
with open(command_file, mode='w', encoding='utf-8') as f: with open(command_file, mode='w', encoding='utf-8') as f:
f.write("# mfbatch\n\n") f.write("# mfbatch\n\n")
# f.write("""
# # :set DESCRIPTION ""
# # :set TITLE ""
# # :set VERSION ""
# # :set ALBUM ""
# # :set ARTIST ""
# # :set TRACKNUMBER ""
# # :set COPYRIGHT ""
# # :set LICENSE ""
# # :set CONTACT ""
# # :set ORGAIZATION ""
# # :set LOCATION ""
# # :set MICROPHONE ""
# """)
metadatums = {} metadatums = {}
flac_files = glob('./**/*.flac', recursive=recursive) flac_files = glob('./**/*.flac', recursive=recursive)
flac_files = sorted(flac_files)
if sort_mode == 'path':
flac_files = sorted(flac_files)
elif sort_mode == 'mtime':
flac_files = sorted(flac_files, key=os.path.getmtime)
elif sort_mode == 'ctime':
flac_files = sorted(flac_files, key=os.path.getctime)
elif sort_mode == 'name':
flac_files = sorted(flac_files, key=os.path.basename)
for path in tqdm(flac_files, unit='File', desc='Scanning FLAC files'): for path in tqdm(flac_files, unit='File', desc='Scanning FLAC files'):
this_file_metadata = metadata_funcs.read_metadata(path) try:
this_file_metadata = metadata_funcs.read_metadata(path)
except CalledProcessError as e:
f.write(f"# !!! METAFLAC ERROR ({e.returncode}) while reading "
f"metadata from the file {path}\n\n")
continue
for this_key, this_value in this_file_metadata.items(): for this_key, this_value in this_file_metadata.items():
if this_key not in metadatums: if this_key not in metadatums:
f.write(f":set {this_key} " f.write(f":set {this_key} "
@@ -78,10 +111,13 @@ def main():
op.add_argument('-W', '--write', default=False, op.add_argument('-W', '--write', default=False,
action='store_true', action='store_true',
help="execute batch list, write to files") help="execute batch list, write to files")
op.add_argument('-p', '--path', metavar='DIR', op.add_argument('-p', '--path', metavar='DIR',
help='chdir to DIR before running', help='chdir to DIR before running',
default=None) default=None)
op.add_argument('-s', '--sort', metavar='MODE', action='store',
default='path', help="when creating, Set mode to sort "
"files by. Default is 'path'. 'ctime, 'mtime' and 'name' "
"are also options.")
op.add_argument('-n', '--dry-run', action='store_true', op.add_argument('-n', '--dry-run', action='store_true',
help="dry-run -W.") help="dry-run -W.")
op.add_argument('-f', '--batchfile', metavar='FILE', op.add_argument('-f', '--batchfile', metavar='FILE',
@@ -101,12 +137,12 @@ def main():
if options.help_commands: if options.help_commands:
print("Command Help\n------------") print("Command Help\n------------")
commands = [command for command in dir(BatchfileParser) if commands = [command for command in dir(BatchfileParser) if
not command.startswith('_')] not command.startswith('_') and command != "eval"]
print(f"{inspect.cleandoc(BatchfileParser.__doc__ or '')}\n\n") print(f"{inspect.cleandoc(BatchfileParser.__doc__ or '')}\n\n")
for command in commands: for command in commands:
meth = getattr(BatchfileParser, command) meth = getattr(BatchfileParser, command)
if isinstance(meth, Callable): if isinstance(meth, Callable):
print(f"{inspect.cleandoc(meth.__doc__ or '')}\n") print(f"- {inspect.cleandoc(meth.__doc__ or '')}\n")
sys.exit(0) sys.exit(0)
@@ -116,7 +152,7 @@ def main():
if options.create: if options.create:
mode_given = True mode_given = True
create_batch_list(options.batchfile) create_batch_list(options.batchfile, sort_mode=options.sort)
if options.edit: if options.edit:
mode_given = True mode_given = True

View File

@@ -9,7 +9,7 @@ import shutil
import re import re
import os.path import os.path
from typing import Dict, Tuple, Optional from typing import Callable, Dict, Tuple, Optional
from mfbatch.metaflac import write_metadata as flac from mfbatch.metaflac import write_metadata as flac
@@ -131,16 +131,17 @@ class CommandEnv:
""" """
Increment all increment keys. Increment all increment keys.
""" """
for k, v in self.incr.items(): for k, _ in self.incr.items():
v = int(v) val = int(self.metadatums[k])
self.metadatums[k] = self.incr[k] % (v + 1) self.metadatums[k] = self.incr[k] % (val + 1)
class BatchfileParser: class BatchfileParser:
""" """
A batchfile is a text file of lines. Lines either begin with a '#' to denote a A batchfile is a text file of lines. Lines either begin with a '#' to denote a
comment, a ':' to denote a Command, and if neither of these are present, the comment, a ':' to denote a Command, and if neither of these are present, the
line is interpreted as a file path to act upon. Empty lines are ignored. line is interpreted as a file path to act upon. Empty lines are ignored. Lines
are split into arguments using `shlex.split`.
If a line ends with a backslash '\\', the backslash is deleted and the contents If a line ends with a backslash '\\', the backslash is deleted and the contents
of the following line are appended to the end of the present line. of the following line are appended to the end of the present line.
@@ -152,6 +153,7 @@ they appear in the batchfile.
dry_run: bool dry_run: bool
env: CommandEnv env: CommandEnv
write_metadata_f: Callable
COMMAND_LEADER = ':' COMMAND_LEADER = ':'
COMMENT_LEADER = '#' COMMENT_LEADER = '#'
@@ -160,6 +162,7 @@ they appear in the batchfile.
self.dry_run = True self.dry_run = True
self.env = CommandEnv() self.env = CommandEnv()
self.write_metadata_f = flac self.write_metadata_f = flac
self.outstream = sys.stdout
def eval(self, line: str, lineno: int, interactive: bool): def eval(self, line: str, lineno: int, interactive: bool):
""" """
@@ -191,11 +194,11 @@ they appear in the batchfile.
def _write_metadata_impl(self, line): def _write_metadata_impl(self, line):
if self.dry_run: if self.dry_run:
print("DRY RUN would write metadata here.") print("DRY RUN would write metadata here.", file=self.outstream)
else: else:
sys.stdout.write("Writing metadata... ") self.outstream.write("Writing metadata... ")
self.write_metadata_f(line, self.env.metadatums) self.write_metadata_f(line, self.env.metadatums)
sys.stdout.write("Complete!") self.outstream.write("Complete!")
self.env.increment_all() self.env.increment_all()
self.env.revert_onces() self.env.revert_onces()
@@ -208,10 +211,10 @@ they appear in the batchfile.
for l in value_lines: for l in value_lines:
if key: if key:
sys.stdout.write(f"{key:.<30} \033[4m{l}\033[0m\n") self.outstream.write(f"{key:.<30} \033[4m{l}\033[0m\n")
key = None key = None
else: else:
sys.stdout.write(f"{' ' * 30} \033[4m{l}\033[0m\n") self.outstream.write(f"{' ' * 30} \033[4m{l}\033[0m\n")
def _handle_file(self, line, interactive): def _handle_file(self, line, interactive):
while True: while True:
@@ -220,9 +223,9 @@ they appear in the batchfile.
self.env.evaluate_patterns() self.env.evaluate_patterns()
if self.dry_run: if self.dry_run:
sys.stdout.write(f"\nDRY RUN File: \033[1m{line}\033[0m\n") self.outstream.write(f"\nDRY RUN File: \033[1m{line}\033[0m\n")
else: else:
sys.stdout.write(f"\nFile: \033[1m{line}\033[0m\n") self.outstream.write(f"\nFile: \033[1m{line}\033[0m\n")
for key, value in self.env.metadatums.items(): for key, value in self.env.metadatums.items():
@@ -326,3 +329,18 @@ they appear in the batchfile.
""" """
val = args[0] val = args[0]
self.env.set_once('DESCRIPTION', val) self.env.set_once('DESCRIPTION', val)
# def picture(self, args):
# """
# picture PATH
# Add PATH as a picture (flac picture type 0) to this and every
# subsequent file.
# """
# pass
#
# def nopicture(self, args):
# """
# unpicture
# Remove all p
# """
# pass

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "mfbatch" name = "mfbatch"
version = "0.4.1" version = "0.5.1"
description = "MetaFlac batch editor" description = "MetaFlac batch editor"
authors = ["Jamie Hardt <jamiehardt@me.com>"] authors = ["Jamie Hardt <jamiehardt@me.com>"]
readme = "README.md" readme = "README.md"
@@ -13,6 +13,7 @@ classifiers = [
'Environment :: Console', 'Environment :: Console',
'License :: OSI Approved :: MIT License', 'License :: OSI Approved :: MIT License',
'Topic :: Multimedia :: Sound/Audio :: Editors', 'Topic :: Multimedia :: Sound/Audio :: Editors',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.10',