Dropboxのカメラアップロードテストのボーナスを最後の500MBまで容赦なくゲットする方法

フォーラムに「4.5GBまでしか貰えないよー」という声が多数あったので調べてた。
http://forums.dropbox.com/topic.php?id=53104&replies=807


「ギガバイトサイズのをインポートしたら増えたよー」「10GB送りつけたら最後に増えた」「10GBアップロードしたけどだめだった」「12GBアップロードしたけど4.5GBのまま」「ビデオと画像別々で計算してるんじゃ?」
という流れで、以下のPostで結論です。

Sin L.

Alright, after experimenting for a while. The way to get the full 5GB bonus 
requires you to:

おーけー、実験してきたよ。5GBのボーナスをフルにゲットするには、

1. Successfully import either a photo or video into your Dropbox account. 
You'll get credit for 500MB.

画像かビデオをインポートすること。+500MB

2. Either successfully imported and sync'ed 4GB worth of photo(s) or video(s),
 to get the 4GB credit.

それから、4GBの画像かビデオをインポート、同期すること。+4GB

3. If you successfully imported and sync'ed only photo(s), you need to do
 at least another 500MB of video(s) to get that final 500MB credit. Same 
applies vice versa, if you only do 4GB of video(s), you need to do at least
 500MB of photo(s).

画像をアップロードした場合は、最後の500MBのボーナスをゲットするために、
更に500MB以上のビデオをアップロードする必要がある。同様に、ビデオを4GB
アップロードした場合は、500MB以上の画像をアップロードする必要がある。

Following this, you will only need to upload exactly 4.5GB of data to get
 5GB of credit space. 4GB of it being either Photo(s) or 4GB Video(s) then
 a +500MB of either Photo(s) or Video(s).

こうすれば、5GBのボーナスをゲットするために必要なのは4.5GBのアップロード
(4GBの写真かビデオ、そして500MBの写真かビデオ)だけ。

Also, I believe if you do a single 4GB video file, it will only count as
 being credited for 500MB. So do smaller size video(s) and nothing greater
 than 500MB.

それから、4GBの単一のビデオをアップロードしても、それは500MBのボーナスに
しかならない。なので、500MBを超えない小さいビデオをアップロードしよう。

For those that are on 2GB~ accounts, you just need to free up space once 
you've accumulated additional spaces to upload again. For example, if you're
 stuck on 3GB data filled and cannot upload more photo(s) or video(s), just
 delete the existing one(s) and re-upload new one(s).

アカウントの容量が2GBちょいだったりするときは、アップロードしたものを消して、
開いたスペースに新しくアップロードするだけ。
例えば(2.4GBからアップロードを始めて)3GBでデータが一杯になって、
それ以上アップロードできなくなったときは、古いのを消して、新しく
アップロードするだけ。

Proof in the link: http://img13.imageshack.us/img13/4030/db5gbbonusverified.png

証拠のリンク。

Edit: Please follow the link to an updated topic regarding the missing 
500MB bonus issue: http://forums.dropbox.com/topic.php?id=53120

http://forums.dropbox.com/topic.php?page=6&id=53104&replies=806#post-393497


僕の環境でも+5GBになり、再現できました!

そしてこれが一番書きたかったこと

以下のリンクから新規アカウントを作ると+250MBで開始できます!
https://www.dropbox.com/referrals/NTE2NTE5MTc5

ふえろーふえろー

Windows7 Home Premiumでバックアップ先をネットワークドライブにする(偽

Windows7 Professionalには搭載されているという噂の、ネットワークドライブにバックアップしちゃうという機能を、擬似的にWin7 Home Premiumでやっちゃおうという試みです。
やや煩雑ですが、わりとイケそうな感触なので記事にしておきます。

Windows7には、VHDをマウントできる機能がある。

はい、勘の良い人はここでもう僕の言いたいことが分かってしまいましたね。VHDファイルをネットワーク上に置いて、それをローカルにマウント、バックアップドライブに指定しましょうということです。

初回の手順

VHDを作成する

”コンピュータの管理”=> ”ディスクの管理”=> ”ディスクの操作”=>”その他”=>”VHDの作成” 

VHDをバックアップ先に選び、バックアップを実行する
VHDを切断

”コンピュータの管理”=> ”ディスクの管理”にて、対象のVHDを右クリック => ”切断”
ネットワーク上のVHDへの接続がうまく行かなければ自動的に切断されるので、放っておいてもいいかもです。

二回目以降の手順

VHDを作成する

”コンピュータの管理”=> ”ディスクの管理”=> ”ディスクの操作”=>”その他”=>”VHDの接続”
ドライブレターは(空いていれば)自動的に以前のものが選ばれます。 

VHDをバックアップ先に選び、バックアップを実行する
VHDを切断

初回と同様

以上です

  • 購入後、最初に一度だけシステムイメージのバックアップをしておきたい場合

などに重宝しそうです。
また、

  • ときどきシステムイメージのバックアップをしたい場合、
  • ファイルのバックアップをしたい場合。

これらの場合は、手動でその度にVHDを接続、あるいは作成することになるでしょう。やや煩雑ですが、それでもノートPCに外付けHDDを挿抜するよりは楽なんじゃないかと思います。

VHDマウント機能で気をつけること

Sambaを使ってる人は

strict allocate = yes

がsmb.confに含まれているかチェックしてください。

strict allocate = no、または記載なしの場合、

VHDは、”ディスクの操作”=> ”その他” => ”VHDの作成”で作るわけですが、ここで ”可変容量”を選んじゃいけません、ゼッタイ。

  • 作成はOK
  • 書き込みもOK

しかし…

  • アンマウント NG => ”このバージョンでは、このファイル形式のバージョンはサポートされません。”
  • 再マウント NG => ”このバージョンでは、このファイル形式のバージョンはサポートされません。”

という感じで、バックアップしてるのに後で開けず、そもそもアンマウントさえもできないという呪われた感じになっちゃいます。ご注意を。
ファイルサーバー上にあるVHDとのネットワークが切れると、VHDが読み込めなくなる。

届いたノートパソコンが悲しかったのでメモ

Thinkpad E420が届いたのだけれど、液晶に汚れ(ドット欠けじゃなく埃でも入ってるみたい)があり、さらにキーボードの右下隅が浮いていてペコペコと間抜けな音をたてるので到着早々なんだけど、修理の旅に出てもらうことにした。
先代のThinkpad Edge14も右下隅が浮いてる症状で入院した記憶がある。Thinkpadといえばキーボードのタッチと赤いポッチなわけで、ここで手を抜いてもらうと困ってしまう。僕は赤いポッチには興味が無いのでこれはなくなってもいい。でもキーボードだけは…。
幸いというかなんというか、Edge14からタッチは向上してると思う。素性はいいんだ。キーボードのフィーリング。エラーが出るわけでもない些細な問題と現場が思っているなら、悲しい事だなぁ。


謎の汚れ


右下隅を押すとわずかに沈む

StormのTopologyをグラフにして見る(手抜き

StormのTopologyBuilderの設計はいまいち好きじゃないんですが(idで指定なのと、setBolt・setSpoutが分かれているところ)、それはともかく、できあがったTopologyはグラフにしないとパッと理解できないので、Pythonで適当に書いてみました。

    builder = TopologyBuilder()
    builder.setSpout(1, Spout())
    builder.setBolt(2, Bolt()).allGrouping(0)
    builder.graphviz_format()
digraph topology {
1 [shape=box, label="1@Spout x(default)"];
2 [label="2@Bolt x(default)"];
1 -> 2 [label="all"];
}

    class TestWordSpout(Spout):
        pass
    class ExclamationBolt(Bolt):
        pass
    builder = TopologyBuilder()        
    builder.setSpout(1, TestWordSpout(), 10)        
    builder.setBolt(2, ExclamationBolt(), 3).shuffleGrouping(1)
    builder.setBolt(3, ExclamationBolt(), 2).shuffleGrouping(2)
    builder.graphviz_format()
digraph topology {
1 [shape=box, label="1@TestWordSpout x10"];
2 [label="2@ExclamationBolt x3"];
3 [label="3@ExclamationBolt x2"];
1 -> 2 [label="shuffle"];
2 -> 3 [label="shuffle"];
}

    class RandomSentenceSpout(Spout):
        pass
    class SplitSentence(Bolt):
        pass
    class WordCount(Bolt):
        pass
    builder = TopologyBuilder()
    builder.setSpout(1, RandomSentenceSpout(), 5)        
    builder.setBolt(2, SplitSentence(), 8).shuffleGrouping(1)
    builder.setBolt(3, WordCount(), 12).fieldsGrouping(2, Fields("word"))
    builder.graphviz_format()
digraph topology {
1 [shape=box, label="1@RandomSentenceSpout x5"];
2 [label="2@SplitSentence x8"];
3 [label="3@WordCount x12"];
1 -> 2 [label="shuffle"];
2 -> 3 [label="fields[word]"];
}

だいぶわかりやすいですね。

TopologyBuilderは

TopologyBuilder builder = new TopologyBuilder();
        
builder.setSpout(1, new RandomSentenceSpout(), 5);        
builder.setBolt(2, new SplitSentence(), 8)
        .shuffleGrouping(1);
builder.setBolt(3, new WordCount(), 12)
        .fieldsGrouping(2, new Fields("word"));

より

TopologyBuilder builder = new TopologyBuilder();
builder.set("random_sentence_spout", 
        new Node(new RandomSentenceSpout()).setPallalelismHint(5));
builder.set("split_sentence", 
        new Node(new SplitSentence()).setPallalelismHint(8).shuffleGrouping("random_sentence_spout")) ; 
builder.set("word_count", 
        new Node(new WordCount()).setPallalelismHint(12).fieldGrouping("split_sentence", new Fields("word"))) ; 

とかのほうがわかりやすいような気がしますが、僕はもう少し抽象的な定義からTopologyを生成して使おうとしているので、出来上がりさえ検証できれば問題ないですね。

コード

InputDeclarerのstreamIdは使わなそうなので実装していません。

Apple IDを変更した

AppleIDはエイリアスを使ってid+alias@gmail.comのかたちで登録していた。これが非常にややこしい。出先でApple関連のサービスにログインするとき、意味のわからないエイリアスを入力することを毎回のように強要され発狂しそうになった。誰だよエイリアスとか考えた天才!遅れてきた中ニ病と相性抜群じゃねーか!

方法

Apple IDの編集」→「名前、ID、メールアドレス」にて「代替メールアドレス」を登録する
登録したアドレスにメールが飛んでくるのでクリックして認証する
同じく「Apple IDの編集」→「名前、ID、メールアドレス」にて「AppleIDと主要メールアドレス」を編集する
編集したアドレスにメールが飛んでくるのでクリックして認証する
完了!

後始末

PCのiTunesにて、ログアウト→新しいIDでログイン
iPhone/iPod touchにてiCloudのログアウトができない?ので削除(これが実質ログアウト?)→新しいIDでログイン
iPhone/iPod touchにてStoreでログアウト→新しいIDでログイン


以上。これで問題なく使えてます。

かるーい気持ちでAndroidに移行しようとして泣いた話

僕はpdfやtxtファイルをDropboxに入れて、iPhoneのGoodReaderでフォルダを同期、ファイルを閲覧していました。このGoodReaderが神アプリで、

  • Dropboxとのフォルダ同期
  • pdf閲覧
  • txt閲覧
  • 他アプリに投げる

と、1アプリで僕のやりたいことができる、欠かせないアプリなわけです。毎日が充実していました。

…しかしその安穏とした日々にも転機が訪れます。ホワイトプラン二年縛りの終了です。iOSの対抗馬と目されてるAndroidでこの環境を果たしてどうすれば構築できるのか。かるーい気持ちでauにNMPして思い知ることになりました。

結果

Dropboxとのフォルダ同期

  Dropbox同期関係、マトモなソフトなし!皆無!
  Dropbox - Android マーケット https://market.android.com/details?id=com.dropbox.android&feature=related_apps
    同期に対応せず
  Dropsync - Android マーケット https://market.android.com/details?id=com.ttxapps.dropsync&feature=search_result
    ファイルサイズ制限あり。途中で止まる…
  Titanium Media Sync - Android マーケット https://market.android.com/details?id=com.keramidas.MediaSync
    有料。同期は完璧に動作する。しかし、Dropbox->Androidは、毎回全てのファイルをダウンロードする仕様みたい。開発者に質問メールを投げたけど返ってこない…

pdf閲覧

  公式アプリあり
  Adobe® Reader® - Android マーケット https://market.android.com/details?id=com.adobe.reader&feature=search_result
    なかなか重いですが、いちおう動きます。
  ezPDF Reader - Android マーケット https://market.android.com/details?id=udk.android.reader&hl=ja
    有料。いいらしいです。試そうかどうか悩んでいるところです。

txt閲覧

  …ほとんどのソフトが3MBも読めない。何を考えて実装(ry
  Androidのテキスト閲覧ソフトの比較 - たていすのメモ http://d.hatena.ne.jp/tateisu/20100822/1282445343
    読めるだけ。かなり使いづらい…

他アプリに投げる

  普通のファイラならイケます。ファイラはいろいろあって充実してます。


ううう〜〜ん??
Xperia acroを手に呆然としています。なう。

Flaskをさわってみる

軽量なWebフレームワークが使えたら便利な場面があったので、最近ちょこちょこ名前を聞くようになったFlaskをテスト。
軽くさわっただけですが、かなり好印象を受けました。使えそうな雰囲気です。

成果物

あまり良いサンプルではないですが、ゲームのスクリプトと音声ファイルから、あるキャラクターをずーっと喋らせるというもの。真紅ー



サーバ側はこれだけです。

# -*- coding:utf-8 -*-

__author__="rubyu"
__date__ ="$2011/09/14 11:25:52$"

import unittest
import logging
logging.basicConfig(
    level=logging.DEBUG,
    format="%(levelname)-8s %(module)-16s %(funcName)-16s@%(lineno)d - %(message)s"
)

import os
import sys
import re
import json

from flask \
    import Flask, make_response, render_template, request
app = Flask(__name__)

from sekai.voice_parser import Voice
from sekai.script_parser import Script
from config import Config

voice = None
script = None

@app.route("/")
def show_root():
    return render_template("index.html")

@app.route("/query/json")
def query_json():
    u"""
    真紅の発言について、キーワードが含まれるものだけを返す。
    
    jsonで、以下の形式で。
    [ [発言, ボイスID], ... ]
    """
    keyword = request.args.get("keyword", "")
    logging.debug("keyword: %s", keyword)
    arr = []
    for text in script.texts:
        str, name, vid = text
        if name == u"真紅" and \
           -1 != str.find(keyword):
            str = html_ruby(str)
            arr.append((str, vid))
    logging.debug("size: %s", len(arr))
    response = make_response()
    response.data = json.dumps(arr)
    response.headers["Content-Type"] = "application/json"
    return response

@app.route("/voice/ogg/<voice_id>")
def output_voice(voice_id):
    u"""
    指定のIDのボイスを切り出して返す。
    形式はもとのoggのまま。
    """
    logging.debug("voice id: %s", voice_id)
    response = make_response()
    response.data = voice.get(voice_id)
    response.headers["Content-Type"] = "audio/ogg"
    response.headers["Content-Disposition"] = "attachment; filename=%s.ogg" % voice_id
    return response

ruby_pat = re.compile("\[(.+?)\|(.+?)\]")
def html_ruby(str):
    u"""
    ...[ルビ|テキスト]...の形式を、HTMLのルビタグに変換する。
    複数個含まれる場合は全て変換する。
    """
    return ruby_pat.sub(r"<ruby>\2<rt>\1</rt></ruby>", str)


class ProcWrapper(object):
    u"""
    コマンドラインから呼び出されるラッパ。
    テスト時はダミーに差し替えられる。
    """
    def app_run(self, voice_path, script_path):
        global voice
        global script
        try:
            voice = Voice.restore("voice.p")
        except Exception, e:
            logging.debug("Excepted: %s", e)
            voice = Voice(voice_path)
            voice.save("voice.p")
        try:
            script = Script.restore("script.p")
        except Exception, e:
            logging.debug("Excepted: %s", e)
            script = Script(script_path)
            script.save("script.p")
        app.run()
            

class OptionParserTestCase(unittest.TestCase):
    u"""
    コマンドラインオプションのテスト。
    """
    class DummyProcWrapper(object):
        u"""
        ダミーのラッパクラス。
        コールされた関数のログを持つ。
        """
        def __init__(self):
            self._log = []

        def __getattr__(self, name):
            def _dummy(self, *args, **kwargs):
                pass
            self._log.append(name)
            return _dummy    
        
        
    @classmethod
    def setUpClass(cls):
        cls._path = "/tmp/sekai"
        cls._argv = sys.argv
    
    @classmethod
    def terDownClass(cls):
        sys.argv = cls._argv
        
    def setUp(self):
        self._wrapper = self.DummyProcWrapper()
        sys.argv = [self._argv[0]]
        
    def test_path(self):
        u"""
        引数pathが与えられた場合。
        """
        sys.argv.append("--path=%s" % self._path)
        parse(self._wrapper)
        self.assertEqual(["app_run"], self._wrapper._log)
        

def parser():
    u"""
    OptionParserのインスタンスを返す。
    """
    from optparse import OptionParser
    p = OptionParser("usage: shinku_player.py --path=game_installed")
    p.add_option(
        "-p", 
        "--path",
        type="string",
        help="directory that the game installed"
    )
    return p

def parse(wrapper):
    u"""
    コマンドラインオプションで分岐し、ラッパをコールする。
    """
    p = parser()
    options, args = p.parse_args()
    
    if options.path:
        config = Config()
        config.path = options.path
        config.save("config.p")
    
    try:
        config = Config.restore("config.p")
    except Exception, e:
        logging.debug("Excepted: %s", e)
        p.error("Program has not enough data. The 'path' parameter never had been given to this program!")
        sys.exit()
        
    voice_path = os.path.join(config.path, "voice.bin")
    script_path = os.path.join(config.path, "World.hcb")
    if not os.path.isdir(config.path) or \
       not os.path.isfile(voice_path) or \
       not os.path.isfile(script_path):
        p.error("File not found. 'voice.bin' and 'World.hcb' must be in the 'path=%s'!" % config.path)
        sys.exit()
    
    app.debug = True
    wrapper.app_run(voice_path, script_path)
    
if __name__ == "__main__":
    debug = False
#    debug = True
    
    if debug:
        unittest.main()
    else:
        parse(ProcWrapper())