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) {

=pod

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

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'.

=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;

  $msg = Clipboard->paste unless $msg;
  $msg = do {local $/; <$msg>} if ref $msg eq 'GLOB';

  # Sanitize escape sequences
  # 1. Remove bells (\a)
  $msg =~ s/(\\a|\a)//g;

  # 2. Convert other escapes to space
  # Literals: \n, \t, \r, \f, \b
  $msg =~ s/\\[ntrfb]/ /g;

  # 3. Convert actual control characters to space
  $msg =~ s/[\n\t\r\f\b]/ /g;

  # 4. Collapse multiple spaces
  $msg =~ s/\s+/ /g;
  $msg =~ s/^\s+|\s+$//g;

  my @mute_paths =
    ($ENV{SPEAK_MUTE}, $ENV{HOME} . "/.speak/shh", "/etc/speak/shh");

  foreach my $path (@mute_paths) {
    if ($path && -f $path) {
      $msg .= ' [silent shh]';



( run in 4.256 seconds using v1.01-cache-2.11-cpan-bbc515a03b3 )