Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kindle 2024 でファイル転送できない #438

Open
nnonaka opened this issue Oct 18, 2024 · 5 comments
Open

Kindle 2024 でファイル転送できない #438

nnonaka opened this issue Oct 18, 2024 · 5 comments

Comments

@nnonaka
Copy link

nnonaka commented Oct 18, 2024

Kindle2024からPC接続時のファイル転送はMTPプロトコルが使われるようになり、KindleをPCにUSBケーブルで接続してもドライブレターが割り当てられなくなったため、narou.rbからKindleが接続されたことを検知できず、Sendコマンドが使えなくなりました。

@rogenobl
Copy link
Contributor

rogenobl commented Jan 5, 2025

ドライブレターを割り当てるツールとスクリプトについてgistにあげてます。参考までに。
https://gist.github.com/rogenobl/f99d989afd48513cbda3b92de034dd53

MTPプロトコルを扱うrubyのライブラリを見つける事ができませんでした。
いちおう、OLE経由でエクスプローラーを操作すればファイル操作できそうですが、それなりにコードを書く必要があると思います。
だれかライブラリ作ってくれると良いんですが。

@nnonaka
Copy link
Author

nnonaka commented Jan 5, 2025

情報ありがとうございます。今の所narou.rbの設定で"convert.copy-to"を指定して、格納されたファイルを手動でエクスプローラー上のKindleのdocumentsフォルダーにコピーすることで暫定運用していますが、コピー時に大量のファイルを一度にコピーするとエラーになるので何回かに分けてコピーするなどしており、MTPでのマウントは今一つ信頼性に欠けるという認識です。
せっかくご教示いただいたのですが、dokanの信頼性が気になるので私個人としては今の暫定運用を続けようかと思っております。
Amasonが方針を元に戻してもらうのが一番なのですが、まずそんなことはありそうにないので、悩ましいですね。

@kubo
Copy link
Contributor

kubo commented Jan 12, 2025

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.rbget_device_root_dir を上のものに置き換えて lib/device.rbFile.directory?, File.exists?, File.mtime? とファイルコピー部分を上の関数に置き換えればなんとかすればいけるのではないかと。

理想的には device.rbFile のクラスメソッドの代わりに Pathname のメソッドを使うようにして、 get_device_root_dir の戻り値は USB デバイスの場合は Pathname のインスタンス、MTPデバイスの場合は device.rb の必要とするメソッドを実装した Pathname もどきにしたほうがきれいだとは思う。

問題点:

  1. CopyHere() でのファイルコピーは呼び出し側はコピーの終了待ちやコピー失敗の検知ができない。代わりにダイアログボックスが画面に表示される。
  2. Android端末の場合、ファイルのコピー後すぐにはエクスプローラに反映されない。他のフォルダをいくつも表示し、しばらく時間が過ぎてから反映されていることがあるが、どのタイミングで反映されるのか不明。Kindle 2024 でも同様かも
  3. 引数のファイル名は絶対パスで指定。ドライブレターが割り当てられているデバイスの場合は File.absolute_path で絶対パスにできるが、MTPの絶対パスに対して使うとパス名が壊される
  4. グローバル変数 SHELL_APPLICATION を定義しているが、 lib/device/library/windows.rb@@FileSystemObject のようにクラス変数にしたほうが良い
  5. エラーチェックは不十分かと思う

普段、Windows を使ってないので、ここまで。

@nnonaka
Copy link
Author

nnonaka commented Jan 13, 2025

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はエラーなしで呼べたのですが、ダイアログは出ずにコピーもされませんでした。

一例なので、何か環境依存があるかどうかまでは調べられていません。

@kubo
Copy link
Contributor

kubo commented Jan 14, 2025

動作確認ありがとうございます。

Shell.Application をWIN32OLEで使う方式はどうやら駄目そうですね……。
あと、この方式はエクスプローラでの操作と基本的に同じなので、エクスプローラで大量ファイルをコピーすると失敗するという現象の回避にはならないでしょう。
また、file_mtime() の動作確認していたとき、USBドライブもMTPもAPIの使い方は同じだろうという甘い考えのもとに、Kindle 10th を繋いてタイムスタンプが正しく取れることは確認した。しかし、MTP接続の Android 上のファイルに使うと 全部1899-12-30 00:00:00 +0900 といった変な値。使えるものではなかった。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants