deepctl-cmd-debug-audio 0.1.13__tar.gz → 0.1.14__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/PKG-INFO +1 -1
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/pyproject.toml +1 -1
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio/command.py +75 -47
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/PKG-INFO +1 -1
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/README.md +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/setup.cfg +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio/__init__.py +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio/models.py +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/SOURCES.txt +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/dependency_links.txt +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/entry_points.txt +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/requires.txt +0 -0
- {deepctl_cmd_debug_audio-0.1.13 → deepctl_cmd_debug_audio-0.1.14}/src/deepctl_cmd_debug_audio.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "deepctl-cmd-debug-audio"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.14" # x-release-please-version
|
|
8
8
|
description = "Audio debug subcommand for deepctl"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -10,15 +10,34 @@ from urllib.parse import urlparse
|
|
|
10
10
|
|
|
11
11
|
import ffmpeg # type: ignore[import-untyped]
|
|
12
12
|
import httpx
|
|
13
|
-
from deepctl_core import
|
|
13
|
+
from deepctl_core import (
|
|
14
|
+
AuthManager,
|
|
15
|
+
BaseCommand,
|
|
16
|
+
Config,
|
|
17
|
+
DeepgramClient,
|
|
18
|
+
get_console,
|
|
19
|
+
get_output_format,
|
|
20
|
+
get_status_console,
|
|
21
|
+
)
|
|
14
22
|
from rich import box
|
|
15
|
-
from rich.console import Console
|
|
16
23
|
from rich.panel import Panel
|
|
17
24
|
from rich.table import Table
|
|
18
25
|
|
|
19
26
|
from .models import AudioDebugResult, AudioFormat, AudioInfo, AudioStream
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
# Two channels, deliberately (#104):
|
|
29
|
+
# console -> stdout, the human rendering of the analysis. Only
|
|
30
|
+
# written in default (human) output mode; in json/yaml/
|
|
31
|
+
# csv/table mode the serialized AudioDebugResult is the
|
|
32
|
+
# whole of stdout, so `dg -o json debug audio | jq` parses.
|
|
33
|
+
# status_console -> stderr, always: progress lines and failure panels.
|
|
34
|
+
console = get_console()
|
|
35
|
+
status_console = get_status_console()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _human_output() -> bool:
|
|
39
|
+
"""True when stdout is for a person, not a parser."""
|
|
40
|
+
return get_output_format() == "default"
|
|
22
41
|
|
|
23
42
|
|
|
24
43
|
class AudioCommand(BaseCommand):
|
|
@@ -87,7 +106,7 @@ class AudioCommand(BaseCommand):
|
|
|
87
106
|
|
|
88
107
|
def _download_url(self, url: str) -> str:
|
|
89
108
|
"""Download a URL to a temporary file and return the path."""
|
|
90
|
-
|
|
109
|
+
status_console.print(f"[blue]Downloading:[/blue] {url}")
|
|
91
110
|
|
|
92
111
|
parsed = urlparse(url)
|
|
93
112
|
ext = os.path.splitext(parsed.path)[1] or ".audio"
|
|
@@ -107,7 +126,7 @@ class AudioCommand(BaseCommand):
|
|
|
107
126
|
tmp.write(chunk)
|
|
108
127
|
total += len(chunk)
|
|
109
128
|
size_mb = total / (1024 * 1024)
|
|
110
|
-
|
|
129
|
+
status_console.print(
|
|
111
130
|
f"[green]Downloaded[/green] {size_mb:.2f} MB → {tmp_name}"
|
|
112
131
|
)
|
|
113
132
|
return tmp_name
|
|
@@ -406,7 +425,7 @@ class AudioCommand(BaseCommand):
|
|
|
406
425
|
|
|
407
426
|
# Check if ffmpeg is installed
|
|
408
427
|
if not self.check_ffmpeg_installed():
|
|
409
|
-
|
|
428
|
+
status_console.print(
|
|
410
429
|
Panel(
|
|
411
430
|
"[red]✗ FFmpeg not found![/red]\n\n"
|
|
412
431
|
"The audio debug command requires FFmpeg to be installed "
|
|
@@ -438,7 +457,7 @@ class AudioCommand(BaseCommand):
|
|
|
438
457
|
downloaded_file = self._download_url(audio_file)
|
|
439
458
|
file_to_analyze = downloaded_file
|
|
440
459
|
except Exception as e:
|
|
441
|
-
|
|
460
|
+
status_console.print(
|
|
442
461
|
Panel(
|
|
443
462
|
f"[red]✗ Failed to download URL[/red]\n\n[dim]{e!s}[/dim]",
|
|
444
463
|
title="Download Failed",
|
|
@@ -454,7 +473,9 @@ class AudioCommand(BaseCommand):
|
|
|
454
473
|
|
|
455
474
|
# Process the audio file
|
|
456
475
|
try:
|
|
457
|
-
|
|
476
|
+
status_console.print(
|
|
477
|
+
f"[blue]Analyzing audio file:[/blue] {file_to_analyze}"
|
|
478
|
+
)
|
|
458
479
|
|
|
459
480
|
# Run ffprobe
|
|
460
481
|
probe_data = self.run_ffprobe(file_to_analyze, ffprobe_args)
|
|
@@ -462,45 +483,52 @@ class AudioCommand(BaseCommand):
|
|
|
462
483
|
# Parse the data
|
|
463
484
|
audio_info = self.parse_audio_info(probe_data)
|
|
464
485
|
|
|
465
|
-
#
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
486
|
+
# Everything below is the human rendering of the analysis. It is
|
|
487
|
+
# the same information the returned AudioDebugResult carries, so
|
|
488
|
+
# in a machine-readable format it would be duplicate prose sitting
|
|
489
|
+
# in front of the payload -- exactly the #104 break. Print it only
|
|
490
|
+
# when stdout is for a person.
|
|
491
|
+
if _human_output():
|
|
492
|
+
# Display results based on verbosity
|
|
493
|
+
if extra_verbose or ffprobe_args:
|
|
494
|
+
self.display_extra_verbose_info(audio_info)
|
|
495
|
+
elif verbose:
|
|
496
|
+
self.display_verbose_info(audio_info)
|
|
497
|
+
else:
|
|
498
|
+
self.display_basic_info(audio_info)
|
|
499
|
+
|
|
500
|
+
# Check for Deepgram compatibility
|
|
501
|
+
console.print("\n[bold]Deepgram Compatibility Check:[/bold]")
|
|
502
|
+
compatibility_issues = []
|
|
503
|
+
|
|
504
|
+
if audio_info.streams:
|
|
505
|
+
for stream in audio_info.streams:
|
|
506
|
+
# Check sample rate
|
|
507
|
+
if stream.sample_rate and int(stream.sample_rate) < 8000:
|
|
508
|
+
compatibility_issues.append(
|
|
509
|
+
f"⚠️ Low sample rate ({stream.sample_rate} Hz) - "
|
|
510
|
+
f"Deepgram works best with 8kHz or higher"
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# Check channels
|
|
514
|
+
if stream.channels and stream.channels > 2:
|
|
515
|
+
compatibility_issues.append(
|
|
516
|
+
f"⚠️ Multi-channel audio ({stream.channels} "
|
|
517
|
+
f"channels) - Consider converting to mono or "
|
|
518
|
+
f"stereo"
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
if compatibility_issues:
|
|
522
|
+
for issue in compatibility_issues:
|
|
523
|
+
console.print(f" {issue}")
|
|
524
|
+
else:
|
|
525
|
+
console.print(
|
|
526
|
+
" [green]✓[/green] Audio appears to be compatible "
|
|
527
|
+
"with Deepgram"
|
|
528
|
+
)
|
|
501
529
|
|
|
502
|
-
|
|
503
|
-
|
|
530
|
+
# Encoding suggestions
|
|
531
|
+
self._suggest_encoding(audio_info)
|
|
504
532
|
|
|
505
533
|
return AudioDebugResult(
|
|
506
534
|
status="success",
|
|
@@ -510,7 +538,7 @@ class AudioCommand(BaseCommand):
|
|
|
510
538
|
)
|
|
511
539
|
|
|
512
540
|
except Exception as e:
|
|
513
|
-
|
|
541
|
+
status_console.print(
|
|
514
542
|
Panel(
|
|
515
543
|
f"[red]✗ Error analyzing audio file[/red]\n\n[dim]{e!s}[/dim]",
|
|
516
544
|
title="Analysis Failed",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|