Eisen's Blog

© 2023. All rights reserved.

Mac 下通过自动周期截图来追溯每天的屏幕使用-2

2022 February-27

在上一篇文章 Mac 下通过自动周期截图来追溯每天的屏幕使用 介绍了下一个简单的录屏脚本。这里做一个跟进,主要是以下两方面的内容:

  1. 对原来脚本的修改,支持切换设备屏幕的情况
  2. 提供 ffmpeg 命令可以把截图转换为 mp4 格式的视频

支持切换屏幕

我的工作电脑是一台 macbook pro ,在不同的场景下会使用不同的屏幕:在办公室会连接一台 LG 外接显示器,开会的时候会直接使用 macbook 的屏幕;而回到家可能会使用一台 Dell 的外接显示器。如果仅仅按照屏幕的序号截屏在图像的连续性上会差一些。不过幸好 mac os api 可以获取目前屏幕的更详细信息,我可以依据显示器型号记录信息。

#!/bin/bash

PATH=/usr/local/bin:/usr/local/sbin:~/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin

# use jq to get array length
result=`system_profiler SPDisplaysDataType -json`
screencount=`echo $result | jq '.SPDisplaysDataType[0].spdisplays_ndrvs | length'`

# get current date
date=$(date +%Y-%m-%d)
# create folder if not exists
mkdir -p $date

# get current date time with well format
NOW=$(date +"%Y-%m-%d-%H-%M-%S")

# make screenshot
for (( i=0; i<$screencount; i++ )) do
    # get screen name
    screenname=`echo $result | jq --raw-output ".SPDisplaysDataType[0].spdisplays_ndrvs[$i]._name"`
    # replace space with _
    screenname=${screenname// /_}
    # to lower case
    screenname=`echo -n $screenname | tr "[:upper:]" "[:lower:]"`
    # use screen name to create file name
    screencapture -C -x -D $((i+1)) $date/screenshot-$NOW-of-$screenname.png
done

如上所示,采用 screenname 作为文件名后缀可以保证截图的连续性。生成的文件就像如下这个样子:

screenshot with screenname

使用 ffmpeg 生成视频

截图一张张翻有两个坏处:

  1. 需要自己一张张翻
  2. 会占据更多存储空间,我这里一天最大的目录大小是 3G ,这里面很多截图是重复的,而众所周知,将这些图片如果以视频来存储可以充分利用视频的压缩算法,极大的降低所使用的存储空间

既然只是为了 review 自己的工作,本来就是想要一个跑马灯的效果。这里我就简单搜索了下,可以用 ffmpeg 把截图按照时间顺序做成视频就好了。

ffmpeg \
    -framerate 15 \                               # 每秒 15 帧    
    -pattern_type glob -i '*dell_u2417h.png' \    # 指定截图文件的路径,dell_u2417h 是截图的屏幕名称
    -c:v libx264 \                                # 指定视频编码器
    -pix_fmt yuv420p \                            # 指定视频的像素格式,没事不用修改
    dell_u2417h.mp4                               # 输出视频文件名,和截图的屏幕名称一直

用以上的命令,可以每个屏幕生成一个视频。

后续工作

  1. 截屏目前做到了自动化,但是拼接视频没有做到自动化,需要手动拼接,可以考虑怎么做
  2. 视频拼接目前是每个屏幕一个视频,如果能把屏幕按照时间线拼接起来会更好

Mac 下通过自动周期截图来追溯每天的屏幕使用

2022 February-10

我自己一直很在意「自我量化」,希望自己的各个方面的行为可以以一种尽量方便尽量被动的方式被记录下来方便后来的追溯。之所以这么想有以下几天原因:

  1. 我认为很多最终结果的产生是每天日积月累的结果,比如有些人每天主动活动的时间很少,几乎没有走动,一上班就可以坐一上午直到去吃饭。但是有一天 TA 发现自己身边同事吃的和自己一样多,但自己越来越胖,人家没什么变化。TA 不晓得什么原因,但如果她了解到同事每天会主动起来活动(摸鱼)就可能意识到是自己就没有注意平时多运动,或许像同事那样每个小时起身活动一下,吃完饭多爬几层楼每天的代谢量就会有不少的提升。
  2. 我认为基于事实的反省和回顾有助于个人成长,而这里「基于事实」很重要,有的时候主观意识并不太客观,有的时候自己觉得自己工作很努力,但如果翻翻自己的时间追溯软件发现自己真正工作的时间可能并不多。因此,有事实留下来供自己回看还是有点重要。
  3. 时间飞逝,很多时候自己都忘记自己前几天在干什么了,没留下点什么真的就这么过去了。作为程序员,有的时候因为某个问题会去做乱七八糟的检索,可能很快就跑偏了,结果一天下来啥也没干成,还觉得自己好累,但回顾时间到底都去哪里了也很模糊。
  4. 不知道你有没有用过 Apple 相册里的「回忆」它会时不时把你以前某一段时间拍下来的照片做成小视频让你看看,有些记忆就这么被一个小小的 app 翻出来了,不禁让人感叹生活的美好。
  5. 从更大的统计学角度来看,每个人客观的量化数据如果能通过某种方式被脱敏后收集可比什么乱七八糟的调查问卷要可靠一些吧,这些数据就是每个人的人生,用它来分析和推断可靠性应该会有一个质的改观吧?不过「过敏」和「全面」应该是个很难做的事情呢。

apple memory

这个话题太大了,这里就此止住,单讲自己屏幕使用(具体到电脑使用)的记录。

目前的屏幕记录应用的问题

之前尝试过一些时间记录的软件,或多或少有如下一些问题:

  1. 只有统计数据,会统计你一天用屏幕多久,用什么 app 多久,看视频多久,然后汇总你一周时间、一个月时间、一年时间,这些数据不是没用,但就是很笼统,甚至你不知道自己每天做这些事情的时间线是如何的,苹果自带的屏幕统计是这样的,rescue time 也是这样的。
  2. 就算是统计也统计的非常不精确,没办法直到你在某一个 app 里干啥了,比如现在 bilibili 或者 youtube 这样的视频网站其内容包罗万象的,你直到在用 bilibili 但是不知道是在看学习视频还是在看游戏直播,绝大部分 app 都做不到获取内容。
  3. 假如真有应用有本事把你每时每秒都在干嘛全上传到服务器,一方面是隐私问题你没法接受,一方面是负载问题,人家服务器也没法接受,这种东西就不存在的。

苹果自带的屏幕使用统计,可以看到就只有用量,看不到时间,看不到某个时间段在干嘛
苹果自带的屏幕使用统计,可以看到就只有用量,看不到时间,看不到某个时间段在干嘛

Rescue time 和苹果的情况类似,虽然可以获取浏览器标签和 url 稍微好点,但也好不了太多
Rescue time 和苹果的情况类似,虽然可以获取浏览器标签和 url 稍微好点,但也好不了太多

注意 在 windows 平台下有个时间记录的软件叫做 manictime 可以做到时间流记录,不过依然存在具体的信息获取不到以及跨平台的问题,同时这个公司似乎非常低调,低调到我感觉它都要倒闭了。

Manic time 可以看到有时间线的概念,图示从视频截图的,毕竟我也很久没用过了
Manic time 可以看到有时间线的概念,图示从视频截图的,毕竟我也很久没用过了

通过截图的简单方案

考虑到如上的一些问题,再仔细想象自己的需求,我最迫切的需求是可以快速回顾某一天都在干嘛,然后才是什么分析统计。那么我可能还是需要保留很具体的信息,那我觉得就只有截图了。

所以,如果可以每几分钟截个图存下来,然后每天一个文件夹是不是就差不多够了?同时,截图信息非常敏感,可能有公司涉密信息,可能有自己涉密信息。那么直接存在本地自己看看也不用担心隐私问题了。

通过简单的调研,我写出来了如下的脚本:

#!/bin/bash

PATH=/usr/local/bin:/usr/local/sbin:~/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin

# 使用 jq 获取目前有几个屏幕
screencount=`system_profiler SPDisplaysDataType -json | jq '.SPDisplaysDataType[0].spdisplays_ndrvs | length'`

# 获取当前日期
date=$(date +%Y-%m-%d)
# 用日期创建文件见
mkdir -p $date

# 获取当前时分秒,作为文件的名字
NOW=$(date +"%Y-%m-%d-%H-%M-%S")

# 截屏
for (( i=1; i<=$screencount; i++ )) do
    # 每个参数都什么意思可以通过 screencapture -h 查看
    screencapture -C -x -D $i $date/screenshot-$NOW-$i.png
done

介绍下功能:

  1. 可以控制截屏的周期,这里我是用 crontab 跑起来这个脚本,具体多久跑一次可以自己控制,我自己是 5 分钟一次,感觉差不多够了
  2. 支持多个屏幕,通过 system_profiler SPDisplaysDataType -json 可以获取当前屏幕个数,我为每一个屏幕都做了截图

这里额外用到的工具只有 jq 可以通过 brew install jq 直接安装。

用 crontab 安装

  1. 打开 terminal 输入 crontab -e 进入编辑模式
  2. 添加一行信息如下,把其中的 <your-sh-file-location> 替换成上面的 sh 的路径
*/5 * * * * cd <your-sh-file-location> && ./screencapture.sh >> ./screencapture.log 2>&1
  1. cron 提供「屏幕录制」的权限。如果不设置权限,你的截图只能截取到背景而已,Screencapture over cron shown background instead window content 阐述了这个问题。你需要在「系统偏好设置设置」-「安全性与隐私」-「隐私」下为 /usr/sbin/cron 提供「屏幕录制」的权限。

privacy for screenshot

  1. 点击上述对话框右侧下方的 + 号就可以添加应用程序了。
  2. 使用快捷键 cmd + shift + g 打开路径输入对话框,输入 /usr/sbin/cron 然后回车就可以进入 cron 的目录并选中它,点击确认就可以把 cron 授予权限了

select cron for privacy

效果

然后就会出现这样的目录了:

screenshot result

如果有两个屏幕就会出现两个截屏:

multi screens

按照排序把截屏扫一遍就大概知道自己一天什么时候都在干嘛了。


集成 spotless gradle intellij

2022 February-07

之前一直使用 google-java-format intellij plugin 与 git commit hook google-java-format-git-pre-commit-hook 配合实现 java 代码的格式化。不过最近在处理 spring security 需要一些自定义格式的时候发现 intellij 的 google-java-format 插件居然不支持 @formatter:off 这样的语法,让我非常头痛。毕竟 web security 的那种「链式调用」如果没有自己的一些格式化会非常难看:

// @formatter:off
http
    .authorizeRequests()
    .antMatchers(HttpMethod.OPTIONS).permitAll()
    .and()
    .httpBasic()
    .and()
    .authorizeRequests()
    .antMatchers("/graphiql", "/graphql").permitAll() // 如果不做自定义,这里的 permitAll 会强制换行
// @formatter:on

如下所示,这样的设置对 google-java-format 插件无效:

intellij config ignore by plugin

一方面由于这个问题,另一方面由于上述所说的 git-pre-commit 的工具对高版本 java 不兼容,于是决定更换格式化工具。

spotless

经过调研决定使用 spotless 有如下几个原因:

  1. 和 gradle 有很好的集成,通过其所提供的 spotless 的 gradle 插件直接跑命令就能格式化代码,这样就可以通过在 ci 执行静态检查保证格式的一致性,起到了类似于 git-pre-commit 的效果,并且不用开发者自行设置,反而更省事了
  2. 支持多种格式化标准,包括 google-java-format 并支持其自定义,这会让我之前的代码风格保持固定
  3. 通过配置可以实现对 @formatter:off 语法的支持

spotless 与 gradle 集成

增加 gradle 的插件:

  plugins {
+       id "com.diffplug.spotless" version "6.2.1"
  }

+ spotless {
+     java {
+         googleJavaFormat()
+     }
+ }

添加后就可以使用以下几个常用命令了:

  1. gradle spotlessJavaCheck: 检查是否有不符合格式的内容,gradle build 也会执行该命令
  2. gradle spotlessJavaApply: 执行格式化修改文件

设置忽略的文件

spotless {
    java {
+       target project.fileTree(project.rootDir) {
+           include '**/*.java'
+           exclude 'build/generated/**/*.*', 'build/generated-examples/**/*.*'
+       }
        googleJavaFormat()
    }
}

通过上面的 include exclude 保证那些生成的文件不必进行格式化。

支持 @formatter 语法

spotless {
    java {
        target project.fileTree(project.rootDir) {
            include '**/*.java'
            exclude 'build/generated/**/*.*', 'build/generated-examples/**/*.*'
        }
+       toggleOffOn('@formatter:off', '@formatter:on')
        googleJavaFormat()
    }
}

通过 toogleOffOn 来支持 @formatter:off 这样的语法,那么通过如下注释包裹的内容将被 spotless 忽略:

// @formatter:off

code here will not be formatted any more

// @formatter:on

spotless 与 intellij 集成

spotless 如果只和 gradle 集成而没有 IDE 的集成,那么在 Intellij 中执行「Reformat」还是会忽略上述的任何格式化设置。不过好在 spotless 也有 Intellij 的插件支持。

安装插件

在插件市场搜索 spotless:

spotless plugin in intellij

修改快捷键映射

安装插件成功后会在「Code」下会多一个「Reformat Code with Spotless」:

reformat with spotless

但没有快捷键,用默认快捷键还是不能调用这个功能,需要修改下快捷键:

keymap spotless

将默认的 reformat 快捷键给 spotless reformat:

set keymap for spotless

提示是否将其他已经使用该快捷键的映射删除,选择「Remove」:

overwrite keymap for spotless