之前收到 Google Domains 邮件,info 域名将在 10 月 26 号之后从 12 美元一年涨价到 22 美元一年,现在剩下的时间不多了,晚上回家处理一下。
刚收到 Google domain 的续费通知邮件,竟然发现 info 域名需要从 $12 涨价到 $22 ,域名注册局的生意一本万利啊!
— Ein Verne (@einverne) September 27,2022
通常不同的域名注册商都会提供动态的注册费用,比如我一直使用的 Google Domains 自我开始使用起 info 域名就是 12 美元一年,很多年没有变化过,但是有一些其他的域名注册提供商会提供更加低廉的第一次注册费用,但是往往续费和转入的费用要更高。
一个域名的费用可以分成好几部分。
域名注册费用组成:1
.com
,.net
,.org
等背后的公司。这些公司都从 ICANN 买断了对 TLD(顶级域名)的管理权,所以有权利决定以什么价格售卖域名。
Verisign 是 .com 的域名注册局,在 2018 年的时候与美国商务部达成协议,允许他在之后的十年中每年将价格提高 7%。2 同样的各大域名注册商也相继提高了 .info, .mobi, .pro 等等域名的注册费用。34
首先到 https://tld-list.com/ 网站上查询 .info
域名,然后发现对于 Transfer 来说 Google 提供的价格就是最便宜的。
综合来说,还是在 Google Domains 先续费 9 年再说吧。
https://news.gandi.net/en/2020/09/how-much-does-a-domain-cost-and-what-comes-with-it/ ↩
https://www.domainnameapi.com/blog/verisign-has-decided-to-increase-the-price-of-the-com ↩
https://news.gandi.net/en/2021/12/price-increase-on-info-mobi-and-pro-domains-on-january-14-2022/ ↩
https://www.techrepublic.com/article/youre-going-to-pay-more-for-org-and-info-domains-following-icanns-lifting-of-price-caps/ ↩
ID3 是一个元数据(metadata) 的容器,通常和 MP3 文件一起。
ID3 有两个版本:
ID3v1 比较简单,存放在 MP3 文件末尾,占用 128 个字节,使用任意一个 16 进制编辑器打开 MP3,就可以看到。
V1 版本以 TAG 字符开始,记录了 MP3 文件的歌手名,标题,专辑名称,年代,风格等信息。
字节 | 长度 | 说明 |
---|---|---|
1-3 | 3 | TAG 字符,说明 ID3v1 开始 |
4-33 | 30 | 歌曲名 |
34-63 | 30 | 歌手 |
64-93 | 30 | 专辑名 |
94-97 | 4 | 年份 |
98-127 | 30 | 附注 |
128 | 1 | 音乐类别,147 种1 |
ID3v2 版本位于 mp3 开头,长度可变。
ID3v2 有四个版本
因为在文件开头,所以对 ID3v2 版本的操作要比 ID3v1 慢。
以 ID3v2.3 为例,由一个标签头和若干标签帧组成,至少有一个标签帧,每个标签帧记录一种信息,比如标题,作曲家等等。
可以通过 vim 打开 mp3 文件,然后执行 %!xxd
以 16 进制查看。
文件开头长度 10 字节,结构如下:
char Header[3]; /*必须为“ID3”否则认为标签不存在*/
char Ver; /*版本号ID3V2.3 就记录3*/
char Revision; /*副版本号此版本记录为0*/
char Flag; /*标志字节,只使用高三位,其它位为0 */
char Size[4]; /*标签大小*/
说明:
标签帧
每个标签帧都有 10 个字节的帧和至少一个字节的不固定长度的内容组成。
帧头部:
char FrameID[4]; /*用四个字符标识一个帧,说明其内容,稍后有常用的标识对照表*/
char Size[4]; /*帧内容的大小,不包括帧头,不得小于 1*/
char Flags[2]; /*存放标志,只定义了 6 位*/
说明:
大体上分三个部分:
0="Blues";
1="ClassicRock";
2="Country";
3="Dance";
4="Disco";
5="Funk";
6="Grunge";
7="Hip-Hop";
8="Jazz";
9="Metal";
10="NewAge";
11="Oldies";
12="Other";
13="Pop";
14="R&B";
15="Rap";
16="Reggae";
17="Rock";
18="Techno";
19="Industrial";
20="Alternative";
21="Ska";
22="Deathl";
23="Pranks";
24="Soundtrack";
25="Euro-Techno";
26="Ambient";
27="Trip-Hop";
28="Vocal";
29="Jazz+Funk";
30="Fusion";
31="Trance";
32="Classical";
33="Instrumental";
34="Acid";
35="House";
36="Game";
37="SoundClip";
38="Gospel";
39="Noise";
40="AlternRock";
41="Bass";
42="Soul";
43="Punk";
44="Space";
45="Meditative";
46="InstrumentalPop";
47="InstrumentalRock";
48="Ethnic";
49="Gothic";
50="Darkwave";
51="Techno-Industrial";
52="Electronic";
53="Pop-Folk";
54="Eurodance";
55="Dream";
56="SouthernRock";
57="Comedy";
58="Cult";
59="Gangsta";
60="Top40";
61="ChristianRap";
62="Pop/Funk";
63="Jungle";
64="NativeAmerican";
65="Cabaret";
66="NewWave";
67="Psychadelic";
68="Rave";
69="Showtunes";
70="Trailer";
71="Lo-Fi";
72="Tribal";
73="AcidPunk";
74="AcidJazz";
75="Polka";
76="Retro";
77="Musical";
78="Rock&Roll";
79="HardRock";
80="Folk";
81="Folk-Rock";
82="NationalFolk";
83="Swing";
84="FastFusion";
85="Bebob";
86="Latin";
87="Revival";
88="Celtic";
89="Bluegrass";
90="Avantgarde";
91="GothicRock";
92="ProgessiveRock";
93="PsychedelicRock";
94="SymphonicRock";
95="SlowRock";
96="BigBand";
97="Chorus";
98="EasyListening";
99="Acoustic";
100="Humour";
101="Speech";
102="Chanson";
103="Opera";
104="ChamberMusic";
105="Sonata";
106="Symphony";
107="BootyBass";
108="Primus";
109="PornGroove";
110="Satire";
111="SlowJam";
112="Club";
113="Tango";
114="Samba";
115="Folklore";
116="Ballad";
117="PowerBallad";
118="RhythmicSoul";
119="Freestyle";
120="Duet";
121="PunkRock";
122="DrumSolo";
123="Acapella";
124="Euro-House";
125="DanceHall";
126="Goa";
127="Drum&Bass";
128="Club-House";
129="Hardcore";
130="Terror";
131="Indie";
132="BritPop";
133="Negerpunk";
134="PolskPunk";
135="Beat";
136="ChristianGangstaRap";
137="Heavyl";
138="Blackl";
139="Crossover";
140="ContemporaryChristian";
141="ChristianRock";
142="Merengue";
143="Salsa";
144="Trashl";
145="Anime";
146="JPop";
147="Synthpop";
见附录 ↩
本文总结一下将字幕文件压制到视频中的方式,(当然我个人是非常不喜欢直接将字幕压制到视频流中作为硬字幕压制的,但有些时候可能就是需要分享这样硬字幕的视频,比如视频网站,所以也会在下文总结一下)。
按照压制方式可以分成,将字幕嵌入视频流(也就是俗称的硬字幕)适合在视频网站分享,将字幕作为单独的字幕流和视频作为封装格式,需要用播放器播放。
压制方式(推荐程度从上到下):
如果要知道如何从 mkv 文件格式中提取字幕,可以参考 这篇文章 。
作为字幕流(内封字幕、软字幕)嵌入到视频容器中。字幕流和视频和音频流具有相同的地位。视频格式中的 mkv 就是一种封装格式,通过 ffmpeg -i video.mkv
就能在输出结果中看到视频流和字幕流是属于不同的 stream 的。
MKV 封装工具:[[MKVToolNix]] MKV 提取工具:gMKVExtractGUI、MKVExtractGUI
借助 MKVToolNix 提供的界面操作即可。
介绍一下如何使用 FFmpeg 将字幕作为单独的字幕流压制到视频中。
ffmpeg -i input.mkv -i subtitles.srt -c copy -c:s mov_text output.mp4
ffmpeg -i input.mkv -i subtitles.srt -c copy -c:s srt output.mkv
说明:
-c copy -c: s mov_text
告诉 FFmpeg 对于视频,音频,和字幕文件都直接 copy-c: v copy -c: a copy -c: s mov_text
假设原始输入文件没有字幕的情况下,也可以直接
ffmpeg -i input.mkv -i subtitles.srt -c copy output.mkv
FFmpeg 会自动识别字幕文件并做映射。
ffmpeg -i input.mp4 -sub_charenc 'UTF-8' -f srt -i input.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s mov_text output.mp4
ffmpeg -i input.mp4 -sub_charenc 'UTF-8' -f srt -i input.srt -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s srt output.mkv
如果要指定语言:
ffmpeg -i input.mp4 -i subtitle.en.srt -c copy -c:s mov_text -metadata:s:s:0 language=eng ouptut_english.mp4
-metadata: s: s:0
设置 metadata 格式 Stream: Subtitle: Number
从 0 开始language=eng
设置字幕的语言,使用 ISO 639 3 位英文表示如果要设置多个字幕:
ffmpeg -i ouptut_english.mp4 -i subtitle.chi.srt -map 0 -map 1 -c copy -c:s mov_text -metadata:s:s:1 language=chi output_chi.mp4
在之前的基础之上,再添加一个中文字幕。
或者直接使用一行命令:
ffmpeg -i input.mp4 -i subtitle.en.srt -i subtitle.chi.srt -map 0 -map 1 -map 2 -c copy -c:s mov_text -metadata:s:s:0 language=eng -metadata:s:s:1 language=chi output_eng_chi.mp4
使用硬编码将字幕嵌入视频的方式会更加耗时,需要重新编码文件。
[[FFmpeg]] 要在视频流上面加上字幕,需要用一个叫做 subtitles 的滤镜,要使用这个滤镜,在命令中写上 -vf subtitles=字幕文件名
,推荐不管文件名如何都在字幕文件两边加上双引号,比如 -vf subtitles="字幕 文件名"
,因为如果文件名中包含空格或其他特殊字符,在不使用双引号的情况下 Shell 会解析失败。
# 使用 subtitles 滤镜为视频添加字幕,字幕文件在视频流中,输出文件不含字幕流
ffmpeg -i input.mkv -vf subtitles="subtitles.srt" output.mkv
说明:
-vf
是 -filter: v
参数的缩写subtitles="subtitle.srt"
则是 filter 的名字,后面是字幕文件将 mkv 文件中的字幕压制到 mp4
# 将 input.mkv 中的字幕(默认)嵌入到 output.mp4 文件
ffmpeg -i input.mkv -vf subtitles=input.mkv output.mp4
如果要将其他的字幕,可以指定,比如第二个字幕:
ffmpeg -i input.mkv -vf subtitles=input.mkv:si=1 output.mkv
subtitle 更多 用法
如果要处理 ass 格式的字幕文件,那么需要 FFmpeg 启用 libass
,可以通过执行 ffmpeg —version
来查看输出中是否有 --enable-libass
,如果没有这个选项那么可能需要重新安装,或者重新编译安装 FFmpeg 。
如果要嵌入 ass
格式字幕,可以:
brew install ffmpeg --with-libass
ffmpeg -i path/to/video.mp4 -vf "ass=subtitle.ass" out.mp4
ass 格式字幕文件提供了更多的格式选择,比如加粗,斜体,字体,颜色等等,可以使用更加专业的字幕制作软件生成。
[[Clash for Windows]] 使用过程中一直没有什么问题,但是昨天心血来潮把 Clash for Windows 从 0.18.8 升级到了最新版本(0.20.5) ,然后发现节点全部 timeout。但可以排除的是这些节点肯定是可以用的,因为在手机上是完全没有问题的。
先是看 Logs 日志里面,timeout 的节点有大量的错误:
22:06:18 WRN [UDP] dial failed error=new vmess client error: dial xxxx:7830 error: 404 Not Found proxy=GLOBAL rAddr=114.114.114.114:53
查询了一通之后发现可能与 Clash Core 版本 升级 有关系,
查看了一下 Clash 的 Release Note ,在 1.90.0 的 Change Logs 中有一行:
注意vmess下的 ws-headers 和 ws-path 选项已更新
原来 Clash Core 新版本中把配置文件的 ws-headers
和 ws-path
改了个名字
ws-path
ws-headers
这两个配置项变成了如下的结构:
ws-opts:
path: /path
headers:
Host: somehost.com
完整配置示例:
# VMess
- name: "v2ray"
type: vmess
server: xxx
port: 443
uuid: 8b0edc
alterId: 0
cipher: auto
# udp: true
tls: true
# skip-cert-verify: true
network: ws
ws-opts:
path: /xxx
headers:
Host: xxxx.com
JSON 格式:
proxies:
- { name: '美国', type: vmess, server: some.pw, port: 6000, uuid: ccfb9fb3, alterId: 0, cipher: auto, udp: true, network: ws, ws-opts: { path: /, headers: { Host: some.com } }, ws-path: /, ws-headers: { Host: some.com } }
如果不想自己配置,那么可以注册使用这个站点。
另外一个可能引起 timeout 的原因可能是 Clash 的配置中开启了 DNS
dns:
enable: true
ipv6: false
开启了 DNS 之后,clash 会将域名解析发送给配置的 nameserver 解析,如果域名解析失败也会发生 timeout 情况。
其他原因:
很早之前就在 Twitter 上看到有人分享了 Arc 浏览器的使用体验,说是非常惊艳,我就稍微的浏览了一下官网,抱持怀疑的态度先注册了一下体验,一直好奇到了 2022 年能够在浏览器上做出什么样的创新,自 Chrome 横空出世以来,快,安全迅速抢占了浏览器市场。剩下的一点点份额被 Firefox,Safari,Edge,[[Vivaldi]] 等等占据,早两年的时候我也写过一篇标题略微耸动的文章 —- 我可能要抛弃用了很多年的 Chrome 改用 Vivaldi ,但事实是 3 年多过去了,我日常用的还是 Chrome,虽然 Google 在浏览器插件,隐私等等问题上这两年来一直被诟病,但至少还没有彻底地激怒我这个用户。
但可能也是 Google 作为一家广告公司,这个背景实在会让人产生一些敬畏。所以这些年 Chrome 也面临了越来越多的挑战,不管是大企业的 Edge,Safari,亦或是初创企业的,比如本文的主角 Arc Browser ,或者注重隐私的 Orion ,或者在 UI 交互上做创新的 Sidekick , SigmaOS ,都给浏览器发展一些新的思路。这么多年过去 Chrome 还能克制能够抱持整体风格的简洁,并且让地址栏发展成为 Omnibox ,Chrome 几乎成为了我的 开机应用 ,并且借助 Chrome Extension 的生态,以及这些年 Web 技术的发展,在浏览器中能做的事情越来越多,很多 Native 的应用也越来越多地被替换成 Web 应用。这一次体验 Arc 最让我惊艳的就是一些应用,比如 Notion 在 Arc 中的体验就像是 Notion 本地应用一样(虽然 Notion 发布的本地应用就能就是套了一层壳而已)。
最开始让我想要了解的功能就是 Space,侧边栏通过双指滑动可以创建新的 Space, 也可以通过下方的 +
号来创建,但是使用之后体验上就感觉是分组的标签页。
Arc 内建了一个画板,可以做笔记,剪切网页图片,这就相当于内嵌了一个素材收集工具。在地址栏边上的截图小工具的交互设计做的确实不错,可以根据页面内容动态调整要截取的内容。
Easel
在侧边栏还有一个 Library 的概念,在 Library 中会展示下载,桌面等等文件夹,其中还包括了 Arc 独有的 存储 Easel 画板的地方,存储在 Arc 中抓取的网页截图。
Library
大部分的快捷键,和使用 Chrome 都是一致的,打开标签页 Cmd+T, 关闭标签页 Cmd+w,恢复关闭的标签页 Cmd+Shift+t, 更多
但 Arc 引入的新功能必然带来新的快捷键,这里就列举一些常见的,全部的快捷键还是查看其官网,或 Cmd+, 查看吧
⌘+s
锁定或隐藏侧边栏Ctrl+Tab
切换标签页Ctrl+Shift++
创建垂直分屏如果在注册的时候遇到 「Unknown server error」 的错误
那么可能是网络无法访问 Arc 的服务,这个问题我在之前体验 Warp 终端 的时候也遇到了,现在出现的这些产品都喜欢在开篇的时候让用户注册账号使用,但是在国内的这种网络环境下就会遇到各种奇葩的问题。解决办法也非常简单,直接开启 系统全局代理 ,或者去一个没有 GFW 的地方。这里推荐使用 Clash for Windows
在 Chrome 中打开网页,我会看去 Tab 上状态,在加载的时候会有 Loading 的转圈,而在 Vivaldi 中则是更加明显的地址栏中会有色彩进度条,我会明确知道这个页面是正在加载的状态,而使用 Arc 第一个感到不适的就是当网络环境比较慢的时候,会有很长一段时间整个页面是空白状态,如果隐藏侧边栏最上方的地址栏就完全不知道是这个网站出错了还是网络有问题。虽然 Arc 刻意隐藏了所有「不必要」的内容,但却也带来了一些使用上的不便。
相同的环境下,Chrome 从来没有发生过页面或交互中间出现卡顿的情况,但是 Arc 使用过程中隔一会儿就会出现。虽然看别人演示的时候都非常流畅,但是就我个人的使用来说这一点是无法忍受的,尤其是当我想打开标签页,快速输入一些内容进行查找时,这个延迟非常明显。
今天花了一段时间体验了一下 Arc,总体来说视觉上,交互上确实带来了一些新鲜感,但似乎目前还不能被我设为默认浏览器,虽然分屏,Space 等等确实给浏览器交互带来了一些新的启发,但对于我而言这些功能并没有那么不可代替。所以短时间内我还是会继续用 Chrome,不过时常回来关心一下 Arc,看能不能带来一些划时代的革新。
如果你也想体验,可以去 官网 申请。或者使用我的邀请 1,邀请 2,邀请 3。
邀请码似乎有数量的限制,我虽然会定期更新邀请,但也会存在更新不及时的情况邀请码失效,所以如果有遇到邀请码失效的朋友,可以通过页面下方或者 About 页面联系到我,我会单独给你发邀请。
说起 Java 语言下的 Web 框架那就非 [[Spring Framework]] 不可了,但是今天在和别人在聊天的过程中发现了一个新奇的项目 Javalin。 Javalin 是一个轻量的 Web 框架。支持 [[WebSocket]], HTTP2 和异步请求。简单的看了一下官方的说明文档,确实非常轻量,几行代码就可以启动一个 HTTP 服务。
[[Javalin]] 最初是 [[SparkJava]] 的一个分支,后来受到 JavaScript 框架 koa.js 的影响,逐渐独立成一个新的项目发展。
首先来看看一个比 Hello World 稍微复杂一些的例子:
var app = Javalin.create(config -> {
config.defaultContentType = "application/json";
config.autogenerateEtags = true;
config.addStaticFiles("/public");
config.asyncRequestTimeout = 10_000L;
config.dynamicGzip = true;
config.enforceSsl = true;
}).routes(() -> {
path("users", () -> {
get(UserController::getAll);
post(UserController::create);
path(":user-id"(() -> {
get(UserController::getOne);
patch(UserController::update);
delete(UserController::delete);
});
ws("events", userController::webSocketEvents);
});
}).start(port);
验证路径参数
var myQpStr = ctx.queryParam("my-qp"); // 没有验证,返回字符串或空
var myQpInt = ctx.pathParam("my-qp", Integer.class).get(); // 返回一个整数或抛出异常
var myQpInt = ctx.formParam("my-qp", Integer.class).check(i -> i > 4).get(); // 整数 > 4
// 验证两个依赖的查询参数 :
var fromDate = ctx.queryParam("from", Instant.class).get();
var toDate = ctx.queryParam("to", Instant.class)
.check(it -> it.isAfter(fromDate), "'to' has to be after 'from'")
.get();
// 验证一个json消息体:
var myObject = ctx.bodyValidator(MyObject.class)
.check(obj -> obj.myObjectProperty == someValue)
.get();
handler
//前置handler
app.before(ctx -> {
// 在所有请求之前运行
});
app.before("/path/*", ctx -> {
// 在/path/*请求之前运行
});
//端点handler
app.get("/", ctx -> {
// 一些代码
ctx.json(object);
});
app.get("/hello/*, ctx -> {
// 捕获所有对/hello/子路径的请求
});
//后置handler
app.after(ctx -> {
// 在所有请求之后运行
});
app.after("/path/*", ctx -> {
// 在/path/*请求之后运行
});
使用 AccessManager 接口来实现验证和授权。
如果要部署 Javalin 应用程序,开发人员只需创建一个包含了依赖(使用 maven-assembly-plugin)的 jar,然后用 java -jar filename.jar
发布该 jar。Javalin 自带一个嵌入式 Jetty 服务器,无需额外的应用程序服务器。
Javalin 还有 专门为教育工作者准备的页面 ,该页面强调学生可以从 Javalin 受益,因为 Javalin 提供了嵌入式的 Jetty 服务器,所以不需要 Servlet Container/Application 服务器配置就可以开始编码。
有一系列教程可供使用,如 Running on GraalVM 和 Kotlin CRUD REST API 。可以在 教程页面 找到完整的列表。
文档页面 提供了有关 Javalin 的更多细节。用户可以通过 maven 或从手动 maven中央库 下载 Javalin。
通过 mvn package
就可以打包一个 jar 文件,直接运行 java -jar xxx.jar
就可以启动。
因为我在 macOS 下启动 Javalin 程序,默认是使用的 7000 端口,但是起来的时候发现端口被占用了。
用 lsof
查看
❯ sudo lsof -nP -i4TCP |grep 7000
Password:
Swinsian 1563 einverne 36u IPv4 0xa107511eb4d4e74b 0t0 TCP 127.0.0.1:50677->127.0.0.1:7000 (CLOSED)
Swinsian 1563 einverne 37u IPv4 0xa107511eb4d4e74b 0t0 TCP 127.0.0.1:50677->127.0.0.1:7000 (CLOSED)
ControlCe 1578 einverne 29u IPv4 0xa107511eb42171fb 0t0 TCP *:7000 (LISTEN)
查看进程
❯ sudo ps aux | grep 1578
einverne 46918 0.7 0.0 34253900 968 s000 S+ 2:37PM 0:00.00 grep --color=auto 1578
einverne 1578 0.0 0.1 36594320 36324 ?? S Sun12PM 1:24.15 /System/Library/CoreServices/ControlCenter.app/Contents/MacOS/ControlCenter
发现竟然是系统的 ControlCenter 占用了本地 7000 端口,用如下的方法禁用。
[[javalin-database]]
之前折腾 GitHub Profile 的时候发现了 [[WakaTime]] 这样一款统计编码时间的工具,之后在读 waka-readme 项目的时候发现,还有两个完全开源的后端兼容版本,一个是 Golang 编写的 [[wakapi]] ,一个是 Huskell 编写的 hakatime 。这篇就来总结一下我使用 wakapi 的过程。
wakapi 是一个兼容 [[WakaTime]] 的可自行架设的后端程序,和 WakaTime 一样可以用来统计代码。
使用 docker-compose 安装。
直接 clone 项目,修改环境变量,然后启动即可。
git clone https://github.com/einverne/dockerfile.git
cd dockerfile/wakapi/
cp env .env
# edit .env setup SALT and WAKAPI_DATA
# SALT 可以执行命令 cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w ${1:-32} | head -n 1
# WAKAPI_DATA 配置一个本地可读写的路径
docker-compose up -d
我的配置中没有暴露 3000 端口,我是和 Nginx Proxy Manager 一起使用的,在 Nginx Proxy Manager 后台,配置一个 HOST,设置 wakapi:3000
,然后去 [[Cloudflare]] 后台将域名 wakapi.einverne.info
设置一个 A 记录指向 Nginx 所在的服务器。等待 DNS 生效,访问后台 wakapi.einverne.info
后台即可。
我个人会一直使用 https://wakapi.einverne.info 服务,所以如果你感兴趣,也可以直接使用这个服务。
服务启动之后,注册登录,然后就可以配置编辑器插件,把 IntelliJ IEDA,[[VSCode]],[[Vim]] 先配置上。这部分可以直接查看 WakaTime 的官方文档。
编辑客户端配置 ~/.wakatime.cfg
,因为使用 Self-hosted 的后端,所以需要设置 api_url
。api_key
则从后台获取即可。
[settings]
api_url=https://wakapi.einverne.info/api
api_key=b5b0xxx
proxy=
debug=false
status_bar_enabled=true
今天偶然在浏览 Obsidian 插件库的时候发现了 WakaTime 的插件,安装之后就和编程 IDE 一样,会直接使用 HOME 目录下的配置。所有的数据都可以完美的上传到 Wakapi 的后台。
[[WakaTime]] 是为程序员打造的编码统计 Dashboard,可以同来统计项目,编程语言,IDE,编码时间等等内容。 之前在折腾 GitHub Profile 的时候发现的,可以在 GitHub Profile 页面中动态的展示最近的编程状态。
WakaTime 可以统计的内容包括:
WakaTime 基础使用是免费的,但有如下限制:
对于 Premium 可以解锁更多的 功能 。
WakaTime 的配置文件在 HOME 目录下的 .wakatime.cfg
文件中。
cat ~/.wakatime.cfg
[settings]
api_key=
proxy =
debug = false
status_bar_enabled = true
插件 Market 中搜索 wakatime,重启 IDEA,在弹出的 WakaTime Settings 中填入 API key。
如果要设置 proxy,可以在下面填入 http_proxy
类似:http://localhost:1080
在 Vim 的配置中增加如下配置,Vim 下使用 vim-plug 来管理插件:
Plug 'wakatime/vim-wakatime'
yt-dlp 是 YouTube-DL 的进阶版本,延续了 YouTube-DL 的开发和维护。
如果想要下载哔哩哔哩 (Bilibili) 的视频,可以看看 lux 这款工具。
如果在 macOS 上还有一款不错的收费应用叫做 [[Downie]] 也可以用来下载 YouTube 视频。数码荔枝 上有正版授权,可以选择购买。
安装 [[FFmpeg]]:
sudo apt install ffmpeg
安装 yt-dlp
:
sudo curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp
sudo chmod a+rx /usr/local/bin/yt-dlp
sudo ln -s /usr/local/bin/yt-dlp /usr/local/bin/yt
基础使用:
yt-dlp -F --proxy socks5://127.0.0.1:8080 https://www.youtube.com/watch?v=3Hq-cgsV7Og
下载最佳画质:
yt-dlp -f best URL
下载最高视频和音频并合并:
yt-dlp -f bestvideo+bestaudio URL
下载最高视频和音频分开两个文件:
yt-dlp -f 'bestvideo,bestaudio' URL
将视频缩略图嵌入到视频文件:
yt-dlp -f 'bv[height=1080][ext=mp4]+ba[ext=m4a]' --embed-thumbnail --merge-output-format mp4 https://www.youtube.com/watch?v=1La4QzGeaaQ -o '%(id)s.mp4'
下载播放列表:
yt-dlp -f 'bv*[height=1080]+ba' --download-archive videos.txt https://www.youtube.com/playlist?list=PLlVlyGVtvuVnUjA4d6gHKCSrLAAm2n1e6 -o '%(channel_id)s/%(playlist_id)s/%(id)s.%(ext)s'
下载整个 YouTube Channel,保存为平台加视频名称
yt-dlp -f 'bv*[height=720]+ba' --download-archive videos.txt https://www.youtube.com/c/FootheFlowerhorn/videos -o '%(channel)s/%(title)s.%(ext)s'
虽然很早就知道 GitHub 发布了 Profile 功能,可以使用 README 来丰富 Profile 页面。但是一直以来没啥动力,大多数时候都不会去到主页去访问。但现在有些时候逛 GitHub 的时候会点到 其他人 的主页去看,发现有一些主页虽然只有寥寥几句,但却可以清楚的知道「他/她」最近在贡献什么内容,擅长什么技能。虽然我在 GitHub 上还是观摹大佬居多,但也想着通过这个契机在整理 GitHub Profile 的时候加深一下对自己的认知。
至于如何建立同名的 repository,如果提交代码就先略过了,官方的帮助和其他文章的内容都非常详细。
刚开始去 Google 「GitHub Profile」 就发现了如下的页面生成器 GitHub Profile Generator ,可以用这个生成器生成一个初始版本,然后在其基础上修改。
在调研的过程中基本发现了两大类主流的用法,一类是通过 GitHub 的 API ,或者其他服务的 API,生成一个 Badge 展示,另外一类就是通过 [[GitHub Actions]] 通过定时任务动态的使用代码聚合一些内容,然后再动态地展示到页面中。
因为 README 中可以直接写 HTML,所以如下的 HTML 也可以直接使用,注意替换其中的链接。
<h3 align="left">Connect with me:</h3>
<p align="left">
<a href="your link" target="blank"><img align="center" src="https://cdn.jsdelivr.net/npm/simple-icons@3.0.1/icons/twitter.svg" alt="" height="30" width="40" /></a>
<a href="your link" target="blank"><img align="center" src="https://cdn.jsdelivr.net/npm/simple-icons@3.0.1/icons/linkedin.svg" alt="" height="30" width="40" /></a>
<a href="your link" target="blank"><img align="center" src="https://cdn.jsdelivr.net/npm/simple-icons@3.0.1/icons/instagram.svg" alt="" height="30" width="40" /></a>
<a href="your link" target="blank"><img align="center" src="https://cdn.jsdelivr.net/npm/simple-icons@3.0.1/icons/youtube.svg" alt="" height="30" width="40" /></a>
</p>
或者使用外部的 Readme-stats 来生成一个数据卡:
<p>
<a href="https://github.com/einverne/">
<img margin-top="-30px" width="55%" align="right" alt="einverne's github stats" src="https://github-readme-stats.vercel.app/api?username=einverne&show_icons=true&include_all_commits=true&count_private=true&layout=compact&hide_border=true" />
</a>
</p>
如果要在页面中放入图标可以到如下的网站寻找。
另外一个寻找 icon 的方式就是利用 https://cs.github.com GitHub 的 Code Search,然后直接搜索,比如我想要找到豆瓣的 svg ,输入 douban.svg
然后就能找到。
github-readme-stats 是一个用来生成 GitHub 统计数据的工具,可以在页面上展示获得的⭐,提交的次数,总共的 PR 等等。
生成从构建,代码覆盖率,开源协议,到社交网络等等,非常多的徽章。
如果想要统计访问 GitHub Profile 的数量,可以使用 GitHub Profile Views Counter 这个项目。
样例:
https://skyline.github.com/
基于 GitHub Actions 动态生成内容展示在 Profile 页面。
因为有 GitHub Actions,所以简单的用脚本可以展示
等等。
可以使用 blog-post-workflow 来显示最近更新的博客内容。通过定时读取 Feed ,来在页面动态展示内容。
GitHub Activity Readme 可以在页面上显示最近在 GitHub 上的动态。
如果你使用 [[WakaTime]] 来统计编码时间,那么可以使用 waka-readme-stats 来展示。
最终的效果见 https://github.com/einverne/