2015-10-01 18:19:52 +02:00
|
|
|
#import "TGUploadMediaSignals.h"
|
|
|
|
|
|
|
|
@implementation TGUploadMediaSignals
|
|
|
|
|
|
|
|
+ (SSignal *)uploadDataWithContext:(TGShareContext *)context data:(NSData *)data outFileId:(int64_t *)outFileId outLargeParts:(bool *)outLargeParts outNumberOfParts:(NSUInteger *)outNumberOfParts
|
|
|
|
{
|
|
|
|
int64_t fileId = 0;
|
|
|
|
arc4random_buf(&fileId, 8);
|
|
|
|
if (outFileId)
|
|
|
|
*outFileId = fileId;
|
|
|
|
|
|
|
|
NSUInteger partSize = 0;
|
|
|
|
if (data.length >= 10 * 1024 * 1024)
|
|
|
|
partSize = 512 * 1024;
|
|
|
|
else
|
|
|
|
partSize = 12 * 1024;
|
|
|
|
|
|
|
|
bool largeParts = partSize >= 500 * 1024;
|
|
|
|
if (outLargeParts)
|
|
|
|
*outLargeParts = largeParts;
|
|
|
|
|
|
|
|
NSUInteger numberOfParts = data.length / partSize + (data.length % partSize == 0 ? 0 : 1);
|
|
|
|
if (outNumberOfParts)
|
|
|
|
*outNumberOfParts = numberOfParts;
|
|
|
|
|
|
|
|
SSignal *uploadSignal = [[context connectionContextForDatacenter:context.mtProto.datacenterId] mapToSignal:^SSignal *(TGPooledDatacenterConnectionContext *datacenterContext)
|
|
|
|
{
|
|
|
|
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)
|
|
|
|
{
|
|
|
|
SDisposableSet *partsDisposables = [[SDisposableSet alloc] init];
|
|
|
|
NSMutableArray *partsSignals = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
for (NSUInteger i = 0; i < numberOfParts; i++)
|
|
|
|
{
|
|
|
|
SSignal *uploadPart = [[SSignal single:nil] mapToSignal:^SSignal *(__unused id next)
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_FunctionContext *functionContext = nil;
|
2015-10-01 18:19:52 +02:00
|
|
|
if (largeParts)
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
functionContext = [Api48 upload_saveBigFilePartWithFileId:@(fileId) filePart:@(i) fileTotalParts:@(numberOfParts) bytes:[data subdataWithRange:NSMakeRange(i * partSize, MIN(data.length - i * partSize, partSize))]];
|
2015-10-01 18:19:52 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
functionContext = [Api48 upload_saveFilePartWithFileId:@(fileId) filePart:@(i) bytes:[data subdataWithRange:NSMakeRange(i * partSize, MIN(data.length - i * partSize, partSize))]];
|
2015-10-01 18:19:52 +02:00
|
|
|
}
|
|
|
|
return [[datacenterContext.context function:functionContext] map:^id(__unused id next)
|
|
|
|
{
|
|
|
|
return @((float)i / (float)numberOfParts);
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
[partsSignals addObject:uploadPart];
|
|
|
|
}
|
|
|
|
|
|
|
|
__block bool cancelled = false;
|
|
|
|
|
|
|
|
SAtomic *partsState = [[SAtomic alloc] initWithValue:@{@"remainingParts": partsSignals, @"uploadingParts": @(0)}];
|
|
|
|
|
|
|
|
__block void (^maybeTakeParts)() = nil;
|
|
|
|
|
|
|
|
void (^maybeTakePartsImpl)() = ^
|
|
|
|
{
|
|
|
|
if (cancelled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
__block bool complete = false;
|
|
|
|
NSMutableArray *takenParts = [[NSMutableArray alloc] init];
|
|
|
|
|
|
|
|
[partsState modify:^id(NSDictionary *state)
|
|
|
|
{
|
|
|
|
if (((NSArray *)state[@"remainingParts"]).count == 0 && [state[@"uploadingParts"] intValue] <= 0)
|
|
|
|
{
|
|
|
|
complete = true;
|
|
|
|
}
|
|
|
|
else if (((NSArray *)state[@"remainingParts"]).count != 0 && [state[@"uploadingParts"] intValue] < 3)
|
|
|
|
{
|
|
|
|
NSMutableArray *remainingParts = [[NSMutableArray alloc] initWithArray:state[@"remainingParts"]];
|
|
|
|
for (int i = 0; i < 3 && remainingParts.count != 0; i++)
|
|
|
|
{
|
|
|
|
[takenParts addObject:remainingParts[0]];
|
|
|
|
[remainingParts removeObjectAtIndex:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
return @{@"remainingParts": remainingParts, @"uploadingParts": @([state[@"uploadingParts"] intValue] + takenParts.count)};
|
|
|
|
}
|
|
|
|
|
|
|
|
return state;
|
|
|
|
}];
|
|
|
|
|
|
|
|
if (complete)
|
|
|
|
[subscriber putCompletion];
|
|
|
|
else if (takenParts.count != 0)
|
|
|
|
{
|
|
|
|
for (SSignal *uploadPart in takenParts)
|
|
|
|
{
|
|
|
|
id<SDisposable> disposable = [uploadPart startWithNext:^(id next)
|
|
|
|
{
|
|
|
|
[subscriber putNext:next];
|
|
|
|
} error:^(id error)
|
|
|
|
{
|
|
|
|
[subscriber putError:error];
|
|
|
|
} completed:^
|
|
|
|
{
|
|
|
|
[partsState modify:^id(NSDictionary *state)
|
|
|
|
{
|
|
|
|
return @{@"remainingParts": state[@"remainingParts"], @"uploadingParts": @([state[@"uploadingParts"] intValue] - 1)};
|
|
|
|
}];
|
|
|
|
maybeTakeParts();
|
|
|
|
}];
|
|
|
|
[partsDisposables add:disposable];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
maybeTakeParts = [maybeTakePartsImpl copy];
|
|
|
|
|
|
|
|
[partsDisposables add:[[SBlockDisposable alloc] initWithBlock:^
|
|
|
|
{
|
|
|
|
cancelled = true;
|
|
|
|
}]];
|
|
|
|
|
|
|
|
maybeTakeParts();
|
|
|
|
|
|
|
|
return partsDisposables;
|
|
|
|
}];
|
|
|
|
|
|
|
|
/*SSignal *uploadPartsSignal = [SSignal complete];
|
|
|
|
for (NSUInteger i = 0; i < numberOfParts; i++)
|
|
|
|
{
|
|
|
|
SSignal *uploadPart = [[SSignal single:nil] mapToSignal:^SSignal *(__unused id next)
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_FunctionContext *functionContext = nil;
|
2015-10-01 18:19:52 +02:00
|
|
|
if (largeParts)
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
functionContext = [Api48 upload_saveBigFilePartWithFileId:@(fileId) filePart:@(i) fileTotalParts:@(numberOfParts) bytes:[data subdataWithRange:NSMakeRange(i * partSize, MIN(data.length - i * partSize, partSize))]];
|
2015-10-01 18:19:52 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-02-25 01:03:51 +01:00
|
|
|
functionContext = [Api48 upload_saveFilePartWithFileId:@(fileId) filePart:@(i) bytes:[data subdataWithRange:NSMakeRange(i * partSize, MIN(data.length - i * partSize, partSize))]];
|
2015-10-01 18:19:52 +02:00
|
|
|
}
|
|
|
|
return [[datacenterContext.context function:functionContext] map:^id(__unused id next)
|
|
|
|
{
|
|
|
|
return @((float)i / (float)numberOfParts);
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
uploadPartsSignal = [uploadPartsSignal then:uploadPart];
|
|
|
|
}
|
|
|
|
|
|
|
|
return uploadPartsSignal;*/
|
|
|
|
}];
|
|
|
|
|
|
|
|
return uploadSignal;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (SSignal *)uploadPhotoWithContext:(TGShareContext *)context data:(NSData *)data
|
|
|
|
{
|
|
|
|
int64_t fileId = 0;
|
|
|
|
bool largeParts = false;
|
|
|
|
NSUInteger numberOfParts = 0;
|
|
|
|
SSignal *uploadSignal = [self uploadDataWithContext:context data:data outFileId:&fileId outLargeParts:&largeParts outNumberOfParts:&numberOfParts];
|
|
|
|
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_InputFile *inputFile = nil;
|
2015-10-01 18:19:52 +02:00
|
|
|
if (largeParts)
|
2016-02-25 01:03:51 +01:00
|
|
|
inputFile = [Api48_InputFile inputFileBigWithPid:@(fileId) parts:@(numberOfParts) name:@"file.jpg"];
|
2015-10-01 18:19:52 +02:00
|
|
|
else
|
2016-02-25 01:03:51 +01:00
|
|
|
inputFile = [Api48_InputFile inputFileWithPid:@(fileId) parts:@(numberOfParts) name:@"file.jpg" md5Checksum:@""];
|
2015-10-01 18:19:52 +02:00
|
|
|
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_InputMedia_inputMediaUploadedPhoto *inputMedia = [Api48_InputMedia inputMediaUploadedPhotoWithFile:inputFile caption:@""];
|
2015-10-01 18:19:52 +02:00
|
|
|
|
|
|
|
uploadSignal = [uploadSignal then:[SSignal single:inputMedia]];
|
|
|
|
|
|
|
|
return uploadSignal;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (SSignal *)uploadFileWithContext:(TGShareContext *)context data:(NSData *)data name:(NSString *)name mimeType:(NSString *)mimeType attributes:(NSArray *)attributes
|
|
|
|
{
|
|
|
|
int64_t fileId = 0;
|
|
|
|
bool largeParts = false;
|
|
|
|
NSUInteger numberOfParts = 0;
|
|
|
|
SSignal *uploadSignal = [self uploadDataWithContext:context data:data outFileId:&fileId outLargeParts:&largeParts outNumberOfParts:&numberOfParts];
|
|
|
|
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_InputFile *inputFile = nil;
|
2015-10-01 18:19:52 +02:00
|
|
|
if (largeParts)
|
2016-02-25 01:03:51 +01:00
|
|
|
inputFile = [Api48_InputFile inputFileBigWithPid:@(fileId) parts:@(numberOfParts) name:name];
|
2015-10-01 18:19:52 +02:00
|
|
|
else
|
2016-02-25 01:03:51 +01:00
|
|
|
inputFile = [Api48_InputFile inputFileWithPid:@(fileId) parts:@(numberOfParts) name:name md5Checksum:@""];
|
2015-10-01 18:19:52 +02:00
|
|
|
|
|
|
|
NSMutableArray *completeAttributes = [[NSMutableArray alloc] init];
|
2016-02-25 01:03:51 +01:00
|
|
|
[completeAttributes addObject:[Api48_DocumentAttribute documentAttributeFilenameWithFileName:name]];
|
2015-10-01 18:19:52 +02:00
|
|
|
[completeAttributes addObjectsFromArray:attributes];
|
|
|
|
|
2016-02-25 01:03:51 +01:00
|
|
|
Api48_InputMedia_inputMediaUploadedDocument *inputMedia = [Api48_InputMedia inputMediaUploadedDocumentWithFile:inputFile mimeType:mimeType.length == 0 ? @"application/octet-stream" : mimeType attributes:completeAttributes caption:@""];
|
2015-10-01 18:19:52 +02:00
|
|
|
|
|
|
|
uploadSignal = [uploadSignal then:[SSignal single:inputMedia]];
|
|
|
|
|
|
|
|
return uploadSignal;
|
|
|
|
}
|
|
|
|
|
2016-02-25 01:03:51 +01:00
|
|
|
+ (SSignal *)uploadVideoWithContext:(TGShareContext *)context data:(NSData *)data thumbData:(NSData *)thumbData duration:(int32_t)duration width:(int32_t)width height:(int32_t)height mimeType:(NSString *)mimeType
|
|
|
|
{
|
|
|
|
int64_t fileId = 0;
|
|
|
|
bool largeParts = false;
|
|
|
|
NSUInteger numberOfParts = 0;
|
|
|
|
SSignal *uploadSignal = [self uploadDataWithContext:context data:data outFileId:&fileId outLargeParts:&largeParts outNumberOfParts:&numberOfParts];
|
|
|
|
|
|
|
|
NSString *fileName = @"file.mov";
|
|
|
|
Api48_InputFile *inputFile = nil;
|
|
|
|
if (largeParts)
|
|
|
|
inputFile = [Api48_InputFile inputFileBigWithPid:@(fileId) parts:@(numberOfParts) name:fileName];
|
|
|
|
else
|
|
|
|
inputFile = [Api48_InputFile inputFileWithPid:@(fileId) parts:@(numberOfParts) name:fileName md5Checksum:@""];
|
|
|
|
|
|
|
|
int64_t thumbFileId = 0;
|
|
|
|
bool thumbLargeParts = false;
|
|
|
|
NSUInteger thumbNumberOfParts = 0;
|
|
|
|
SSignal *thumbUploadSignal = [[self uploadDataWithContext:context data:thumbData outFileId:&thumbFileId outLargeParts:&thumbLargeParts outNumberOfParts:&thumbNumberOfParts] filter:^bool(id value)
|
|
|
|
{
|
|
|
|
return ![value isKindOfClass:[NSNumber class]];
|
|
|
|
}];
|
|
|
|
|
|
|
|
NSString *thumbFileName = @"file.jpg";
|
|
|
|
Api48_InputFile *inputThumbFile = nil;
|
|
|
|
if (thumbLargeParts)
|
|
|
|
inputThumbFile = [Api48_InputFile inputFileBigWithPid:@(thumbFileId) parts:@(thumbNumberOfParts) name:thumbFileName];
|
|
|
|
else
|
|
|
|
inputThumbFile = [Api48_InputFile inputFileWithPid:@(thumbFileId) parts:@(thumbNumberOfParts) name:thumbFileName md5Checksum:@""];
|
|
|
|
|
|
|
|
Api48_InputMedia_inputMediaUploadedThumbDocument *inputMedia = [Api48_InputMedia inputMediaUploadedThumbDocumentWithFile:inputFile thumb:inputThumbFile mimeType:@"video/mp4" attributes:@[
|
|
|
|
[Api48_DocumentAttribute documentAttributeVideoWithDuration:@(duration) w:@(width) h:@(height)],
|
|
|
|
[Api48_DocumentAttribute documentAttributeFilenameWithFileName:@"video.mp4"]
|
|
|
|
] caption:@""];
|
|
|
|
|
|
|
|
uploadSignal = [[uploadSignal then:thumbUploadSignal] then:[SSignal single:inputMedia]];
|
|
|
|
|
|
|
|
return uploadSignal;
|
|
|
|
}
|
|
|
|
|
2015-10-01 18:19:52 +02:00
|
|
|
@end
|