背景:
Alfred的介绍和使用不过多介绍,作为mac上的神器,他的workflow支持外部扩展,也可以自己开发,支持多种开发语言,其实远不止他显示的这几种,因为支持bash,只要本地能运行的程序理论上都支持。
作为豆瓣的轻度依赖者,我基本上使用豆瓣来查看书籍和电影的评分,主要用于判断是否值得投入时间来看。之前用的插件是 GitHub - xinhangliu/alfred-workflow,他依赖了豆瓣提供的api。现在豆瓣应该不提供api了,导致基本不可用了,我只能用他来快速跳转到豆瓣,没办法直接查看相关的评分和信息。调研了一下,发现可以通过解析网页的方式来获取相应的信息。加上原来的插件是用python2写的,也算是自己熟悉的语言,于是想着改造应该也不困难。 改造之后的代码:GitHub - ykdsg/workflow-douban: alfred workflow for douban. Use python3 and lxml.
workflow相关概念:
首先开发workflow,需要对他的核心组件有一定的了解,像Trigger、Inputs、Actions、Utilities、Outputs 分别代表什么意思。最便捷的方式是通过查看别人的workflow,以及官方的示例。
还有就是查看官方文档了 Workflows - Alfred Help and Support。
这里直接引用这篇文章里面的介绍:使用Python写一个Alfred Workflow - 执子之手
Triggers: Activate Alfred from a hotkey, another Alfred feature or an external source. 简单的说Triggers的用途就是通过热键或者其他方式启动Alfred,进而启动Workflow。
Inputs: Keyword-based objects used to perform an action, on its own or followed by a query. Inputs通过一个定义的关键字对象来执行一个action,这个关键字可以带查询参数也可以不带。例如你定义关键字mc,那么在Alfred搜索框中输入mc就可以启动这个功能。
Actions: The objects that do most of the work in your workflows; opening or revealing files and web searches, running scripts and performing commands. Actions对象在workflows中负责完成绝大多数的工作,例如可以打开文件,打开搜索,执行脚本等等。
Utilities: Utilities give you control over how your objects are connected together and how the arguments output by the previous object is passed on to the next object. Utilities可以让你控制你的对象如何连接在一起工作,控制参数如何在对象之间传递。这个对象我用的不多。
Outputs: Collect the information from the earlier objects in your workflow to pop up a Notification Centre message, show output in Large Type, copy to clipboard or run a script containing the result of your workflow. Outputs对象负责从你的Workflow中的前一个对象中收集信息,然后真正输出到系统中,这个输出可能是:弹出一个通托盘通知,拷贝到剪贴板,写一个文件,播放一个声音等等。
这是几个大类,每个大类下有很多具体的组件,实际用不上这么多,刚开始对大类有个印象就行了。
这里直接用网上的一张图来说明各个部分的大概作用。上面是有道翻译的workflow,可以比较清晰的看到各个部分的作用。
插件开发:
因为在原来的插件基础上开发,所以先看下原来插件的情况
双击第一个script filter
可以看到就是在alfred中输入“douban”会唤起这个插件,并调用selection.py 这个脚本,点击右下角的方块可以打开workflow的本地目录,就能看到相应文件。看了下selection.py这个文件,逻辑还是比较简单的 ,想要达到的效果就是:
对于 script filter 输出结果需要满足Alfred的要求,可以具体参考 : Script Filter JSON Format - Workflow Input Objects - Alfred Help and Support。
是一个json格式,类似:
{
"items":[
{
"title":"Search Douban for 'test'",
"icon":{
"path":"image/douban.png"
},
"arg":"test",
"variables":{
"selection":"douban"
}
},
{
"title":"Book",
"icon":{
"path":"image/book.png"
},
"arg":"test",
"variables":{
"selection":"book"
}
},
{
"title":"Movie",
"icon":{
"path":"image/movie.png"
},
"arg":"test",
"variables":{
"selection":"movie"
}
},
{
"title":"Settings",
"icon":{
"path":"image/settings.png"
},
"arg":"test",
"variables":{
"selection":"settings"
}
}
]
}
比较重要的是arg和variables,arg会作为参数传递到后面的组件,variables中的变量用于区分我们选择了book还是movie。
接着继续查看后面的组件
可以看到根据上面variables中的变量selection用于判断相应的流程,但是这里的形式已经变成了{var:selection} 。
接着再往下看下一个Script Filter,这个就是这个插件最核心的部分。
其中{query} 就是上一步 操作选中item的arg。很多时候我们也需要selection,但是在这里是不能用{var:selection}来传递的,这个在后面我自己维护的时候发现是个坑。貌似{var:selection}只能用于workflow自己组件内部,在bash里面没有传入,但是可以通过环境变量来获取,神不神奇。
selection = os.getenv('selection')
查看core.py,整体逻辑就是通过访问douban api,然后输出json,格式跟上面的items一样。 清楚之后改造就简单了,利用python的lxml库能够比较方便的解析html结构,获取相应的信息组装成items就行了。源码在 GitHub - ykdsg/workflow-douban: alfred workflow for douban. Use python3 and lxml.。
Debug:
在开发的过程中,通常是本地测试好之后,在workflow中跑起来,实际还是会有一些问题,这个时候就需要debug。
点击右上角就能够开启debug模式,会输出相应的日志,注意这里只会输出标出错误的信息,就是普通的log.info 是不会输出的,只有log.error 才会输出。
需要特别注意的地方:
- 虽然支持bash,zsh 但是跟本地终端还是不一样的,会发现他很多命令找不到,但是本地终端上是明确可以运行的。这个是Alfred 的默认保护机制,他只加载少数特定的目录,类似/usr/bin ,/usr/local/bin ,其他不会加载。所以如果你要用的命令找不到的话,就需要使用绝对路径。
- variables 在程序中只能通过环境变量获取,在bash 中通过{var:selection} 是传递不了的。
关于go 版本
python 的开发还是比较简单的,因为动态语言的特性,加上丰富的三方库,特别适合干这类杂活。但是存在一个问题是如果要把插件分享出去,就需要对应的同学有相应的环境:python的版本以及依赖的lxml,这个实在太不友好了,目前也没找到特别好的解决方案(可以把lxml拷贝到当前workflow的目录下,但是python3环境不一定所有人都有)。刚好最近在学习go ,就想着能不能用go重写这个插件,因为可以打包成可执行文件,就不存在环境依赖的问题。所以用go 重新写了这个插件 ,Releases · ykdsg/my_go (github.com) 。
源码在:https://github.com/ykdsg/my_go/tree/master/douban
这里吐槽下go的开发:
- 大小写决定是否private,所谓简单的原则,在很多时候造成的是麻烦,比如alfred 规定了items的json格式,而且是大小写敏感的,那就非常坑了,必须用下面这种格式来标记序列化之后的字段
Title string `json:"title"`
- log.Panicln 会导致整个程序退出,我天,简直刷新三观了。我打印一下日志,你就理所当然的帮我exit了,go标榜的简单哲学是不是太过了。说好的只做一件事呢,这么干确定不会增加程序员的心智负担吗。
最后看一下这个版本的效果:
选择book之后(略微有一丢慢,这个是douban网站的速度决定的):