一个在编程时“玲姐”语音各种称赞你的 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