source: trunk/macosx/Torrent.m @ 700

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

Capitalization change.

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