mirror of
https://github.com/iluvcapra/mfbatch.git
synced 2025-12-31 08:50:51 +00:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef1377a616 | ||
|
|
b7535618e6 | ||
|
|
dfa2a2a5ad | ||
|
|
66ac136270 | ||
|
|
aa64d5e183 | ||
|
|
6766e81b23 | ||
|
|
a2ce03a259 | ||
|
|
0ba40893df | ||
|
|
e2b93f5183 | ||
|
|
7015e80cf9 | ||
|
|
042f3116dd | ||
|
|
20518fa31c | ||
|
|
c4a2e380de | ||
|
|
4ec028b51b | ||
|
|
ac00f93a8d | ||
|
|
8bcfd2ee54 | ||
|
|
63ec226be1 | ||
|
|
69cdb6dec1 | ||
|
|
720eacc2a4 | ||
|
|
afa02e4c96 | ||
|
|
b986a36281 | ||
|
|
fb90b5db3c | ||
|
|
e69573b2a4 | ||
|
|
55cf591690 | ||
|
|
f0e05a9609 |
2
.github/workflows/pylint.yml
vendored
2
.github/workflows/pylint.yml
vendored
@@ -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 }}
|
||||||
|
|||||||
2
.github/workflows/python-publish.yml
vendored
2
.github/workflows/python-publish.yml
vendored
@@ -35,4 +35,4 @@ jobs:
|
|||||||
- name: Build package
|
- name: Build package
|
||||||
run: python -m build
|
run: python -m build
|
||||||
- name: Publish package
|
- name: Publish package
|
||||||
uses: pypa/gh-action-pypi-publish@v1.8.6
|
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
dist/
|
dist/
|
||||||
*.pyc
|
*.pyc
|
||||||
poetry.lock
|
poetry.lock
|
||||||
|
.venv/
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "mfbatch"
|
name = "mfbatch"
|
||||||
version = "0.4.1"
|
version = "0.5.2"
|
||||||
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',
|
||||||
|
|||||||
Reference in New Issue
Block a user