source: trunk/macosx/Torrent.m @ 446

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

The beginning of queueing. Has some work to go, but the basic infrastructure is set up.

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