Closed tenax66 closed 1 year ago
チャッピーに聞いてみました。ただしめっちゃ嘘かもしれません(未検証です)
本体ファイルをダウンロードせずに、ピースだけを抜き出す方法は、libtorrentのread_piece()関数を使用して、特定のピースのデータを直接取得する方法です。この方法でも一時的にダウンロードしたピースのデータがキャッシュされますが、ファイル全体をダウンロードすることはありません。
以下に、read_piece()関数を使用して指定したピースをバイト配列として取得し、.binファイルに書き出す方法を示します。
import os
import time
import libtorrent as lt
# 他の部分は同じ
# ダウンロードが完了したら、指定したピースのデータを取得
piece_data = handle.read_piece(piece_index)
# ダウンロードが完了するまで待機
while not piece_data.is_finished():
time.sleep(1)
piece_data = handle.read_piece(piece_index)
# ピースのデータをバイト配列として取得
piece_bytes = piece_data.buffer()
# .binファイルに書き出す
with open(os.path.join(save_path, f"piece_{piece_index}.bin"), "wb") as f:
f.write(piece_bytes)
この方法では、ダウンロードしたピースのデータだけが.binファイルに書き出され、本体ファイルは作成されません。ただし、read_piece()関数を使用する場合でも、キャッシュサイズに注意してください。libtorrentのキャッシュサイズを制限するには、session_settingsのcache_sizeオプションを使用できます。例えば、以下のように設定できます。
settings = lt.session_settings()
settings.cache_size = 1024 # キャッシュサイズを1024 KiBに制限
session.set_settings(settings)
※read_piece()関数の仕様について
read_piece()関数は、libtorrentのtorrent_handleクラスのメソッドで、指定したピースインデックスに対応するピースのデータを読み込むために使用されます。この関数は、非同期で実行され、alertオブジェクトを生成して結果を返します。read_piece()関数の仕様は以下の通りです。
read_piece(piece_index: int) -> read_piece_alert
引数:
piece_index: int - 読み込むピースのインデックス(0から始まる) 戻り値:
read_piece_alert: read_piece_alertオブジェクトを返します。このオブジェクトには、読み込んだピースのデータや状態が含まれています。 read_piece_alertオブジェクトは、以下の属性を持っています。
piece: int - 読み込んだピースのインデックス buffer(): bytes - 読み込んだピースのデータをバイト配列として返すメソッド size: int - 読み込んだピースのデータサイズ(バイト単位) is_finished(): bool - 読み込みが完了したかどうかを示す真偽値を返すメソッド read_piece()関数を使用してピースを読み込む際は、以下の手順で行います。
torrent_handleオブジェクトを使用して、read_piece()関数を呼び出し、指定したインデックスのピースを読み込みます。 read_piece_alertオブジェクトのis_finished()メソッドを使用して、読み込みが完了したかどうかを確認します。完了していない場合は、適当な間隔で再度確認します。 読み込みが完了したら、read_piece_alertオブジェクトのbuffer()メソッドを使用して、読み込んだピースのデータをバイト配列として取得します。
この方法で、指定したインデックスのピースのデータを読み込み、バイト配列として取得することができます。
ありがとうございます!
いまの実装もこの方針(read_piece
メソッドとalertを用いる)にはなっているのですが、それでも本体ファイルは作成されてしまいますね。
このサンプルも踏まえた上でもう少し様子を見てみます。
今の挙動(ピースをひとつだけダウンロードしたつもりが、サイズの大きいファイルができてしまう)の原因っぽい仕様は公式ドキュメントで確認できました。
storage allocation There are two modes in which storage (files on disk) are allocated in libtorrent.
The traditional full allocation mode, where the entire files are filled up with zeros before anything is downloaded. Files are allocated on demand, the first time anything is written to them. The main benefit of this mode is that it avoids creating heavily fragmented files. The sparse allocation, sparse files are used, and pieces are downloaded directly to where they belong. This is the recommended (and default) mode.
再度聞いてみたところ「ムリそう」との回答を得ました。 というわけで都度、作成されたファイルを消す処理になるのかなと思いますが、同時に複数(100以上とか)走らせると容量がやばそうなので、統合時には1つのファイル・1つのIPごとに、順番に実行していくのがよさそうです。
完全割り当てモードによるゼロ埋めにしても、なんらかの形で本体ファイルが作成されてしまう形にはなると思うのですが、これを行わずにダウンロードしたピース断片のバイト配列だけを取得する方法はありませんか?
libtorrentを使用して直接ピースのバイト配列のみを取得することはできません。これは、libtorrentが設計上、ファイルシステム上のファイルにデータを書き込むように作られているためです。しかし、ダウンロードしたピース断片のバイト配列だけを取得したい場合は、一時ディレクトリにダウンロードしてから、ピースのバイト配列を読み取り、その後一時ファイルを削除するというアプローチが考えられます。
具体的には、以下の手順で実行できます。
1.tempfileモジュールを使用して一時ディレクトリを作成します。 2.download_piece()関数で指定したピースを一時ディレクトリにダウンロードします。 3.ダウンロードが完了したら、一時ディレクトリ内のファイルからピースのバイト配列を読み取ります。 4.ピースのバイト配列を取得した後、一時ディレクトリを削除します。
import tempfile
import os
import shutil
# 一時ディレクトリを作成
temp_dir = tempfile.mkdtemp()
# download_piece()関数を実行して、指定したピースを一時ディレクトリにダウンロード
download_piece(torrent_path, temp_dir, piece_index, peer)
# ダウンロードしたファイルからピースのバイト配列を取得
file_path = os.path.join(temp_dir, "your_downloaded_file_name_here")
with open(file_path, "rb") as f:
piece_bytes = f.read()
# 一時ディレクトリを削除
shutil.rmtree(temp_dir)
あ、スパースファイルについて聞いてみたところ、実際にはディスク容量を専有していないという回答を得ました。確認すると確かに、「Big Buck Bunny.mp4」の「ディスク上のサイズ」は4MBとなっています。
ということは、このスパースファイル自体を証拠として貯めていっても問題ないのかもしれません。(DL後に、ファイル名を変更してしまえば可能?)
スパースファイルのプロパティを確認したとき、大きな容量が表示されるのですが、この容量は実際にはディスクスペースを専有していないということですか。
はい、その通りです。スパースファイルのプロパティを確認すると、ファイル全体の論理サイズが表示されますが、実際にディスクスペースを消費しているのは、ファイル内のデータが存在する部分だけです。論理サイズは、ファイル内のすべてのデータが埋められた場合のサイズを示していますが、未使用の部分(ゼロで埋められた領域)はディスク上で実際には割り当てられていません。
スパースファイルにおいて、実際にディスクスペースを消費している部分を知りたい場合、以下の方法があります。
Windowsの場合:エクスプローラーでファイルを右クリックし、「プロパティ」を選択します。表示されるダイアログで、「サイズ」と「サイズ(ディスク上)」の2つの値が表示されます。「サイズ」がファイル全体の論理サイズを示し、「サイズ(ディスク上)」が実際にディスク上で消費されている物理サイズを示します。
Linuxの場合:duコマンドを使用して、実際にディスクスペースを消費している部分を確認できます。du -h
のように実行し、表示される値が実際にディスク上で消費されている物理サイズです。 スパースファイルを扱う際には、論理サイズと実際のディスク使用量が異なることを考慮してください。ただし、スパースファイルをサポートしていないファイルシステムやストレージデバイスにコピーすると、未使用部分がゼロで埋められ、全体の論理サイズ分のディスク容量が消費されることがあります。
ありがとうございます、いまわたしもちょうどそれにたどりついたところでした。 duコマンドで確認すると確かに数百kbしか使っていないですね。
このスパースファイル自体を証拠として貯めていっても問題ないのかもしれません
この対応も考えられますが、一時フォルダにダウンロード→削除のほうがよさそうなので、そちらで実装しようかなと思います。
スパースファイルをそのまま証拠として使う場合、以下のような問題が発生しそうです。
いま見たら「big-buck-bunny.torrent」で設定されているピースサイズは256KBなので、4MBとなると妙にデカく、スパースファイルはピースサイズ自体よりだいぶ大きくなってしまうようですね。
先に書いていただいた点もありますので、やはり都度で消すほうがよさそうです。よろしくお願いいたします。
了解です! ちなみに、macOSのduコマンドで確認すると各スパースファイルの実サイズはそれぞれ256KB, 4KBとなってますね
apple@MBP Big Buck Bunny % du -h Big\ Buck\ Bunny.mp4
256K Big Buck Bunny.mp4
apple@MBP Big Buck Bunny % du -h Big\ Buck\ Bunny.en.srt
4.0K Big Buck Bunny.en.srt
なるほど?Windowsだとまた違うのかもしれないですね。
現在、ピースのダウンロード処理は以下のように実装しています。
.bin
ファイルに書き出す。証拠として必要なのは
.bin
ファイルだけなので、1~2でできる本体ファイルは不要です。そのまま残しておくと証拠フォルダに不要なファイルが残り続けることになり、容量的に問題があるので、対応を考えたいです。本体ファイルをダウンロードせずに
.bin
だけ抜く方法があれば一番よいです。 もしなければ、1~4の後に不要なファイルを削除する処理を入れるのかなと思っています。