Generate video thumb from file url instead of public url (#6922)

This commit is contained in:
Elias Nahum
2023-01-05 13:49:04 +02:00
committed by GitHub
parent 001a6699fb
commit ca1f6df1c6
5 changed files with 122 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
@@ -20,8 +21,12 @@ import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.mattermost.helpers.Credentials;
import com.reactlibrary.createthumbnail.CreateThumbnailModule;
import com.mattermost.helpers.RealPathUtil;
import java.io.File;
@@ -29,6 +34,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.nio.channels.FileChannel;
public class MattermostManagedModule extends ReactContextBaseJavaModule {
@@ -206,6 +212,30 @@ public class MattermostManagedModule extends ReactContextBaseJavaModule {
}
}
@ReactMethod
public void createThumbnail(ReadableMap options, Promise promise) {
try {
WritableMap optionsMap = Arguments.createMap();
optionsMap.merge(options);
String url = options.hasKey("url") ? options.getString("url") : "";
URL videoUrl = new URL(url);
String serverUrl = videoUrl.getProtocol() + "://" + videoUrl.getHost() + ":" + videoUrl.getPort();
String token = Credentials.getCredentialsForServerSync(this.reactContext, serverUrl);
if (!TextUtils.isEmpty(token)) {
WritableMap headers = Arguments.createMap();
if (optionsMap.hasKey("headers")) {
headers.merge(optionsMap.getMap("headers"));
}
headers.putString("Authorization", "Bearer " + token);
optionsMap.putMap("headers", headers);
}
CreateThumbnailModule thumb = new CreateThumbnailModule(this.reactContext);
thumb.create(optionsMap.copy(), promise);
} catch (Exception e) {
promise.reject("CreateThumbnail_ERROR", e);
}
}
private static class SaveDataTask extends GuardedResultAsyncTask<Object> {
private final WeakReference<Context> weakContext;
private final String fromFile;

View File

@@ -2,11 +2,10 @@
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {StyleSheet, useWindowDimensions, View} from 'react-native';
import {createThumbnail} from 'react-native-create-thumbnail';
import {StyleSheet, useWindowDimensions, View, NativeModules} from 'react-native';
import {updateLocalFile} from '@actions/local/file';
import {buildFilePreviewUrl, fetchPublicLink} from '@actions/remote/file';
import {buildFilePreviewUrl, buildFileUrl} from '@actions/remote/file';
import CompassIcon from '@components/compass_icon';
import ProgressiveImage from '@components/progressive_image';
import {useServerUrl} from '@context/server';
@@ -18,6 +17,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import FileIcon from './file_icon';
import type {ResizeMode} from 'react-native-fast-image';
const {createThumbnail} = NativeModules.MattermostManaged;
type Props = {
index: number;
@@ -84,11 +84,9 @@ const VideoFile = ({
try {
const exists = data.mini_preview ? await fileExists(data.mini_preview) : false;
if (!data.mini_preview || !exists) {
// We use the public link to avoid having to pass the token through a third party
// library
const publicUri = await fetchPublicLink(serverUrl, data.id!);
if (('link') in publicUri) {
const {path: uri, height, width} = await createThumbnail({url: data.localPath || publicUri.link, timeStamp: 2000});
const videoUrl = buildFileUrl(serverUrl, data.id!);
if (videoUrl) {
const {path: uri, height, width} = await createThumbnail({url: data.localPath || videoUrl, timeStamp: 2000});
data.mini_preview = uri;
data.height = height;
data.width = width;

View File

@@ -27,4 +27,12 @@ import Gekidou
@objc func setPreference(_ value: Any?, forKey name: String) {
Preferences.default.set(value, forKey: name)
}
@objc func getToken(for url: String) -> String? {
if let token = try? Keychain.default.getToken(for: url) {
return token
}
return nil
}
}

View File

@@ -8,6 +8,8 @@
#import "AppDelegate.h"
#import "MattermostManaged.h"
#import "CreateThumbnail.h"
#import "Mattermost-Swift.h"
@implementation MattermostManaged
@@ -107,7 +109,6 @@ RCT_EXPORT_METHOD(renameDatabase: (NSString *)databaseName to: (NSString *) new
NSDictionary *appGroupDir = [self appGroupSharedDirectory];
NSString *databaseDir;
NSString *newDBDir;
if(databaseName){
databaseDir = [NSString stringWithFormat:@"%@/%@%@", appGroupDir[@"databasePath"], databaseName , @".db"];
@@ -200,4 +201,27 @@ RCT_EXPORT_METHOD(lockPortrait)
}
RCT_EXPORT_METHOD(createThumbnail:(NSDictionary *)config findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
NSMutableDictionary *newConfig = [config mutableCopy];
NSMutableDictionary *headers = [config[@"headers"] ?: @{} mutableCopy];
NSString *url = (NSString *)[config objectForKey:@"url"] ?: @"";
NSURL *vidURL = nil;
NSString *url_ = [url lowercaseString];
if ([url_ hasPrefix:@"http://"] || [url_ hasPrefix:@"https://"] || [url_ hasPrefix:@"file://"]) {
vidURL = [NSURL URLWithString:url];
NSString *serverUrl = [NSString stringWithFormat:@"%@://%@:%@", vidURL.scheme, vidURL.host, vidURL.port];
if (vidURL != nil) {
NSString *token = [[GekidouWrapper default] getTokenFor:serverUrl];
if (token != nil) {
headers[@"Authorization"] = [NSString stringWithFormat:@"Bearer %@", token];
newConfig[@"headers"] = headers;
}
}
}
[CreateThumbnail create:newConfig findEventsWithResolver:resolve rejecter:reject];
}
@end

View File

@@ -0,0 +1,53 @@
diff --git a/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.h b/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.h
index 28b1d9b..cb63c52 100644
--- a/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.h
+++ b/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.h
@@ -3,5 +3,5 @@
#import <UIKit/UIKit.h>
@interface CreateThumbnail : NSObject <RCTBridgeModule>
-
++(void)create:(NSDictionary *)config findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject;
@end
diff --git a/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.m b/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.m
index 92cc49f..23cc83c 100644
--- a/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.m
+++ b/node_modules/react-native-create-thumbnail/ios/CreateThumbnail.m
@@ -6,6 +6,10 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_METHOD(create:(NSDictionary *)config findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
+ [CreateThumbnail create:config findEventsWithResolver:resolve rejecter:reject];
+}
+
++(void) create:(NSDictionary *)config findEventsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
NSString *url = (NSString *)[config objectForKey:@"url"] ?: @"";
int timeStamp = [[config objectForKey:@"timeStamp"] intValue] ?: 0;
NSString *format = (NSString *)[config objectForKey:@"format"] ?: @"jpeg";
@@ -82,7 +86,7 @@ RCT_EXPORT_METHOD(create:(NSDictionary *)config findEventsWithResolver:(RCTPromi
}
}
-- (unsigned long long) sizeOfFolderAtPath:(NSString *)path {
++ (unsigned long long) sizeOfFolderAtPath:(NSString *)path {
NSArray *files = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:nil];
NSEnumerator *enumerator = [files objectEnumerator];
NSString *fileName;
@@ -93,7 +97,7 @@ RCT_EXPORT_METHOD(create:(NSDictionary *)config findEventsWithResolver:(RCTPromi
return size;
}
-- (void) cleanDir:(NSString *)path forSpace:(unsigned long long)size {
++ (void) cleanDir:(NSString *)path forSpace:(unsigned long long)size {
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
unsigned long long deletedSize = 0;
@@ -110,7 +114,7 @@ RCT_EXPORT_METHOD(create:(NSDictionary *)config findEventsWithResolver:(RCTPromi
return;
}
-- (void) generateThumbImage:(AVURLAsset *)asset atTime:(int)timeStamp completion:(void (^)(UIImage* thumbnail))completion failure:(void (^)(NSError* error))failure {
++ (void) generateThumbImage:(AVURLAsset *)asset atTime:(int)timeStamp completion:(void (^)(UIImage* thumbnail))completion failure:(void (^)(NSError* error))failure {
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generator.appliesPreferredTrackTransform = YES;
generator.maximumSize = CGSizeMake(512, 512);