讓Alfred支持拼音搜索

jopen 8年前發布 | 11K 次閱讀 Python iOS開發 移動開發

Alfred是個好東西,不過檢索程序的時候不支持拼音搜索;我在論壇看到有人給作者反饋過,無奈作者說支持中文,他不知道拼音是什么,于是就不了了之了。舉個例子:我想打開 網易云音樂 ,可是當我輸入 wangyiyunyinyue 的時候卻是這樣的結果:

要么我知道這個App的名字叫做 NeteaseMusic ,要么我就需要用中文輸入 網易云音樂 打開了;如果恰巧輸入法是英文輸入狀態,那么就會遇到上圖的情況;這時候再把已經輸入的刪除然后切換輸入法打開,效率無疑大大折扣。

就算這里搜索這個App可以使用英文名字解決,可是對于某些系統程序比如郵件可能還知道是 Mail ,那么備忘錄呢?便簽呢?還有一些別的中文程序沒有英文名的比如馬克飛象?如果Alfred能支持拼音搜索,這些問題全部都沒了!而且,Alfred可以強制使用英文輸入,直接使用字母檢索,不用切換輸入法了。

原理

經過簡單的觀察之后,發現Alfred檢索程序不僅僅是檢索名字,還收集了一些額外的信息;在Alfred作者的幫助下,知道它利用了Mac文件系統的一個 拓展信息 的字段;如果你發現某些目錄后面有 @ 那么就是有拓展信息了:

drwxr-xr-x+  3 root    wheel  102  9 10  2014 Stickies.app/
drwxr-xr-x@  3 weishu  admin  102  3 26  2015 Sublime Text.app/
drwxr-xr-x+  3 root    wheel  102  9 10  2014 Stickies.app/
drwxr-xr-x@  3 weishu  admin  102  3 26  2015 SublimeText.app/

可以借助命令行工具 xattr 進行操作;具體使用可以 man xattr .

所以,我們可以通過 把拼音信息添加到文件的拓展信息里面 去,這樣Alfred就能借助這些信息幫助拼音檢索了。

實現

獲取程序名

程序名不僅僅是一個文件名這么簡單,Mac軟件有一個叫做 localization 的概念,大致就是國際化吧;程序結構把國際化的字段存放在不同的文件里面,在程序本地化之后自動load.

我們要使用的那個字段是 CFBundleName ;存放在 /<App>/Contents/Resources/<language>/InfoPlist.strings 這個文件里面;我們把這個名字讀取出來即可。

嘗試過使用 objc 的接口 NSBundle.localizedInfoDiction 來獲取本地化的字段,無奈拿到的永遠是英文字段;只好手工解析中文字段了(不會Objc );使用的命令行工具 plutil :

def _get_localized_name(abs_path):
    '''get the localized name of given app'''
    bundle = NSBundle.new()
    bundle.initWithPath_(abs_path)
    localizations = bundle.localizations()
    chinese = ('zh_CN', 'zh_Hans')

    b = any(map(lambda x: x in localizations, chinese))
    if not b: return 

    for ch in chinese:
        path = bundle.pathForResource_ofType_inDirectory_forLanguage_('InfoPlist', 'strings', None, ch)
        if not path: continue
        # the path must surround with "", there may be space characters
        json_str = subprocess.check_output(u'plutil -convert json -o - "%s"' % path, shell=True)
        # print json_str
        json_res = json.loads(json_str, encoding='utf8')
        name = json_res.get('CFBundleName')
        if name: return name
def_get_localized_name(abs_path):
    '''get the localized name of given app'''
    bundle = NSBundle.new()
    bundle.initWithPath_(abs_path)
    localizations = bundle.localizations()
    chinese = ('zh_CN', 'zh_Hans')
 
    b = any(map(lambda x: x in localizations, chinese))
    if not b: return 
 
    for chin chinese:
        path = bundle.pathForResource_ofType_inDirectory_forLanguage_('InfoPlist', 'strings', None, ch)
        if not path: continue
        # the path must surround with "", there may be space characters
        json_str = subprocess.check_output(u'plutil -convert json -o - "%s"' % path, shell=True)
        # print json_str
        json_res = json.loads(json_str, encoding='utf8')
        name = json_res.get('CFBundleName')
        if name: return name

轉換為拼音

可以直接使用python的拼音轉換庫 pypinyin ,借助這個工具,一行代碼搞定:

def _get_app_pinyin_name(app_name):
    reduce(lambda x, y: x + y, lazy_pinyin(app_name, errors='ignore'))
def_get_app_pinyin_name(app_name):
    reduce(lambda x, y: x + y, lazy_pinyin(app_name, errors='ignore'))

添加拼音信息

拼音信息被添加到文件的拓展信息里面,直接使用 xattr 添加即可:

def _add_meta_data(app_pinyin_name, app_path):
    ''' add meta data(comments) to the app, which can help Alfred or SpotLight find it'''
    subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s %s' % (app_pinyin_name, app_path), shell=True)
def_add_meta_data(app_pinyin_name, app_path):
    ''' add meta data(comments) to the app, which can help Alfred or SpotLight find it'''
    subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s %s' % (app_pinyin_name, app_path), shell=True)

好了,把這些代碼整合起來,就能得到最終的結果了,完整的代碼在 這里

def main():
    pattern = re.compile(r'^[\w\s.]+$')

    workspace = NSWorkspace.sharedWorkspace()

    for app_dir in APP_DIRECTORYS:
        if not os.path.exists(app_dir): continue

        for root, dirs, files in os.walk(app_dir, topdown=True):
            remove_list = []
            for directory in dirs:
                # print type(directory), root, directory
                full_path = os.path.join(root, directory)
                if not _is_application(workspace, full_path): continue

                remove_list.append(directory)

                localized_name =  _get_localized_name(full_path)
                app_name = localized_name if localized_name else directory.rsplit(r'.')[0]

                if pattern.match(app_name): 
                    continue

                _add_meta_data(_get_app_pinyin_name(app_name), full_path)

            # if this directory is already a Application
            # do not traverse this; some app may be very large 
            # and there won't be any other app inside it
            dirs[:] = [d for d in dirs if d not in remove_list]
defmain():
    pattern = re.compile(r'^[\w\s.]+$')
 
    workspace = NSWorkspace.sharedWorkspace()
 
    for app_dirin APP_DIRECTORYS:
        if not os.path.exists(app_dir): continue
 
        for root, dirs, filesin os.walk(app_dir, topdown=True):
            remove_list = []
            for directoryin dirs:
                # print type(directory), root, directory
                full_path = os.path.join(root, directory)
                if not _is_application(workspace, full_path): continue
 
                remove_list.append(directory)
 
                localized_name =  _get_localized_name(full_path)
                app_name = localized_nameif localized_nameelse directory.rsplit(r'.')[0]
 
                if pattern.match(app_name): 
                    continue
 
                _add_meta_data(_get_app_pinyin_name(app_name), full_path)
 
            # if this directory is already a Application
            # do not traverse this; some app may be very large
            # and there won't be any other app inside it
            dirs[:] = [d for d in dirsif d not in remove_list]

最后,我們執行這一段腳本即可 sudo python main.py 。之所以需要 sudo 是因為某些系統程序(比如家計算器),直接使用是沒有權限的。

完整代碼見原文: http://www.tianweishu.com/2015/12/07/make-alfred-support-pinyinyin-search

最后看效果:

來自: http://python.jobbole.com/84107/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!