source: trunk/macosx/Torrent.m @ 535

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

Re-added state to inspector because state isn't visible from minimal view and state can still differ from what's displayed (for example, it might display "waiting to start" when status is "paused").

  • Property svn:keywords set to Date Rev Author Id
File size: 18.0 KB
Line 
1/******************************************************************************
2 * $Id: Torrent.m 535 2006-07-06 00:03:27Z 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            stringToAppend = [NSString stringWithFormat: @"DL: %@, ", [NSString stringForSpeed: [self downloadRate]]];
239        stringToAppend = [stringToAppend stringByAppendingString: [@"UL: " stringByAppendingString:
240                                                [NSString stringForSpeed: [self uploadRate]]]];
241
242        [fStatusString appendFormat: @" - %@", stringToAppend];
243        [fShortStatusString setString: stringToAppend];
244    }
245}
246
247- (void) startTransfer
248{
249    if (![self isActive])
250    {
251        tr_torrentStart(fHandle);
252
253        fFinishedSeeding = NO;
254        fWaitToStart = NO;
255       
256        [[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentSettingChange" object: self];
257    }
258}
259
260- (void) stopTransfer
261{
262    if ([self isActive])
263    {
264        BOOL wasSeeding = [self isSeeding];
265   
266        tr_torrentStop(fHandle);
267
268        if (!wasSeeding)
269            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
270    }
271}
272
273- (void) stopTransferForQuit
274{
275    if ([self isActive])
276        tr_torrentStop(fHandle);
277}
278
279- (void) removeForever
280{
281    if (fPrivateTorrent)
282        tr_torrentRemoveSaved(fHandle);
283}
284
285- (void) sleep
286{
287    if ((fResumeOnWake = [self isActive]))
288        tr_torrentStop(fHandle);
289}
290
291- (void) wakeUp
292{
293    if (fResumeOnWake)
294        tr_torrentStart(fHandle);
295}
296
297- (float) ratio
298{
299    float downloaded = [self downloadedTotal];
300    return downloaded > 0 ? (float)[self uploadedTotal] / downloaded : -1;
301}
302
303- (int) stopRatioSetting
304{
305        return fStopRatioSetting;
306}
307
308- (void) setStopRatioSetting: (int) setting
309{
310    fStopRatioSetting = setting;
311}
312
313- (float) ratioLimit
314{
315    return fRatioLimit;
316}
317
318- (void) setRatioLimit: (float) limit
319{
320    if (limit >= 0)
321        fRatioLimit = limit;
322}
323
324- (void) setWaitToStart: (BOOL) wait
325{
326    fWaitToStart = wait;
327}
328
329- (BOOL) waitingToStart
330{
331    return fWaitToStart;
332}
333
334- (void) revealData
335{
336    [[NSWorkspace sharedWorkspace] selectFile: [self dataLocation] inFileViewerRootedAtPath: nil];
337}
338
339- (void) trashData
340{
341    [self trashFile: [self dataLocation]];
342}
343
344- (void) trashTorrent
345{
346    if (fPublicTorrent)
347        [self trashFile: [self publicTorrentLocation]];
348}
349
350- (NSImage *) icon
351{
352    return fIcon;
353}
354
355- (NSImage *) iconFlipped
356{
357    return fIconFlipped;
358}
359
360- (NSImage *) iconSmall
361{
362    return fIconSmall;
363}
364
365- (NSString *) name
366{
367    return [NSString stringWithUTF8String: fInfo->name];
368}
369
370- (uint64_t) size
371{
372    return fInfo->totalSize;
373}
374
375- (NSString *) tracker
376{
377    return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
378}
379
380- (NSString *) announce
381{
382    return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
383}
384
385- (int) pieceSize
386{
387    return fInfo->pieceSize;
388}
389
390- (int) pieceCount
391{
392    return fInfo->pieceCount;
393}
394
395- (NSString *) hashString
396{
397    return [NSString stringWithUTF8String: fInfo->hashString];
398}
399
400- (NSString *) torrentLocation
401{
402    return [NSString stringWithUTF8String: fInfo->torrent];
403}
404
405- (NSString *) publicTorrentLocation
406{
407    return fPublicTorrentLocation;
408}
409
410- (NSString *) torrentLocationString
411{
412    return fPrivateTorrent ? @"Transmission Support Folder"
413            : [fPublicTorrentLocation stringByAbbreviatingWithTildeInPath];
414}
415
416- (NSString *) dataLocation
417{
418    return [[self downloadFolder] stringByAppendingPathComponent: [self name]];
419}
420
421- (BOOL) publicTorrent
422{
423    return fPublicTorrent;
424}
425
426- (BOOL) privateTorrent
427{
428    return fPrivateTorrent;
429}
430
431- (NSString *) stateString
432{
433    switch( fStat->status )
434    {
435        case TR_STATUS_PAUSE:
436            return @"Paused";
437            break;
438
439        case TR_STATUS_CHECK:
440            return [@"Checking existing files" stringByAppendingEllipsis];
441            break;
442
443        case TR_STATUS_DOWNLOAD:
444            return @"Downloading";
445            break;
446
447        case TR_STATUS_SEED:
448            return @"Seeding";
449            break;
450
451        case TR_STATUS_STOPPING:
452            return [@"Stopping" stringByAppendingEllipsis];
453            break;
454       
455        default:
456            return @"N/A";
457    }
458}
459
460- (float) progress
461{
462    return fStat->progress;
463}
464
465- (int) eta
466{
467    return fStat->eta;
468}
469
470- (BOOL) isActive
471{
472    return fStat->status & TR_STATUS_ACTIVE;
473}
474
475- (BOOL) isSeeding
476{
477    return fStat->status == TR_STATUS_SEED;
478}
479
480- (BOOL) isPaused
481{
482    return fStat->status == TR_STATUS_PAUSE;
483}
484
485- (BOOL) justFinished
486{
487    return tr_getFinished(fHandle);
488}
489
490- (NSString *) progressString
491{
492    return fProgressString;
493}
494
495- (NSString *) statusString
496{
497    return fStatusString;
498}
499
500- (NSString *) shortStatusString
501{
502    return fShortStatusString;
503}
504
505- (int) seeders
506{
507    return fStat->seeders;
508}
509
510- (int) leechers
511{
512    return fStat->leechers;
513}
514
515- (int) totalPeers
516{
517    return fStat->peersTotal;
518}
519
520//peers uploading to you
521- (int) peersUploading
522{
523    return fStat->peersUploading;
524}
525
526//peers downloading from you
527- (int) peersDownloading
528{
529    return fStat->peersDownloading;
530}
531
532- (float) downloadRate
533{
534    return fStat->rateDownload;
535}
536
537- (float) uploadRate
538{
539    return fStat->rateUpload;
540}
541
542- (float) downloadedValid
543{
544    return [self progress] * [self size];
545}
546
547- (uint64_t) downloadedTotal
548{
549    return fStat->downloaded;
550}
551
552- (uint64_t) uploadedTotal
553{
554    return fStat->uploaded;
555}
556
557- (NSNumber *) orderValue
558{
559    return [NSNumber numberWithInt: fOrderValue];
560}
561
562- (void) setOrderValue: (int) orderValue
563{
564    fOrderValue = orderValue;
565}
566
567- (NSArray *) fileList
568{
569    int count = fInfo->fileCount, i;
570    NSMutableArray * files = [NSMutableArray arrayWithCapacity: count];
571    for (i = 0; i < count; i++)
572        [files addObject: [[self downloadFolder] stringByAppendingPathComponent:
573            [NSString stringWithUTF8String: fInfo->files[i].name]]];
574    return files;
575}
576
577- (NSDate *) date
578{
579    return fDate;
580}
581
582- (NSNumber *) stateSortKey
583{
584    if (![self isActive])
585        return [NSNumber numberWithInt: 0];
586    else if ([self isSeeding])
587        return [NSNumber numberWithInt: 1];
588    else
589        return [NSNumber numberWithInt: 2];
590}
591
592- (NSNumber *) progressSortKey
593{
594    return [NSNumber numberWithFloat: [self progress]];
595}
596
597@end
598
599
600@implementation Torrent (Private)
601
602//if a hash is given, attempt to load that; otherwise, attempt to open file at path
603- (id) initWithHash: (NSString *) hashString path: (NSString *) path lib: (tr_handle_t *) lib
604        privateTorrent: (NSNumber *) privateTorrent publicTorrent: (NSNumber *) publicTorrent
605        date: (NSDate *) date stopRatioSetting: (NSNumber *) stopRatioSetting
606        ratioLimit: (NSNumber *) ratioLimit waitToStart: (NSNumber *) waitToStart
607        orderValue: (NSNumber *) orderValue
608{
609    if (!(self = [super init]))
610        return nil;
611
612    fLib = lib;
613    fDefaults = [NSUserDefaults standardUserDefaults];
614
615    fPrivateTorrent = privateTorrent ? [privateTorrent boolValue] : [fDefaults boolForKey: @"SavePrivateTorrent"];
616    fPublicTorrent = !fPrivateTorrent || (publicTorrent ? [publicTorrent boolValue]
617                                            : ![fDefaults boolForKey: @"DeleteOriginalTorrent"]);
618
619    int error;
620    if (fPrivateTorrent && hashString)
621        fHandle = tr_torrentInitSaved(fLib, [hashString UTF8String], TR_FSAVEPRIVATE, & error);
622   
623    if (!fHandle && path)
624        fHandle = tr_torrentInit(fLib, [path UTF8String], fPrivateTorrent ? TR_FSAVEPRIVATE : 0, & error);
625
626    if (!fHandle)
627    {
628        [self release];
629        return nil;
630    }
631   
632    fInfo = tr_torrentInfo( fHandle );
633
634    if (fPublicTorrent)
635        fPublicTorrentLocation = [path retain];
636
637    fDate = date ? [date retain] : [[NSDate alloc] init];
638   
639    fStopRatioSetting = stopRatioSetting ? [stopRatioSetting intValue] : -1;
640    fRatioLimit = ratioLimit ? [ratioLimit floatValue] : [fDefaults floatForKey: @"RatioLimit"];
641    fFinishedSeeding = NO;
642   
643    fWaitToStart = waitToStart ? [waitToStart boolValue]
644                    : ![[fDefaults stringForKey: @"StartSetting"] isEqualToString: @"Manual"];
645    fOrderValue = orderValue ? [orderValue intValue] : tr_torrentCount(fLib) - 1;
646   
647    NSString * fileType = fInfo->multifile ? NSFileTypeForHFSTypeCode('fldr') : [[self name] pathExtension];
648    fIcon = [[NSWorkspace sharedWorkspace] iconForFileType: fileType];
649    [fIcon retain];
650   
651    fIconFlipped = [fIcon copy];
652    [fIconFlipped setFlipped: YES];
653   
654    fIconSmall = [fIconFlipped copy];
655    [fIconSmall setScalesWhenResized: YES];
656    [fIconSmall setSize: NSMakeSize(16.0, 16.0)];
657
658    fProgressString = [[NSMutableString alloc] initWithCapacity: 50];
659    fStatusString = [[NSMutableString alloc] initWithCapacity: 75];
660    fShortStatusString = [[NSMutableString alloc] initWithCapacity: 50];
661
662    [self update];
663    return self;
664}
665
666- (void) trashFile: (NSString *) path
667{
668    //attempt to move to trash
669    if (![[NSWorkspace sharedWorkspace] performFileOperation: NSWorkspaceRecycleOperation
670            source: [path stringByDeletingLastPathComponent] destination: @""
671            files: [NSArray arrayWithObject: [path lastPathComponent]] tag: nil])
672    {
673        //if cannot trash, just delete it (will work if it is on a remote volume)
674        if (![[NSFileManager defaultManager] removeFileAtPath: path handler: nil])
675            NSLog([@"Could not trash " stringByAppendingString: path]);
676    }
677}
678
679@end
Note: See TracBrowser for help on using the repository browser.