source: trunk/macosx/Torrent.m @ 361

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

By popular demand: the return of "Remove Torrent File." It only applies to the files not moved to the private folder.

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