yxjxx
1/6/2016 - 12:53 AM

make alfred support pinyin search

make alfred support pinyin search

#! /usr/bin/env python
# -*- coding: utf-8 -*-

# @author weishu @2015/12/7

import subprocess
import os
import re
import json

from AppKit import NSWorkspace, NSBundle
from pypinyin import lazy_pinyin

# copy from Alfred 2 preferences, if you have applications installed at other place, add it here.
APP_DIRECTORYS = [
    u'/Applications',
    u'/Applications/Xcode.app/Contents/Applications',
    u'/Developer/Applications',
    u'/Library/PreferencePannes',
    u'/opt/homebrew-cask/Caskroom',
    u'/System/Library/PreferencePannes',
    u'/usr/local/Cellar',
    u'~/Library/Caches/Metadata',
    u'~/Library/Mobile Documents',
    u'~/Library/PreferencePannes'
]

def _is_application(workspace, abspath):
    ''' is a `abspath` stand for a mac app'''
    return workspace.isFilePackageAtPath_(abspath)

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

def _add_meta_data(app_pinyin_name, app_path):
    ''' add meta data(comments) to the app, which can help Alfred or SpotLight find it'''
    print "processing %s" % app_path
    try:
        subprocess.check_call('xattr -w com.apple.metadata:kMDItemFinderComment %s "%s"' % (app_pinyin_name, app_path), shell=True)
    except:
        print "process %s failed, ignore." % app_path
        
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', 'zh-Hans', 'zh-CN')

    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 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:
                full_path = os.path.join(root, directory)
                # print full_path
                if not _is_application(workspace, full_path): continue

                remove_list.append(directory)
                try:
                    localized_name =  _get_localized_name(full_path)
                except:
                    print "get localized name for %s failed. ignore" % full_path
                    # continue
                
                # print "localized_name:", localized_name if localized_name else None
                app_name = localized_name if localized_name else directory.rsplit(r'.')[0]

                if pattern.match(app_name):
                    # print "app_name: %s not match" % app_name
                    continue
                try:
                    _add_meta_data(_get_app_pinyin_name(app_name), full_path)
                except:
                    # may be empty, just ignore
                    print "add meta for %s failed" % 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]

if __name__ == '__main__':
    main()