fix python instance init, remove and edit clip meta, add meta on record

This commit is contained in:
michalcourson
2026-02-14 11:57:12 -05:00
parent f3b883602e
commit f9fdfb629b
14 changed files with 116 additions and 30 deletions

1
audio-service/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
recordings/

View File

@ -1,10 +1,17 @@
{
"Test": [
"Test": [],
"Uncategorized": [
{
"name": "test 2",
"filePath": "C:\\Users\\mickl\\Music\\clips\\original\\audio_capture_20250105_131700.wav",
"volume": 1,
"playbackType": "playStop"
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260214_114317.wav",
"name": "Clip 20260214_114317",
"playbackType": "playStop",
"volume": 0.8
},
{
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260214_114712.wav",
"name": "Clip 20260214_114712",
"playbackType": "playStop",
"volume": 1.0
}
]
}

View File

@ -3,6 +3,7 @@ import numpy as np
import os
from datetime import datetime
import scipy.io.wavfile as wavfile
from metadata_manager import MetaDataManager
class AudioRecorder:
_instance = None
@ -11,8 +12,9 @@ class AudioRecorder:
if cls._instance is None:
print("Creating new AudioRecorder instance")
cls._instance = super().__new__(cls)
cls._instance.init()
return cls._instance
def __init__(self, duration=30, sample_rate=44100, channels=2, recordings_dir='recordings'):
def init(self):
"""
Initialize audio recorder with configurable parameters.
@ -20,12 +22,12 @@ class AudioRecorder:
:param sample_rate: Audio sample rate (if None, use default device sample rate)
:param channels: Number of audio channels
"""
self.duration = duration
self.sample_rate = sample_rate
self.channels = channels
self.buffer = np.zeros((int(duration * sample_rate), channels), dtype=np.float32)
self.recordings_dir = recordings_dir
print(f"Initializing AudioRecorder")
self.duration = 30
self.sample_rate = 44100
self.channels = 2
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
self.recordings_dir = "recordings"
self.stream = sd.InputStream(
samplerate=self.sample_rate,
channels=self.channels,
@ -92,6 +94,16 @@ class AudioRecorder:
# Write buffer to file
wavfile.write(filename, int(self.sample_rate), audio_data_int16)
meta = MetaDataManager()
meta.add_clip_to_collection("Uncategorized",
{
"filename": filename,
"name": f"Clip {timestamp}",
"playbackType":"playStop",
"volume": 1.0,
})
return filename

View File

@ -18,7 +18,6 @@ import threading
app = Flask(__name__)
def main():
global recorder, audio_manager
# Create argument parser
parser = argparse.ArgumentParser(description='Audio Recording Service')
@ -35,17 +34,6 @@ def main():
# Ensure save path exists
os.makedirs(settings.get_settings('save_path'), exist_ok=True)
# Handle input device selection
# Create Singletons with correct parameters
recorder = AudioRecorder(
duration=settings.get_settings('recording_length'),
recordings_dir=settings.get_settings('save_path'),
# channels=min(2, devices[input_device]['max_input_channels']),
)
meta = MetaDataManager()
audio_manager = WindowsAudioManager()
# Register blueprints

View File

@ -7,8 +7,9 @@ class MetaDataManager:
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.init()
return cls._instance
def __init__(self):
def init(self):
# read metadata file from executing directory
self.metadata_file = os.path.join(os.getcwd(), "metadata.json")
if os.path.exists(self.metadata_file):
@ -16,6 +17,9 @@ class MetaDataManager:
self.collections = json.load(f)
else:
self.collections = {}
if(collections := self.collections.get("Uncategorized")) is None:
self.collections["Uncategorized"] = []
self.save_metadata()
def create_collection(self, name):
if name in self.collections:
@ -34,6 +38,31 @@ class MetaDataManager:
raise ValueError(f"Collection '{collection_name}' does not exist.")
self.collections[collection_name].append(clip_metadata)
self.save_metadata()
def remove_clip_from_collection(self, collection_name, clip_metadata):
if collection_name not in self.collections:
raise ValueError(f"Collection '{collection_name}' does not exist.")
# Remove all clips with the same file name as clip_metadata["file_name"]
in_list = any(clip.get("filename") == clip_metadata.get("filename") for clip in self.collections[collection_name])
if not in_list:
raise ValueError(f"Clip with filename '{clip_metadata.get('filename')}' not found in collection '{collection_name}'.")
self.collections[collection_name] = [
clip for clip in self.collections[collection_name]
if clip.get("filename") != clip_metadata.get("filename")
]
self.save_metadata()
def edit_clip_in_collection(self, collection_name, new_clip_metadata):
if collection_name not in self.collections:
raise ValueError(f"Collection '{collection_name}' does not exist.")
# Find the index of the clip with the same file name as old_clip_metadata["file_name"]
index = next((i for i, clip in enumerate(self.collections[collection_name]) if clip.get("filename") == new_clip_metadata.get("filename")), None)
if index is None:
raise ValueError(f"Clip with filename '{new_clip_metadata.get('filename')}' not found in collection '{collection_name}'.")
self.collections[collection_name][index] = new_clip_metadata
self.save_metadata()
def get_collections(self):
return list(self.collections.keys())
@ -43,6 +72,18 @@ class MetaDataManager:
raise ValueError(f"Collection '{collection_name}' does not exist.")
return self.collections[collection_name]
def reorder_clips_in_collection(self, collection_name, new_order):
if collection_name not in self.collections:
raise ValueError(f"Collection '{collection_name}' does not exist.")
existing_filenames = {clip.get("filename") for clip in self.collections[collection_name]}
new_filenames = {clip.get("filename") for clip in new_order}
if not new_filenames.issubset(existing_filenames):
raise ValueError("New order contains clips that do not exist in the collection.")
self.collections[collection_name] = new_order
self.save_metadata()
def save_metadata(self):
with open(self.metadata_file, "w") as f:
json.dump(self.collections, f, indent=4)

View File

@ -20,15 +20,26 @@ def add_collection():
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@metadata_bp.route('/meta/collection/clips', methods=['GET'])
def get_clips_in_collection():
@metadata_bp.route('/meta/collection/clips/<name>', methods=['GET'])
def get_clips_in_collection(name):
meta_manager = MetaDataManager()
collection_name = request.args.get('name')
collection_name = name
try:
clips = meta_manager.get_clips_in_collection(collection_name)
return jsonify({'status': 'success', 'clips': clips})
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@metadata_bp.route('/meta/collection/clips/reorder', methods=['POST']):
def reorder_clips_in_collection():
meta_manager = MetaDataManager()
collection_name = request.json.get('name')
new_order = request.json.get('clips')
try:
meta_manager.reorder_clips_in_collection(collection_name, new_order)
return jsonify({'status': 'success', 'clips': new_order})
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@metadata_bp.route('/meta/collection/clips/add', methods=['POST'])
def add_clip_to_collection():
@ -41,3 +52,27 @@ def add_clip_to_collection():
return jsonify({'status': 'success', 'clips': clips})
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@metadata_bp.route('/meta/collection/clips/remove', methods=['POST'])
def remove_clip_from_collection():
meta_manager = MetaDataManager()
collection_name = request.json.get('name')
clip_metadata = request.json.get('clip')
try:
meta_manager.remove_clip_from_collection(collection_name, clip_metadata)
clips = meta_manager.get_clips_in_collection(collection_name)
return jsonify({'status': 'success', 'clips': clips})
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400
@metadata_bp.route('/meta/collection/clips/edit', methods=['POST'])
def edit_clip_in_collection():
meta_manager = MetaDataManager()
collection_name = request.json.get('name')
clip_metadata = request.json.get('clip')
try:
meta_manager.edit_clip_in_collection(collection_name, clip_metadata)
clips = meta_manager.get_clips_in_collection(collection_name)
return jsonify({'status': 'success', 'clips': clips})
except ValueError as e:
return jsonify({'status': 'error', 'message': str(e)}), 400

View File

@ -9,8 +9,9 @@ class SettingsManager:
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.init()
return cls._instance
def __init__(self):
def init(self):
# read settings file from executing directory
self.settings_file = os.path.join(os.getcwd(), "settings.json")
if os.path.exists(self.settings_file):

View File

@ -12,8 +12,9 @@ class WindowsAudioManager:
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.init()
return cls._instance
def __init__(self):
def init(self):
"""
Initialize Windows audio device and volume management.
"""