Skip to content

Commit

Permalink
[core] Use the new 'UsePartialFiles' setting
Browse files Browse the repository at this point in the history
If a file is actively being downloaded it will be given a special
suffix (.!mt) to indicate it's a partial download when the
UsePartialFiles setting is enabled. When the file is fully downloaded
it will be renamed to remove the special suffix.
  • Loading branch information
alanmcgovern committed Sep 19, 2021
1 parent ab91ded commit 56f0d35
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,9 @@ public async Task FlushAsync (ITorrentData manager, int startIndex, int endIndex
}
}

internal Task MoveFileAsync (ITorrentFileInfo file, string newPath)
=> MoveFileAsync ((TorrentFileInfo) file, newPath);

internal Task MoveFileAsync (TorrentFileInfo file, string newPath)
=> MoveFileAsync (file, newPath, false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ namespace MonoTorrent.Client
{
class TorrentFileInfo : ITorrentFileInfo
{
public static string IncompleteFileSuffix => ".!mt";

public string DownloadCompleteFullPath { get; set; }

public string DownloadIncompleteFullPath { get; set; }

public string FullPath { get; set; }

ITorrentFile TorrentFile { get; }
Expand All @@ -53,15 +59,10 @@ class TorrentFileInfo : ITorrentFileInfo

public long Length => TorrentFile.Length;

public TorrentFileInfo (ITorrentFile torrentFile)
: this (torrentFile, torrentFile.Path)
{
}

public TorrentFileInfo (ITorrentFile torrentFile, string fullPath)
{
TorrentFile = torrentFile;
FullPath = fullPath;
FullPath = DownloadCompleteFullPath = DownloadIncompleteFullPath = fullPath;
BitField = new MutableBitField (torrentFile.EndPieceIndex - torrentFile.StartPieceIndex + 1);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ public async Task MoveFileAsync (ITorrentFileInfo file, string path)
throw new TorrentException ("Cannot move files when the torrent is active");

try {
await Engine.DiskManager.MoveFileAsync ((TorrentFileInfo)file, path);
await Engine.DiskManager.MoveFileAsync (file, path);
} catch (Exception ex) {
TrySetError (Reason.WriteFailure, ex);
throw;
Expand Down Expand Up @@ -617,9 +617,24 @@ internal void SetMetadata (Torrent torrent)

// All files marked as 'Normal' priority by default so 'PartialProgressSelector'
// should be set to 'true' for each piece as all files are being downloaded.
Files = Torrent.Files.Select (file =>
new TorrentFileInfo (file, Path.Combine (savePath, file.Path))
).Cast<ITorrentFileInfo> ().ToList ().AsReadOnly ();
Files = Torrent.Files.Select (file => {
var downloadCompleteFullPath = Path.Combine (savePath, file.Path);
var downloadIncompleteFullPath = downloadCompleteFullPath + TorrentFileInfo.IncompleteFileSuffix;

// FIXME: Is this the best place to futz with actually moving files?
if (!Engine.Settings.UsePartialFiles) {
downloadIncompleteFullPath = downloadCompleteFullPath;

if (File.Exists (downloadIncompleteFullPath) && !File.Exists (downloadCompleteFullPath))
File.Move (downloadIncompleteFullPath, downloadCompleteFullPath);
}

var currentPath = File.Exists (downloadCompleteFullPath) ? downloadCompleteFullPath : downloadIncompleteFullPath;
return new TorrentFileInfo (file, currentPath) {
DownloadCompleteFullPath = downloadCompleteFullPath,
DownloadIncompleteFullPath = downloadIncompleteFullPath
};
}).Cast<ITorrentFileInfo> ().ToList ().AsReadOnly ();

PieceManager.Initialise ();
MetadataTask.SetResult (Torrent);
Expand Down Expand Up @@ -857,9 +872,30 @@ internal void OnPieceHashed (int index, bool hashPassed, int piecesHashed, int t

var files = Files;
var fileIndex = files.FindFileByPieceIndex (index);
for (int i = fileIndex; i < files.Count && files[i].StartPieceIndex <= index; i++)
for (int i = fileIndex; i < files.Count && files[i].StartPieceIndex <= index; i++) {
((MutableBitField) files[i].BitField)[index - files[i].StartPieceIndex] = hashPassed;

// If we're only hashing 1 piece then we can start moving files now. This occurs when a torrent
// is actively downloading.
if (totalToHash == 1) {
if (files[i].BitField.AllTrue && files[i].FullPath != files[i].DownloadCompleteFullPath)
_ = Engine.DiskManager.MoveFileAsync (files[i], files[i].DownloadCompleteFullPath);
else if (!files[i].BitField.AllTrue && files[i].FullPath != files[i].DownloadIncompleteFullPath)
_ = Engine.DiskManager.MoveFileAsync (files[i], files[i].DownloadIncompleteFullPath);
}
}

// If we're hashing many pieces, wait for the final piece to be hashed, then start trying to move files.
// This occurs when we're hash checking, or loading, torrents.
if (totalToHash > 1 && piecesHashed == totalToHash) {
for (int i = 0; i < files.Count; i++) {
if (files[i].BitField.AllTrue && files[i].FullPath != files[i].DownloadCompleteFullPath)
_ = Engine.DiskManager.MoveFileAsync (files[i], files[i].DownloadCompleteFullPath);
else if (!files[i].BitField.AllTrue && files[i].FullPath != files[i].DownloadIncompleteFullPath)
_ = Engine.DiskManager.MoveFileAsync (files[i], files[i].DownloadIncompleteFullPath);
}
}

if (hashPassed) {
List<PeerId> connected = Peers.ConnectedPeers;
for (int i = 0; i < connected.Count; i++)
Expand Down Expand Up @@ -916,7 +952,7 @@ public void LoadFastResume (FastResume data)
throw new ArgumentException ("The fast resume data does not match this torrent", "fastResumeData");

for (int i = 0; i < Torrent.Pieces.Count; i++)
OnPieceHashed (i, data.Bitfield[i], i, Torrent.Pieces.Count);
OnPieceHashed (i, data.Bitfield[i], i + 1, Torrent.Pieces.Count);
UnhashedPieces.From (data.UnhashedPieces);

HashChecked = true;
Expand Down
2 changes: 2 additions & 0 deletions src/MonoTorrent.Client/MonoTorrent/TorrentCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class TorrentCreator : EditableTorrent
{
internal class InputFile : ITorrentFileInfo
{
public string DownloadCompleteFullPath { get; }
public string DownloadIncompleteFullPath { get; }
public string Path { get; set; }
public string FullPath { get; set; }
public byte[] MD5 { get; set; }
Expand Down
19 changes: 19 additions & 0 deletions src/MonoTorrent/MonoTorrent/ITorrentFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,29 @@ namespace MonoTorrent
{
public interface ITorrentFile
{
/// <summary>
/// The relative path to the file within the torrent.
/// </summary>
string Path { get; }

/// <summary>
/// The first piece which contains data for this file
/// </summary>
int StartPieceIndex { get; }

/// <summary>
/// The last piece which contains data for this file.
/// </summary>
int EndPieceIndex { get; }

/// <summary>
/// The size of this file in bytes.
/// </summary>
long Length { get; }

/// <summary>
/// The offset, relative to the first byte in the torrent, where this file begins.
/// </summary>
long OffsetInTorrent { get; }
}
}
16 changes: 14 additions & 2 deletions src/MonoTorrent/MonoTorrent/ITorrentFileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,26 @@ namespace MonoTorrent
{
public interface ITorrentFileInfo : ITorrentFile
{
/// <summary>
/// The <see cref="BitField"/> tracking which pieces of this file have been downloaded.
/// </summary>
BitField BitField { get; }

/// <summary>
/// The full path to the file on disk. Can be modified by calling <see cref="TorrentManager.MoveFileAsync(ITorrentFileInfo, string)" />
/// or <see cref="TorrentManager.MoveFilesAsync(string, bool)"/>.
/// If the file is currently being downloaded, this will be the same as <see cref="DownloadIncompleteFullPath"/>. Otherwise it will be <see cref="DownloadCompleteFullPath"/>
/// </summary>
string FullPath { get; }

/// <summary>
/// The file will exist at this path after it has been fully downloaded. Can be modified by calling <see cref="TorrentManager.MoveFileAsync(ITorrentFileInfo, string)" />
/// </summary>
string DownloadCompleteFullPath { get; }

/// <summary>
/// The file will exist at this path when it is partially downloaded. This value may be the same as <see cref="DownloadCompleteFullPath"/>.
/// </summary>
string DownloadIncompleteFullPath { get; }

/// <summary>
/// The priority of the file when downloading. Can be modified by calling <see cref="TorrentManager.SetFilePriorityAsync(ITorrentFileInfo, Priority)"/>
/// </summary>
Expand Down

0 comments on commit 56f0d35

Please sign in to comment.