Kindle 2024 でファイル転送できない
Kindle2024からPC接続時のファイル転送はMTPプロトコルが使われるようになり、KindleをPCにUSBケーブルで接続してもドライブレターが割り当てられなくなったため、narou.rbからKindleが接続されたことを検知できず、Sendコマンドが使えなくなりました。
ドライブレターを割り当てるツールとスクリプトについてgistにあげてます。参考までに。 https://gist.github.com/rogenobl/f99d989afd48513cbda3b92de034dd53
MTPプロトコルを扱うrubyのライブラリを見つける事ができませんでした。 いちおう、OLE経由でエクスプローラーを操作すればファイル操作できそうですが、それなりにコードを書く必要があると思います。 だれかライブラリ作ってくれると良いんですが。
情報ありがとうございます。今の所narou.rbの設定で"convert.copy-to"を指定して、格納されたファイルを手動でエクスプローラー上のKindleのdocumentsフォルダーにコピーすることで暫定運用していますが、コピー時に大量のファイルを一度にコピーするとエラーになるので何回かに分けてコピーするなどしており、MTPでのマウントは今一つ信頼性に欠けるという認識です。 せっかくご教示いただいたのですが、dokanの信頼性が気になるので私個人としては今の暫定運用を続けようかと思っております。 Amasonが方針を元に戻してもらうのが一番なのですが、まずそんなことはありそうにないので、悩ましいですね。
[!NOTE] 下記の Shell.Application を使う方法は駄目そう。
Kindle 2024 がないので動作確認できませんが、エクスプローラのPCの下に表示されるデバイス名が Kindle のみの場合は、以下で Kindle デバイスの検出とファイルのコピーはできそうです。 (MTP接続のAndroid端末でデバイス検出とファイルのコピーを確認し、Kindle 用に名前を変えたもの)
require 'win32ole'
SHELL_APPLICATION = WIN32OLE.new("Shell.Application")
def get_device_root_dir(volume_name)
volume = SHELL_APPLICATION.NameSpace(0x11).Items.each.select do |item|
name = item.Name
name = $` if name =~ / \([A-Z]:\)$/ # ドライブレターがあったら削除
name.casecmp(volume_name) == 0
end.first
volume ? volume.Path.gsub('\\', '/') : nil
end
def copy_file(src_file, dest_directory)
dest = SHELL_APPLICATION.NameSpace(dest_directory.gsub('/', '\\'))
# マジックナンバー16でファイルが存在するとき上書き
# 参照: https://learn.microsoft.com/ja-jp/windows/win32/shell/folder-copyhere
dest.CopyHere(src_file.gsub('/', '\\'), 16)
end
def file_exist?(filename)
dir, file = File.split(filename)
begin
folder = SHELL_APPLICATION.NameSpace(dir.gsub('/', '\\'))
rescue WIN32OLE::RuntimeError
return false
end
folder && folder.Items.each.any? do |item|
item.Name == file
end
end
def file_mtime(filename)
dir, file = File.split(filename)
folder = SHELL_APPLICATION.NameSpace(dir.gsub('/', '\\'))
return nil if folder.nil?
item = folder.Items.each.find do |item|
item.Name == file
end
return nil if item.nil?
item.ModifyDate
end
def is_directory?(filename)
begin
SHELL_APPLICATION.NameSpace(filename.gsub('/', '\\'))
true
rescue WIN32OLE::RuntimeError
false
end
end
file_name = 'documentsの下にコピーするローカルディスクのファイル名'
kindle_device = get_device_root_dir('Kindle')
documents_dir = File.join(kindle_device, 'documents')
copy_file(File.absolute_path(file_name), documents_dir)
lib/device/library/windows.rb の get_device_root_dir を上のものに置き換えて lib/device.rb の File.directory?, File.exists?, File.mtime? とファイルコピー部分を上の関数に置き換えればなんとかすればいけるのではないかと。
理想的には device.rb で File のクラスメソッドの代わりに Pathname のメソッドを使うようにして、 get_device_root_dir の戻り値は USB デバイスの場合は Pathname のインスタンス、MTPデバイスの場合は device.rb の必要とするメソッドを実装した Pathname もどきにしたほうがきれいだとは思う。
問題点:
-
CopyHere()でのファイルコピーは呼び出し側はコピーの終了待ちやコピー失敗の検知ができない。代わりにダイアログボックスが画面に表示される。 - Android端末の場合、ファイルのコピー後すぐにはエクスプローラに反映されない。他のフォルダをいくつも表示し、しばらく時間が過ぎてから反映されていることがあるが、どのタイミングで反映されるのか不明。Kindle 2024 でも同様かも
- 引数のファイル名は絶対パスで指定。ドライブレターが割り当てられているデバイスの場合は
File.absolute_pathで絶対パスにできるが、MTPの絶対パスに対して使うとパス名が壊される - グローバル変数
SHELL_APPLICATIONを定義しているが、lib/device/library/windows.rbの@@FileSystemObjectのようにクラス変数にしたほうが良い - エラーチェックは不十分かと思う
普段、Windows を使ってないので、ここまで。
kuboさんのスクリプトを試してみました。
実際のデバイス名とフォルダー構成は私の場合 PC\Kindle Paperwhite Signature Edition\Internal Storage\documents となっていて、デバイス名は機種毎に異なるものと思われます。そこで
- name.casecmp(volume_name) == 0 > name.start_with?(volume_name)
- documents_dir = File.join(kindle_device, 'documents') > documents_dir = File.join(kindle_device, 'Internal Storage/documents')
に変更し、テストファイルのコピーを試したところ、copy_fileはエラーなしで呼べたのですが、ダイアログは出ずにコピーもされませんでした。
一例なので、何か環境依存があるかどうかまでは調べられていません。
動作確認ありがとうございます。
Shell.Application をWIN32OLEで使う方式はどうやら駄目そうですね……。
あと、この方式はエクスプローラでの操作と基本的に同じなので、エクスプローラで大量ファイルをコピーすると失敗するという現象の回避にはならないでしょう。
また、file_mtime() の動作確認していたとき、USBドライブもMTPもAPIの使い方は同じだろうという甘い考えのもとに、Kindle 10th を繋いてタイムスタンプが正しく取れることは確認した。しかし、MTP接続の Android 上のファイルに使うと 全部1899-12-30 00:00:00 +0900 といった変な値。使えるものではなかった。
1899-12-30 00:00:00は、Accessや古いSQL Serverでの日時のデフォルト値で、SQL Server は定義された時間ソースに接続できない場合、この日付をデフォルトとして返す。
だからちゃんとした専用のSQL構文を組まないとダメかも