Skip to content

Commit

Permalink
Merge branch 'main' of https://github.com/chucker/Mastonaut
Browse files Browse the repository at this point in the history
  • Loading branch information
Sören Kuklau committed Nov 29, 2022
2 parents fa29cca + 91d2c73 commit 8d3b3c7
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 68 deletions.
12 changes: 6 additions & 6 deletions CoreTootin/Agents/AttachmentUploader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ public class AttachmentUploader
{
public typealias ConvertedDataProvider = () throws -> Data

public static let supportedImageTypes: [UTType] = [.jpeg, .png, .heic, .gif, .tiff]
public static let supportedMovieTypes: [UTType] = [.movie]
public static let supportedImageTypes = [kUTTypeJPEG, kUTTypePNG, kUTTypeJPEG2000, kUTTypeHEIC, kUTTypeGIF, kUTTypeTIFF]
public static let supportedMovieTypes = [kUTTypeMovie]
public static let supportedAttachmentTypes = supportedImageTypes + supportedMovieTypes
public static let maxAttachmentImageSize = NSSize(width: 4096, height: 4096)

public static let imageTypeConversionMap: [UTType: UTType] = [
// kUTTypeJPEG2000: kUTTypeJPEG, JPEG2000 no longer supported
.heic: .jpeg,
.tiff: .jpeg
public static let imageTypeConversionMap: [CFString: CFString] = [
kUTTypeJPEG2000: kUTTypeJPEG,
kUTTypeHEIC: kUTTypeJPEG,
kUTTypeTIFF: kUTTypeJPEG
]

private var activeUploadFutures: [Upload: FutureTask] = [:]
Expand Down
9 changes: 4 additions & 5 deletions CoreTootin/Agents/FileMetadataGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,16 @@ public struct FileMetadataGenerator
{
assert(!Thread.isMainThread)

if fileUrl.fileConforms(toUTT: .movie)
if fileUrl.fileConforms(toUTI: kUTTypeMovie)
{
/* TODO: AVAsset.duration has been deprecated, and we're supposed to use
* an async call to asset.load(.duration) to fetch metadata and then do stuff with it.
* That introduces async/await into the mix, which is fine, but is out of the scope
* of the current work. */
let asset = AVURLAsset(url: fileUrl)
let duration = asset.duration.seconds
return .movie(duration: duration)
let duration = AVURLAsset(url: fileUrl).duration.seconds
return .movie(duration: duration)
}
else if fileUrl.fileConforms(toUTT: .image)
else if fileUrl.fileConforms(toUTI: kUTTypeImage)
{
guard
let imageSource = CGImageSourceCreateWithURL(fileUrl as CFURL, nil),
Expand Down
17 changes: 8 additions & 9 deletions CoreTootin/Agents/ImageRestrainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,22 @@
//

import AppKit
import UniformTypeIdentifiers

public class ImageRestrainer {
let typeConversionMap: [UTType: UTType]
let typeConversionMap: [CFString: CFString]
let maximumImageSize: NSSize

init(typeConversionMap: [UTType: UTType], maximumImageSize: NSSize) {
init(typeConversionMap: [CFString: CFString], maximumImageSize: NSSize) {
self.typeConversionMap = typeConversionMap
self.maximumImageSize = maximumImageSize
}

func restrain(imageAtURL fileURL: URL, fileUTT: UTType) throws -> Data {
let restrainedFileUTT = restrain(type: fileUTT)
func restrain(imageAtURL fileURL: URL, fileUTI: CFString) throws -> Data {
let restrainedFileUTI = restrain(type: fileUTI)

func fallbackData() throws -> Data {
let image = NSImage(byReferencing: fileURL)
return try restrain(staticImage: image).dataUsingRepresentation(for: restrainedFileUTT)
return try restrain(staticImage: image).dataUsingRepresentation(for: restrainedFileUTI)
}

// Check if this is an animation. If not, just use the static image restrainer.
Expand All @@ -50,10 +49,10 @@ public class ImageRestrainer {
let frameCount = CGImageSourceGetCount(imageSource)

// Check if animated image needs to be resized or converted
guard originalSize.area >= maximumImageSize.area || restrainedFileUTT != fileUTT,
guard originalSize.area >= maximumImageSize.area || restrainedFileUTI != fileUTI,
let destinationData = CFDataCreateMutable(kCFAllocatorDefault, originalData.count),
let imageDestination = CGImageDestinationCreateWithData(destinationData,
restrainedFileUTT.identifier as CFString,
restrainedFileUTI,
frameCount, nil)
else {
return originalData
Expand Down Expand Up @@ -92,7 +91,7 @@ public class ImageRestrainer {
return staticImage.resizedImage(withSize: newSize)
}

func restrain(type: UTType) -> UTType {
func restrain(type: CFString) -> CFString {
return typeConversionMap[type] ?? type
}
}
19 changes: 9 additions & 10 deletions CoreTootin/Extensions/NSImage+Additions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
//

import AppKit
import UniformTypeIdentifiers

public extension NSImage
{
Expand Down Expand Up @@ -73,23 +72,23 @@ public extension NSImage
.max(by: { ($0.pixelsWide * $0.pixelsHigh) < ($1.pixelsWide * $1.pixelsHigh) })
}

private static let utiTypeMap: [UTType: NSBitmapImageRep.FileType] = [
.png: .png,
.jpeg: .jpeg,
// kUTTypeJPEG2000: .jpeg2000, -- JPEG2000 is no longer supported in macOS
.gif: .gif,
.bmp: .bmp,
.tiff: .tiff
private static let utiTypeMap: [CFString: NSBitmapImageRep.FileType] = [
kUTTypePNG: .png,
kUTTypeJPEG: .jpeg,
kUTTypeJPEG2000: .jpeg2000,
kUTTypeGIF: .gif,
kUTTypeBMP: .bmp,
kUTTypeTIFF: .tiff
]

func dataUsingRepresentation(for UTT: UTType?) throws -> Data
func dataUsingRepresentation(for UTI: CFString?) throws -> Data
{
guard let rawData = tiffRepresentation, let bitmap = NSBitmapImageRep(data: rawData) else
{
throw EncodeErrors.noRawData
}

guard let fileType = NSImage.utiTypeMap[UTT ?? .png] else
guard let fileType = NSImage.utiTypeMap[UTI ?? kUTTypePNG] else
{
throw EncodeErrors.unknownExpectedFormat
}
Expand Down
44 changes: 38 additions & 6 deletions CoreTootin/Extensions/URL+Additions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,56 @@ import UniformTypeIdentifiers

public extension URL
{
var fileUTT: UTType?
var fileUTI: String?
{
return (try? resourceValues(forKeys: [.contentTypeKey]).contentType)
return (try? resourceValues(forKeys: [.typeIdentifierKey]).typeIdentifier) ?? fallbackFileUTI
}

/// This routine allows computing the UTI for remote URLs and URLs for files that don't exist.
private var fallbackFileUTI: String?
{
let utiRef = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)

guard let utiCFString = utiRef?.takeUnretainedValue() else
{
utiRef?.release()
return nil
}

let utiString = String(utiCFString)
utiRef?.release()
return utiString
}

var preferredMimeType: String?
{
return fileUTT?.preferredMIMEType
guard
let fileUTI = self.fileUTI,
let mimeReference = UTTypeCopyPreferredTagWithClass(fileUTI as CFString, kUTTagClassMIMEType)
else
{
return nil
}

let mimeType = String(mimeReference.takeUnretainedValue())
mimeReference.release()

return mimeType
}

func fileConforms(toUTI: String) -> Bool
{
return fileConforms(toUTI: toUTI as CFString)
}

func fileConforms(toUTT: UTType) -> Bool
func fileConforms(toUTI: CFString) -> Bool
{
guard let fileUTT = self.fileUTT else
guard let fileUTI = self.fileUTI else
{
return false
}

return fileUTT.conforms(to: toUTT)
return UTTypeConformsTo(fileUTI as CFString, toUTI)
}

var mastodonHandleFromAccountURI: String
Expand Down
13 changes: 6 additions & 7 deletions CoreTootin/Models/Upload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import Foundation
import MastodonKit
import AppKit
import UniformTypeIdentifiers

public class Upload
{
Expand All @@ -45,13 +44,13 @@ public class Upload

public init?(fileUrl: URL, imageRestrainer: ImageRestrainer)
{
guard let preferredMimeType = fileUrl.preferredMimeType, let fileUTT = fileUrl.fileUTT else { return nil }
guard let preferredMimeType = fileUrl.preferredMimeType, let fileUTI = fileUrl.fileUTI else { return nil }

if fileUTT.conforms(to: .image)
if UTTypeConformsTo(fileUTI as CFString, kUTTypeImage)
{
let restrainedType = imageRestrainer.restrain(type: fileUTT)
dataLoader = { try imageRestrainer.restrain(imageAtURL: fileUrl, fileUTT: restrainedType) }
mimeType = restrainedType.preferredMIMEType ?? "*/*"
let restrainedType = imageRestrainer.restrain(type: fileUTI as CFString)
dataLoader = { try imageRestrainer.restrain(imageAtURL: fileUrl, fileUTI: restrainedType) }
mimeType = restrainedType as String
}
else
{
Expand All @@ -72,7 +71,7 @@ public class Upload
fileExtension = "png"
fileName = nil
mimeType = "image/png"
dataLoader = { try image.dataUsingRepresentation(for: .png) }
dataLoader = { try image.dataUsingRepresentation(for: kUTTypePNG) }
thumbnailProvider = { image }

let selfPromise = WeakPromise<Upload>()
Expand Down
3 changes: 1 addition & 2 deletions CoreTootin/Subcontrollers/AttachmentsSubcontroller.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import AppKit
import MastodonKit
import UniformTypeIdentifiers

@objc public protocol StatusComposerController: AnyObject
{
Expand Down Expand Up @@ -440,7 +439,7 @@ public extension AttachmentsSubcontroller
{
func addAttachments(pasteboard: NSPasteboard) -> Bool
{
let types = AttachmentUploader.supportedAttachmentTypes
let types = AttachmentUploader.supportedAttachmentTypes as [String]

if let fileUrls = pasteboard.readObjects(forClasses: [NSURL.self],
options: [.urlReadingContentsConformToTypes: types,
Expand Down
3 changes: 1 addition & 2 deletions Mastonaut/Controls/FileDropImageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@
//

import Cocoa
import UniformTypeIdentifiers

class FileDropImageView: NSImageView
{
var allowedDropFileTypes: [UTType]? = nil
var allowedDropFileTypes: [CFString]? = nil

private(set) var isReceivingDrag: Bool = false
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class AccountsPreferencesController: BaseAccountsPreferencesViewController
private lazy var openPanel: NSOpenPanel = {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.allowedContentTypes = AttachmentUploader.supportedImageTypes
panel.allowedFileTypes = AttachmentUploader.supportedImageTypes.map({ $0 as String })
panel.message = 🔠("Select an image to upload.")
panel.prompt = 🔠("Upload")
return panel
Expand Down
8 changes: 4 additions & 4 deletions Mastonaut/Window Controllers/AttachmentWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,12 @@ private extension AttachmentWindowController
{
guard
let image = currentAttachment?.image,
let fileType = currentAttachment?.attachment.parsedUrl.fileUTT
let fileType = currentAttachment?.attachment.parsedUrl.fileUTI
else { return }

do
{
try image.dataUsingRepresentation(for: fileType).write(to: url)
try image.dataUsingRepresentation(for: fileType as CFString).write(to: url)
}
catch
{
Expand Down Expand Up @@ -424,11 +424,11 @@ extension AttachmentWindowController
guard
let window = self.window,
let attachment = currentAttachment?.attachment,
let fileType = attachment.parsedUrl.fileUTT
let fileType = attachment.parsedUrl.fileUTI
else { return }

let savePanel = NSSavePanel()
savePanel.allowedContentTypes = [fileType]
savePanel.allowedFileTypes = [fileType]
savePanel.nameFieldStringValue = attachment.parsedUrl.lastPathComponent
savePanel.beginSheetModal(for: window)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ class StatusComposerWindowController: NSWindowController, UserPopUpButtonDisplay
private lazy var openPanel: NSOpenPanel = {
let panel = NSOpenPanel()
panel.allowsMultipleSelection = true
panel.allowedContentTypes = AttachmentUploader.supportedAttachmentTypes
panel.allowedFileTypes = AttachmentUploader.supportedAttachmentTypes.map({ $0 as String })
panel.message = 🔠("Select one or more files to upload and attach to your status.")
panel.prompt = 🔠("Attach")
return panel
Expand Down
17 changes: 8 additions & 9 deletions QuickToot/ShareViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import Cocoa
import CoreTootin
import MastodonKit
import UniformTypeIdentifiers

class ShareViewController: NSViewController, UserPopUpButtonDisplaying
{
Expand Down Expand Up @@ -268,17 +267,17 @@ class ShareViewController: NSViewController, UserPopUpButtonDisplaying

for attachment in attachments
{
if attachment.hasItemConformingToTypeIdentifier(UTType.url.identifier)
if attachment.hasItemConformingToTypeIdentifier(kUTTypeURL as String)
{
dispatchGroup.enter()
loadURL(from: attachment) { dispatchGroup.leave() }
}
else if attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier)
else if attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String)
{
dispatchGroup.enter()
loadImage(from: attachment) { dispatchGroup.leave() }
}
else if attachment.hasItemConformingToTypeIdentifier(UTType.plainText.identifier)
else if attachment.hasItemConformingToTypeIdentifier(kUTTypePlainText as String)
{
dispatchGroup.enter()
loadString(from: attachment) { dispatchGroup.leave() }
Expand All @@ -299,12 +298,12 @@ class ShareViewController: NSViewController, UserPopUpButtonDisplaying

private func loadURL(from attachment: NSItemProvider, completion: @escaping () -> Void)
{
let supportedUTTs = AttachmentUploader.supportedAttachmentTypes
let supportedUTIs = AttachmentUploader.supportedAttachmentTypes

attachment.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { [weak self] (object, _) in
attachment.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { [weak self] (object, _) in
if let url = object as? URL, let self = self
{
if url.isFileURL, let utt = url.fileUTT, supportedUTTs.contains(utt)
if url.isFileURL, let uti = url.fileUTI, supportedUTIs.contains(uti as CFString)
{
self.attachmentsSubcontroller.addAttachments([url])
}
Expand All @@ -320,7 +319,7 @@ class ShareViewController: NSViewController, UserPopUpButtonDisplaying

private func loadImage(from attachment: NSItemProvider, completion: @escaping () -> Void)
{
attachment.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { [weak self] (object, _) in
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil) { [weak self] (object, _) in
if let image = object as? NSImage
{
self?.attachmentsSubcontroller.addAttachments([image])
Expand All @@ -331,7 +330,7 @@ class ShareViewController: NSViewController, UserPopUpButtonDisplaying

private func loadString(from attachment: NSItemProvider, completion: @escaping () -> Void)
{
attachment.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { [weak self] (object, _) in
attachment.loadItem(forTypeIdentifier: kUTTypePlainText as String, options: nil) { [weak self] (object, _) in
if let string = object as? String
{
self?.textElementsToInsert.insert(string, at: 0)
Expand Down
Loading

0 comments on commit 8d3b3c7

Please sign in to comment.