1#import "transmission.h" 2#import "NSStringAdditions.h" 3 4OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); 5void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); 6 7NSString * generateIconData(NSString * fileExtension, NSUInteger width, NSMutableDictionary * allImgProps) 8{ 9 NSString * rawFilename = ![fileExtension isEqualToString: @""] ? fileExtension : @"blank_file_name_transmission"; 10 NSString * iconFileName = [NSString stringWithFormat: @"%ldx%@.tiff", width, rawFilename]; //we need to do this once per file extension, per size 11 12 if (![allImgProps objectForKey: iconFileName]) 13 { 14 NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFileType: fileExtension]; 15 16 const NSRect iconFrame = NSMakeRect(0.0, 0.0, width, width); 17 NSImage * renderedIcon = [[NSImage alloc] initWithSize: iconFrame.size]; 18 [renderedIcon lockFocus]; 19 [icon drawInRect: iconFrame fromRect: NSZeroRect operation: NSCompositeCopy fraction: 1.0]; 20 [renderedIcon unlockFocus]; 21 22 NSData * iconData = [renderedIcon TIFFRepresentation]; 23 [renderedIcon release]; 24 25 NSDictionary * imgProps = @{ 26 (NSString *)kQLPreviewPropertyMIMETypeKey : @"image/png", 27 (NSString *)kQLPreviewPropertyAttachmentDataKey : iconData }; 28 [allImgProps setObject: imgProps forKey: iconFileName]; 29 } 30 31 return [@"cid:" stringByAppendingString: iconFileName]; 32} 33 34OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options) 35{ 36 // Before proceeding make sure the user didn't cancel the request 37 if (QLPreviewRequestIsCancelled(preview)) 38 return noErr; 39 40 //we need this call to ensure NSApp is initialized (not done automatically for plugins) 41 [NSApplication sharedApplication]; 42 43 //try to parse the torrent file 44 tr_info inf; 45 tr_ctor * ctor = tr_ctorNew(NULL); 46 tr_ctorSetMetainfoFromFile(ctor, [[(NSURL *)url path] UTF8String]); 47 const int err = tr_torrentParse(ctor, &inf); 48 tr_ctorFree(ctor); 49 if (err) 50 return noErr; 51 52 NSBundle * bundle = [NSBundle bundleWithIdentifier: @"org.m0k.transmission.QuickLookPlugin"]; 53 54 NSURL * styleURL = [bundle URLForResource: @"style" withExtension: @"css"]; 55 NSString * styleContents = [NSString stringWithContentsOfURL: styleURL encoding: NSUTF8StringEncoding error: NULL]; 56 57 NSMutableString * htmlString = [NSMutableString string]; 58 [htmlString appendFormat: @"<html><style type=\"text/css\">%@</style><body>", styleContents]; 59 60 NSMutableDictionary * allImgProps = [NSMutableDictionary dictionary]; 61 62 NSString * name = [NSString stringWithUTF8String: inf.name]; 63 NSString * fileTypeString = inf.isMultifile ? NSFileTypeForHFSTypeCode(kGenericFolderIcon) : [name pathExtension]; 64 65 const NSUInteger width = 32; 66 [htmlString appendFormat: @"<h2><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@</h2>", generateIconData(fileTypeString, width, allImgProps), width, width, name]; 67 68 NSString * fileSizeString = [NSString stringForFileSize: inf.totalSize]; 69 if (inf.isMultifile) 70 { 71 NSString * fileCountString; 72 if (inf.fileCount == 1) 73 fileCountString = NSLocalizedStringFromTableInBundle(@"1 file", nil, bundle, "quicklook file count"); 74 else 75 fileCountString= [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"%@ files", nil, bundle, "quicklook file count"), [NSString formattedUInteger: inf.fileCount]]; 76 fileSizeString = [NSString stringWithFormat: @"%@, %@", fileCountString, fileSizeString]; 77 } 78 [htmlString appendFormat: @"<p>%@</p>", fileSizeString]; 79 80 NSString * dateCreatedString = inf.dateCreated > 0 ? [NSDateFormatter localizedStringFromDate: [NSDate dateWithTimeIntervalSince1970: inf.dateCreated] dateStyle: NSDateFormatterLongStyle timeStyle: NSDateFormatterShortStyle] : nil; 81 NSString * creatorString = inf.creator ? [NSString stringWithUTF8String: inf.creator] : nil; 82 if ([creatorString isEqualToString: @""]) creatorString = nil; 83 NSString * creationString = nil; 84 if (dateCreatedString && creatorString) 85 creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created on %@ with %@", nil, bundle, "quicklook creation info"), dateCreatedString, creatorString]; 86 else if (dateCreatedString) 87 creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created on %@", nil, bundle, "quicklook creation info"), dateCreatedString]; 88 else if (creatorString) 89 creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created with %@", nil, bundle, "quicklook creation info"), creatorString]; 90 if (creationString) 91 [htmlString appendFormat: @"<p>%@</p>", creationString]; 92 93 if (inf.comment) 94 { 95 NSString * comment = [NSString stringWithUTF8String: inf.comment]; 96 if (![comment isEqualToString: @""]) 97 [htmlString appendFormat: @"<p>%@</p>", comment]; 98 } 99 100 NSMutableArray * lists = [NSMutableArray array]; 101 102 if (inf.webseedCount > 0) 103 { 104 NSMutableString * listSection = [NSMutableString string]; 105 [listSection appendString: @"<table>"]; 106 107 NSString * headerTitleString = inf.webseedCount == 1 ? NSLocalizedStringFromTableInBundle(@"1 Web Seed", nil, bundle, "quicklook web seed header") : [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"%@ Web Seeds", nil, bundle, "quicklook web seed header"), [NSString formattedUInteger: inf.webseedCount]]; 108 [listSection appendFormat: @"<tr><th>%@</th></tr>", headerTitleString]; 109 110 for (int i = 0; i < inf.webseedCount; ++i) 111 [listSection appendFormat: @"<tr><td>%s<td></tr>", inf.webseeds[i]]; 112 113 [listSection appendString:@"</table>"]; 114 115 [lists addObject: listSection]; 116 } 117 118 if (inf.trackerCount > 0) 119 { 120 NSMutableString * listSection = [NSMutableString string]; 121 [listSection appendString: @"<table>"]; 122 123 NSString * headerTitleString = inf.trackerCount == 1 ? NSLocalizedStringFromTableInBundle(@"1 Tracker", nil, bundle, "quicklook tracker header") : [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"%@ Trackers", nil, bundle, "quicklook tracker header"), [NSString formattedUInteger: inf.trackerCount]]; 124 [listSection appendFormat: @"<tr><th>%@</th></tr>", headerTitleString]; 125 126#warning handle tiers? 127 for (int i = 0; i < inf.trackerCount; ++i) 128 [listSection appendFormat: @"<tr><td>%s<td></tr>", inf.trackers[i].announce]; 129 130 [listSection appendString:@"</table>"]; 131 132 [lists addObject: listSection]; 133 } 134 135 if (inf.isMultifile) 136 { 137 NSMutableString * listSection = [NSMutableString string]; 138 [listSection appendString: @"<table>"]; 139 140 NSString * fileTitleString = inf.fileCount == 1 ? NSLocalizedStringFromTableInBundle(@"1 File", nil, bundle, "quicklook file header") : [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"%@ Files", nil, bundle, "quicklook file header"), [NSString formattedUInteger: inf.fileCount]]; 141 [listSection appendFormat: @"<tr><th>%@</th></tr>", fileTitleString]; 142 143#warning display size? 144#warning display folders? 145 for (int i = 0; i < inf.fileCount; ++i) 146 { 147 NSString * fullFilePath = [NSString stringWithUTF8String: inf.files[i].name]; 148 NSCAssert([fullFilePath hasPrefix: [name stringByAppendingString: @"/"]], @"Expected file path %@ to begin with %@/", fullFilePath, name); 149 150 NSString * shortenedFilePath = [fullFilePath substringFromIndex: [name length]+1]; 151 152 const NSUInteger width = 16; 153 [listSection appendFormat: @"<tr><td><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@<td></tr>", generateIconData([shortenedFilePath pathExtension], width, allImgProps), width, width, shortenedFilePath]; 154 } 155 156 [listSection appendString:@"</table>"]; 157 158 [lists addObject: listSection]; 159 } 160 161 if ([lists count] > 0) 162 [htmlString appendFormat: @"<hr/><br>%@", [lists componentsJoinedByString: @"<br>"]]; 163 164 [htmlString appendString: @"</body></html>"]; 165 166 tr_metainfoFree(&inf); 167 168 NSDictionary * props = @{ (NSString *)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8", 169 (NSString *)kQLPreviewPropertyMIMETypeKey : @"text/html", 170 (NSString *)kQLPreviewPropertyAttachmentsKey : allImgProps }; 171 172 QLPreviewRequestSetDataRepresentation(preview, (CFDataRef)[htmlString dataUsingEncoding: NSUTF8StringEncoding], kUTTypeHTML, (CFDictionaryRef)props); 173 174 return noErr; 175} 176 177void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview) 178{ 179 // Implement only if supported 180} 181