From 90498052e00944d24a638840bc98c749cac7ff22 Mon Sep 17 00:00:00 2001 From: Eden Refael Date: Thu, 28 Nov 2024 00:41:14 +0200 Subject: [PATCH] created a dedicated pseudo algorithm for the amixer sget and tried various of strings. --- jc/parsers/amixer.py | 227 ++++++++++++++++++++++++------------------- 1 file changed, 127 insertions(+), 100 deletions(-) diff --git a/jc/parsers/amixer.py b/jc/parsers/amixer.py index 8bd5dea6..629871c6 100644 --- a/jc/parsers/amixer.py +++ b/jc/parsers/amixer.py @@ -40,6 +40,7 @@ def _process(proc_data: List[Dict]) -> List[Dict]: # entry[key] = jc.utils.convert_to_int(entry[key]) # # return proc_data + pass def parse( @@ -66,131 +67,157 @@ def parse( # check if string jc.utils.input_type_check(data) - raw_output = [] - lines = list(filter(None, data.splitlines())) # lines=cleandata - - data = {} - # Strip and check for the first line to extract the control name directly + # split the output lines \r\n or \n + # raw_output = [] + # lines: list = list(filter(None, data.splitlines())) # lines=cleandata + mapping = {} + lines = data.splitlines() first_line = lines[0].strip() + + # Extract the control name from the first line if first_line.startswith("Simple mixer control"): - # Extract the control name, removing the single quotes - control_name = first_line.split("'")[1] # Extracts the part inside the single quotes + control_name = first_line.split("'")[1] else: raise ValueError("Invalid amixer output format: missing control name.") + # map the control name + mapping["control_name"] = control_name - # Add control name to the parsed data as a key - data["control_name"] = control_name - - # Parse the rest of the output + # Process subsequent lines for capabilities, channels, limits, and channel-specific mapping. + # gets the lines from the next line - because we already took care the first line. for line in lines[1:]: + # strip the line (maybe there are white spaces in the begin&end) line = line.strip() + if line.startswith("Capabilities:"): - data["capabilities"] = line.split(":")[1].strip().split() + mapping["capabilities"] = line.split(":")[1].strip().split() elif line.startswith("Playback channels:"): - data["playback_channels"] = line.split(":")[1].strip().split(" - ") + mapping["playback_channels"] = line.split(":")[1].strip().split(" - ") elif line.startswith("Limits:"): limits = line.split(":")[1].strip().split(" - ") - data["limits"] = { - "playback_min": int(limits[0].split()[1]), - "playback_max": int(limits[1]) + mapping["limits"] = { + "playback_min": limits[0].split()[1], + "playback_max": limits[1] } elif line.startswith("Mono:") or line.startswith("Front Left:") or line.startswith("Front Right:"): - # Identifying whether it's Mono, Front Left, or Front Right + # Identify the channel name and parse its information channel_name = line.split(":")[0].strip().lower().replace(" ", "_") channel_info = line.split(":")[1].strip() - # Example: "Playback 255 [100%] [0.00dB] [on]" channel_data = channel_info.split(" ") - playback_value = int(channel_data[0]) - percentage = channel_data[1][1:-1] # Extract percentage e.g. "100%" - db_value = channel_data[2][1:-3] # Extract dB value e.g. "0.00dB" - status = channel_data[3][1:-1] # Extract status "on" or "off" + if channel_data[0] == "": + continue + playback_value = channel_data[1] + percentage = channel_data[2].strip("[]") # Extract percentage e.g., "100%" + db_value = channel_data[3].strip("[]") # Extract dB value e.g., "0.00dB" + status = channel_data[4].strip("[]") # Extract status e.g., "on" or "off" - # Storing channel data in the dict - data[channel_name] = { + # Store channel mapping in the dictionary + mapping[channel_name] = { "playback_value": playback_value, "percentage": percentage, "dB": db_value, "status": status } - return data + return mapping - # if jc.utils.has_data(data): +""" +Input Explained/Rules/Pseudo Algorithm: +1. There will always be the first line which tells the user about the control name. +2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`. +3. After that we'll need to distinct between the Channel - Could be many of channels - It will be listed and separated + by `" "`. + 3a. Capture channels - List of channels + 3b. Playback channels - List of channels +4. Limits - We'll always have the minimum limit and the maximum limit. - # remove final Entries row if -v was used - # if cleandata[-1].startswith('Entries:'): - # cleandata.pop(-1) - # - # # detect if freebsd/osx style was used - # if cleandata[0][-1] == ']': - # for line in cleandata: - # splitline = line.split() - # output_line: Dict[str, Any] = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': splitline[-1].lstrip('[').rstrip(']'), - # 'hwaddress': splitline[3], - # 'iface': splitline[5] - # } - # - # if 'permanent' in splitline: - # output_line['permanent'] = True - # else: - # output_line['permanent'] = False - # - # if 'expires' in splitline: - # output_line['expires'] = splitline[-3] - # - # raw_output.append(output_line) - # - # # detect if linux style was used - # elif cleandata[0].startswith('Address'): - # - # # fix header row to change Flags Mask to flags_mask - # cleandata[0] = cleandata[0].replace('Flags Mask', 'flags_mask') - # cleandata[0] = cleandata[0].lower() - # - # raw_output = jc.parsers.universal.simple_table_parse(cleandata) - # - # # otherwise, try bsd style - # else: - # for line in cleandata: - # splitline = line.split() - # - # # Ignore AIX bucket information - # if 'bucket:' in splitline[0]: - # continue - # elif 'There' in splitline[0] and 'are' in splitline[1]: - # continue - # - # # AIX uses (incomplete) - # elif '' not in splitline and '(incomplete)' not in splitline: - # output_line = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': splitline[4].lstrip('[').rstrip(']'), - # 'hwaddress': splitline[3], - # } - # # Handle permanence and ignore interface in AIX - # if 'permanent' in splitline: - # output_line['permanent'] = True - # elif 'in' not in splitline[6]: # AIX doesn't show interface - # output_line['iface'] = splitline[6] - # - # else: - # output_line = { - # 'name': splitline[0], - # 'address': splitline[1].lstrip('(').rstrip(')'), - # 'hwtype': None, - # 'hwaddress': None, - # } - # # AIX doesn't show interface - # if len(splitline) >= 5: - # output_line['iface'] = splitline[5] - # - # raw_output.append(output_line) - # - # return raw_output if raw else _process(raw_output) +Input Example: + +1.user@kazuar-endpoint:~$ amixer sget Capture +Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on] + + +2.user@kazuar-endpoint:~$ amixer sget Master +Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on] + + + + + +3.user@kazuar-endpoint:~$ amixer sget Speaker +Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on] + + + + +4.user@kazuar-endpoint:~$ amixer sget Headphone +Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off] + + + +""" + +if __name__ == '__main__': + data_sget_master = """Simple mixer control 'Master',0 + Capabilities: pvolume pvolume-joined pswitch pswitch-joined + Playback channels: Mono + Limits: Playback 0 - 87 + Mono: Playback 87 [100%] [0.00dB] [on]""" + + data_sget_capture = """Simple mixer control 'Capture',0 + Capabilities: cvolume cswitch + Capture channels: Front Left - Front Right + Limits: Capture 0 - 63 + Front Left: Capture 63 [100%] [30.00dB] [on] + Front Right: Capture 63 [100%] [30.00dB] [on]""" + + + data_sget_speakers = """Simple mixer control 'Speaker',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 87 [100%] [0.00dB] [on] + Front Right: Playback 87 [100%] [0.00dB] [on]""" + + data_sget_headphones = """Simple mixer control 'Headphone',0 + Capabilities: pvolume pswitch + Playback channels: Front Left - Front Right + Limits: Playback 0 - 87 + Mono: + Front Left: Playback 0 [0%] [-65.25dB] [off] + Front Right: Playback 0 [0%] [-65.25dB] [off]""" + output_data_sget_master = parse(data=data_sget_master) + output_data_sget_speakers = parse(data=data_sget_speakers) + output_data_sget_headphones = parse(data=data_sget_headphones) + output_data_sget_capture = parse(data=data_sget_capture) + di = {'master': output_data_sget_master, + 'speakers': output_data_sget_speakers, + 'headphones': output_data_sget_headphones, + 'capture': output_data_sget_capture} + for key, val in di.items(): + print(f"[info] for key: {key}") + print(f"[info] the output is: {val}") \ No newline at end of file