source: trunk/macosx/Torrent.m @ 1054

Last change on this file since 1054 was 1054, checked in by livings124, 15 years ago

remove option to save a private torrent copy...just do it automatically

  • Property svn:keywords set to Date Rev Author Id
File size: 31.8 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 1054 2006-11-05 19:08: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#define BAR_HEIGHT 12.0
29
30#define MAX_PIECES 324
31#define BLANK_PIECE -99
32
33@interface Torrent (Private)
34
35- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
36        publicTorrent: (NSNumber *) publicTorrent
37        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
38        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
39        orderValue: (NSNumber *) orderValue;
40
41- (NSImage *) advancedBar;
42
43- (void) trashFile: (NSString *) path;
44
45@end
46
47@implementation Torrent
48
49// Used to optimize drawing. They contain packed RGBA pixels for every color needed.
50#define BE OSSwapBigToHostConstInt32
51
52static uint32_t kRed   = BE(0xFF6450FF), //255, 100, 80
53                kBlue = BE(0x50A0FFFF), //80, 160, 255
54                kBlue2 = BE(0x1E46B4FF), //30, 70, 180
55                kGray  = BE(0x969696FF), //150, 150, 150
56                kGreen1 = BE(0x99FFCCFF), //153, 255, 204
57                kGreen2 = BE(0x66FF99FF), //102, 255, 153
58                kGreen3 = BE(0x00FF66FF), //0, 255, 102
59                kWhite = BE(0xFFFFFFFF); //255, 255, 255
60
61- (id) initWithPath: (NSString *) path lib: (tr_handle_t *) lib
62{
63    self = [self initWithHash: nil path: path lib: lib publicTorrent: nil
64            date: nil stopRatioSetting: nil ratioLimit: nil waitToStart: nil orderValue: nil];
65   
66    if (self)
67    {
68        fUseIncompleteFolder = [fDefaults boolForKey: @"UseIncompleteDownloadFolder"];
69        fIncompleteFolder = [[fDefaults stringForKey: @"IncompleteDownloadFolder"] copy];
70       
71        if (!fPublicTorrent)
72            [self trashFile: path];
73    }
74    return self;
75}
76
77- (id) initWithHistory: (NSDictionary *) history lib: (tr_handle_t *) lib
78{
79    self = [self initWithHash: [history objectForKey: @"TorrentHash"]
80                path: [history objectForKey: @"TorrentPath"] lib: lib
81                publicTorrent: [history objectForKey: @"PublicCopy"]
82                date: [history objectForKey: @"Date"]
83                stopRatioSetting: [history objectForKey: @"StopRatioSetting"]
84                ratioLimit: [history objectForKey: @"RatioLimit"]
85                waitToStart: [history objectForKey: @"WaitToStart"]
86                orderValue: [history objectForKey: @"OrderValue"]];
87   
88    if (self)
89    {
90        //download folders
91        NSString * downloadFolder;
92        if (!(downloadFolder = [history objectForKey: @"DownloadFolder"]))
93            downloadFolder = [[fDefaults stringForKey: @"DownloadFolder"] stringByExpandingTildeInPath];
94       
95        NSNumber * useIncompleteFolder;
96        if ((useIncompleteFolder = [history objectForKey: @"UseIncompleteFolder"]))
97        {
98            if ((fUseIncompleteFolder = [useIncompleteFolder boolValue]))
99            {
100                NSString * incompleteFolder;
101                if (incompleteFolder = [history objectForKey: @"IncompleteFolder"])
102                    fIncompleteFolder = [incompleteFolder copy];
103                else
104                    fIncompleteFolder = [[[fDefaults stringForKey: @"IncompleteDownloadFolder"]
105                                            stringByExpandingTildeInPath] copy];
106            }
107        }
108        else
109            fUseIncompleteFolder = NO;
110       
111        [self setDownloadFolder: downloadFolder];
112
113        NSString * paused;
114        if (!(paused = [history objectForKey: @"Paused"]) || [paused isEqualToString: @"NO"])
115        {
116            fStat = tr_torrentStat(fHandle);
117            [self startTransfer];
118        }
119    }
120    return self;
121}
122
123- (NSDictionary *) history
124{
125    NSMutableDictionary * history = [NSMutableDictionary dictionaryWithObjectsAndKeys:
126                    [NSNumber numberWithBool: fPublicTorrent], @"PublicCopy",
127                    [self hashString], @"TorrentHash",
128                    fDownloadFolder, @"DownloadFolder",
129                    [NSNumber numberWithBool: fUseIncompleteFolder], @"UseIncompleteFolder",
130                    [self isActive] ? @"NO" : @"YES", @"Paused",
131                    [self date], @"Date",
132                    [NSNumber numberWithInt: fStopRatioSetting], @"StopRatioSetting",
133                    [NSNumber numberWithFloat: fRatioLimit], @"RatioLimit",
134                    [NSNumber numberWithBool: fWaitToStart], @"WaitToStart",
135                    [self orderValue], @"OrderValue", nil];
136   
137    if (fUseIncompleteFolder)
138        [history setObject: fIncompleteFolder forKey: @"IncompleteFolder"];
139
140    if (fPublicTorrent)
141        [history setObject: [self publicTorrentLocation] forKey: @"TorrentPath"];
142   
143    return history;
144}
145
146- (void) dealloc
147{
148    if (fHandle)
149    {
150        tr_torrentClose(fLib, fHandle);
151       
152        if (fDownloadFolder)
153            [fDownloadFolder release];
154        if (fIncompleteFolder)
155            [fIncompleteFolder release];
156       
157        if (fPublicTorrentLocation)
158            [fPublicTorrentLocation release];
159       
160        [fDate release];
161       
162        [fIcon release];
163        [fIconFlipped release];
164        [fIconSmall release];
165       
166        [fProgressString release];
167        [fStatusString release];
168        [fShortStatusString release];
169        [fRemainingTimeString release];
170       
171       
172        [fBitmap release];
173        free(fPieces);
174    }
175    [super dealloc];
176}
177
178- (void) setDownloadFolder: (NSString *) path
179{
180    fDownloadFolder = [path copy];
181   
182    if (!fUseIncompleteFolder || [[NSFileManager defaultManager] fileExistsAtPath:
183                                    [path stringByAppendingPathComponent: [self name]]])
184        tr_torrentSetFolder(fHandle, [path UTF8String]);
185    else
186        tr_torrentSetFolder(fHandle, [fIncompleteFolder UTF8String]);
187}
188
189- (NSString *) downloadFolder
190{
191    return [NSString stringWithUTF8String: tr_torrentGetFolder(fHandle)];
192}
193
194- (void) getAvailability: (int8_t *) tab size: (int) size
195{
196    tr_torrentAvailability(fHandle, tab, size);
197}
198
199- (void) getAmountFinished: (float *) tab size: (int) size
200{
201    tr_torrentAmountFinished(fHandle, tab, size);
202}
203
204- (void) update
205{
206    fStat = tr_torrentStat(fHandle);
207   
208    //notification when downloading finished
209    if ([self justFinished])
210    {
211        //move file from incomplete folder to download folder
212        if (fUseIncompleteFolder && ![[self downloadFolder] isEqualToString: fDownloadFolder])
213        {
214            tr_torrentStop(fHandle);
215            if ([[NSFileManager defaultManager] movePath: [[self downloadFolder] stringByAppendingPathComponent: [self name]]
216                                    toPath: [fDownloadFolder stringByAppendingPathComponent: [self name]] handler: nil])
217                tr_torrentSetFolder(fHandle, [fDownloadFolder UTF8String]);
218            tr_torrentStart(fHandle);
219        }
220       
221        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentFinishedDownloading" object: self];
222    }
223   
224    //check to stop for ratio
225    if ([self isSeeding] && ((fStopRatioSetting == RATIO_CHECK && [self ratio] >= fRatioLimit)
226            || (fStopRatioSetting == RATIO_GLOBAL && [fDefaults boolForKey: @"RatioCheck"]
227            && [self ratio] >= [fDefaults floatForKey: @"RatioLimit"])))
228    {
229        [self stopTransfer];
230        [self setStopRatioSetting: RATIO_NO_CHECK];
231        fFinishedSeeding = YES;
232       
233        fStat = tr_torrentStat(fHandle);
234       
235        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStoppedForRatio" object: self];
236    }
237
238    [fProgressString setString: @""];
239    if ([self progress] < 1.0)
240        [fProgressString appendFormat: NSLocalizedString(@"%@ of %@ (%.2f%%)", "Torrent -> progress string"),
241                            [NSString stringForFileSize: [self downloadedValid]],
242                            [NSString stringForFileSize: [self size]], 100.0 * [self progress]];
243    else
244        [fProgressString appendFormat: NSLocalizedString(@"%@, uploaded %@ (Ratio: %@)", "Torrent -> progress string"),
245                [NSString stringForFileSize: [self size]], [NSString stringForFileSize: [self uploadedTotal]],
246                [NSString stringForRatioWithDownload: [self downloadedTotal] upload: [self uploadedTotal]]];
247
248    switch (fStat->status)
249    {
250        NSString * tempString;
251   
252        case TR_STATUS_PAUSE:
253            if (fFinishedSeeding)
254                tempString = NSLocalizedString(@"Seeding complete", "Torrent -> status string");
255            else if (fWaitToStart)
256                tempString = [NSLocalizedString(@"Waiting to start", "Torrent -> status string") stringByAppendingEllipsis];
257            else
258                tempString = NSLocalizedString(@"Paused", "Torrent -> status string");
259           
260            [fStatusString setString: tempString];
261            [fShortStatusString setString: tempString];
262           
263            break;
264
265        case TR_STATUS_CHECK:
266            tempString = [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
267           
268            [fStatusString setString: tempString];
269            [fShortStatusString setString: tempString];
270            [fRemainingTimeString setString: tempString];
271           
272            break;
273
274        case TR_STATUS_DOWNLOAD:
275            [fStatusString setString: @""];
276            if ([self totalPeers] > 1)
277                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peers",
278                                                "Torrent -> status string"), [self peersUploading], [self totalPeers]];
279            else
280                [fStatusString appendFormat: NSLocalizedString(@"Downloading from %d of %d peer",
281                                                "Torrent -> status string"), [self peersUploading], [self totalPeers]];
282           
283            [fRemainingTimeString setString: @""];
284            int eta = [self eta];
285            if (eta < 0)
286            {
287                [fRemainingTimeString setString: NSLocalizedString(@"Unknown", "Torrent -> remaining time")];
288                [fProgressString appendString: NSLocalizedString(@" - remaining time unknown", "Torrent -> progress string")];
289            }
290            else
291            {
292                if (eta < 60)
293                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d sec", "Torrent -> remaining time"), eta];
294                else if (eta < 3600) //60 * 60
295                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d min %02d sec", "Torrent -> remaining time"),
296                                                            eta / 60, eta % 60];
297                else if (eta < 86400) //24 * 60 * 60
298                    [fRemainingTimeString appendFormat: NSLocalizedString(@"%d hr %02d min", "Torrent -> remaining time"),
299                                                            eta / 3600, (eta / 60) % 60];
300                else
301                {
302                    if (eta / 86400 > 1)
303                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d days %d hr", "Torrent -> remaining time"),
304                                                                eta / 86400, (eta / 3600) % 24];
305                    else
306                        [fRemainingTimeString appendFormat: NSLocalizedString(@"%d day %d hr", "Torrent -> remaining time"),
307                                                                eta / 86400, (eta / 3600) % 24];
308                }
309               
310                [fProgressString appendFormat: NSLocalizedString(@" - %@ remaining", "Torrent -> progress string"),
311                                                                    fRemainingTimeString];
312            }
313           
314            break;
315
316        case TR_STATUS_SEED:
317            [fStatusString setString: @""];
318            if ([self totalPeers] > 1)
319                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peers", "Torrent -> status string"),
320                                                [self peersDownloading], [self totalPeers]];
321            else
322                [fStatusString appendFormat: NSLocalizedString(@"Seeding to %d of %d peer", "Torrent -> status string"),
323                                                [self peersDownloading], [self totalPeers]];
324           
325            break;
326
327        case TR_STATUS_STOPPING:
328            tempString = [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
329       
330            [fStatusString setString: tempString];
331            [fShortStatusString setString: tempString];
332           
333            break;
334    }
335   
336    if (fStat->error & TR_ETRACKER)
337    {
338        [fStatusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString:
339                                    [NSString stringWithUTF8String: fStat->trackerError]]];
340        if (!fError && [self isActive])
341        {
342            fError = YES;
343            if (![self isSeeding])
344                [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
345        }
346    }
347    else
348    {
349        if (fError)
350            fError = NO;
351    }
352
353    if ([self isActive])
354    {
355        NSString * stringToAppend = @"";
356        if ([self progress] < 1.0)
357        {
358            stringToAppend = [NSString stringWithFormat: NSLocalizedString(@"DL: %@, ", "Torrent -> status string"),
359                                [NSString stringForSpeed: [self downloadRate]]];
360            [fShortStatusString setString: @""];
361        }
362        else
363        {
364            NSString * ratioString = [NSString stringForRatioWithDownload: [self downloadedTotal]
365                                                upload: [self uploadedTotal]];
366       
367            [fShortStatusString setString: [NSString stringWithFormat: NSLocalizedString(@"Ratio: %@, ",
368                                            "Torrent -> status string"), ratioString]];
369            [fRemainingTimeString setString: [NSLocalizedString(@"Ratio: ", "Torrent -> status string")
370                                                stringByAppendingString: ratioString]];
371        }
372       
373        stringToAppend = [stringToAppend stringByAppendingString: [NSLocalizedString(@"UL: ", "Torrent -> status string")
374                                            stringByAppendingString: [NSString stringForSpeed: [self uploadRate]]]];
375
376        [fStatusString appendFormat: @" - %@", stringToAppend];
377        [fShortStatusString appendString: stringToAppend];
378    }
379}
380
381- (NSDictionary *) infoForCurrentView
382{
383    NSMutableDictionary * info = [NSMutableDictionary dictionaryWithObjectsAndKeys:
384                                    [self name], @"Name",
385                                    [NSNumber numberWithBool: [self isSeeding]], @"Seeding",
386                                    [NSNumber numberWithFloat: [self progress]], @"Progress",
387                                    [NSNumber numberWithBool: [self isActive]], @"Active",
388                                    [NSNumber numberWithBool: [self isError]], @"Error", nil];
389   
390    if (![fDefaults boolForKey: @"SmallView"])
391    {
392        [info setObject: fIconFlipped forKey: @"Icon"];
393        [info setObject: [self progressString] forKey: @"ProgressString"];
394        [info setObject: [self statusString] forKey: @"StatusString"];
395    }
396    else
397    {
398        [info setObject: fIconSmall forKey: @"Icon"];
399        [info setObject: [self remainingTimeString] forKey: @"RemainingTimeString"];
400        [info setObject: [self shortStatusString] forKey: @"ShortStatusString"];
401    }
402   
403    if ([fDefaults boolForKey: @"UseAdvancedBar"])
404        [info setObject: [self advancedBar] forKey: @"AdvancedBar"];
405   
406    return info;
407}
408
409- (void) startTransfer
410{
411    fWaitToStart = NO;
412    fFinishedSeeding = NO;
413   
414    if (![self isActive] && [self remainingDiskSpaceForTorrent])
415        tr_torrentStart(fHandle);
416}
417
418- (void) stopTransfer
419{
420    fError = NO;
421   
422    if ([self isActive])
423    {
424        BOOL wasSeeding = [self isSeeding];
425   
426        tr_torrentStop(fHandle);
427
428        if (!wasSeeding)
429            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
430    }
431}
432
433- (void) stopTransferForQuit
434{
435    if ([self isActive])
436        tr_torrentStop(fHandle);
437}
438
439- (void) removeForever
440{
441    tr_torrentRemoveSaved(fHandle);
442}
443
444- (void) sleep
445{
446    if ((fResumeOnWake = [self isActive]))
447        tr_torrentStop(fHandle);
448}
449
450- (void) wakeUp
451{
452    if (fResumeOnWake)
453        tr_torrentStart(fHandle);
454}
455
456- (float) ratio
457{
458    float downloaded = [self downloadedTotal];
459    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
460}
461
462- (int) stopRatioSetting
463{
464        return fStopRatioSetting;
465}
466
467- (void) setStopRatioSetting: (int) setting
468{
469    fStopRatioSetting = setting;
470}
471
472- (float) ratioLimit
473{
474    return fRatioLimit;
475}
476
477- (void) setRatioLimit: (float) limit
478{
479    if (limit >= 0)
480        fRatioLimit = limit;
481}
482
483- (void) setWaitToStart: (BOOL) wait
484{
485    fWaitToStart = wait;
486}
487
488- (BOOL) waitingToStart
489{
490    return fWaitToStart;
491}
492
493- (void) revealData
494{
495    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
496}
497
498- (void) revealPublicTorrent
499{
500    if (fPublicTorrent)
501        [[NSWorkspace sharedWorkspace] selectFile: fPublicTorrentLocation inFileViewerRootedAtPath: nil];
502}
503
504- (void) trashData
505{
506    [self trashFile: [self dataLocation]];
507}
508
509- (void) trashTorrent
510{
511    if (fPublicTorrent)
512        [self trashFile: [self publicTorrentLocation]];
513}
514
515- (BOOL) remainingDiskSpaceForTorrent
516{
517    if ([self progress] >= 1.0)
518        return YES;
519   
520    NSString * location = [self dataLocation],
521                * volume = [[[NSFileManager defaultManager] componentsToDisplayForPath: location] objectAtIndex: 0];
522    NSDictionary * fsAttributes = [[NSFileManager defaultManager] fileSystemAttributesAtPath: location];
523    uint64_t remainingSpace = [[fsAttributes objectForKey: NSFileSystemFreeSize] unsignedLongLongValue],
524            torrentRemaining = [self size] - (uint64_t)[self downloadedValid];
525   
526    /*NSLog(@"Volume: %@", volume);
527    NSLog(@"Remaining disk space: %qu (%@)", remainingSpace, [NSString stringForFileSize: remainingSpace]);
528    NSLog(@"Torrent remaining size: %qu (%@)", torrentRemaining, [NSString stringForFileSize: torrentRemaining]);*/
529   
530    if (volume && remainingSpace <= torrentRemaining)
531    {
532        NSAlert * alert = [[NSAlert alloc] init];
533        [alert setMessageText: [NSString stringWithFormat:
534                                NSLocalizedString(@"Not enough remaining disk space to download \"%@\" completely.",
535                                    "Torrent file disk space alert -> title"), [self name]]];
536        [alert setInformativeText: [NSString stringWithFormat:
537                        NSLocalizedString(@"The transfer will be paused. Clear up space on %@ to continue.",
538                                            "Torrent file disk space alert -> message"), volume]];
539        [alert addButtonWithTitle: NSLocalizedString(@"OK", "Torrent file disk space alert -> button")];
540        [alert addButtonWithTitle: NSLocalizedString(@"Download Anyway", "Torrent file disk space alert -> button")];
541       
542        if ([alert runModal] == NSAlertFirstButtonReturn)
543        {
544            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
545            return NO;
546        }
547        else
548            return YES;
549    }
550    return YES;
551}
552
553- (NSImage *) icon
554{
555    return fIcon;
556}
557
558- (NSImage *) iconFlipped
559{
560    return fIconFlipped;
561}
562
563- (NSImage *) iconSmall
564{
565    return fIconSmall;
566}
567
568- (NSString *) name
569{
570    return [NSString stringWithUTF8String: fInfo->name];
571}
572
573- (uint64_t) size
574{
575    return fInfo->totalSize;
576}
577
578- (NSString *) tracker
579{
580    return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
581}
582
583- (NSString *) announce
584{
585    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
586}
587
588- (int) pieceSize
589{
590    return fInfo->pieceSize;
591}
592
593- (int) pieceCount
594{
595    return fInfo->pieceCount;
596}
597
598- (NSString *) hashString
599{
600    return [NSString stringWithUTF8String: fInfo->hashString];
601}
602
603- (NSString *) torrentLocation
604{
605    return [NSString stringWithUTF8String: fInfo->torrent];
606}
607
608- (NSString *) publicTorrentLocation
609{
610    return fPublicTorrentLocation;
611}
612
613- (NSString *) dataLocation
614{
615    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
616}
617
618- (BOOL) publicTorrent
619{
620    return fPublicTorrent;
621}
622
623- (NSString *) stateString
624{
625    switch( fStat->status )
626    {
627        case TR_STATUS_PAUSE:
628            return NSLocalizedString(@"Paused", "Torrent -> status string");
629            break;
630
631        case TR_STATUS_CHECK:
632            return [NSLocalizedString(@"Checking existing files", "Torrent -> status string") stringByAppendingEllipsis];
633            break;
634
635        case TR_STATUS_DOWNLOAD:
636            return NSLocalizedString(@"Downloading", "Torrent -> status string");
637            break;
638
639        case TR_STATUS_SEED:
640            return NSLocalizedString(@"Seeding", "Torrent -> status string");
641            break;
642
643        case TR_STATUS_STOPPING:
644            return [NSLocalizedString(@"Stopping", "Torrent -> status string") stringByAppendingEllipsis];
645            break;
646       
647        default:
648            return NSLocalizedString(@"N/A", "Torrent -> status string");
649    }
650}
651
652- (float) progress
653{
654    return fStat->progress;
655}
656
657- (int) eta
658{
659    return fStat->eta;
660}
661
662- (BOOL) isActive
663{
664    return fStat->status & TR_STATUS_ACTIVE;
665}
666
667- (BOOL) isSeeding
668{
669    return fStat->status == TR_STATUS_SEED;
670}
671
672- (BOOL) isPaused
673{
674    return fStat->status == TR_STATUS_PAUSE;
675}
676
677- (BOOL) isError
678{
679    return fStat->error & TR_ETRACKER;
680}
681
682- (BOOL) justFinished
683{
684    return tr_getFinished(fHandle);
685}
686
687- (NSArray *) peers
688{
689    int totalPeers, i;
690    tr_peer_stat_t * peers = tr_torrentPeers(fHandle, & totalPeers);
691   
692    NSMutableArray * peerDics = [NSMutableArray arrayWithCapacity: totalPeers];
693    tr_peer_stat_t peer;
694    NSString * client;
695    for (i = 0; i < totalPeers; i++)
696    {
697        peer = peers[i];
698        [peerDics addObject: [NSDictionary dictionaryWithObjectsAndKeys:
699            [NSNumber numberWithBool: peer.isConnected], @"Connected",
700            [NSNumber numberWithBool: peer.isIncoming], @"Incoming",
701            [NSString stringWithCString: (char *) peer.addr encoding: NSUTF8StringEncoding], @"IP",
702            [NSString stringWithCString: (char *) peer.client encoding: NSUTF8StringEncoding], @"Client",
703            [NSNumber numberWithBool: peer.isDownloading], @"UL To",
704            [NSNumber numberWithBool: peer.isUploading], @"DL From", nil]];
705    }
706   
707    tr_torrentPeersFree(peers, totalPeers);
708   
709    return peerDics;
710}
711
712- (NSString *) progressString
713{
714    return fProgressString;
715}
716
717- (NSString *) statusString
718{
719    return fStatusString;
720}
721
722- (NSString *) shortStatusString
723{
724    return fShortStatusString;
725}
726
727- (NSString *) remainingTimeString
728{
729    return fRemainingTimeString;
730}
731
732- (int) seeders
733{
734    return fStat->seeders;
735}
736
737- (int) leechers
738{
739    return fStat->leechers;
740}
741
742- (int) totalPeers
743{
744    return fStat->peersTotal;
745}
746
747- (int) totalPeersIncoming
748{
749    return fStat->peersIncoming;
750}
751
752- (int) totalPeersOutgoing
753{
754    return [self totalPeers] - [self totalPeersIncoming];
755}
756
757//peers uploading to you
758- (int) peersUploading
759{
760    return fStat->peersUploading;
761}
762
763//peers downloading from you
764- (int) peersDownloading
765{
766    return fStat->peersDownloading;
767}
768
769- (float) downloadRate
770{
771    return fStat->rateDownload;
772}
773
774- (float) uploadRate
775{
776    return fStat->rateUpload;
777}
778
779- (float) downloadedValid
780{
781    return [self progress] * [self size];
782}
783
784- (uint64_t) downloadedTotal
785{
786    return fStat->downloaded;
787}
788
789- (uint64_t) uploadedTotal
790{
791    return fStat->uploaded;
792}
793
794- (float) swarmSpeed
795{
796    return fStat->swarmspeed;
797}
798
799- (NSNumber *) orderValue
800{
801    return [NSNumber numberWithInt: fOrderValue];
802}
803
804- (void) setOrderValue: (int) orderValue
805{
806    fOrderValue = orderValue;
807}
808
809- (NSArray *) fileList
810{
811    int count = fInfo->fileCount, i;
812    tr_file_t file;
813    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
814   
815    for (i = 0; i < count; i++)
816    {
817        file = fInfo->files[i];
818        [files addObject: [NSDictionary dictionaryWithObjectsAndKeys:
819            [[self downloadFolder] stringByAppendingPathComponent: [NSString stringWithUTF8String: file.name]], @"Name",
820            [NSNumber numberWithUnsignedLongLong: file.length], @"Size", nil]];
821    }
822   
823    return files;
824}
825
826- (NSDate *) date
827{
828    return fDate;
829}
830
831- (NSNumber *) stateSortKey
832{
833    if (![self isActive])
834        return [NSNumber numberWithInt: 0];
835    else if ([self isSeeding])
836        return [NSNumber numberWithInt: 1];
837    else
838        return [NSNumber numberWithInt: 2];
839}
840
841- (NSNumber *) progressSortKey
842{
843    //if finished downloading sort by ratio instead of progress
844    float progress = [self progress];
845    return [NSNumber numberWithFloat: progress < 1.0 ? progress : 2.0 + [self ratio]];
846}
847
848@end
849
850
851@implementation Torrent (Private)
852
853//if a hash is given, attempt to load that; otherwise, attempt to open file at path
854- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
855        publicTorrent: (NSNumber *) publicTorrent
856        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
857        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
858        orderValue: (NSNumber *) orderValue
859{
860    if (!(self = [super init]))
861        return nil;
862
863    fLib = lib;
864    fDefaults = [NSUserDefaults standardUserDefaults];
865
866    fPublicTorrent = path && (publicTorrent ? [publicTorrent boolValue] : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
867    if (fPublicTorrent)
868        fPublicTorrentLocation = [path retain];
869
870    int error;
871    if (hashString)
872        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
873   
874    if (!fHandle && path)
875        fHandle = tr_torrentInit(fLib, [path UTF8String], TR_FSAVEPRIVATE, & error);
876
877    if (!fHandle)
878    {
879        [self release];
880        return nil;
881    }
882   
883    fInfo = tr_torrentInfo( fHandle );
884
885    fDate = date ? [date retain] : [[NSDate alloc] init];
886   
887    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
888    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
889    fFinishedSeeding = NO;
890   
891    fWaitToStart = waitToStart ? [waitToStart boolValue] : [fDefaults boolForKey: @"AutoStartDownload"];
892    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
893    fError = NO;
894   
895    NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
896    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
897    [fIcon retain];
898   
899    fIconFlipped = [fIcon copy];
900    [fIconFlipped setFlipped: YES];
901   
902    fIconSmall = [fIconFlipped copy];
903    [fIconSmall setScalesWhenResized: YES];
904    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
905
906    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
907    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
908    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 30];
909    fRemainingTimeString = [[NSMutableString alloc] initWithCapacity: 30];
910   
911    //set up advanced bar
912    fBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
913        pixelsWide: MAX_PIECES pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
914        isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
915   
916    fPieces = malloc(MAX_PIECES);
917    int i;
918    for (i = 0; i < MAX_PIECES; i++)
919        fPieces[i] = BLANK_PIECE;
920
921    [self update];
922    return self;
923}
924
925- (NSImage *) advancedBar
926{
927    int h, w;
928    uint32_t * p;
929    uint8_t * bitmapData = [fBitmap bitmapData];
930    int bytesPerRow = [fBitmap bytesPerRow];
931
932    int8_t * pieces = malloc(MAX_PIECES);
933    [self getAvailability: pieces size: MAX_PIECES];
934   
935    //lines 2 to 14: blue, green, or gray depending on whether we have the piece or not
936    int have = 0, avail = 0;
937    uint32_t color;
938    BOOL change;
939    for (w = 0; w < MAX_PIECES; w++)
940    {
941        change = NO;
942        if (pieces[w] < 0)
943        {
944            if (fPieces[w] != -1)
945            {
946                color = kBlue;
947                fPieces[w] = -1;
948                change = YES;
949            }
950            have++;
951        }
952        else if (pieces[w] == 0)
953        {
954            if (fPieces[w] != 0)
955            {
956                color = kGray;
957                fPieces[w] = 0;
958                change = YES;
959            }
960        }
961        else
962        {
963            if (pieces[w] == 1)
964            {
965                if (fPieces[w] != 1)
966                {
967                    color = kGreen1;
968                    fPieces[w] = 1;
969                    change = YES;
970                }
971            }
972            else if (pieces[w] == 2)
973            {
974                if (fPieces[w] != 2)
975                {
976                    color = kGreen2;
977                    fPieces[w] = 2;
978                    change = YES;
979                }
980            }
981            else
982            {
983                if (fPieces[w] != 3)
984                {
985                    color = kGreen3;
986                    fPieces[w] = 3;
987                    change = YES;
988                }
989            }
990            avail++;
991        }
992       
993        if (change)
994        {
995            //point to pixel (w, 2) and draw "vertically"
996            p = (uint32_t *)(bitmapData + 2 * bytesPerRow) + w;
997            for (h = 2; h < BAR_HEIGHT; h++)
998            {
999                p[0] = color;
1000                p = (uint32_t *)((uint8_t *)p + bytesPerRow);
1001            }
1002        }
1003    }
1004   
1005    //first two lines: dark blue to show progression, green to show available
1006    p = (uint32_t *) bitmapData;
1007    for (w = 0; w < have; w++)
1008    {
1009        p[w] = kBlue2;
1010        p[w + bytesPerRow / 4] = kBlue2;
1011    }
1012    for (; w < avail + have; w++)
1013    {
1014        p[w] = kGreen3;
1015        p[w + bytesPerRow / 4] = kGreen3;
1016    }
1017    for (; w < MAX_PIECES; w++)
1018    {
1019        p[w] = kWhite;
1020        p[w + bytesPerRow / 4] = kWhite;
1021    }
1022   
1023    free(pieces);
1024   
1025    //actually draw image
1026    NSImage * bar = [[NSImage alloc] initWithSize: [fBitmap size]];
1027    [bar addRepresentation: fBitmap];
1028    [bar setScalesWhenResized: YES];
1029   
1030    return [bar autorelease];
1031}
1032
1033- (void) trashFile: (NSString *) path
1034{
1035    //attempt to move to trash
1036    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
1037            source: [path stringByDeletingLastPathComponent] destination: @""
1038            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
1039    {
1040        //if cannot trash, just delete it (will work if it is on a remote volume)
1041        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
1042            NSLog([@"Could not trash " stringByAppendingString: path]);
1043    }
1044}
1045
1046@end
Note: See TracBrowser for help on using the repository browser.