Speak

 view release on metacpan or  search on metacpan

lib/Speak.pm  view on Meta::CPAN

    my $content = $response->content;
    if (length ($content) == 0) {
      carp "Fetch successful but content is empty";
      return;
    }

    # Check if we got HTML instead of MP3 (e.g. Captcha/Error)
    if ($content =~ /^\s*<(!DOCTYPE|html)/i) {
      carp
"Received HTML response instead of MP3 (likely CAPTCHA/Blocked) from URL: $url";
      return;
    }

    return $content;
  } else {
    carp "Failed to fetch TTS: " . $response->status_line;
    return;
  }
} ## end sub _fetch_mp3

sub _convert_mp3_to_wav ($mp3, $wav) {

  # Try ffmpeg
  if (system ("which ffmpeg >/dev/null 2>&1") == 0) {
    return system ("ffmpeg -y -v error -i \"$mp3\" \"$wav\"") == 0;
  }

  # Try mpg123
  if (system ("which mpg123 >/dev/null 2>&1") == 0) {
    return system ("mpg123 -q -w \"$wav\" \"$mp3\"") == 0;
  }

  # Try lame
  if (system ("which lame >/dev/null 2>&1") == 0) {
    return system ("lame --decode --quiet \"$mp3\" \"$wav\"") == 0;
  }

  # Fallback to sox (might fail if no mp3 handler)
  return system ("sox \"$mp3\" \"$wav\"") == 0;
} ## end sub _convert_mp3_to_wav

## no critic (Subroutines::ProhibitExcessComplexity)
sub speak ($msg, $log = undef, $lang = undef, $persona = undef) {

=pod

=head2 speak($msg, $log, $lang, $persona)

Convert $msg to speech.

Parameters:

=for html <blockquote>

=over

=item $msg:

Message to speak. If $msg is defined and scalar then that is the message
to speak. If it is a file handle then the text will be read from that file.
Otherwise the text in the clipboard will be used.

=item $log

If provided, errors and messages will be logged to the logfile, otherwise to speak.log

=item $lang

Language code (e.g. 'en', 'en-gb', 'en-au'). Defaults to $ENV{SPEAK_LANG} or 'en'.

=item $persona

Optional. The persona name to use for the voice. If provided, the MCP server is used instead of standard TTS.

=back



=for html </blockquote>

Returns:

=for html <blockquote>

=over

=item Nothing

=back

=for html </blockquote>

=cut

  $log = Speak::Logger->new (
    path        => '/var/log',
    name        => 'speak',
    timestamped => 'yes',
    append      => 1,
  ) unless $log;

  unless ($msg) {
    $msg = eval {
      ## no critic (InputOutput::RequireBriefOpen)
      open my $olderr, '>&', \*STDERR    or croak "Cannot dup STDERR: $!";
      open STDERR,     '>',  '/dev/null' or croak "Cannot redirect STDERR: $!";
      my $res = Clipboard->paste;
      open STDERR, '>&', $olderr or croak "Cannot restore STDERR: $!";
      close $olderr;
      ## use critic
      $res;
    };
    $msg //= '';
  } ## end unless ($msg)
  $msg = do {local $/; <$msg>} if ref $msg eq 'GLOB';

  my %sys_conf;
  foreach my $conf_file (@CONFIG_PATHS) {
    if (-f $conf_file) {
      %sys_conf = _get_config ($conf_file);
      last;



( run in 1.799 second using v1.01-cache-2.11-cpan-ff9377addf4 )