source: trunk/macosx/Torrent.m @ 419

Last change on this file since 419 was 419, checked in by livings124, 16 years ago

Now that sorting by stat sorts by progress, it should be resorted more often.

  • Property svn:keywords set to Date Rev Author Id
File size: 15.5 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 419 2006-06-20 19:20:36Z livings124 $
3 *
4 * Copyright (c) 2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "Torrent.h"
26#import "StringAdditions.h"
27
28@interface Torrent (Private)
29
30- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
31        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
32        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
33        ratioLimit: (NSNumber *) ratioLimit;
34
35- (void) trashFile: (NSString *) path;
36
37@end
38
39
40@implementation Torrent
41
42- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
43{
44    self = [self initWithHash: nil path: path lib: lib
45            privateTorrent: nil publicTorrent: nil
46            date: nil stopRatioSetting: nil ratioLimit: nil];
47   
48    if (self)
49    {
50        if (!fPublicTorrent)
51            [self trashFile: path];
52    }
53    return self;
54}
55
56- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
57{
58    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
59                path: [history objectForKey: @"TorrentPath"] lib: lib
60                privateTorrent: [history objectForKey: @"PrivateCopy"]
61                publicTorrent: [history objectForKey: @"PublicCopy"]
62                date: [history objectForKey: @"Date"]
63                stopRatioSetting: [history objectForKey: @"StopRatioSetting"]
64                ratioLimit: [history objectForKey: @"RatioLimit"]];
65   
66    if (self)
67    {
68        NSString * downloadFolder;
69        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
70            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"]
71                                stringByExpandingTildeInPath];
72        [self setDownloadFolder: downloadFolder];
73
74        NSString * paused;
75        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
76            [self start];
77    }
78    return self;
79}
80
81- (NSDictionary *) history
82{
83    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
84                    [NSNumber numberWithBool: fPrivateTorrent], @"PrivateCopy",
85                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
86                    [self downloadFolder], @"DownloadFolder",
87                    [self isActive] ? @"NO" : @"YES", @"Paused",
88                    [self date], @"Date",
89                    [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
90                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit", nil];
91           
92    if (fPrivateTorrent)
93        [history setObject: [self hashString] forKey: @"TorrentHash"];
94
95    if (fPublicTorrent)
96        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
97   
98    return history;
99}
100
101- (void) dealloc
102{
103    if( fHandle )
104    {
105        tr_torrentClose( fLib, fHandle );
106       
107        if (fPublicTorrentLocation)
108            [fPublicTorrentLocation release];
109       
110        [fDate release];
111        [fIcon release];
112        [fIconFlipped release];
113        [fProgressString release];
114        [fStatusString release];
115    }
116    [super dealloc];
117}
118
119- (void) setDownloadFolder: (NSString *) path
120{
121    tr_torrentSetFolder( fHandle, [path UTF8String] );
122}
123
124- (NSString *) downloadFolder
125{
126    return [NSString stringWithUTF8String: tr_torrentGetFolder( fHandle )];
127}
128
129- (void) getAvailability: (int8_t *) tab size: (int) size
130{
131    tr_torrentAvailability( fHandle, tab, size );
132}
133
134- (void) update
135{
136    fStat = tr_torrentStat( fHandle );
137   
138    if ([self isSeeding]
139        && ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
140            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
141            && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
142    {
143        [self stop];
144        [self setStopRatioSetting: RATIO_NO_CHECK];
145        fFinishedSeeding = YES;
146       
147        fStat = tr_torrentStat( fHandle );
148       
149        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentRatioChanged" object: self];
150    }
151
152    [fProgressString setString: @""];
153    if ([self progress] < 1.0)
154        [fProgressString appendFormat: @"%@ of %@ (%.2f%%)", [NSString stringForFileSize:
155                [self downloadedValid]], [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
156    else
157        [fProgressString appendFormat: @"%@, uploaded %@ (ratio: %@)", [NSString stringForFileSize:
158                [self size]], [NSString stringForFileSize: [self uploadedTotal]],
159                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]];
160
161    switch( fStat->status )
162    {
163        case TR_STATUS_PAUSE:
164            [fStatusString setString: fFinishedSeeding ? @"Seeding Complete" : @"Paused"];
165            break;
166
167        case TR_STATUS_CHECK:
168            [fStatusString setString: [@"Checking existing files" stringByAppendingEllipsis]];
169            break;
170
171        case TR_STATUS_DOWNLOAD:
172            [fStatusString setString: @""];
173            [fStatusString appendFormat:
174                @"Downloading from %d of %d peer%s",
175                [self peersUploading], [self totalPeers],
176                ([self totalPeers] == 1) ? "" : "s"];
177           
178            int eta = fStat->eta;
179            if (eta < 0)
180                [fProgressString appendString: @" - remaining time unknown"];
181            else
182            {
183                if (eta < 60)
184                    [fProgressString appendFormat: @" - %d sec remaining", eta];
185                else if (eta < 3600)
186                    [fProgressString appendFormat: @" - %d min %02d sec remaining",
187                                                    eta / 60, eta % 60];
188                else
189                    [fProgressString appendFormat: @" - %d hr %02d min remaining",
190                                                    eta / 3600, (eta / 60) % 60];
191            }
192            break;
193
194        case TR_STATUS_SEED:
195            [fStatusString setString: @""];
196            [fStatusString appendFormat:
197                @"Seeding to %d of %d peer%s",
198                [self peersDownloading], [self totalPeers],
199                ([self totalPeers] == 1) ? "" : "s"];
200            break;
201
202        case TR_STATUS_STOPPING:
203            [fStatusString setString: [@"Stopping" stringByAppendingEllipsis]];
204            break;
205    }
206   
207    if( fStat->error & TR_ETRACKER )
208    {
209        [fStatusString setString: [@"Error: " stringByAppendingString:
210            [NSString stringWithUTF8String: fStat->trackerError]]];
211    }
212
213    if ([self isActive])
214    {
215        [fStatusString appendString: @" - "];
216        if ([self progress] < 1.0)
217            [fStatusString appendFormat: @"DL: %@, ", [NSString stringForSpeed: [self downloadRate]]];
218        [fStatusString appendString: [@"UL: " stringByAppendingString:
219                                                [NSString stringForSpeed: [self uploadRate]]]];
220    }
221}
222
223- (void) start
224{
225    if( fStat->status & TR_STATUS_INACTIVE )
226    {
227        tr_torrentStart( fHandle );
228        fFinishedSeeding = NO;
229    }
230}
231
232- (void) stop
233{
234    if( fStat->status & TR_STATUS_ACTIVE )
235    {
236        tr_torrentStop( fHandle );
237    }
238}
239
240- (void) removeForever
241{
242    if (fInfo->flags & TR_FSAVEPRIVATE)
243        tr_torrentRemoveSaved(fHandle);
244}
245
246- (void) sleep
247{
248    if( ( fResumeOnWake = ( fStat->status & TR_STATUS_ACTIVE ) ) )
249    {
250        [self stop];
251    }
252}
253
254- (void) wakeUp
255{
256    if( fResumeOnWake )
257    {
258        [self start];
259    }
260}
261
262- (float) ratio
263{
264    float downloaded = [self downloadedTotal];
265    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
266}
267
268- (int) stopRatioSetting
269{
270        return fStopRatioSetting;
271}
272
273- (void) setStopRatioSetting: (int) setting
274{
275    fStopRatioSetting = setting;
276}
277
278- (float) ratioLimit
279{
280    return fRatioLimit;
281}
282
283- (void) setRatioLimit: (float) limit
284{
285    if (limit >= 0)
286        fRatioLimit = limit;
287}
288
289- (void) reveal
290{
291    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation]
292        inFileViewerRootedAtPath: nil];
293}
294
295- (void) trashData
296{
297    [self trashFile: [self dataLocation]];
298}
299
300- (void) trashTorrent
301{
302    if (fPublicTorrent)
303        [self trashFile: [self publicTorrentLocation]];
304}
305
306- (NSImage *) icon
307{
308    return fIcon;
309}
310
311- (NSImage *) iconFlipped
312{
313    return fIconFlipped;
314}
315
316- (NSString *) name
317{
318    return [NSString stringWithUTF8String: fInfo->name];
319}
320
321- (uint64_t) size
322{
323    return fInfo->totalSize;
324}
325
326- (NSString *) tracker
327{
328    return [NSString stringWithFormat: @"%s:%d",
329            fInfo->trackerAddress, fInfo->trackerPort];
330}
331
332- (NSString *) announce
333{
334    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
335}
336
337- (int) pieceSize
338{
339    return fInfo->pieceSize;
340}
341
342- (int) pieceCount
343{
344    return fInfo->pieceCount;
345}
346
347- (NSString *) hashString
348{
349    return [NSString stringWithUTF8String: fInfo->hashString];
350}
351
352- (NSString *) torrentLocation
353{
354    return [NSString stringWithUTF8String: fInfo->torrent];
355}
356
357- (NSString *) publicTorrentLocation
358{
359    return fPublicTorrentLocation;
360}
361
362- (NSString *) torrentLocationString
363{
364    return fPrivateTorrent ? @"Transmission Support Folder"
365            : [fPublicTorrentLocation stringByAbbreviatingWithTildeInPath];
366}
367
368- (NSString *) dataLocation
369{
370    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
371}
372
373- (BOOL) publicTorrent
374{
375    return fPublicTorrent;
376}
377
378- (BOOL) privateTorrent
379{
380    return fPrivateTorrent;
381}
382
383/*- (NSString *) state
384{
385    switch( fStat->status )
386    {
387        case TR_STATUS_PAUSE:
388            return @"Paused";
389            break;
390
391        case TR_STATUS_CHECK:
392            return [@"Checking existing files" stringByAppendingEllipsis];
393            break;
394
395        case TR_STATUS_DOWNLOAD:
396            return @"Downloading";
397            break;
398
399        case TR_STATUS_SEED:
400            return @"Seeding";
401            break;
402
403        case TR_STATUS_STOPPING:
404            return [@"Stopping" stringByAppendingEllipsis];
405            break;
406       
407        default:
408            return @"N/A";
409    }
410}*/
411
412- (float) progress
413{
414    return fStat->progress;
415}
416
417- (BOOL) isActive
418{
419    return ( fStat->status & TR_STATUS_ACTIVE );
420}
421
422- (BOOL) isSeeding
423{
424    return ( fStat->status == TR_STATUS_SEED );
425}
426
427- (BOOL) isPaused
428{
429    return ( fStat->status == TR_STATUS_PAUSE );
430}
431
432- (BOOL) justFinished
433{
434    return tr_getFinished( fHandle );
435}
436
437- (NSString *) progressString
438{
439    return fProgressString;
440}
441
442- (NSString *) statusString
443{
444    return fStatusString;
445}
446
447- (int) seeders
448{
449    return fStat->seeders;
450}
451
452- (int) leechers
453{
454    return fStat->leechers;
455}
456
457- (int) totalPeers
458{
459    return fStat->peersTotal;
460}
461
462//peers uploading to you
463- (int) peersUploading
464{
465    return fStat->peersUploading;
466}
467
468//peers downloading from you
469- (int) peersDownloading
470{
471    return fStat->peersDownloading;
472}
473
474- (float) downloadRate
475{
476    return fStat->rateDownload;
477}
478
479- (float) uploadRate
480{
481    return fStat->rateUpload;
482}
483
484- (float) downloadedValid
485{
486    return [self progress] * [self size];
487}
488
489- (uint64_t) downloadedTotal
490{
491    return fStat->downloaded;
492}
493
494- (uint64_t) uploadedTotal
495{
496    return fStat->uploaded;
497}
498
499- (NSArray *) fileList
500{
501    int count = fInfo->fileCount, i;
502    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
503    for (i = 0; i < count; i++)
504        [files addObject: [[self downloadFolder] stringByAppendingPathComponent:
505            [NSString stringWithUTF8String: fInfo->files[i].name]]];
506    return files;
507}
508
509- (NSDate *) date
510{
511    return fDate;
512}
513
514- (NSNumber *) stateSortKey
515{
516    if (fStat->status & TR_STATUS_INACTIVE)
517        return [NSNumber numberWithInt: 0];
518    else if (fStat->status == TR_STATUS_SEED)
519        return [NSNumber numberWithInt: 1];
520    else
521        return [NSNumber numberWithInt: 2];
522}
523
524- (NSNumber *) progressSortKey
525{
526    return [NSNumber numberWithFloat: [self progress]];
527}
528
529@end
530
531
532@implementation Torrent (Private)
533
534//if a hash is given, attempt to load that; otherwise, attempt to open file at path
535- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
536        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
537        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
538        ratioLimit: (NSNumber *) ratioLimit
539{
540    if (!(self = [super init]))
541        return nil;
542
543    fLib = lib;
544    fDefaults = [NSUserDefaults standardUserDefaults];
545
546    fPrivateTorrent = privateTorrent ? [privateTorrent boolValue] : [fDefaults boolForKey: @"SavePrivateTorrent"];
547    fPublicTorrent = !fPrivateTorrent || (publicTorrent ? [publicTorrent boolValue]
548                                            : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
549
550    int error;
551    if (fPrivateTorrent && hashString)
552        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
553   
554    if (!fHandle && path)
555        fHandle = tr_torrentInit(fLib, [path UTF8String], fPrivateTorrent ? TR_FSAVEPRIVATE : 0, & error);
556
557    if (!fHandle)
558    {
559        [self release];
560        return nil;
561    }
562   
563    fInfo = tr_torrentInfo( fHandle );
564
565    if (fPublicTorrent)
566        fPublicTorrentLocation = [path retain];
567
568    fDate = date ? [date retain] : [[NSDate alloc] init];
569   
570    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
571    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
572    fFinishedSeeding = NO;
573   
574    NSString * fileType = fInfo->multifile ?
575        NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
576    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
577    [fIcon retain];
578   
579    fIconFlipped = [fIcon copy];
580    [fIconFlipped setFlipped: YES];
581
582    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
583    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
584
585    [self update];
586    return self;
587}
588
589- (void) trashFile: (NSString *) path
590{
591    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
592            source: [path stringByDeletingLastPathComponent] destination: @""
593            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
594    {
595        /* We can't move it to the trash, let's try just to delete it
596           (will work if it is on a remote volume) */
597        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
598            NSLog([@"Could not trash " stringByAppendingString: path]);
599    }
600}
601
602@end
Note: See TracBrowser for help on using the repository browser.