Winse Blog

走走停停, 熙熙攘攘, 忙忙碌碌, 不知何畏.

视频自动翻译

现在语音翻译应用越来越广泛了。其实视频内的音频应该也可以通过语音的处理方式,来达到添加字幕以及翻译的效果。

google翻译页面已有语音输入的按钮,只是需要我们把电脑的声音转换作为 电脑输入 就行了。

语音识别翻译

https://speechlogger.appspot.com/zh/

翻译

处理方法

转换的工具

第一种(推荐): 使用 VoiceMeeter

第二种:使用 virtual audio cable sofeware

步骤:

  1. 安装(任意一种)转换工具
  2. 设置 播放设备
  3. 在浏览器中点击录音按钮后,点击浏览器地址栏的右侧麦克风按钮,设置 麦克风 的设备
  4. (可选)如果要翻译同时自己也听到,打开 Voicemeeter 软件就行了,程序会自动输出选择一个输出。

记住,不能静音,同时要打开麦克风!!

–END

斐讯K2刷机记录

很久以前就在JD弄了一个K2,当时没有啥需求,所以也没有折腾 。最近尝试DDNS域名绑定到动态的IP,想在家有一个能提供SSH访问的机器。原来的树莓派被弄坏了,就想着折腾折腾刷刷K2,在上面安装一个SSH。

同时也把官网提供的系统净化净化。

原K2的详细信息

斐讯K2 1200M智能双频无线路由器 WIFI穿墙 PSG1218

了解刷机流程

  • 官方版本可能存在的问题:

http://www.right.com.cn/forum/thread-208302-1-1.html

  • 刷机直接参考

【2017-12-01】斐讯K2 V22.5.9.163官方固件定制版,集成breed,支持官版直刷【V1.8】

详细步骤

  1. 更新版本到 V22.5.9.163

    查看官网提供的软件, 下载对应的版本

    • K2_A2_V21.4.6.12.bin
    • K2_V22.5.9.163.bin
  2. 刷净化版(带Bread)k2_163_v18_breed.rar

    • 下载地址

    • breed刷入第三方固件

      进入Bread方法,这个了解下就行,这里不刷第三方的。

      拔除K2上Wan口的网线,路由器断电,持续按住路由器上的reset按钮,接通路由器电源,3秒后松开reset按钮。 在浏览器地址栏输入 http://192.168.1.1 访问Breed Web。

  3. 启动telnet/手动安装SSH

3.1. 启动telnet

用 高级设置 - 系统设置 - WebShell 执行命令

1
/www/cgi-bin# /usr/sbin/telnetd -l /bin/login.sh

直接连,不用密码!!

1
winse@DESKTOP-ADH7K1Q:~$ telnet 192.168.2.1

同时修改下密码:

1
2
# 更改root密码为 admin
echo -e 'admin\nadmin' | passwd root

3.2. 安装SSH

这个版本没有带opkg,需要首先把opkg安装好。

直接下载 opkg.zip 然后本地起一个 httpserver 提供一个下载的服务。

1
2
winse@DESKTOP-ADH7K1Q:/mnt/e/SOFTWARE/k2$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

在telnet窗口执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
root@K2:/www/cgi-bin# cd /bin
root@K2:/bin# wget http://192.168.2.160:8000/opkg
--2018-06-20 22:50:18--  http://192.168.2.160:8000/opkg
Connecting to 192.168.2.160:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 130247 (127K) [application/octet-stream]
Saving to: 'opkg'

opkg                                  100%[=========================================================================>] 127.19K   176KB/s   in 0.7s

2018-06-20 22:50:18 (176 KB/s) - 'opkg' saved [130247/130247]

root@K2:/bin# chmod +x opkg

注意:用完后就删掉吧 rm -rf /bin/opkg ,空间不够!!查看安装了那些软件

1
2
3
4
rm -rf /bin/opkg

root@K2:/overlay# du -sh */*/*
root@K2:/overlay# rm -rf usr/lib/opkg

然后安装ssh:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
opkg install http://downloads.openwrt.org/barrier_breaker/14.07/ramips/mt7620a/packages/base/dropbear_2014.63-2_ramips_24kec.ipk
# 开机自启
/etc/init.d/dropbear enable

# https://openwrt.org/docs/guide-user/base-system/ssh_configuration
# https://wiki.openwrt.org/doc/uci/dropbear
vi /etc/config/dropbear
        option GatewayPorts '1'
        
# 启动
/etc/init.d/dropbear start

uci show dropbear

# 如果需要放开防火墙
iptables -I INPUT 1 -p tcp -m tcp --dport 22 -j ACCEPT


vi /etc/firewall.user
# 删除无用文件
rm -rf /etc/dropbear/dropbear_dss_host_key

注意:需要持久化的话,把这句开放22端口的指令写到 /etc/firewall.user 。

客户端登录:

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
winse@DESKTOP-ADH7K1Q:~$ ssh root@192.168.2.1
The authenticity of host '192.168.2.1 (192.168.2.1)' can't be established.
RSA key fingerprint is SHA256:vuAY65qk3Us4MyjYT8KPT8lYsTSTqru6W4e7My6CRkk.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.2.1' (RSA) to the list of known hosts.
root@192.168.2.1's password:


BusyBox v1.22.1 (2017-02-15 13:52:46 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

    ___  __ _______________  __  _____  ___  ________  ___
   / _ \/ // /  _/ ___/ __ \/  |/  /  |/  / / __/ __ \/ _ \
  / ___/ _  // // /__/ /_/ / /|_/ / /|_/ / _\ \/ /_/ / ___/
 /_/  /_//_/___/\___/\____/_/  /_/_/  /_/ /___/\____/_/
 ----------------------------------------------------------
 Barrier Breaker, unknown
 ----------------------------------------------------------
 PID=K2
 BUILD_TYPE=release
 BUILD_NUMBER=163
 BUILD_TIME=20170215-134532
 ----------------------------------------------------------
 MTK OpenWrt SDK V3.4
 revision : adab2180
 benchmark : APSoC SDK 5.0.1.0
 kernel : 144992
 ----------------------------------------------------------
root@K2:~#

不推荐用密码,最好使用公钥的方式来处理。但公钥访问有点问题,.ssh的目录权限是个麻烦事 (其实文件的位置不对!!)。

参考: Dropbear public-key authentication HowTo

ssh root@192.168.1.1 “tee -a /etc/dropbear/authorized_keys” < ~/.ssh/id_rsa.pub

把 authorized_keys 文件移到 /etc/dropbear 下面就可以了!

1
2
3
4
5
root@K2:~/.ssh# ls -la
drwx------    2 root     root             0 Jun 21 10:35 .
drwx------    1 root     root             0 Jun 21 08:57 ..
-rw-------    1 root     root           397 Jun 21 10:35 authorized_keys
root@K2:~/.ssh# mv authorized_keys /etc/dropbear/

其他拓展

增加空间,挂载windows共享目录

https://blog.vircloud.net/linux/openwrt-psg1218.html

K2 官方版式不带 USB,因此就限制了很多可玩的东西,但是我们可以通过 SMB 挂载的方式来增加存储空间,需要注意的是老毛子挂载 SMB 的方式与其他 OpenWRT 不同,使用 mount 命令是挂载不成功的,正确的方法是:

位置:高级设置 - 自定义设置 - 脚本 - 在路由器启动后执行 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
### SMB资源挂载(局域网共享映射,无USB也能挂载储存空间)
### 说明:共享路径填写时,【\】要写成【\\】。
sleep 10
modprobe des_generic
modprobe cifs CIFSMaxBufSize=64512
#mkdir -p /media/cifs
#mount -t cifs \\\\{host}\\{share} /media/cifs -o username={user},password={pass}
mount -t cifs \\\\192.168.31.100\\移动磁盘-C /mnt -o username=guest,password=guest

sleep 10
mdev -s
sleep 5
stop_ftpsamba
sleep 2
run_ftpsamba
sleep 5

Breed进入方式

  1. 将要刷的第三方固件准备好。
  2. 断电按着reset键不松手,然后通电5秒后再松开reset键。
  3. 打开浏览器输入http://192.168.1.1%E5%8D%B3%E5%8F%AFBreed Web恢复控制台(记得先在Breed Web恢复控制台中的固件备份里备份下EEPROM和编程器固件,以后可能用得着)。
  4. 恢复固件之前最好在Breed Web恢复控制台恢复一下出厂设置,固件类型:Config区(公版)

参考:

其他参考

–END

使用VMWare安装Mac OS X

参考:

实际操作:

  • 安装 VMware-workstation-full-12.5.7-5813279 。
  • 下载 unlocker208.zip 并使用管理员权限安装 win-install.cmd 。
  • 添加虚拟机,选择 Apple Mac OS X(M) - OS X 10.9;然后修改vmx配置,在 smc.present = "TRUE" 后面添加 smc.version = "0"
  • 然后光盘选择 Mavericks_Install_13A603.cdr 安装系统。磁盘格式化:实用工具 - 磁盘工具
  • 安装VMWare Tools。光盘选择 darwiniso.zip 压缩包里面的 darwin6.0.3.iso 。
  • 配置共享文件夹。进入系统后,Finder - 偏好设置 - 已连接的服务器

–END

使用注解生成代码

Java里面随处可见annotation(注解),RetentionPolicy 指示了注解使用的情况:

  • SOURCE,比如 @Override, @SuppressWarnings
  • RUNTIME,最熟悉的莫过于Spring Bean中使用的 @Controller, @Service 一般和反射同时使用。
  • CLASS

而 CLASS 则是用于 compile 编译阶段的注解。一个注解的处理器,以Java代码(或编译过的字节码)作为输入,生成Java文件。这些生成的Java文件,会同其他普通的手动编写的Java源代码一样被javac编译。

可以自己实现一些类似groovy语法糖的功能(lombok框架修改bytecode为类生成新方法getter/setter、或者使用生成新的辅助类等);减少机械的、冗余代码的管理,使得代码更简洁便于阅读。

代码生成

先来了解下整个过程,javac 从 ServiceLoader 获取一个 Processor 标注处理类,判断是否为符合条件的标注,再收集类的相关信息,然后使用 Filer 创建新的类。Java Annotation Processing and Creating a Builderjava annotation processor 主要涉及到如下三部分:

  • Annotation: @BuilderProperty
  • Processor: BuilderProcessor
  • Service:

    通过google的auto-service来注册服务,最终会在 META-INF/services/ 生成名称为 javax.annotation.processing.Processor 的文件,内容为当前被标注的类名。

项目的目录结构如下:

具体实现:

  • BuilderProperty 注解
1
2
3
4
5
6
7
8
9
10
11
package com.github.winse.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface BuilderProperty {
}
  • BuilderProcessor
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package com.github.winse.processor;

import com.github.winse.annotation.BuilderProperty;
import com.google.auto.service.AutoService;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ExecutableType;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @see BuilderProperty
 */
@SupportedAnnotationTypes("com.github.winse.annotation.BuilderProperty")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class BuilderProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            Set<? extends Element> annotationElements = roundEnv.getElementsAnnotatedWith(annotation);

            Map<Boolean, List<Element>> annotationMethods = annotationElements.stream()
                    .collect(Collectors.partitioningBy(element -> ((ExecutableType) element.asType()).getParameterTypes().size() == 1 && element.getSimpleName().toString().startsWith("set")));

            List<Element> setters = annotationMethods.get(true);
            List<Element> otherMethods = annotationMethods.get(false);

            otherMethods.forEach(element -> processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "@BuildProperty must be applied to a setXxx method with a single argument", element));

            if (setters.isEmpty()) {
                continue;
            }

            String className = ((TypeElement) setters.get(0).getEnclosingElement()).getQualifiedName().toString();

            Map<String, String> setterMap = setters.stream().collect(Collectors.toMap(
                    setter -> setter.getSimpleName().toString(),
                    setter -> ((ExecutableType) setter.asType()).getParameterTypes().get(0).toString()
            ));

            try {
                writeBuilderType(className, setterMap);
            } catch (IOException e) {
                processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
            }
        }
        return true;
    }

    private void writeBuilderType(String className, Map<String, String> setterMap) throws IOException {
        String packageName = null;
        int lastDot = className.lastIndexOf(".");
        if (lastDot > 0) {
            packageName = className.substring(0, lastDot);
        }

        String simpleClassName = className.substring(lastDot + 1);
        String builderClassName = className + "Builder";
        String builderSimpleClassName = builderClassName.substring(lastDot + 1);

        JavaFileObject builderFile = processingEnv.getFiler().createSourceFile(builderClassName);
        try (PrintWriter out = new PrintWriter(builderFile.openWriter())) {
            if (packageName != null) {
                out.printf("package %s;\n", packageName);
                out.println();
            }

            out.printf("public class %s {\n", builderSimpleClassName);
            out.println();
            out.printf("  private %s object = new %s();\n", simpleClassName, simpleClassName);
            out.println();
            out.printf("  public %s build() {\n", simpleClassName);
            out.printf("    return object;\n");
            out.printf("  }\n");
            out.println();

            setterMap.entrySet().forEach(setter -> {
                String methodName = setter.getKey();
                String argumentType = setter.getValue();

                out.printf("  public %s %s(%s value){\n", builderSimpleClassName, methodName, argumentType);
                out.printf("    object.%s(value);\n", methodName);
                out.printf("    return this;\n");
                out.printf("  }\n");
                out.println();
            });

            out.printf("}\n");

        }
    }

}

测试使用:

  • build.gradle

我使用的是4.7的版本,4.7以上版本可以直接使用 annotationProcessor 来添加标注处理器。(其他版本可以使用 apt 来处理)

1
2
3
4
5
6
7
8
9
10
plugins {
    id "net.ltgt.apt" version "0.10"
}

sourceSets.main.java.srcDirs += ['build/generated/source/apt/main']

dependencies {
    compile rootProject
    annotationProcessor project(':compiler')
}
  • Person

这是一个POJO类,BuilderProcessor处理器会根据BuilderProperty注解来生成PersonBuilder类。

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
package com.github.winse.example;

import com.github.winse.annotation.BuilderProperty;

public class Person {
    private int age;
    private String name;

    @BuilderProperty
    public void setAge(int age) {
        this.age = age;
    }

    @BuilderProperty
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public String getName() {
        return name;
    }
}

生成代码效果

在 gradle 面板中选择子项目 :example ,然后选择 Tasks 下的 build 任务进行构建。构建完后在 example/build/generated/source/apt 目录下生成了对应的 Builder 代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.github.winse.example;

public class PersonBuilder {

  private Person object = new Person();

  public Person build() {
    return object;
  }

  public PersonBuilder setName(java.lang.String value){
    object.setName(value);
    return this;
  }

  public PersonBuilder setAge(int value){
    object.setAge(value);
    return this;
  }

}

注解处理器调试

不会调试说明还没有真正的入门。并且没有调试的情况下,解决异常、错误也是一件异常痛苦的事情。注解处理器生成代码是在编译阶段来生成代码的,所以调试的选项配置添加到 javac 。而 gradle 提供了一种相对简单的方式来进行。

参考

具体步骤如下:

  1. 在命令行运行构建

    添加调试参数后,gradle 会 暂停等待远程调试 ,相当于添加了 JVM 调试参数。Gradle properties

    hello-annotation-processor\example>gradle clean build --no-daemon -Dorg.gradle.debug=true
    或者
    hello-annotation-processor>gradle example:clean example:compileJava --no-daemon -Dorg.gradle.debug=true
    

    注: –no-daemon 不加也是可以的,但是运行该次构建后不会停止。

  2. 远程调试

其他调试配置方式

  • 通过环境变量

    example>set GRADLE_OPTS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005
    
    example>gradle clean build
    Listening for transport dt_socket at address: 5005
    
  • 修改 ~/.gradle/gradle.properties

    这种方式不推荐,因为它是全局的。

    org.gradle.daemon=false
    org.gradle.debug=true
    

    或者

    org.gradle.daemon=true
    org.gradle.jvmargs=-XX:MaxPermSize=4g -XX:+HeapDumpOnOutOfMemoryError -Xmx4g -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006
    
    $ gradle --daemon
    

    Then attach your debugger client to port 5006, set your breakpoint, then run your test.

    注:该配置放到项目目录下没用。

其他

–END

科学上网(续)

到新的环境就会遇到新的问题,需要不断的学习更新来适应新的环境。上网也是一样,工作地点和家里存在了一道鸿沟。过去断断续续的有一些解决的方式,但是总是有点间接。

上周和同事讨论到在家访问公司服务器的方式时,可以通过花生壳的DDNS来实现域名动态绑定,相当于了把家里的宽带看做一个公网IP,花生壳实时的把域名解析更新为最新的IP。

有了公网IP后,就可以在公司访问自己的域名(绑定到了家里的IP),然后 反向代理 就可以在家访问公司环境了。

但是查了下对于花生壳的口碑都不咋的,其实只要能自动的更新绑定域名和宽带的IP,和花生壳的效果是一样。然后在 github 查到了 aliyun-ddns 定时检测和更新阿里云上的域名解析。

配置公网域名

我在此基础上调整了一个本地命令行版本 ,直接运行一个脚本就可以更新域名解析了:

1
./client.sh myhome.winseliu.com

注:默认电信宽带给你分配的内网IP的,你可以打10000号要他们给你分配改成外网IP。

本地环境配置

  • 本机SSHD配置
1
2
winse@DESKTOP-ADH7K1Q:~$ sudo dpkg-reconfigure openssh-server
winse@DESKTOP-ADH7K1Q:~$ sudo service ssh start

注:启动后,wsl shell窗口不能关!!窗口关闭后,wsl的所有服务都会停掉!

  • 无秘钥登录

为了安全,最好通过秘钥登录,把使用SSH的密码登录关掉。

1
winse@DESKTOP-ADH7K1Q:~/.ssh$ cat /business/server/id_rsa.pub >>authorized_keys
  • 路由器配置

  • 本机防火墙

参考 开放windows服务器端口—–以打开端口8080为例

配置反向代理

服务端访问自己域名,使用 -R 参数在本地创建一个10022的端口,数据转发到服务器的22端口。当你连本地的 127.0.0.1:10022 就相当于连接服务器的 22 端口。

1
/usr/bin/autossh -M 0 -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o StrictHostKeyChecking=no -NR 10022:localhost:22 autossh@myhome.winseliu.com -i ~/.ssh/id_rsa

当连接太慢、SSH提示信息一直不出来,你完全有理由怀疑本地端口被占用了!!查看本地端口状态:

1
2
3
C:\Users\winse>netstat /?

C:\Users\winse>netstat -ano |findstr 10022

如果端口被占用了,需要去任务管理器中关掉对应的PID的程序。

小结

速度比 teamviewer vpn 的方式快狠多狠多!!这个10000号值得打,这个ddns值得一试。

后记

说说 VS Code调试

使用Ubuntu中安装的Node:

1
"useWSL": true

https://code.visualstudio.com/docs/nodejs/nodejs-debugging

注意:这种外部启动的方式,会通过bash.sh运行node,所以就算停止调试后,Node进程还是一直存在的!!!需要通过任务管理器关闭。

–END