mirror of
https://github.com/iluvcapra/mfbatch.git
synced 2026-01-01 09:20:54 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88add2da85 | ||
|
|
fd228493c6 | ||
|
|
11405ef06c | ||
|
|
229467c408 | ||
|
|
dc1c6cc742 | ||
|
|
d7a30275d1 | ||
|
|
61458660c9 | ||
|
|
d286c4e6c7 | ||
|
|
8d299e2335 | ||
|
|
e0431da6df | ||
|
|
d334181dc8 | ||
|
|
0d765f9d84 | ||
|
|
ce6542b64d | ||
|
|
5a47985154 | ||
|
|
649427dd33 | ||
|
|
21277aff15 | ||
|
|
1b044025ea | ||
|
|
2078c1559a |
8
.github/workflows/pylint.yml
vendored
8
.github/workflows/pylint.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Pylint
|
name: Lint and Test
|
||||||
|
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.8", "3.9", "3.10"]
|
python-version: ["3.9", "3.10", "3.11", "3.12"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
@@ -22,3 +22,7 @@ jobs:
|
|||||||
- name: Analysing the code with pylint
|
- name: Analysing the code with pylint
|
||||||
run: |
|
run: |
|
||||||
pylint $(git ls-files '*.py')
|
pylint $(git ls-files '*.py')
|
||||||
|
- name: Testing with unittest
|
||||||
|
run: |
|
||||||
|
python -m unittest tests
|
||||||
|
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
dist/
|
dist/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
poetry.lock
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|

|
||||||
|
  [](https://pypi.org/project/mfbatch/) 
|
||||||
|
|
||||||
|
[](https://github.com/iluvcapra/mfbatch/actions/workflows/pylint.yml)
|
||||||
|
|
||||||
# mfbatch
|
# mfbatch
|
||||||
|
|
||||||
`mfbatch` is a command-line tool for batch-editing FLAC audio file metadata.
|
`mfbatch` is a command-line tool for batch-editing FLAC audio file metadata.
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import inspect
|
|||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from mfbatch.util import readline_with_escaped_newlines
|
from mfbatch.util import readline_with_escaped_newlines
|
||||||
import mfbatch.metaflac as flac
|
import mfbatch.metaflac as metadata_funcs
|
||||||
from mfbatch.commands import BatchfileParser
|
from mfbatch.commands import BatchfileParser
|
||||||
|
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ def create_batch_list(command_file: str, recursive=True):
|
|||||||
flac_files = glob('./**/*.flac', recursive=recursive)
|
flac_files = glob('./**/*.flac', recursive=recursive)
|
||||||
flac_files = sorted(flac_files)
|
flac_files = sorted(flac_files)
|
||||||
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 = flac.read_metadata(path)
|
this_file_metadata = metadata_funcs.read_metadata(path)
|
||||||
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} "
|
||||||
@@ -66,7 +66,8 @@ def main():
|
|||||||
"""
|
"""
|
||||||
Entry point implementation
|
Entry point implementation
|
||||||
"""
|
"""
|
||||||
op = ArgumentParser(usage="%prog (-c | -e | -W) [options]")
|
op = ArgumentParser(
|
||||||
|
prog='mfbatch', usage='%(prog)s (-c | -e | -W) [options]')
|
||||||
|
|
||||||
op.add_argument('-c', '--create', default=False,
|
op.add_argument('-c', '--create', default=False,
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import os.path
|
|||||||
|
|
||||||
from typing import Dict, Tuple, Optional
|
from typing import Dict, Tuple, Optional
|
||||||
|
|
||||||
import mfbatch.metaflac as flac
|
from mfbatch.metaflac import write_metadata as flac
|
||||||
|
|
||||||
|
|
||||||
class UnrecognizedCommandError(Exception):
|
class UnrecognizedCommandError(Exception):
|
||||||
@@ -64,6 +64,18 @@ class CommandEnv:
|
|||||||
self.incr.pop(k, None)
|
self.incr.pop(k, None)
|
||||||
self.patterns.pop(k, None)
|
self.patterns.pop(k, None)
|
||||||
|
|
||||||
|
def reset_keys(self):
|
||||||
|
"""
|
||||||
|
Reset all keys in the environment
|
||||||
|
"""
|
||||||
|
all_keys = list(self.metadatums.keys())
|
||||||
|
|
||||||
|
for key in all_keys:
|
||||||
|
self.unset_key(key)
|
||||||
|
|
||||||
|
self.patterns = {}
|
||||||
|
self.incr = {}
|
||||||
|
|
||||||
def set_pattern(self, to: str, frm: str, pattern: str, repl: str):
|
def set_pattern(self, to: str, frm: str, pattern: str, repl: str):
|
||||||
"""
|
"""
|
||||||
Establish a pattern replacement in the environment
|
Establish a pattern replacement in the environment
|
||||||
@@ -128,7 +140,7 @@ 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
|
||||||
are 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.
|
||||||
|
|
||||||
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.
|
||||||
@@ -147,6 +159,7 @@ they appear in the batchfile.
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.dry_run = True
|
self.dry_run = True
|
||||||
self.env = CommandEnv()
|
self.env = CommandEnv()
|
||||||
|
self.write_metadata_f = flac
|
||||||
|
|
||||||
def eval(self, line: str, lineno: int, interactive: bool):
|
def eval(self, line: str, lineno: int, interactive: bool):
|
||||||
"""
|
"""
|
||||||
@@ -181,7 +194,7 @@ they appear in the batchfile.
|
|||||||
print("DRY RUN would write metadata here.")
|
print("DRY RUN would write metadata here.")
|
||||||
else:
|
else:
|
||||||
sys.stdout.write("Writing metadata... ")
|
sys.stdout.write("Writing metadata... ")
|
||||||
flac.write_metadata(line, self.env.metadatums)
|
self.write_metadata_f(line, self.env.metadatums)
|
||||||
sys.stdout.write("Complete!")
|
sys.stdout.write("Complete!")
|
||||||
|
|
||||||
self.env.increment_all()
|
self.env.increment_all()
|
||||||
@@ -267,7 +280,7 @@ they appear in the batchfile.
|
|||||||
"""
|
"""
|
||||||
reset
|
reset
|
||||||
All keys in the environment will be reset, subsequent files will have
|
All keys in the environment will be reset, subsequent files will have
|
||||||
no keys set.
|
no keys set, including keys set by the `setinc` and `setp` commands.
|
||||||
"""
|
"""
|
||||||
all_keys = list(self.env.metadatums.keys())
|
all_keys = list(self.env.metadatums.keys())
|
||||||
for k in all_keys:
|
for k in all_keys:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "mfbatch"
|
name = "mfbatch"
|
||||||
version = "0.4.0"
|
version = "0.4.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,10 @@ 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.12',
|
||||||
|
'Programming Language :: Python :: 3.11',
|
||||||
|
'Programming Language :: Python :: 3.10',
|
||||||
|
'Programming Language :: Python :: 3.9',
|
||||||
]
|
]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
|
|||||||
65
tests/__init__.py
Normal file
65
tests/__init__.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
"mfbatch tests"
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
from mfbatch.commands import BatchfileParser
|
||||||
|
|
||||||
|
|
||||||
|
class BatchfileParserTests(unittest.TestCase):
|
||||||
|
"""
|
||||||
|
Tests the BatchfileParser class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.command_parser = BatchfileParser()
|
||||||
|
self.command_parser.dry_run = False
|
||||||
|
self.command_parser.write_metadata_f = MagicMock()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_set_without_write(self):
|
||||||
|
"Test setting a key without writing"
|
||||||
|
self.command_parser.set(['TYPE', 'Everything'])
|
||||||
|
self.assertFalse(cast(MagicMock,
|
||||||
|
self.command_parser.write_metadata_f).called)
|
||||||
|
self.assertEqual(self.command_parser.env.metadatums['TYPE'],
|
||||||
|
'Everything')
|
||||||
|
|
||||||
|
def test_set_command(self):
|
||||||
|
"Test set command"
|
||||||
|
self.command_parser.set(['X', 'Y'])
|
||||||
|
self.command_parser.eval("./testfile.flac", lineno=1,
|
||||||
|
interactive=False)
|
||||||
|
self.assertTrue(cast(MagicMock,
|
||||||
|
self.command_parser.write_metadata_f).called)
|
||||||
|
self.assertEqual(cast(MagicMock,
|
||||||
|
self.command_parser.write_metadata_f).call_args.args,
|
||||||
|
('./testfile.flac', {'X': 'Y'}))
|
||||||
|
|
||||||
|
def test_unset_command(self):
|
||||||
|
"Test unset command"
|
||||||
|
self.command_parser.set(['A', '1'])
|
||||||
|
self.assertEqual(self.command_parser.env.metadatums['A'], '1')
|
||||||
|
self.command_parser.unset(['A'])
|
||||||
|
self.assertNotIn('A', self.command_parser.env.metadatums.keys())
|
||||||
|
|
||||||
|
def test_setp(self):
|
||||||
|
"Test setp command"
|
||||||
|
self.command_parser.set(['VAL', 'ABC123'])
|
||||||
|
self.command_parser.setp(['DONE', 'VAL', r"([A-Z]+)123", r"X\1"])
|
||||||
|
self.command_parser.eval("./testfile.flac", lineno=1,
|
||||||
|
interactive=False)
|
||||||
|
|
||||||
|
self.assertTrue(cast(MagicMock,
|
||||||
|
self.command_parser.write_metadata_f).called)
|
||||||
|
self.assertEqual(cast(MagicMock,
|
||||||
|
self.command_parser.write_metadata_f).call_args.args,
|
||||||
|
("./testfile.flac", {'VAL': 'ABC123', 'DONE': 'XABC'}))
|
||||||
|
|
||||||
|
def test_eval(self):
|
||||||
|
"Test eval"
|
||||||
|
self.command_parser.eval(":set A 1", 1, False)
|
||||||
|
self.assertEqual(self.command_parser.env.metadatums['A'], '1')
|
||||||
Reference in New Issue
Block a user