fix python instance init, remove and edit clip meta, add meta on record
This commit is contained in:
1
audio-service/.gitignore
vendored
Normal file
1
audio-service/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
recordings/
|
||||||
@ -1,10 +1,17 @@
|
|||||||
{
|
{
|
||||||
"Test": [
|
"Test": [],
|
||||||
|
"Uncategorized": [
|
||||||
{
|
{
|
||||||
"name": "test 2",
|
"filename": "C:\\Users\\mickl\\Desktop\\cliptrim-ui\\ClipTrimApp\\audio-service\\recordings\\audio_capture_20260214_114317.wav",
|
||||||
"filePath": "C:\\Users\\mickl\\Music\\clips\\original\\audio_capture_20250105_131700.wav",
|
"name": "Clip 20260214_114317",
|
||||||
"volume": 1,
|
"playbackType": "playStop",
|
||||||
"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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -3,6 +3,7 @@ import numpy as np
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import scipy.io.wavfile as wavfile
|
import scipy.io.wavfile as wavfile
|
||||||
|
from metadata_manager import MetaDataManager
|
||||||
|
|
||||||
class AudioRecorder:
|
class AudioRecorder:
|
||||||
_instance = None
|
_instance = None
|
||||||
@ -11,8 +12,9 @@ class AudioRecorder:
|
|||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
print("Creating new AudioRecorder instance")
|
print("Creating new AudioRecorder instance")
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance.init()
|
||||||
return cls._instance
|
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.
|
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 sample_rate: Audio sample rate (if None, use default device sample rate)
|
||||||
:param channels: Number of audio channels
|
:param channels: Number of audio channels
|
||||||
"""
|
"""
|
||||||
|
print(f"Initializing AudioRecorder")
|
||||||
self.duration = duration
|
self.duration = 30
|
||||||
self.sample_rate = sample_rate
|
self.sample_rate = 44100
|
||||||
self.channels = channels
|
self.channels = 2
|
||||||
self.buffer = np.zeros((int(duration * sample_rate), channels), dtype=np.float32)
|
self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
|
||||||
self.recordings_dir = recordings_dir
|
self.recordings_dir = "recordings"
|
||||||
self.stream = sd.InputStream(
|
self.stream = sd.InputStream(
|
||||||
samplerate=self.sample_rate,
|
samplerate=self.sample_rate,
|
||||||
channels=self.channels,
|
channels=self.channels,
|
||||||
@ -93,6 +95,16 @@ class AudioRecorder:
|
|||||||
# Write buffer to file
|
# Write buffer to file
|
||||||
wavfile.write(filename, int(self.sample_rate), audio_data_int16)
|
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
|
return filename
|
||||||
|
|
||||||
def set_buffer_duration(self, duration):
|
def set_buffer_duration(self, duration):
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import threading
|
|||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
global recorder, audio_manager
|
|
||||||
# Create argument parser
|
# Create argument parser
|
||||||
parser = argparse.ArgumentParser(description='Audio Recording Service')
|
parser = argparse.ArgumentParser(description='Audio Recording Service')
|
||||||
|
|
||||||
@ -36,17 +35,6 @@ def main():
|
|||||||
# Ensure save path exists
|
# Ensure save path exists
|
||||||
os.makedirs(settings.get_settings('save_path'), exist_ok=True)
|
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
|
# Register blueprints
|
||||||
app.register_blueprint(recording_bp)
|
app.register_blueprint(recording_bp)
|
||||||
|
|||||||
@ -7,8 +7,9 @@ class MetaDataManager:
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance.init()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
def __init__(self):
|
def init(self):
|
||||||
# read metadata file from executing directory
|
# read metadata file from executing directory
|
||||||
self.metadata_file = os.path.join(os.getcwd(), "metadata.json")
|
self.metadata_file = os.path.join(os.getcwd(), "metadata.json")
|
||||||
if os.path.exists(self.metadata_file):
|
if os.path.exists(self.metadata_file):
|
||||||
@ -16,6 +17,9 @@ class MetaDataManager:
|
|||||||
self.collections = json.load(f)
|
self.collections = json.load(f)
|
||||||
else:
|
else:
|
||||||
self.collections = {}
|
self.collections = {}
|
||||||
|
if(collections := self.collections.get("Uncategorized")) is None:
|
||||||
|
self.collections["Uncategorized"] = []
|
||||||
|
self.save_metadata()
|
||||||
|
|
||||||
def create_collection(self, name):
|
def create_collection(self, name):
|
||||||
if name in self.collections:
|
if name in self.collections:
|
||||||
@ -35,6 +39,31 @@ class MetaDataManager:
|
|||||||
self.collections[collection_name].append(clip_metadata)
|
self.collections[collection_name].append(clip_metadata)
|
||||||
self.save_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):
|
def get_collections(self):
|
||||||
return list(self.collections.keys())
|
return list(self.collections.keys())
|
||||||
|
|
||||||
@ -43,6 +72,18 @@ class MetaDataManager:
|
|||||||
raise ValueError(f"Collection '{collection_name}' does not exist.")
|
raise ValueError(f"Collection '{collection_name}' does not exist.")
|
||||||
return self.collections[collection_name]
|
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):
|
def save_metadata(self):
|
||||||
with open(self.metadata_file, "w") as f:
|
with open(self.metadata_file, "w") as f:
|
||||||
json.dump(self.collections, f, indent=4)
|
json.dump(self.collections, f, indent=4)
|
||||||
|
|||||||
Binary file not shown.
@ -20,16 +20,27 @@ def add_collection():
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return jsonify({'status': 'error', 'message': str(e)}), 400
|
return jsonify({'status': 'error', 'message': str(e)}), 400
|
||||||
|
|
||||||
@metadata_bp.route('/meta/collection/clips', methods=['GET'])
|
@metadata_bp.route('/meta/collection/clips/<name>', methods=['GET'])
|
||||||
def get_clips_in_collection():
|
def get_clips_in_collection(name):
|
||||||
meta_manager = MetaDataManager()
|
meta_manager = MetaDataManager()
|
||||||
collection_name = request.args.get('name')
|
collection_name = name
|
||||||
try:
|
try:
|
||||||
clips = meta_manager.get_clips_in_collection(collection_name)
|
clips = meta_manager.get_clips_in_collection(collection_name)
|
||||||
return jsonify({'status': 'success', 'clips': clips})
|
return jsonify({'status': 'success', 'clips': clips})
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return jsonify({'status': 'error', 'message': str(e)}), 400
|
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'])
|
@metadata_bp.route('/meta/collection/clips/add', methods=['POST'])
|
||||||
def add_clip_to_collection():
|
def add_clip_to_collection():
|
||||||
meta_manager = MetaDataManager()
|
meta_manager = MetaDataManager()
|
||||||
@ -41,3 +52,27 @@ def add_clip_to_collection():
|
|||||||
return jsonify({'status': 'success', 'clips': clips})
|
return jsonify({'status': 'success', 'clips': clips})
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return jsonify({'status': 'error', 'message': str(e)}), 400
|
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
|
||||||
|
|||||||
@ -9,8 +9,9 @@ class SettingsManager:
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance.init()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
def __init__(self):
|
def init(self):
|
||||||
# read settings file from executing directory
|
# read settings file from executing directory
|
||||||
self.settings_file = os.path.join(os.getcwd(), "settings.json")
|
self.settings_file = os.path.join(os.getcwd(), "settings.json")
|
||||||
if os.path.exists(self.settings_file):
|
if os.path.exists(self.settings_file):
|
||||||
|
|||||||
@ -12,8 +12,9 @@ class WindowsAudioManager:
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
|
cls._instance.init()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
def __init__(self):
|
def init(self):
|
||||||
"""
|
"""
|
||||||
Initialize Windows audio device and volume management.
|
Initialize Windows audio device and volume management.
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user