2008/02/13

Recent entries from same category

  1. Vim で XML Reformat
  2. Lingr Radar For Linuxってのを書いた
  3. デスクトップアプリケーションでも認証可能なOAuth「xAuth」をpythonから試してみた。
  4. Mumblesを使ったGitHubのGrowl通知アプリケーションを作った。
  5. Python2.6にはcursesのバイナリが含まれていないので作る

ShareOnTumblr
「CShellExt::QueryContextMenu」でやれば出来なく無いけどコストがデカイし別のシェルエクステンション混ぜると落ちたりするんですよね。
Gyazowin tumblr for Windowsにファイルのアップロード機能を追加して名前をGyamblr for Windowsに変更 « ZeroMemory
Windowsのコンテキストメニューのコピーとかにcopy to tumblrみたいなのを追加できないかなと思ったんだけど、標準のやつはともかくアプリケーションの右クリックはアプリケーション管轄で制御されていてWindowsシロウト同然の自分には外からいじれなそうだったのであきらめました。
本当は「CShellExt::QueryContextMenu」でやるのがいいんだろうけど、今回はコマンドラインユーザでも使える様にコマンド形式で「送る」に登録する方法にした。

動作させる為にはpythonが必要。
pythonをインストールしたら、以下のソースを「tumblr_post.py」として保存する。
import os, sys
import re, getopt, glob
import urllib, urllib2
import mimetypes

settings = {}
supported_encodings = ['ms932', 'utf-8', 'euc-jp', 'iso-2022-jp']

class Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

class MultipartPostHandler(urllib2.BaseHandler):
    handler_order = urllib2.HTTPHandler.handler_order - 10

    def http_request(self, request):
        data = request.get_data()
        if data is not None and type(data) != str:
            v_files = []
            v_vars = []
            try:
                 for(key, value) in data.items():
                     if type(value) == file:
                         v_files.append((key, value))
                     else:
                         v_vars.append((key, value))
            except TypeError:
                systype, value, traceback = sys.exc_info()
                raise TypeError, "not a valid non-string sequence or mapping object", traceback

            if len(v_files) == 0:
                data = urllib.urlencode(v_vars, 1)
            else:
                boundary, data = self.multipart_encode(v_vars, v_files)
                contenttype = 'multipart/form-data; boundary=%s' % boundary
                if request.has_header('Content-type'):
                    pass
                request.add_unredirected_header('Content-type', contenttype)

            request.add_data(data)

        return request

    def multipart_encode(vars, files, boundary = None, buffer = None):
        if boundary is None:
            boundary = mimetools.choose_boundary()
        if buffer is None:
            buffer = ''
        for(key, value) in vars:
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"' % key
            buffer += '\r\n\r\n' + value + '\r\n'
        for(key, fd) in files:
            file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
            filename = fd.name.split('/')[-1]
            contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
            buffer += '--%s\r\n' % boundary
            buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)
            buffer += 'Content-Type: %s\r\n' % contenttype
            fd.seek(0)
            buffer += '\r\n' + fd.read() + '\r\n'
        buffer += '--%s--\r\n\r\n' % boundary
        return boundary, buffer
    multipart_encode = Callable(multipart_encode)

    https_request = http_request

def post_object(object, type=None):
    fields = {
        'email': settings['usermail'],
        'password': settings['password'],
        'private': settings['private'],
        'type': 'regular'
    }
    types = {
        'audio': 'audio',
        'video': 'video',
        'application': None,
    }
    if type:
        fields['type'] = type['type']
        fields[type['key']] = object
    else:
        mime = mimetypes.guess_type(object)[0]
        if not mime:
            raise RuntimeError('unknown content type of "%s"' % object)
        reg = re.compile('^([^/]+)/')
        m = reg.search(mime)

        if m.group(1) == 'text':
            fields['type'] = 'quote'
            fields['quote'] = open(object, 'rb').read()
            for encoding in supported_encodings:
                try:
                    fields['quote'] = fields['quote'].decode(encoding).encode('utf-8')
                    break
                except UnicodeError:
                    continue
        elif m.group(1) == 'image':
            fields['type'] = 'photo'
            fields['data'] = open(object, 'rb').read()
        else:
            fields['type'] = types[m.group(1)]
            fields['data'] = open(object, 'rb').read()

    if not fields['type']:
        raise RuntimeError('unknown content type of "%s"' % object)

    u = None
    try:
        opener = urllib2.build_opener(MultipartPostHandler)
        u = opener.open('http://www.tumblr.com/api/write', fields)
        u.read()
    except urllib2.HTTPError, e:
        if e.code != 201: raise e
    finally:
        if u: u.close()

    return fields['type'], object

def load_config():
    file = None
    try:
        file = open(os.path.expanduser('~/.tumblr_post'), 'r')
        reg = re.compile('^([^=]+)=(\S*)$')
        while True:
            line = file.readline()
            if not line: break
            if line == '\n': continue
            m = reg.search(line)
            settings[m.group(1)] = m.group(2)
    finally:
        if (file): file.close()

def main(argv):
    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:], "rs")
    except getopt.GetoptError:
        print 'usage: %s [files ...]' % argv[0]
        return -1

    load_config()
    for arg in args:
        if re.compile('^http[s]*://').match(arg):
            type, object = post_object(arg, {'type': 'link', 'key': 'url'})
            print 'posted "%s" as %s' % (object, type)
        else:
            for file in glob.glob(arg):
                type, object = post_object(file)
                print 'posted "%s" as %s' % (object, type)
    return 0

if __name__ == '__main__': sys.exit(main(sys.argv))
保存先は何処でも良いけど例として
C:¥tumblr_post¥tumblr_post.py にしておく。
次に「C:¥Python25¥pythonw.exe」(python.exeでなくpythonw.exe)のショートカットを作り、ショートカットのプロパティで「リンク先」を
C:¥Python25¥pythonw.exe から C:¥Python25¥pythonw.exe C:¥tumblr_post¥tumblr_post.py に変更する。そして「アイコンの変更」をクリックしてtumblrのロゴアイコン
tumblr icon - http://assets.tumblr.com/images/favicon.gif
を適当なfavicon生成サイトで変換したicoファイルに設定する。

後はこのショートカットを C:¥Documents and Settings¥貴方のアカウント¥SendTo¥ にコピーすれば完成!

このツールには設定ファイルが必要で C:¥Documents and Settings¥貴方のアカウント¥.tumblr_post というファイルを作る必要があります。
中身は usermail=your.name@your.email.com
password=your-password
private=0
といった感じ。
「private=1」にすると非公開になりますから、tumblr著作権ウゼーーという方は、この設定でアップするのが良いかと。

なお、このツールは
  • 画像
  • 動画
  • 音声
  • テキスト
  • リンク
に対応しており、上4つに関しては「送る」->「tumblr_post」でポスト出来ます。
コマンドラインからの操作に限り C:¥> tumblr_post.py http://www.tumblr.com/ といった使い方が出来る様になっています。pythonなのでun*xでも使えます。
殆どtumblr使ってない私ですが、よろしければどうぞ。

Enjoy!
Posted at by | Edit