1 | #import "transmission.h" |
---|
2 | #import "NSStringAdditions.h" |
---|
3 | |
---|
4 | OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); |
---|
5 | void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); |
---|
6 | |
---|
7 | NSString * 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 | |
---|
34 | OSStatus 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 | //try to parse the torrent file |
---|
41 | tr_info inf; |
---|
42 | tr_ctor * ctor = tr_ctorNew(NULL); |
---|
43 | tr_ctorSetMetainfoFromFile(ctor, [[(NSURL *)url path] UTF8String]); |
---|
44 | const int err = tr_torrentParse(ctor, &inf); |
---|
45 | tr_ctorFree(ctor); |
---|
46 | if (err) |
---|
47 | return noErr; |
---|
48 | |
---|
49 | NSBundle * bundle = [NSBundle bundleWithIdentifier: @"org.m0k.transmission.QuickLookPlugin"]; |
---|
50 | |
---|
51 | NSURL * styleURL = [bundle URLForResource: @"style" withExtension: @"css"]; |
---|
52 | NSString * styleContents = [NSString stringWithContentsOfURL: styleURL encoding: NSUTF8StringEncoding error: NULL]; |
---|
53 | |
---|
54 | NSMutableString * htmlString = [NSMutableString string]; |
---|
55 | [htmlString appendFormat: @"<html><style type=\"text/css\">%@</style><body>", styleContents]; |
---|
56 | |
---|
57 | NSMutableDictionary * allImgProps = [NSMutableDictionary dictionary]; |
---|
58 | |
---|
59 | NSString * name = [NSString stringWithUTF8String: inf.name]; |
---|
60 | NSString * fileTypeString = inf.isMultifile ? NSFileTypeForHFSTypeCode(kGenericFolderIcon) : [name pathExtension]; |
---|
61 | |
---|
62 | const NSUInteger width = 32; |
---|
63 | [htmlString appendFormat: @"<h2><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@</h2>", generateIconData(fileTypeString, width, allImgProps), width, width, name]; |
---|
64 | |
---|
65 | NSString * fileSizeString = [NSString stringForFileSize: inf.totalSize]; |
---|
66 | if (inf.isMultifile) |
---|
67 | { |
---|
68 | NSString * fileCountString; |
---|
69 | if (inf.fileCount == 1) |
---|
70 | fileCountString = NSLocalizedStringFromTableInBundle(@"1 file", nil, bundle, "quicklook file count"); |
---|
71 | else |
---|
72 | fileCountString= [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"%@ files", nil, bundle, "quicklook file count"), [NSString formattedUInteger: inf.fileCount]]; |
---|
73 | fileSizeString = [NSString stringWithFormat: @"%@, %@", fileCountString, fileSizeString]; |
---|
74 | } |
---|
75 | [htmlString appendFormat: @"<p>%@</p>", fileSizeString]; |
---|
76 | |
---|
77 | NSString * dateCreatedString = inf.dateCreated > 0 ? [NSDateFormatter localizedStringFromDate: [NSDate dateWithTimeIntervalSince1970: inf.dateCreated] dateStyle: NSDateFormatterLongStyle timeStyle: NSDateFormatterShortStyle] : nil; |
---|
78 | NSString * creatorString = inf.creator ? [NSString stringWithUTF8String: inf.creator] : nil; |
---|
79 | if ([creatorString isEqualToString: @""]) creatorString = nil; |
---|
80 | NSString * creationString = nil; |
---|
81 | if (dateCreatedString && creatorString) |
---|
82 | creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created on %@ with %@", nil, bundle, "quicklook creation info"), dateCreatedString, creatorString]; |
---|
83 | else if (dateCreatedString) |
---|
84 | creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created on %@", nil, bundle, "quicklook creation info"), dateCreatedString]; |
---|
85 | else if (creatorString) |
---|
86 | creationString = [NSString stringWithFormat: NSLocalizedStringFromTableInBundle(@"Created with %@", nil, bundle, "quicklook creation info"), creatorString]; |
---|
87 | if (creationString) |
---|
88 | [htmlString appendFormat: @"<p>%@</p>", creationString]; |
---|
89 | |
---|
90 | if (inf.comment) |
---|
91 | { |
---|
92 | NSString * comment = [NSString stringWithUTF8String: inf.comment]; |
---|
93 | if (![comment isEqualToString: @""]) |
---|
94 | [htmlString appendFormat: @"<p>%@</p>", comment]; |
---|
95 | } |
---|
96 | |
---|
97 | NSMutableArray * lists = [NSMutableArray array]; |
---|
98 | |
---|
99 | if (inf.webseedCount > 0) |
---|
100 | { |
---|
101 | NSMutableString * listSection = [NSMutableString string]; |
---|
102 | [listSection appendString: @"<table>"]; |
---|
103 | |
---|
104 | 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]]; |
---|
105 | [listSection appendFormat: @"<tr><th>%@</th></tr>", headerTitleString]; |
---|
106 | |
---|
107 | for (int i = 0; i < inf.webseedCount; ++i) |
---|
108 | [listSection appendFormat: @"<tr><td>%s<td></tr>", inf.webseeds[i]]; |
---|
109 | |
---|
110 | [listSection appendString:@"</table>"]; |
---|
111 | |
---|
112 | [lists addObject: listSection]; |
---|
113 | } |
---|
114 | |
---|
115 | if (inf.trackerCount > 0) |
---|
116 | { |
---|
117 | NSMutableString * listSection = [NSMutableString string]; |
---|
118 | [listSection appendString: @"<table>"]; |
---|
119 | |
---|
120 | 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]]; |
---|
121 | [listSection appendFormat: @"<tr><th>%@</th></tr>", headerTitleString]; |
---|
122 | |
---|
123 | #warning handle tiers? |
---|
124 | for (int i = 0; i < inf.trackerCount; ++i) |
---|
125 | [listSection appendFormat: @"<tr><td>%s<td></tr>", inf.trackers[i].announce]; |
---|
126 | |
---|
127 | [listSection appendString:@"</table>"]; |
---|
128 | |
---|
129 | [lists addObject: listSection]; |
---|
130 | } |
---|
131 | |
---|
132 | if (inf.isMultifile) |
---|
133 | { |
---|
134 | NSMutableString * listSection = [NSMutableString string]; |
---|
135 | [listSection appendString: @"<table>"]; |
---|
136 | |
---|
137 | 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]]; |
---|
138 | [listSection appendFormat: @"<tr><th>%@</th></tr>", fileTitleString]; |
---|
139 | |
---|
140 | #warning display size? |
---|
141 | #warning display folders? |
---|
142 | for (int i = 0; i < inf.fileCount; ++i) |
---|
143 | { |
---|
144 | NSString * fullFilePath = [NSString stringWithUTF8String: inf.files[i].name]; |
---|
145 | NSCAssert([fullFilePath hasPrefix: [name stringByAppendingString: @"/"]], @"Expected file path %@ to begin with %@/", fullFilePath, name); |
---|
146 | |
---|
147 | NSString * shortenedFilePath = [fullFilePath substringFromIndex: [name length]+1]; |
---|
148 | |
---|
149 | const NSUInteger width = 16; |
---|
150 | [listSection appendFormat: @"<tr><td><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@<td></tr>", generateIconData([shortenedFilePath pathExtension], width, allImgProps), width, width, shortenedFilePath]; |
---|
151 | } |
---|
152 | |
---|
153 | [listSection appendString:@"</table>"]; |
---|
154 | |
---|
155 | [lists addObject: listSection]; |
---|
156 | } |
---|
157 | |
---|
158 | if ([lists count] > 0) |
---|
159 | [htmlString appendFormat: @"<hr/><br>%@", [lists componentsJoinedByString: @"<br>"]]; |
---|
160 | |
---|
161 | [htmlString appendString: @"</body></html>"]; |
---|
162 | |
---|
163 | tr_metainfoFree(&inf); |
---|
164 | |
---|
165 | NSDictionary * props = @{ (NSString *)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8", |
---|
166 | (NSString *)kQLPreviewPropertyMIMETypeKey : @"text/html", |
---|
167 | (NSString *)kQLPreviewPropertyAttachmentsKey : allImgProps }; |
---|
168 | |
---|
169 | QLPreviewRequestSetDataRepresentation(preview, (CFDataRef)[htmlString dataUsingEncoding: NSUTF8StringEncoding], kUTTypeHTML, (CFDictionaryRef)props); |
---|
170 | |
---|
171 | return noErr; |
---|
172 | } |
---|
173 | |
---|
174 | void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview) |
---|
175 | { |
---|
176 | // Implement only if supported |
---|
177 | } |
---|