mirror of
https://github.com/iluvcapra/wavinfo.git
synced 2026-10-01 17:27:42 +00:00
Ruff
This commit is contained in:
+6
-1
@@ -45,7 +45,7 @@ dependencies = [
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8.3.5",
|
||||
"ruff>=0.14.0",
|
||||
"ruff>=0.16.8",
|
||||
]
|
||||
doc = [
|
||||
"sphinx>=7.1.2",
|
||||
@@ -64,6 +64,11 @@ indent-width = 4
|
||||
|
||||
[tool.ruff.lint]
|
||||
fixable = ['ALL']
|
||||
ignore = [
|
||||
'UP022', # Prefer `capture_output` over sending `stdout` and `stderr` to `PIPE`
|
||||
'SIM115', # Use a context manager for opening files
|
||||
'UP031', #Use format specifiers instead of percent format
|
||||
]
|
||||
|
||||
# [tool.pylint]
|
||||
# max-line-length = 88
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
from enum import IntEnum
|
||||
|
||||
# from collections import namedtuple
|
||||
from typing import NamedTuple, Optional
|
||||
from typing import NamedTuple
|
||||
|
||||
from lxml import etree as ET
|
||||
|
||||
@@ -76,7 +78,7 @@ class SteinbergMetadata:
|
||||
self.parsed = xml.find(self.Steinberg_xpath)
|
||||
|
||||
@property
|
||||
def audio_speaker_arrangement(self) -> Optional[AudioSpeakerArrangement]:
|
||||
def audio_speaker_arrangement(self) -> AudioSpeakerArrangement | None:
|
||||
"""
|
||||
`AudioSpeakerArrangement` property
|
||||
"""
|
||||
@@ -87,7 +89,7 @@ class SteinbergMetadata:
|
||||
return type(self).AudioSpeakerArrangement(int(val.text))
|
||||
|
||||
@property
|
||||
def sample_format_size(self) -> Optional[int]:
|
||||
def sample_format_size(self) -> int | None:
|
||||
"""
|
||||
AudioSampleFormatSize
|
||||
"""
|
||||
@@ -96,7 +98,7 @@ class SteinbergMetadata:
|
||||
return int(val.text)
|
||||
|
||||
@property
|
||||
def media_company(self) -> Optional[str]:
|
||||
def media_company(self) -> str | None:
|
||||
"""
|
||||
MediaCompany
|
||||
"""
|
||||
@@ -105,7 +107,7 @@ class SteinbergMetadata:
|
||||
return val.text
|
||||
|
||||
@property
|
||||
def media_drop_frames(self) -> Optional[bool]:
|
||||
def media_drop_frames(self) -> bool | None:
|
||||
"""
|
||||
MediaDropFrames
|
||||
"""
|
||||
@@ -114,7 +116,7 @@ class SteinbergMetadata:
|
||||
return val.text == "1"
|
||||
|
||||
@property
|
||||
def media_duration(self) -> Optional[float]:
|
||||
def media_duration(self) -> float | None:
|
||||
"""
|
||||
MediaDuration
|
||||
"""
|
||||
@@ -122,33 +124,29 @@ class SteinbergMetadata:
|
||||
if val is not None:
|
||||
return float(val.text)
|
||||
|
||||
@property
|
||||
def media_start_time(self) -> Optional[float]:
|
||||
"""
|
||||
MediaStartTime
|
||||
"""
|
||||
pass
|
||||
# @property
|
||||
# def media_start_time(self) -> float | None:
|
||||
# """
|
||||
# MediaStartTime
|
||||
# """
|
||||
|
||||
@property
|
||||
def media_track_title(self) -> Optional[str]:
|
||||
"""
|
||||
MediaTrackTitle
|
||||
"""
|
||||
pass
|
||||
# @property
|
||||
# def media_track_title(self) -> str | None:
|
||||
# """
|
||||
# MediaTrackTitle
|
||||
# """
|
||||
|
||||
@property
|
||||
def program_name(self) -> Optional[str]:
|
||||
"""
|
||||
ProgramName
|
||||
"""
|
||||
pass
|
||||
# @property
|
||||
# def program_name(self) -> str | None:
|
||||
# """
|
||||
# ProgramName
|
||||
# """
|
||||
|
||||
@property
|
||||
def program_version(self) -> Optional[str]:
|
||||
"""
|
||||
ProgramVersion
|
||||
"""
|
||||
pass
|
||||
# @property
|
||||
# def program_version(self) -> str | None:
|
||||
# """
|
||||
# ProgramVersion
|
||||
# """
|
||||
|
||||
|
||||
class WavIXMLFormat:
|
||||
@@ -166,7 +164,7 @@ class WavIXMLFormat:
|
||||
parser = ET.XMLParser(recover=True)
|
||||
self.parsed: ET.ElementTree = ET.parse(xml_bytes, parser=parser)
|
||||
|
||||
def _get_text_value(self, xpath) -> Optional[str]:
|
||||
def _get_text_value(self, xpath) -> str | None:
|
||||
e = self.parsed.find("./" + xpath)
|
||||
if e is not None:
|
||||
return e.text
|
||||
@@ -200,35 +198,35 @@ class WavIXMLFormat:
|
||||
)
|
||||
|
||||
@property
|
||||
def project(self) -> Optional[str]:
|
||||
def project(self) -> str | None:
|
||||
"""
|
||||
The project/film name entered for the recording.
|
||||
"""
|
||||
return self._get_text_value("PROJECT")
|
||||
|
||||
@property
|
||||
def scene(self) -> Optional[str]:
|
||||
def scene(self) -> str | None:
|
||||
"""
|
||||
Scene/slate.
|
||||
"""
|
||||
return self._get_text_value("SCENE")
|
||||
|
||||
@property
|
||||
def take(self) -> Optional[str]:
|
||||
def take(self) -> str | None:
|
||||
"""
|
||||
Take number.
|
||||
"""
|
||||
return self._get_text_value("TAKE")
|
||||
|
||||
@property
|
||||
def tape(self) -> Optional[str]:
|
||||
def tape(self) -> str | None:
|
||||
"""
|
||||
Tape name.
|
||||
"""
|
||||
return self._get_text_value("TAPE")
|
||||
|
||||
@property
|
||||
def family_uid(self) -> Optional[str]:
|
||||
def family_uid(self) -> str | None:
|
||||
"""
|
||||
The globally-unique ID for this file family. This may be in the format
|
||||
of a GUID, or an EBU Rec 9 source identifier, or some other dumb
|
||||
@@ -237,14 +235,14 @@ class WavIXMLFormat:
|
||||
return self._get_text_value("FILE_SET/FAMILY_UID")
|
||||
|
||||
@property
|
||||
def family_name(self) -> Optional[str]:
|
||||
def family_name(self) -> str | None:
|
||||
"""
|
||||
The name of this file's file family.
|
||||
"""
|
||||
return self._get_text_value("FILE_SET/FAMILY_NAME")
|
||||
|
||||
@property
|
||||
def steinberg(self) -> Optional[SteinbergMetadata]:
|
||||
def steinberg(self) -> SteinbergMetadata | None:
|
||||
"""
|
||||
Steinberg vendor iXML metadata if present.
|
||||
"""
|
||||
@@ -254,12 +252,12 @@ class WavIXMLFormat:
|
||||
return None
|
||||
|
||||
def to_dict(self):
|
||||
return dict(
|
||||
track_list=list(map(lambda x: x._asdict(), self.track_list)),
|
||||
project=self.project,
|
||||
scene=self.scene,
|
||||
take=self.take,
|
||||
tape=self.tape,
|
||||
family_uid=self.family_uid,
|
||||
family_name=self.family_name,
|
||||
)
|
||||
return {
|
||||
"track_list": [x._asdict() for x in self.track_list],
|
||||
"project": self.project,
|
||||
"scene": self.scene,
|
||||
"take": self.take,
|
||||
"tape": self.tape,
|
||||
"family_uid": self.family_uid,
|
||||
"family_name": self.family_name,
|
||||
}
|
||||
|
||||
+18
-21
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import struct
|
||||
from typing import Any, Generator, NamedTuple, Optional
|
||||
from typing import Any, Generator, NamedTuple
|
||||
|
||||
from .riff_parser import ChunkDescriptor, ListChunkDescriptor, parse_chunk
|
||||
from .wave_adm_reader import WavADMReader
|
||||
@@ -58,31 +59,31 @@ class WavInfoReader:
|
||||
self.bext_encoding = bext_encoding
|
||||
|
||||
#: Wave audio data format.
|
||||
self.fmt: Optional[WavAudioFormat] = None
|
||||
self.fmt: WavAudioFormat | None = None
|
||||
|
||||
#: Statistics of the `data` section.
|
||||
self.data: Optional[WavDataDescriptor] = None
|
||||
self.data: WavDataDescriptor | None = None
|
||||
|
||||
#: Broadcast-Wave metadata.
|
||||
self.bext: Optional[WavBextReader] = None
|
||||
self.bext: WavBextReader | None = None
|
||||
|
||||
#: iXML metadata.
|
||||
self.ixml: Optional[WavIXMLFormat] = None
|
||||
self.ixml: WavIXMLFormat | None = None
|
||||
|
||||
#: ADM Audio Definiton Model metadata.
|
||||
self.adm: Optional[WavADMReader] = None
|
||||
self.adm: WavADMReader | None = None
|
||||
|
||||
#: Dolby bitstream metadata.
|
||||
self.dolby: Optional[WavDolbyMetadataReader] = None
|
||||
self.dolby: WavDolbyMetadataReader | None = None
|
||||
|
||||
#: RIFF INFO metadata.
|
||||
self.info: Optional[WavInfoChunkReader] = None
|
||||
self.info: WavInfoChunkReader | None = None
|
||||
|
||||
#: RIFF cues markers, labels, and notes.
|
||||
self.cues: Optional[WavCuesReader] = None
|
||||
self.cues: WavCuesReader | None = None
|
||||
|
||||
#: Sampler `smpl` metadata
|
||||
self.smpl: Optional[WavSmplReader] = None
|
||||
self.smpl: WavSmplReader | None = None
|
||||
|
||||
if hasattr(path, "read"):
|
||||
self.get_wav_info(path)
|
||||
@@ -97,8 +98,8 @@ class WavInfoReader:
|
||||
|
||||
self.path = absolute_path
|
||||
|
||||
with open(path, "rb") as path:
|
||||
self.get_wav_info(path)
|
||||
with open(path, "rb") as file:
|
||||
self.get_wav_info(file)
|
||||
|
||||
def get_wav_info(self, wavfile):
|
||||
chunks = parse_chunk(wavfile)
|
||||
@@ -117,9 +118,7 @@ class WavInfoReader:
|
||||
self.smpl = self._get_sampler_loops(wavfile)
|
||||
self.data = self._describe_data()
|
||||
|
||||
def _find_chunk_data(
|
||||
self, ident, from_stream, default_none=False
|
||||
) -> Optional[bytes]:
|
||||
def _find_chunk_data(self, ident, from_stream, default_none=False) -> bytes | None:
|
||||
top_chunks = (
|
||||
chunk
|
||||
for chunk in self.main_list
|
||||
@@ -130,7 +129,7 @@ class WavInfoReader:
|
||||
|
||||
return chunk_descriptor.read_data(from_stream) if chunk_descriptor else None
|
||||
|
||||
def _find_list_chunk(self, signature) -> Optional[ListChunkDescriptor]:
|
||||
def _find_list_chunk(self, signature) -> ListChunkDescriptor | None:
|
||||
top_chunks = (
|
||||
chunk
|
||||
for chunk in self.main_list
|
||||
@@ -261,10 +260,8 @@ class WavInfoReader:
|
||||
if self.__getattribute__(scope)
|
||||
else {}
|
||||
)
|
||||
for key in mdict.keys():
|
||||
for key in mdict:
|
||||
yield scope, key, mdict[key]
|
||||
|
||||
def __repr__(self):
|
||||
return "WavInfoReader({}, {}, {})".format(
|
||||
self.path, self.info_encoding, self.bext_encoding
|
||||
)
|
||||
return f"WavInfoReader({self.path}, {self.info_encoding}, {self.bext_encoding})"
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import struct
|
||||
from typing import List, NamedTuple, Tuple
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class WaveSmplLoop(NamedTuple):
|
||||
@@ -69,13 +71,13 @@ class WavSmplReader:
|
||||
|
||||
#: The SMPTE offset to apply, as a tuple of four ints representing
|
||||
#: hh, mm, ss, ff
|
||||
self.smpte_offset: Tuple[int, int, int, int] = unpacked_data[6:10]
|
||||
self.smpte_offset: tuple[int, int, int, int] = unpacked_data[6:10]
|
||||
|
||||
loop_count = unpacked_data[10]
|
||||
sampler_udata_length = unpacked_data[11]
|
||||
|
||||
#: List of loops in the file.
|
||||
self.sample_loops: List[WaveSmplLoop] = []
|
||||
self.sample_loops: list[WaveSmplLoop] = []
|
||||
|
||||
loop_buffer = smpl_data[header_size : header_size + loop_size * loop_count]
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
""" """
|
||||
"""
|
||||
wavfind.py
|
||||
|
||||
Under construction! Do Not Use!
|
||||
"""
|
||||
import sys
|
||||
from optparse import OptionGroup, OptionParser
|
||||
|
||||
@@ -21,7 +24,7 @@ def main():
|
||||
|
||||
primaries.add_option("--desc", help="Search descriptions", metavar="DESC")
|
||||
|
||||
(options, args) = parser.parse_args(sys.argv)
|
||||
(_options, _args) = parser.parse_args(sys.argv)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+5
-3
@@ -42,12 +42,14 @@ class TestADMWave(TestCase):
|
||||
adm = info.adm
|
||||
assert adm is not None
|
||||
t1 = adm.track_info(0)
|
||||
self.assertTrue("channel_format_name" in t1.keys())
|
||||
assert t1 is not None
|
||||
self.assertTrue("channel_format_name" in t1)
|
||||
self.assertEqual("RoomCentricLeft", t1["channel_format_name"])
|
||||
|
||||
self.assertTrue("pack_format_name" in t1.keys())
|
||||
self.assertTrue("pack_format_name" in t1)
|
||||
self.assertEqual("AtmosCustomPackFormat1", t1["pack_format_name"])
|
||||
|
||||
t10 = adm.track_info(10)
|
||||
self.assertTrue("content_name" in t10.keys())
|
||||
assert t10 is not None
|
||||
self.assertTrue("content_name" in t10)
|
||||
self.assertEqual("Dialog", t10["content_name"])
|
||||
|
||||
@@ -9,24 +9,15 @@ from wavinfo.__main__ import main
|
||||
class MainTest(unittest.TestCase):
|
||||
def test_empty_argv(self):
|
||||
with patch.object(sys, "argv", []):
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
self.fail(f"main() throwing an exception: {e}")
|
||||
|
||||
def test_a_file(self):
|
||||
for path in glob.glob("tests/test_files/**/*.wav"):
|
||||
with patch.object(sys, "argv", ["TEST", path]):
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
self.fail(f"main() throwing an exception: {e}")
|
||||
|
||||
def test_ixml(self):
|
||||
with patch.object(
|
||||
sys, "argv", ["TEST", "--ixml", "tests/test_files/sounddevices/A101_1.WAV"]
|
||||
):
|
||||
try:
|
||||
main()
|
||||
except Exception as e:
|
||||
self.fail(f"main() throwing an exception: {e}")
|
||||
|
||||
+5
-7
@@ -11,12 +11,10 @@ class TestWalk(unittest.TestCase):
|
||||
|
||||
tested_data, tested_format = False, False
|
||||
for scope, key, value in info.walk():
|
||||
if scope == "fmt":
|
||||
if key == "channel_count":
|
||||
if scope == "fmt" and key == "channel_count":
|
||||
tested_format = True
|
||||
self.assertEqual(value, 2)
|
||||
if scope == "data":
|
||||
if key == "frame_count":
|
||||
if scope == "data" and key == "frame_count":
|
||||
tested_data = True
|
||||
self.assertEqual(value, 144140)
|
||||
|
||||
@@ -26,11 +24,11 @@ class TestWalk(unittest.TestCase):
|
||||
for file in glob.glob("tests/test_files/**/*.wav"):
|
||||
info = wavinfo.WavInfoReader(file)
|
||||
|
||||
try:
|
||||
# try:
|
||||
for _, _, _ in info.walk():
|
||||
pass
|
||||
except Exception as e:
|
||||
self.fail(f"Failed to walk metadata in file {file}: {e}")
|
||||
# except Exception as e:
|
||||
# self.fail(f"Failed to walk metadata in file {file}: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -14,7 +14,7 @@ class TestWaveInfo(TestCase):
|
||||
info = wavinfo.WavInfoReader(wav_file)
|
||||
self.assertEqual(
|
||||
info.__repr__(),
|
||||
"WavInfoReader({}, latin_1, ascii)".format(os.path.abspath(wav_file)),
|
||||
f"WavInfoReader({os.path.abspath(wav_file)}, latin_1, ascii)",
|
||||
)
|
||||
self.assertIsNotNone(info)
|
||||
|
||||
|
||||
+1
-13
@@ -1,7 +1,6 @@
|
||||
import json
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
from subprocess import PIPE
|
||||
|
||||
FFPROBE = "ffprobe"
|
||||
@@ -9,18 +8,7 @@ FFPROBE = "ffprobe"
|
||||
|
||||
def ffprobe(path):
|
||||
arguments = [FFPROBE, "-of", "json", "-show_format", "-show_streams", path]
|
||||
if int(sys.version[0]) < 3:
|
||||
process = subprocess.Popen(arguments, stdout=PIPE)
|
||||
process.wait()
|
||||
if process.returncode == 0:
|
||||
output = process.communicate()[0]
|
||||
if output:
|
||||
output_str = output.decode("utf-8")
|
||||
return json.loads(output_str)
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
process = subprocess.run(arguments, stdin=None, stdout=PIPE, stderr=PIPE)
|
||||
process = subprocess.run(arguments, stdin=None, stdout=PIPE, stderr=PIPE, check=True)
|
||||
if process.returncode == 0:
|
||||
output_str = process.stdout.decode("utf-8")
|
||||
return json.loads(output_str)
|
||||
|
||||
Reference in New Issue
Block a user