source: trunk/macosx/Torrent.m @ 525

Last change on this file since 525 was 525, checked in by joshe, 16 years ago

Move the ETA cap of 99:59:59 from libtransmission to the MacOS X GUI.
Display the ETA better in the GTK GUI.

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