source: trunk/macosx/Torrent.m @ 536

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

In minimal view show ratio when seeding.

  • Property svn:keywords set to Date Rev Author Id
File size: 18.2 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 536 2006-07-06 00:19:38Z 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 waitToStart: (NSNumber *) waitToStart
34        orderValue: (NSNumber *) orderValue;
35
36- (void) trashFile: (NSString *) path;
37
38@end
39
40
41@implementation Torrent
42
43- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
44{
45    self = [self initWithHash: nil path: path lib: lib privateTorrent: nil publicTorrent: nil
46            date: nil stopRatioSetting: nil ratioLimit: nil waitToStart: nil orderValue: 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                waitToStart: [history objectForKey: @"WaitToStart"]
66                orderValue: [history objectForKey: @"OrderValue"]];
67   
68    if (self)
69    {
70        NSString * downloadFolder;
71        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
72            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath];
73        [self setDownloadFolder: downloadFolder];
74
75        NSString * paused;
76        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
77            tr_torrentStart(fHandle);
78    }
79    return self;
80}
81
82- (NSDictionary *) history
83{
84    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
85                    [NSNumber numberWithBool: fPrivateTorrent], @"PrivateCopy",
86                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
87                    [self downloadFolder], @"DownloadFolder",
88                    [self isActive] ? @"NO" : @"YES", @"Paused",
89                    [self date], @"Date",
90                    [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
91                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
92                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
93                    [self orderValue], @"OrderValue", nil];
94           
95    if (fPrivateTorrent)
96        [history setObject: [self hashString] forKey: @"TorrentHash"];
97
98    if (fPublicTorrent)
99        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
100   
101    return history;
102}
103
104- (void) dealloc
105{
106    if (fHandle)
107    {
108        tr_torrentClose(fLib, fHandle);
109       
110        if (fPublicTorrentLocation)
111            [fPublicTorrentLocation release];
112       
113        [fDate release];
114       
115        [fIcon release];
116        [fIconFlipped release];
117        [fIconSmall release];
118       
119        [fProgressString release];
120        [fStatusString release];
121        [fShortStatusString release];
122    }
123    [super dealloc];
124}
125
126- (void) setDownloadFolder: (NSString *) path
127{
128    tr_torrentSetFolder(fHandle, [path UTF8String]);
129}
130
131- (NSString *) downloadFolder
132{
133    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
134}
135
136- (void) getAvailability: (int8_t *) tab size: (int) size
137{
138    tr_torrentAvailability(fHandle, tab, size);
139}
140
141- (void) update
142{
143    fStat = tr_torrentStat(fHandle);
144   
145    if ([self isSeeding] && ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
146            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
147            && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
148    {
149        [self stopTransfer];
150        [self setStopRatioSetting: RATIO_NO_CHECK];
151        fFinishedSeeding = YES;
152       
153        fStat = tr_torrentStat(fHandle);
154       
155        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentSettingChange" object: self];
156    }
157
158    [fProgressString setString: @""];
159    if ([self progress] < 1.0)
160        [fProgressString appendFormat: @"%@ of %@ (%.2f%%)", [NSString stringForFileSize:
161                [self downloadedValid]], [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
162    else
163        [fProgressString appendFormat: @"%@, uploaded %@ (ratio: %@)", [NSString stringForFileSize:
164                [self size]], [NSString stringForFileSize: [self uploadedTotal]],
165                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]];
166
167    switch (fStat->status)
168    {
169        NSString * tempString;
170   
171        case TR_STATUS_PAUSE:
172            if (fFinishedSeeding)
173                tempString = @"Seeding complete";
174            else if (fWaitToStart && [[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Wait"])
175                tempString = [@"Waiting to start" stringByAppendingEllipsis];
176            else
177                tempString = @"Paused";
178           
179            [fStatusString setString: tempString];
180            [fShortStatusString setString: tempString];
181           
182            break;
183
184        case TR_STATUS_CHECK:
185            tempString = [@"Checking existing files" stringByAppendingEllipsis];
186           
187            [fStatusString setString: tempString];
188            [fShortStatusString setString: tempString];
189           
190            break;
191
192        case TR_STATUS_DOWNLOAD:
193            [fStatusString setString: @""];
194            [fStatusString appendFormat:
195                @"Downloading from %d of %d peer%s", [self peersUploading], [self totalPeers],
196                [self totalPeers] == 1 ? "" : "s"];
197           
198            int eta = [self eta];
199            if (eta < 0)
200                [fProgressString appendString: @" - remaining time unknown"];
201            else if (eta < 60)
202                [fProgressString appendFormat: @" - %d sec remaining", eta];
203            else if (eta < 3600) //60 * 60
204            [fProgressString appendFormat: @" - %d min %02d sec remaining", eta / 60, eta % 60];
205            else if (eta < 86400) //24 * 60 * 60
206                [fProgressString appendFormat: @" - %d hr %02d min remaining", eta / 3600, (eta / 60) % 60];
207            else
208                [fProgressString appendFormat: @" - %d day%s %d hr remaining",
209                                            eta / 86400, eta / 86400 == 1 ? "" : "s", (eta / 3600) % 24];
210           
211            break;
212
213        case TR_STATUS_SEED:
214            [fStatusString setString: @""];
215            [fStatusString appendFormat:
216                @"Seeding to %d of %d peer%s",
217                [self peersDownloading], [self totalPeers], [self totalPeers] == 1 ? "" : "s"];
218           
219            break;
220
221        case TR_STATUS_STOPPING:
222            tempString = [@"Stopping" stringByAppendingEllipsis];
223       
224            [fStatusString setString: tempString];
225            [fShortStatusString setString: tempString];
226           
227            break;
228    }
229   
230    if( fStat->error & TR_ETRACKER )
231        [fStatusString setString: [@"Error: " stringByAppendingString:
232                        [NSString stringWithUTF8String: fStat->trackerError]]];
233
234    if ([self isActive])
235    {
236        NSString * stringToAppend = @"";
237        if ([self progress] < 1.0)
238        {
239            stringToAppend = [NSString stringWithFormat: @"DL: %@, ", [NSString stringForSpeed: [self downloadRate]]];
240            [fShortStatusString setString: @""];
241        }
242        else
243            [fShortStatusString setString: [NSString stringWithFormat: @"ratio: %@, ",
244                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]]];
245       
246        stringToAppend = [stringToAppend stringByAppendingString: [@"UL: " stringByAppendingString:
247                                                [NSString stringForSpeed: [self uploadRate]]]];
248
249        [fStatusString appendFormat: @" - %@", stringToAppend];
250        [fShortStatusString appendString: stringToAppend];
251    }
252}
253
254- (void) startTransfer
255{
256    if (![self isActive])
257    {
258        tr_torrentStart(fHandle);
259
260        fFinishedSeeding = NO;
261        fWaitToStart = NO;
262       
263        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentSettingChange" object: self];
264    }
265}
266
267- (void) stopTransfer
268{
269    if ([self isActive])
270    {
271        BOOL wasSeeding = [self isSeeding];
272   
273        tr_torrentStop(fHandle);
274
275        if (!wasSeeding)
276            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
277    }
278}
279
280- (void) stopTransferForQuit
281{
282    if ([self isActive])
283        tr_torrentStop(fHandle);
284}
285
286- (void) removeForever
287{
288    if (fPrivateTorrent)
289        tr_torrentRemoveSaved(fHandle);
290}
291
292- (void) sleep
293{
294    if ((fResumeOnWake = [self isActive]))
295        tr_torrentStop(fHandle);
296}
297
298- (void) wakeUp
299{
300    if (fResumeOnWake)
301        tr_torrentStart(fHandle);
302}
303
304- (float) ratio
305{
306    float downloaded = [self downloadedTotal];
307    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
308}
309
310- (int) stopRatioSetting
311{
312        return fStopRatioSetting;
313}
314
315- (void) setStopRatioSetting: (int) setting
316{
317    fStopRatioSetting = setting;
318}
319
320- (float) ratioLimit
321{
322    return fRatioLimit;
323}
324
325- (void) setRatioLimit: (float) limit
326{
327    if (limit >= 0)
328        fRatioLimit = limit;
329}
330
331- (void) setWaitToStart: (BOOL) wait
332{
333    fWaitToStart = wait;
334}
335
336- (BOOL) waitingToStart
337{
338    return fWaitToStart;
339}
340
341- (void) revealData
342{
343    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
344}
345
346- (void) trashData
347{
348    [self trashFile: [self dataLocation]];
349}
350
351- (void) trashTorrent
352{
353    if (fPublicTorrent)
354        [self trashFile: [self publicTorrentLocation]];
355}
356
357- (NSImage *) icon
358{
359    return fIcon;
360}
361
362- (NSImage *) iconFlipped
363{
364    return fIconFlipped;
365}
366
367- (NSImage *) iconSmall
368{
369    return fIconSmall;
370}
371
372- (NSString *) name
373{
374    return [NSString stringWithUTF8String: fInfo->name];
375}
376
377- (uint64_t) size
378{
379    return fInfo->totalSize;
380}
381
382- (NSString *) tracker
383{
384    return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
385}
386
387- (NSString *) announce
388{
389    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
390}
391
392- (int) pieceSize
393{
394    return fInfo->pieceSize;
395}
396
397- (int) pieceCount
398{
399    return fInfo->pieceCount;
400}
401
402- (NSString *) hashString
403{
404    return [NSString stringWithUTF8String: fInfo->hashString];
405}
406
407- (NSString *) torrentLocation
408{
409    return [NSString stringWithUTF8String: fInfo->torrent];
410}
411
412- (NSString *) publicTorrentLocation
413{
414    return fPublicTorrentLocation;
415}
416
417- (NSString *) torrentLocationString
418{
419    return fPrivateTorrent ? @"Transmission Support Folder"
420            : [fPublicTorrentLocation stringByAbbreviatingWithTildeInPath];
421}
422
423- (NSString *) dataLocation
424{
425    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
426}
427
428- (BOOL) publicTorrent
429{
430    return fPublicTorrent;
431}
432
433- (BOOL) privateTorrent
434{
435    return fPrivateTorrent;
436}
437
438- (NSString *) stateString
439{
440    switch( fStat->status )
441    {
442        case TR_STATUS_PAUSE:
443            return @"Paused";
444            break;
445
446        case TR_STATUS_CHECK:
447            return [@"Checking existing files" stringByAppendingEllipsis];
448            break;
449
450        case TR_STATUS_DOWNLOAD:
451            return @"Downloading";
452            break;
453
454        case TR_STATUS_SEED:
455            return @"Seeding";
456            break;
457
458        case TR_STATUS_STOPPING:
459            return [@"Stopping" stringByAppendingEllipsis];
460            break;
461       
462        default:
463            return @"N/A";
464    }
465}
466
467- (float) progress
468{
469    return fStat->progress;
470}
471
472- (int) eta
473{
474    return fStat->eta;
475}
476
477- (BOOL) isActive
478{
479    return fStat->status & TR_STATUS_ACTIVE;
480}
481
482- (BOOL) isSeeding
483{
484    return fStat->status == TR_STATUS_SEED;
485}
486
487- (BOOL) isPaused
488{
489    return fStat->status == TR_STATUS_PAUSE;
490}
491
492- (BOOL) justFinished
493{
494    return tr_getFinished(fHandle);
495}
496
497- (NSString *) progressString
498{
499    return fProgressString;
500}
501
502- (NSString *) statusString
503{
504    return fStatusString;
505}
506
507- (NSString *) shortStatusString
508{
509    return fShortStatusString;
510}
511
512- (int) seeders
513{
514    return fStat->seeders;
515}
516
517- (int) leechers
518{
519    return fStat->leechers;
520}
521
522- (int) totalPeers
523{
524    return fStat->peersTotal;
525}
526
527//peers uploading to you
528- (int) peersUploading
529{
530    return fStat->peersUploading;
531}
532
533//peers downloading from you
534- (int) peersDownloading
535{
536    return fStat->peersDownloading;
537}
538
539- (float) downloadRate
540{
541    return fStat->rateDownload;
542}
543
544- (float) uploadRate
545{
546    return fStat->rateUpload;
547}
548
549- (float) downloadedValid
550{
551    return [self progress] * [self size];
552}
553
554- (uint64_t) downloadedTotal
555{
556    return fStat->downloaded;
557}
558
559- (uint64_t) uploadedTotal
560{
561    return fStat->uploaded;
562}
563
564- (NSNumber *) orderValue
565{
566    return [NSNumber numberWithInt: fOrderValue];
567}
568
569- (void) setOrderValue: (int) orderValue
570{
571    fOrderValue = orderValue;
572}
573
574- (NSArray *) fileList
575{
576    int count = fInfo->fileCount, i;
577    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
578    for (i = 0; i < count; i++)
579        [files addObject: [[self downloadFolder] stringByAppendingPathComponent:
580            [NSString stringWithUTF8String: fInfo->files[i].name]]];
581    return files;
582}
583
584- (NSDate *) date
585{
586    return fDate;
587}
588
589- (NSNumber *) stateSortKey
590{
591    if (![self isActive])
592        return [NSNumber numberWithInt: 0];
593    else if ([self isSeeding])
594        return [NSNumber numberWithInt: 1];
595    else
596        return [NSNumber numberWithInt: 2];
597}
598
599- (NSNumber *) progressSortKey
600{
601    return [NSNumber numberWithFloat: [self progress]];
602}
603
604@end
605
606
607@implementation Torrent (Private)
608
609//if a hash is given, attempt to load that; otherwise, attempt to open file at path
610- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
611        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
612        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
613        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
614        orderValue: (NSNumber *) orderValue
615{
616    if (!(self = [super init]))
617        return nil;
618
619    fLib = lib;
620    fDefaults = [NSUserDefaults standardUserDefaults];
621
622    fPrivateTorrent = privateTorrent ? [privateTorrent boolValue] : [fDefaults boolForKey: @"SavePrivateTorrent"];
623    fPublicTorrent = !fPrivateTorrent || (publicTorrent ? [publicTorrent boolValue]
624                                            : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
625
626    int error;
627    if (fPrivateTorrent && hashString)
628        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
629   
630    if (!fHandle && path)
631        fHandle = tr_torrentInit(fLib, [path UTF8String], fPrivateTorrent ? TR_FSAVEPRIVATE : 0, & error);
632
633    if (!fHandle)
634    {
635        [self release];
636        return nil;
637    }
638   
639    fInfo = tr_torrentInfo( fHandle );
640
641    if (fPublicTorrent)
642        fPublicTorrentLocation = [path retain];
643
644    fDate = date ? [date retain] : [[NSDate alloc] init];
645   
646    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
647    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
648    fFinishedSeeding = NO;
649   
650    fWaitToStart = waitToStart ? [waitToStart boolValue]
651                    : ![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Manual"];
652    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
653   
654    NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
655    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
656    [fIcon retain];
657   
658    fIconFlipped = [fIcon copy];
659    [fIconFlipped setFlipped: YES];
660   
661    fIconSmall = [fIconFlipped copy];
662    [fIconSmall setScalesWhenResized: YES];
663    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
664
665    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
666    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
667    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 50];
668
669    [self update];
670    return self;
671}
672
673- (void) trashFile: (NSString *) path
674{
675    //attempt to move to trash
676    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
677            source: [path stringByDeletingLastPathComponent] destination: @""
678            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
679    {
680        //if cannot trash, just delete it (will work if it is on a remote volume)
681        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
682            NSLog([@"Could not trash " stringByAppendingString: path]);
683    }
684}
685
686@end
Note: See TracBrowser for help on using the repository browser.