2016-09-10 17:08:58 +00:00
|
|
|
#import <QuickLook/QuickLook.h>
|
|
|
|
|
2018-09-30 10:37:30 +00:00
|
|
|
#include <libtransmission/transmission.h>
|
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
#import "NSStringAdditions.h"
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
OSStatus GeneratePreviewForURL(void* thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options);
|
|
|
|
void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview);
|
2012-08-12 12:11:33 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* generateIconData(NSString* fileExtension, NSUInteger width, NSMutableDictionary* allImgProps)
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* rawFilename = ![fileExtension isEqualToString:@""] ? fileExtension : @"blank_file_name_transmission";
|
|
|
|
// we need to do this once per file extension, per size
|
|
|
|
NSString* iconFileName = [NSString stringWithFormat:@"%ldx%@.tiff", width, rawFilename];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
if (![allImgProps objectForKey:iconFileName])
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSImage* icon = [[NSWorkspace sharedWorkspace] iconForFileType:fileExtension];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSRect const iconFrame = NSMakeRect(0.0, 0.0, width, width);
|
|
|
|
NSImage* renderedIcon = [[NSImage alloc] initWithSize:iconFrame.size];
|
2012-08-12 12:11:33 +00:00
|
|
|
[renderedIcon lockFocus];
|
2021-10-31 15:18:27 +00:00
|
|
|
[icon drawInRect:iconFrame fromRect:NSZeroRect operation:NSCompositingOperationCopy fraction:1.0];
|
2012-08-12 12:11:33 +00:00
|
|
|
[renderedIcon unlockFocus];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSData* iconData = [renderedIcon TIFFRepresentation];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSDictionary* imgProps = @{
|
|
|
|
(NSString*)kQLPreviewPropertyMIMETypeKey : @"image/png",
|
|
|
|
(NSString*)kQLPreviewPropertyAttachmentDataKey : iconData
|
|
|
|
};
|
|
|
|
[allImgProps setObject:imgProps forKey:iconFileName];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
return [@"cid:" stringByAppendingString:iconFileName];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
OSStatus GeneratePreviewForURL(void* thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options)
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
|
|
|
// Before proceeding make sure the user didn't cancel the request
|
|
|
|
if (QLPreviewRequestIsCancelled(preview))
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
2012-08-12 12:11:33 +00:00
|
|
|
return noErr;
|
2021-08-15 09:41:48 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-09-06 01:15:31 +00:00
|
|
|
//we need this call to ensure NSApp is initialized (not done automatically for plugins)
|
|
|
|
[NSApplication sharedApplication];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
//try to parse the torrent file
|
|
|
|
tr_info inf;
|
2021-08-15 09:41:48 +00:00
|
|
|
tr_ctor* ctor = tr_ctorNew(NULL);
|
|
|
|
tr_ctorSetMetainfoFromFile(ctor, [[(__bridge NSURL*)url path] UTF8String]);
|
|
|
|
int const err = tr_torrentParse(ctor, &inf);
|
2012-08-12 12:11:33 +00:00
|
|
|
tr_ctorFree(ctor);
|
|
|
|
if (err)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
2012-08-12 12:11:33 +00:00
|
|
|
return noErr;
|
2021-08-15 09:41:48 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSBundle* bundle = [NSBundle bundleWithIdentifier:@"org.m0k.transmission.QuickLookPlugin"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSURL* styleURL = [bundle URLForResource:@"style" withExtension:@"css"];
|
|
|
|
NSString* styleContents = [NSString stringWithContentsOfURL:styleURL encoding:NSUTF8StringEncoding error:NULL];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableString* htmlString = [NSMutableString string];
|
|
|
|
[htmlString appendFormat:@"<html><style type=\"text/css\">%@</style><body>", styleContents];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableDictionary* allImgProps = [NSMutableDictionary dictionary];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* name = [NSString stringWithUTF8String:inf.name];
|
|
|
|
NSString* fileTypeString = inf.isFolder ? NSFileTypeForHFSTypeCode(kGenericFolderIcon) : [name pathExtension];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSUInteger const width = 32;
|
|
|
|
[htmlString appendFormat:@"<h2><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@</h2>",
|
|
|
|
generateIconData(fileTypeString, width, allImgProps),
|
|
|
|
width,
|
|
|
|
width,
|
|
|
|
name];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* fileSizeString = [NSString stringForFileSize:inf.totalSize];
|
2014-06-08 22:16:01 +00:00
|
|
|
if (inf.isFolder)
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* fileCountString;
|
2012-08-12 12:11:33 +00:00
|
|
|
if (inf.fileCount == 1)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
2012-09-03 18:37:18 +00:00
|
|
|
fileCountString = NSLocalizedStringFromTableInBundle(@"1 file", nil, bundle, "quicklook file count");
|
2021-08-15 09:41:48 +00:00
|
|
|
}
|
2012-08-12 12:11:33 +00:00
|
|
|
else
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
fileCountString = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ files", nil, bundle, "quicklook file count"),
|
|
|
|
[NSString formattedUInteger:inf.fileCount]];
|
|
|
|
}
|
|
|
|
fileSizeString = [NSString stringWithFormat:@"%@, %@", fileCountString, fileSizeString];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2021-08-15 09:41:48 +00:00
|
|
|
[htmlString appendFormat:@"<p>%@</p>", fileSizeString];
|
|
|
|
|
|
|
|
NSString* dateCreatedString = inf.dateCreated > 0 ?
|
|
|
|
[NSDateFormatter localizedStringFromDate:[NSDate dateWithTimeIntervalSince1970:inf.dateCreated] dateStyle:NSDateFormatterLongStyle
|
|
|
|
timeStyle:NSDateFormatterShortStyle] :
|
|
|
|
nil;
|
|
|
|
NSString* creatorString = inf.creator ? [NSString stringWithUTF8String:inf.creator] : nil;
|
|
|
|
if ([creatorString isEqualToString:@""])
|
|
|
|
{
|
|
|
|
creatorString = nil;
|
|
|
|
}
|
|
|
|
NSString* creationString = nil;
|
2012-08-12 14:05:55 +00:00
|
|
|
if (dateCreatedString && creatorString)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
creationString = [NSString
|
|
|
|
stringWithFormat:NSLocalizedStringFromTableInBundle(@"Created on %@ with %@", nil, bundle, "quicklook creation info"),
|
|
|
|
dateCreatedString,
|
|
|
|
creatorString];
|
|
|
|
}
|
2012-08-12 14:05:55 +00:00
|
|
|
else if (dateCreatedString)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
creationString = [NSString
|
|
|
|
stringWithFormat:NSLocalizedStringFromTableInBundle(@"Created on %@", nil, bundle, "quicklook creation info"), dateCreatedString];
|
|
|
|
}
|
2012-08-12 14:05:55 +00:00
|
|
|
else if (creatorString)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
creationString = [NSString
|
|
|
|
stringWithFormat:NSLocalizedStringFromTableInBundle(@"Created with %@", nil, bundle, "quicklook creation info"), creatorString];
|
|
|
|
}
|
2012-08-12 14:05:55 +00:00
|
|
|
if (creationString)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
[htmlString appendFormat:@"<p>%@</p>", creationString];
|
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
if (inf.comment)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* comment = [NSString stringWithUTF8String:inf.comment];
|
|
|
|
if (![comment isEqualToString:@""])
|
|
|
|
[htmlString appendFormat:@"<p>%@</p>", comment];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableArray* lists = [NSMutableArray array];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
if (inf.webseedCount > 0)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableString* listSection = [NSMutableString string];
|
|
|
|
[listSection appendString:@"<table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
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]];
|
|
|
|
[listSection appendFormat:@"<tr><th>%@</th></tr>", headerTitleString];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
for (int i = 0; i < inf.webseedCount; ++i)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
[listSection appendFormat:@"<tr><td>%s<td></tr>", inf.webseeds[i]];
|
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 20:14:26 +00:00
|
|
|
[listSection appendString:@"</table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
[lists addObject:listSection];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
if (inf.trackerCount > 0)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableString* listSection = [NSMutableString string];
|
|
|
|
[listSection appendString:@"<table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
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]];
|
|
|
|
[listSection appendFormat:@"<tr><th>%@</th></tr>", headerTitleString];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
#warning handle tiers?
|
|
|
|
for (int i = 0; i < inf.trackerCount; ++i)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
[listSection appendFormat:@"<tr><td>%s<td></tr>", inf.trackers[i].announce];
|
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 20:14:26 +00:00
|
|
|
[listSection appendString:@"</table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
[lists addObject:listSection];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2014-06-08 22:16:01 +00:00
|
|
|
if (inf.isFolder)
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSMutableString* listSection = [NSMutableString string];
|
|
|
|
[listSection appendString:@"<table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
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]];
|
|
|
|
[listSection appendFormat:@"<tr><th>%@</th></tr>", fileTitleString];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
#warning display size?
|
|
|
|
#warning display folders?
|
|
|
|
for (int i = 0; i < inf.fileCount; ++i)
|
|
|
|
{
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* fullFilePath = [NSString stringWithUTF8String:inf.files[i].name];
|
|
|
|
NSCAssert([fullFilePath hasPrefix:[name stringByAppendingString:@"/"]], @"Expected file path %@ to begin with %@/", fullFilePath, name);
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSString* shortenedFilePath = [fullFilePath substringFromIndex:[name length] + 1];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSUInteger const width = 16;
|
|
|
|
[listSection appendFormat:@"<tr><td><img class=\"icon\" src=\"%@\" width=\"%ld\" height=\"%ld\" />%@<td></tr>",
|
|
|
|
generateIconData([shortenedFilePath pathExtension], width, allImgProps),
|
|
|
|
width,
|
|
|
|
width,
|
|
|
|
shortenedFilePath];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 20:14:26 +00:00
|
|
|
[listSection appendString:@"</table>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
[lists addObject:listSection];
|
2012-08-12 12:11:33 +00:00
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 20:14:26 +00:00
|
|
|
if ([lists count] > 0)
|
2021-08-15 09:41:48 +00:00
|
|
|
{
|
|
|
|
[htmlString appendFormat:@"<hr/><br>%@", [lists componentsJoinedByString:@"<br>"]];
|
|
|
|
}
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
[htmlString appendString:@"</body></html>"];
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
tr_metainfoFree(&inf);
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
NSDictionary* props = @{
|
|
|
|
(NSString*)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8",
|
|
|
|
(NSString*)kQLPreviewPropertyMIMETypeKey : @"text/html",
|
|
|
|
(NSString*)kQLPreviewPropertyAttachmentsKey : allImgProps
|
|
|
|
};
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
QLPreviewRequestSetDataRepresentation(
|
|
|
|
preview,
|
|
|
|
(__bridge CFDataRef)[htmlString dataUsingEncoding:NSUTF8StringEncoding],
|
|
|
|
kUTTypeHTML,
|
|
|
|
(__bridge CFDictionaryRef)props);
|
2017-01-24 17:53:16 +00:00
|
|
|
|
2012-08-12 12:11:33 +00:00
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
2021-08-15 09:41:48 +00:00
|
|
|
void CancelPreviewGeneration(void* thisInterface, QLPreviewRequestRef preview)
|
2012-08-12 12:11:33 +00:00
|
|
|
{
|
|
|
|
// Implement only if supported
|
|
|
|
}
|