利用可能なツールを備えたForvo.comサービスのクライアント

外国語の発音がわかると、外国語の方が覚えやすいのは誰にとっても秘密ではないと思います。 幸いなことに、このための優れたForvoオンラインサービス、 つまり単語の発音のデータベースがあります。 このサービスは、データベースにアクセスして単語を聞くためのWebインターフェース(およびいくつかの制限のあるAPIを含む)を提供します。 ただし、毎回ブラウザを開いて聴くことはあまり便利ではありません。 そこで、単純なforvoクライアントを探し始めました。 私の要件は次のとおりでした:使いやすさ、GUIなし、簡単な移植性、設定を保存する必要なし。 しかし、ここに不幸があります-Linux用の同様の単純なクライアントを見つける試みはすべて失敗しました。 結局のところ、そのようなクライアントの実装は、実際にはそれほど難しくないタスクです。 したがって、私はユーティリティを自分で記述する必要があることに気付きました。


問題の声明


  1. 上記の要件を満たす必要があるforvoクライアントをできるだけ単純にします。
  2. シンプルなコマンドラインインターフェイスが必要です。
    $say hello world # "hello world"
    $say -lng=ru # ( en, ru, tt, etc...)


ツール選択


もちろん、この問題を解決するには多くのアプローチがあります。 最良の選択は、 bash + awk + ​​curl + mpg123 (または他のプレイヤー)を使用することだと思いました。 そのため、最初に必要なパッケージを配置します。たとえば、Debianベースのシステムの場合:

$sudo apt-get install gawk curl mpg123

解決策


将来を見据えて-私はforvo-apiを使用しませんでした。記事の最後で理由を説明します。

forvo検索ページを調べると、フォームがこのPOSTリクエストによって送信されていることがわかります。
    パラメータ:
         id_lang = $LANGUAGE_ID。LANGUAGE_IDは発音言語識別子です。
         word_search = $ WORD、ここでWORDは検索ワードです
    ポストURL:
         http://www.forvo.com/search/-送信アドレスのリクエスト

したがって、以下を呼び出すことでこのリクエストを実装できます。

 #!/bin/bash LANGUAGE_ID=39 #id   (        id_lang) WORD="hello world" curl -d "id_lang=$LANGUAGE_ID&word_search=$WORD" -L 'http://www.forvo.com/search/' 

このリクエストに対する答えは、検索された単語の発音のオーディオストリームのURLを含むリンク(最初に必要です)を含むhtmlの形式で私たちに届きます。 したがって、オーディオストリームのURLを抽出するパーサーを実装する必要があります。 Awkの実装:

 # # parser.awk # /var (_SERVER_HOST|_AUDIO_HTTP_HOST)/{ if(match($0, /var[ \t]+(_SERVER_HOST|_AUDIO_HTTP_HOST)[ \t]*=[ \t]*'?([^']+)'?/, arr)){ if(arr[1] == "_SERVER_HOST"){ srv_host = arr[2]; } else if(arr[1] == "_AUDIO_HTTP_HOST") { audio_http_host = arr[2]; } } } /<a href.+onclick="Play\(/{ if(match($0, /onclick="Play\([^,]+,'([^,]+)'.+\)/, arr)){ mp3Path = arr[1]; if (srv_host == audio_http_host){ mp3Path = ("http://" srv_host "/player-mp3Handler.php?path=" mp3Path); } else { mp3Path = ("http://" audio_http_host "/mp3/" base64_decode(mp3Path)); } } exit; } function base64_decode(val){ command = ("echo '" val "' | base64 -d"); command | getline ret; close(command); return ret; } END{ if(mp3Path) print mp3Path; } 

オーディオストリームのurlを受け取ったら、 mpg123マイクロプレーヤーを使用してそれを再現します。 これは合理的な質問を提起するかもしれません:なぜmpg123であり、他のプレイヤーではないのですか? うーん...プレーヤーを選ぶとき、ストリーミングオーディオを再生できる最もミニマルなプレーヤーを探していました。
したがって、メインスクリプトは次のようになります。

 # # say # LANGUAGE_ID=39 WORD=$@ if [[ -n $WORD ]]; then URL=$(curl -d "id_lang=$LANGUAGE_ID&word_search=$WORD" -L 'http://www.forvo.com/search/' 2> /dev/null | awk -f ${0%/*}/parser.awk) if [[ -n $URL ]]; then mpg123 -q $URL else echo not found fi fi 

しかし、ここで最初の問題が発生します。2つのファイル( sayparser.awk )になりましたが、このような小さなユーティリティにはあまり適していません。 このユーティリティを1つのファイルで提供したいと思います。 これは疑問を提起します: シェル(bash)awkで書かれた2つの異種プログラムをどのように組み合わせるか?

オプション1
標準のawk機能を使用します-プログラムを引用符で囲み、コマンドラインパラメーターとして渡します。

 # # examlpe1.sh # echo "from shell script" AWK_PRG="BEGIN{ print \"from awk program\" }" awk "$AWK_PRG" 


このアプローチは、単一行のawkプログラムに適しています。 プログラムがワンライナーよりわずかに大きい場合、引用符をエスケープするのが難しい場合があります(シングルとダブルの両方)。 スクリーニングは、プログラム自体を「ポイ捨て」し、そのサポートと拡張を複雑にします。 したがって、このアプローチはこの場合には適していません。

オプション2
策略。 まず、考えてみましょう。 シェルスクリプトが解釈可能であること、つまり、 スクリプトはコマンドによって(または行ごとに)実行されます。 したがって、 シェルスクリプトの最後にawkプログラムを配置し、 シェルスクリプト全体を実行した後、 bashインタープリターがawkプログラムの読み取りを開始しないように、 exitコマンドをその前に配置するとどうなるでしょうか。 そのため、 シェルスクリプトをawkプログラムと組み合わせることに成功しました。 しかし、ファイルの末尾にあるこのawkプログラムをどのように読んで実行するのでしょうか? 答えはそれ自身を示唆しています-私たちはawkを使用しています) シェルスクリプトの終わりとawkプログラムの始まりをマーカー(たとえば、コメント)でマークし、このファイルを処理のために、マーカーの後のすべてを読み取る別のawkプログラムに渡すだけです。

 # # examlpe2.sh # echo "this is shell script" AWK_PRG=$(awk '(/^### AWK PROGRAMM MARKER ###$/ || body){body=1; print $0}' $0) awk "$AWK_PRG" exit ### AWK PROGRAMM MARKER ### BEGIN{ print "from awk program" } 


このアプローチにより、変更を加えることなく、 シェルスクリプトにプログラムのawkコード(だけでなく)を含めることができます。 リポジトリには、 getAwkProgram関数の実装があります。これにより、統合されたawkプログラムに名前を付けてシェルスクリプトにロードすることができます。 ここではこの機能を提供しないことにしました。メイントピックから逸​​れると思います。

オプション3
awkプログラムをシェルスクリプトに統合する別の方法を思い出してくれたxaizekに感謝します

 # # examlpe1.sh # echo "from shell script" AWK_PRG=$(cat << 'EOL' BEGIN{ print "from awk program" } EOL ) awk "$AWK_PRG" 


このメソッドは、 heredoc構文に基づいています 。 このアプローチは( bashの観点から)より自然であり、オプション1(インラインプログラム)よりも間違いなく優れていますが、オプション2に比べて読みにくいと考えています。

したがって、2番目のアプローチを使用すると、forvoクライアントは1つのファイルに簡単に収まります。

 #!/bin/bash LANGUAGE_ID=39 #english # Trick for mixing AWK and Shell programs in the same file PARSER_PRG=$(awk '(/^### AWK PROGRAMM MARKER ###$/ || body){body=1; print $0}' $0) WORD=$@ if [[ -n $WORD ]]; then URL=$(curl -d "id_lang=$LANGUAGE_ID&word_search=$WORD" -L 'http://www.forvo.com/search/' 2> /dev/null | awk "$PARSER_PRG") if [[ -n $URL ]]; then mpg123 -q $URL else echo not found fi fi exit ### AWK PROGRAMM MARKER ### # parser /var (_SERVER_HOST|_AUDIO_HTTP_HOST)/{ if(match($0, /var[ \t]+(_SERVER_HOST|_AUDIO_HTTP_HOST)[ \t]*=[ \t]*'?([^']+)'?/, arr)){ if(arr[1] == "_SERVER_HOST"){ srv_host = arr[2]; } else if(arr[1] == "_AUDIO_HTTP_HOST") { audio_http_host = arr[2]; } } } /<a href.+onclick="Play\(/{ if(match($0, /onclick="Play\([^,]+,'([^,]+)'.+\)/, arr)){ mp3Path = arr[1]; if (srv_host == audio_http_host){ mp3Path = ("http://" srv_host "/player-mp3Handler.php?path=" mp3Path); } else { mp3Path = ("http://" audio_http_host "/mp3/" base64_decode(mp3Path)); } } exit; } function base64_decode(val){ command = ("echo '" val "' | base64 -d"); command | getline ret; close(command); return ret; } END{ if(mp3Path) print mp3Path; } 


結論


ここに記載されているアプローチの長所と短所、およびforvo-apiを使用するアプローチを以下に示します。

ちょっとした注意が必要です-何らかの理由で、私にとって、mpg123はforvo-apiリクエストで受け取ったリンクの受け入れを拒否しました。

おわりに


この記事の目的は、このような問題を解決するための可能な方法を示すことだったので、ここではクライアントの基本的な実装(発音言語の永続的な切り替えの可能性なし)を提供することにしました。 クライアントのより完全なバージョンはgithub.comで入手できます。

あとがき


ハブでは、外国語のトピックに関連する何らかの方法で有用な投稿が複数回公開されました。 最近、私はサンドボックスから投稿を実行しました。そこでは、カスタム辞書を作成するというアイデアが気に入りました。 また、選択した単語/フレーズを翻訳するためにキーの組み合わせを結ぶというアイデアが提案された投稿もありました。 これらの記事のアイデアを組み合わせて、このスキームを推奨できます。
これで、なじみのない単語に出会ったら、ワンクリックでその翻訳とその発音を調べることができます。 次に、それを個人の文字起こし辞書に追加する必要があります。 同様のスキームを使用しますが、ブラウザプラグインを使用した単語の翻訳のみを確認します。

Source: https://habr.com/ru/post/J147842/


All Articles