一個在編程時“玲姐”語音各種稱贊你的 IDEA 擴展插件,蘿莉音帶耳機!IDEA 彩虹屁插件!


是否聽說過程序員鼓勵師,不久前出了一款vscode的插件rainbow-fart,可以在寫代碼的時候,匹配到特定關鍵詞就瘋狂的拍你馬屁。

vscode的下載嘗試過,但是作為日常將IDEA作為主力生產工具的同學來說,如何體驗呢? 於是假期花了一點時間,寫了一個idea版本的插件idea-rainbow-fart。

安裝方法

先到下載最新的插件。關注公眾號Java精選,掃描下方二維碼,回復彩虹屁,即可獲取下載文件。直接放到對應目錄即可應用,試一試被瘋狂拍你馬屁的插件吧!!!

qrcode.jpg

image.png

下載rainbow-fart-1.0-SNAPSHOT.zip,然后打開Idea的插件目錄,比如筆者的目錄是C:\Program Files\JetBrains\IntelliJ IDEA 2018.2.4\plugins

將rainbow-fart-1.0-SNAPSHOT.zip解壓到plugins目錄,如圖所示:

image.png

解壓

然后重啟IDEA即可。

使用內置語音包

打開設置:

將voice package type設置為builtin

可以選擇內置語音包,共三個,一個官方的中文和英文,一個tts合成的(玲姐姐)

image.png

使用第三方語音包

image.png

將voice package type設置為custom

可以到 https://github.com/topics/vscode-rainbow-fart 查找語音包。

點擊確定生效。

使用TTS(推薦)

本插件特色功能,支持自定義關鍵詞和文本,鼠標點擊表格可以修改關鍵詞和回復語,修改時enter回車換行,一行代表一個

image.png

TTS 使用科大訊飛提供的流式API。

原理

沒啥原理,就是一款簡單的idea插件,對沒寫過插件的我來說,需要先看下官方文檔,基本上看下面這一篇就OK:

https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started.html

讀取語音包

先來看下語音包的設計:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
   "name" "KugimiyaRie" ,
   "display-name" "KugimiyaRie 釘宮理惠 (Japanese)" ,
   "avatar" "louise.png" ,
   "avatar-dark" "shana.png" ,
   "version" "0.0.1" ,
   "description" "傲嬌釘宮,鞭寫鞭罵" ,
   "languages" : [
     "javascript"
   ],
   "author" "zthxxx" ,
   "gender" "female" ,
   "locale" "jp" ,
   "contributes" : [
     {
       "keywords" : [
         "function" ,
         "=>"
       ],
       "voices" : [
         "function_01.mp3" ,
         "function_02.mp3" ,
         "function_03.mp3"
       ]
     },
...
]
}

對Java來說,定義兩個bean類,解析json即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
      * 加載配置
      */
     public  static  void  loadConfig() {
         try  {
             //
             FartSettings settings = FartSettings.getInstance();
             if  (!settings.isEnable()) {
                 return ;
             }
             String json = readVoicePackageJson( "manifest.json" );
             Gson gson =  new  Gson();
             Manifest manifest = gson.fromJson(json, Manifest. class );
             // load contributes.json
             if  (manifest.getContributes() ==  null ) {
                 String contributesText = readVoicePackageJson( "contributes.json" );
                 Manifest contributes = gson.fromJson(contributesText, Manifest. class );
                 if  (contributes.getContributes() !=  null ) {
                     manifest.setContributes(contributes.getContributes());
                 }
             }
             Context.init(manifest);
         catch  (IOException e) {
         }
     }

監控用戶輸入

自定義一個Handler類繼承TypedActionHandlerBase即可,需要實現的方法原型是:

1
public  void  execute( @NotNull  Editor editor,  char  charTyped,  @NotNull  DataContext dataContext)

chartTyped就是輸入的字符,我們可以簡單粗暴的將這些組合到一起即可,用一個list緩存,然后將拼接后的字符串匹配關鍵詞。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  private  List<String> candidates =  new  ArrayList<>();
     @Override
     public  void  execute( @NotNull  Editor editor,  char  charTyped,  @NotNull  DataContext dataContext) {
         candidates.add(String.valueOf(charTyped));
         String str = StringUtils.join(candidates,  "" );
         try  {
             List<String> voices = Context.getCandidate(str);
             if  (!voices.isEmpty()) {
                 Context.play(voices);
                 candidates.clear();
             }
         } catch  (Exception e){
             // TODO
             candidates.clear();
         }
         if  ( this .myOriginalHandler !=  null ) {
             this .myOriginalHandler.execute(editor, charTyped, dataContext);
         }
     }

匹配關鍵詞更簡單,將讀取出來的json,放到hashmap中,然后遍歷map,如果包含關鍵詞就作為語音候選:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  static  List<String> getCandidate(String inputHistory) {
         final  List<String> candidate =  new  ArrayList<>();
         FartSettings settings = FartSettings.getInstance();
         if  (!settings.isEnable()) {
             return  candidate;
         }
         if  (keyword2Voices !=  null ) {
             keyword2Voices.forEach((keyword, voices) -> {
                 if  (inputHistory.contains(keyword)) {
                     candidate.addAll(voices);
                 }
             });
         }
         if  (candidate.isEmpty()) {
             candidate.addAll(findSpecialKeyword(inputHistory));
         }
         return  candidate;
     }

如果找到候選,就播放。

播放

為了防止同時播放多個語音,我們用一個單線程線程池來搞定。播放器使用javazoom.jl.player.Player

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
      * play in a single thread pool
      */
     static  ExecutorService playerTheadPool;
static  {
         ThreadFactory playerFactory =  new  ThreadFactoryBuilder()
                 .setNameFormat( "player-pool-%d" ).build();
         playerTheadPool =  new  ThreadPoolExecutor( 1 1 ,
                 0L, TimeUnit.MILLISECONDS,
                 new  LinkedBlockingQueue<>( 1024 ), playerFactory,  new  ThreadPoolExecutor.AbortPolicy());
     }
public  static  void  play(List<String> voices) {
         FartSettings settings = FartSettings.getInstance();
         if  (!settings.isEnable()) {
             return ;
         }
         // play in single thread
         playerTheadPool.submit(() -> {
             String file = voices.get( new  Random().nextInt() % voices.size());
             try  {
                 InputStream inputStream =  null ;
                 if  (StringUtils.isEmpty(settings.getCustomVoicePackage())) {
                     inputStream = Context. class .getResourceAsStream( "/build-in-voice-chinese/"  + file);
                 else  {
                     File mp3File = Paths.get(settings.getCustomVoicePackage(), file).toFile();
                     if  (mp3File.exists()) {
                         try  {
                             inputStream =  new  FileInputStream(mp3File);
                         catch  (FileNotFoundException e) {
                         }
                     else  {
                         return ;
                     }
                 }
                 if  (inputStream !=  null ) {
                     Player player =  new  Player(inputStream);
                     player.play();
                     player.close();
                 }
             catch  (JavaLayerException e) {
             }
         });
     }

開源地址: https://github.com/jadepeng/idea-rainbow-fart,歡迎fork代碼,貢獻代碼。

插件參考,感謝原作者的貢獻

插件開發參考 https://github.com/izhangzhihao/intellij-rainbow-fart

語音包和插件icon源於 https://github.com/SaekiRaku/vscode-rainbow-fart

作者:jqpeng的技術記事本

來源:https://www.cnblogs.com/xiaoqi/p/idea-rainbow-fart.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM