22using System . Diagnostics ;
33using System . IO ;
44using System . Text ;
5- using Newtonsoft . Json . Linq ;
5+ using JetBrains . Annotations ;
6+ using Newtonsoft . Json ;
7+ using PatchKit . Logging ;
8+ using PatchKit . Unity . Patcher . AppData . Remote . Downloaders . Torrents ;
9+ using PatchKit . Unity . Patcher . AppData . Remote . Downloaders . Torrents . Protocol ;
10+ using PatchKit . Unity . Patcher . Cancellation ;
611using PatchKit . Unity . Patcher . Debug ;
712
813namespace PatchKit . Unity . Patcher . AppData . Remote . Downloaders
@@ -11,9 +16,9 @@ namespace PatchKit.Unity.Patcher.AppData.Remote.Downloaders
1116 /// Provides an easy access for torrent client program.
1217 /// </summary>
1318 /// <seealso cref="System.IDisposable" />
14- public class TorrentClient : IDisposable
19+ public sealed class TorrentClient : IDisposable
1520 {
16- private static readonly DebugLogger DebugLogger = new DebugLogger ( typeof ( TorrentClient ) ) ;
21+ private readonly ILogger _logger ;
1722
1823 private readonly ITorrentClientProcessStartInfoProvider _processStartInfoProvider ;
1924
@@ -27,62 +32,110 @@ public class TorrentClient : IDisposable
2732
2833 public TorrentClient ( ITorrentClientProcessStartInfoProvider processStartInfoProvider )
2934 {
30- DebugLogger . LogConstructor ( ) ;
31-
3235 _processStartInfoProvider = processStartInfoProvider ;
3336
37+ _logger = PatcherLogManager . DefaultLogger ;
3438 _process = StartProcess ( ) ;
3539 _stdOutput = CreateStdOutputStream ( ) ;
3640 _stdInput = CreateStdInputStream ( ) ;
3741 }
3842
39- /// <summary>
40- /// Executes the command and returns the result.
41- /// </summary>
42- public JToken ExecuteCommand ( string command )
43+ private static string ConvertPathForTorrentClient ( string path )
44+ {
45+ return path . Replace ( "\\ " , "/" ) . Replace ( " " , "\\ " ) ;
46+ }
47+
48+ public TorrentClientStatus GetStatus ( CancellationToken cancellationToken )
4349 {
44- Checks . ArgumentNotNull ( command , "command" ) ;
50+ try
51+ {
52+ _logger . LogDebug ( "Getting status..." ) ;
53+
54+ var result = ExecuteCommand < TorrentClientStatus > ( "status" , cancellationToken ) ;
4555
46- DebugLogger . Log ( string . Format ( "Executing command {0}" , command ) ) ;
56+ _logger . LogDebug ( "Getting status finished." ) ;
4757
48- WriteCommand ( command ) ;
49- string resultStr = ReadCommandResult ( ) ;
50- return ParseCommandResult ( resultStr ) ;
58+ return result ;
59+ }
60+ catch ( Exception e )
61+ {
62+ _logger . LogError ( "Getting status failed." , e ) ;
63+ throw ;
64+ }
5165 }
5266
53- private void WriteCommand ( string command )
67+ public void AddTorrent ( string torrentFilePath , string downloadDirectoryPath ,
68+ CancellationToken cancellationToken )
5469 {
55- _stdInput . WriteLine ( command ) ;
56- _stdInput . Flush ( ) ;
70+ try
71+ {
72+ _logger . LogDebug ( string . Format ( "Adding torrent from {0}..." , torrentFilePath ) ) ;
73+ _logger . LogTrace ( "downloadDirectoryPath = " + downloadDirectoryPath ) ;
74+
75+ var convertedTorrentFilePath = ConvertPathForTorrentClient ( torrentFilePath ) ;
76+ var convertedDownloadDirectoryPath = ConvertPathForTorrentClient ( downloadDirectoryPath ) ;
77+
78+ _logger . LogTrace ( "convertedTorrentFilePath = " + convertedTorrentFilePath ) ;
79+ _logger . LogTrace ( "convertedDownloadDirectoryPath = " + convertedDownloadDirectoryPath ) ;
80+
81+ var command = string . Format ( "add-torrent {0} {1}" , convertedTorrentFilePath ,
82+ convertedDownloadDirectoryPath ) ;
83+
84+ var result = ExecuteCommand < TorrentClientMessage > ( command , cancellationToken ) ;
85+
86+ _logger . LogTrace ( "result.Message = " + result . Message ) ;
87+ _logger . LogTrace ( "result.Status = " + result . Status ) ;
88+
89+ if ( result . Status != "ok" )
90+ {
91+ throw new AddTorrentFailureException (
92+ string . Format ( "Invalid add-torrent status: {0}" , result . Status ) ) ;
93+ }
94+
95+ _logger . LogDebug ( "Adding torrent finished." ) ;
96+ }
97+ catch ( Exception e )
98+ {
99+ _logger . LogError ( "Adding torrent failed." , e ) ;
100+ throw ;
101+ }
57102 }
58103
59- private JToken ParseCommandResult ( string resultStr )
104+ private TResult ExecuteCommand < TResult > ( [ NotNull ] string command , CancellationToken cancellationToken )
60105 {
61- return JToken . Parse ( resultStr ) ;
106+ if ( command == null ) throw new ArgumentNullException ( "command" ) ;
107+
108+ _logger . LogDebug ( string . Format ( "Executing command {0}" , command ) ) ;
109+
110+ _stdInput . WriteLine ( command ) ;
111+ _stdInput . Flush ( ) ;
112+
113+ string resultStr = ReadCommandResult ( cancellationToken ) ;
114+
115+ _logger . LogDebug ( "Command execution finished. Parsing result..." ) ;
116+ _logger . LogTrace ( "result = " + resultStr ) ;
117+
118+ var result = JsonConvert . DeserializeObject < TResult > ( resultStr ) ;
119+
120+ _logger . LogDebug ( "Parsing finished." ) ;
121+
122+ return result ;
62123 }
63124
64- private string ReadCommandResult ( )
125+ private string ReadCommandResult ( CancellationToken cancellationToken )
65126 {
66127 var str = new StringBuilder ( ) ;
67128
68129 while ( ! str . ToString ( ) . EndsWith ( "#=end" ) )
69130 {
70- ThrowIfProcessExited ( ) ;
131+ cancellationToken . ThrowIfCancellationRequested ( ) ;
71132
72- str . Append ( ( char ) _stdOutput . Read ( ) ) ;
133+ str . Append ( ( char ) _stdOutput . Read ( ) ) ;
73134 }
74135
75136 return str . ToString ( ) . Substring ( 0 , str . Length - 5 ) ;
76137 }
77138
78- private void ThrowIfProcessExited ( )
79- {
80- if ( _process . HasExited )
81- {
82- throw new TorrentClientException ( "torrent-client process has exited." ) ;
83- }
84- }
85-
86139 private StreamReader CreateStdOutputStream ( )
87140 {
88141 return new StreamReader ( _process . StandardOutput . BaseStream , CreateStdEncoding ( ) ) ;
@@ -93,16 +146,22 @@ private StreamWriter CreateStdInputStream()
93146 return new StreamWriter ( _process . StandardInput . BaseStream , CreateStdEncoding ( ) ) ;
94147 }
95148
96- private Encoding CreateStdEncoding ( )
149+ private static Encoding CreateStdEncoding ( )
97150 {
98151 return new UTF8Encoding ( false ) ;
99152 }
100153
101154 private Process StartProcess ( )
102155 {
103156 var processStartInfo = _processStartInfoProvider . GetProcessStartInfo ( ) ;
157+ _logger . LogTrace ( "processStartInfo.FileName" + processStartInfo . FileName ) ;
158+ _logger . LogTrace ( "processStartInfo.Arguments" + processStartInfo . Arguments ) ;
159+
160+ _logger . LogDebug ( "Starting torrent-client process..." ) ;
161+ var process = Process . Start ( processStartInfo ) ;
162+ _logger . LogDebug ( "torrent-client process started." ) ;
104163
105- return Process . Start ( processStartInfo ) ;
164+ return process ;
106165 }
107166
108167 public void Dispose ( )
@@ -116,38 +175,54 @@ public void Dispose()
116175 Dispose ( false ) ;
117176 }
118177
119- protected virtual void Dispose ( bool disposing )
178+ private void Dispose ( bool disposing )
120179 {
121- if ( _disposed )
180+ if ( _disposed )
122181 {
123182 return ;
124183 }
125184
126- DebugLogger . LogDispose ( ) ;
127-
128- if ( disposing )
185+ if ( disposing )
129186 {
130187 _stdOutput . Dispose ( ) ;
131188 _stdInput . Dispose ( ) ;
132189
133- if ( ! _process . HasExited )
190+ try
134191 {
135- try
192+ _logger . LogDebug ( "Killing torrent-client process..." ) ;
193+
194+ if ( ! _process . HasExited )
136195 {
137- DebugLogger . Log ( "Killing torrent client process ...") ;
196+ _logger . LogDebug ( "Sending kill request and waiting one second ...") ;
138197 _process . Kill ( ) ;
139198 _process . WaitForExit ( 1000 ) ;
140- DebugLogger . Log ( "Torrent client process has been killed." ) ;
199+ if ( _process . HasExited )
200+ {
201+ _logger . LogDebug ( "torrent-client process killed." ) ;
202+ }
203+ else
204+ {
205+ _logger . LogWarning (
206+ "torrent-client process hasn't been killed. Ignoring in order to not freeze application execution." ) ;
207+ }
141208 }
142- catch ( Exception exception )
209+ else
143210 {
144- DebugLogger . LogWarning ( "Killing torrent client process has failed: an exception has occured." ) ;
145- DebugLogger . LogException ( exception ) ;
211+ _logger . LogDebug ( "torrent-client process is already killed." ) ;
146212 }
147213 }
214+ catch ( Exception e )
215+ {
216+ _logger . LogError ( "Killing torrent-client process failed." , e ) ;
217+ throw ;
218+ }
219+ finally
220+ {
221+ _process . Dispose ( ) ;
222+ }
148223 }
149224
150225 _disposed = true ;
151226 }
152227 }
153- }
228+ }
0 commit comments