2014-05-30 08:53:19 +00:00
/*
This file is part of Telegram Desktop ,
2014-12-01 10:47:38 +00:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 08:53:19 +00:00
Telegram Desktop is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
It is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2014-12-01 10:47:38 +00:00
Copyright ( c ) 2014 John Preston , https : //desktop.telegram.org
2014-05-30 08:53:19 +00:00
*/
# include "stdafx.h"
# include "fileuploader.h"
2014-06-16 09:31:10 +00:00
FileUploader : : FileUploader ( ) : sentSize ( 0 ) , uploading ( 0 ) {
2014-10-30 16:23:44 +00:00
memset ( sentSizes , 0 , sizeof ( sentSizes ) ) ;
2014-05-30 08:53:19 +00:00
nextTimer . setSingleShot ( true ) ;
connect ( & nextTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( sendNext ( ) ) ) ;
2014-10-30 16:23:44 +00:00
killSessionsTimer . setSingleShot ( true ) ;
connect ( & killSessionsTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( killSessions ( ) ) ) ;
2014-05-30 08:53:19 +00:00
}
void FileUploader : : uploadMedia ( MsgId msgId , const ReadyLocalMedia & media ) {
if ( media . type = = ToPreparePhoto ) {
App : : feedPhoto ( media . photo , media . photoThumbs ) ;
} else if ( media . type = = ToPrepareDocument ) {
DocumentData * document ;
if ( media . photoThumbs . isEmpty ( ) ) {
2015-01-02 14:55:24 +00:00
document = App : : feedDocument ( media . document ) ;
2014-05-30 08:53:19 +00:00
} else {
2015-01-02 14:55:24 +00:00
document = App : : feedDocument ( media . document , media . photoThumbs . begin ( ) . value ( ) ) ;
2014-05-30 08:53:19 +00:00
}
document - > status = FileUploading ;
if ( ! media . file . isEmpty ( ) ) {
2015-05-19 15:46:45 +00:00
document - > location = FileLocation ( StorageFilePartial , media . file ) ;
2014-05-30 08:53:19 +00:00
}
2015-05-29 18:52:43 +00:00
} else if ( media . type = = ToPrepareAudio ) {
AudioData * audio = App : : feedAudio ( media . audio ) ;
audio - > status = FileUploading ;
audio - > data = media . data ;
2014-05-30 08:53:19 +00:00
}
queue . insert ( msgId , File ( media ) ) ;
sendNext ( ) ;
}
void FileUploader : : currentFailed ( ) {
Queue : : iterator j = queue . find ( uploading ) ;
if ( j ! = queue . end ( ) ) {
if ( j - > media . type = = ToPreparePhoto ) {
emit photoFailed ( j . key ( ) ) ;
} else if ( j - > media . type = = ToPrepareDocument ) {
DocumentData * doc = App : : document ( j - > media . id ) ;
if ( doc - > status = = FileUploading ) {
doc - > status = FileFailed ;
}
emit documentFailed ( j . key ( ) ) ;
2015-05-29 18:52:43 +00:00
} else if ( j - > media . type = = ToPrepareAudio ) {
AudioData * audio = App : : audio ( j - > media . id ) ;
if ( audio - > status = = FileUploading ) {
audio - > status = FileFailed ;
}
emit audioFailed ( j . key ( ) ) ;
2014-05-30 08:53:19 +00:00
}
queue . erase ( j ) ;
}
requestsSent . clear ( ) ;
docRequestsSent . clear ( ) ;
2014-10-30 16:23:44 +00:00
dcMap . clear ( ) ;
2014-05-30 08:53:19 +00:00
uploading = 0 ;
sentSize = 0 ;
2014-10-30 16:23:44 +00:00
for ( int i = 0 ; i < MTPUploadSessionsCount ; + + i ) {
sentSizes [ i ] = 0 ;
}
2014-05-30 08:53:19 +00:00
sendNext ( ) ;
}
2014-10-30 16:23:44 +00:00
void FileUploader : : killSessions ( ) {
for ( int i = 0 ; i < MTPUploadSessionsCount ; + + i ) {
2014-12-05 13:44:27 +00:00
MTP : : stopSession ( MTP : : upl [ i ] ) ;
2014-10-30 16:23:44 +00:00
}
}
2014-05-30 08:53:19 +00:00
void FileUploader : : sendNext ( ) {
2014-10-30 16:23:44 +00:00
if ( sentSize > = MaxUploadFileParallelSize ) return ;
bool killing = killSessionsTimer . isActive ( ) ;
if ( queue . isEmpty ( ) ) {
if ( ! killing ) {
2014-11-05 17:43:32 +00:00
killSessionsTimer . start ( MTPAckSendWaiting + MTPKillFileSessionTimeout ) ;
2014-10-30 16:23:44 +00:00
}
return ;
}
2014-05-30 08:53:19 +00:00
2014-10-30 16:23:44 +00:00
if ( killing ) {
killSessionsTimer . stop ( ) ;
}
2014-05-30 08:53:19 +00:00
Queue : : iterator i = uploading ? queue . find ( uploading ) : queue . begin ( ) ;
if ( ! uploading ) {
uploading = i . key ( ) ;
} else if ( i = = queue . end ( ) ) {
i = queue . begin ( ) ;
uploading = i . key ( ) ;
}
2014-10-30 16:23:44 +00:00
int todc = 0 ;
for ( int dc = 1 ; dc < MTPUploadSessionsCount ; + + dc ) {
if ( sentSizes [ dc ] < sentSizes [ todc ] ) {
todc = dc ;
}
}
2014-05-30 08:53:19 +00:00
if ( i - > media . parts . isEmpty ( ) ) {
if ( i - > docSentParts > = i - > docPartsCount ) {
if ( requestsSent . isEmpty ( ) & & docRequestsSent . isEmpty ( ) ) {
if ( i - > media . type = = ToPreparePhoto ) {
emit photoReady ( uploading , MTP_inputFile ( MTP_long ( i - > media . id ) , MTP_int ( i - > partsCount ) , MTP_string ( i - > media . filename ) , MTP_string ( i - > media . jpeg_md5 ) ) ) ;
} else if ( i - > media . type = = ToPrepareDocument ) {
QByteArray docMd5 ( 32 , Qt : : Uninitialized ) ;
2015-05-29 18:52:43 +00:00
hashMd5Hex ( i - > md5Hash . result ( ) , docMd5 . data ( ) ) ;
2014-05-30 08:53:19 +00:00
MTPInputFile doc = ( i - > docSize > UseBigFilesFrom ) ? MTP_inputFileBig ( MTP_long ( i - > media . id ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > media . filename ) ) : MTP_inputFile ( MTP_long ( i - > media . id ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > media . filename ) , MTP_string ( docMd5 ) ) ;
if ( i - > partsCount ) {
2015-01-05 20:17:33 +00:00
emit thumbDocumentReady ( uploading , doc , MTP_inputFile ( MTP_long ( i - > media . thumbId ) , MTP_int ( i - > partsCount ) , MTP_string ( qsl ( " thumb. " ) + i - > media . thumbExt ) , MTP_string ( i - > media . jpeg_md5 ) ) ) ;
2014-05-30 08:53:19 +00:00
} else {
emit documentReady ( uploading , doc ) ;
}
2015-05-29 18:52:43 +00:00
} else if ( i - > media . type = = ToPrepareAudio ) {
QByteArray audioMd5 ( 32 , Qt : : Uninitialized ) ;
hashMd5Hex ( i - > md5Hash . result ( ) , audioMd5 . data ( ) ) ;
MTPInputFile audio = ( i - > docSize > UseBigFilesFrom ) ? MTP_inputFileBig ( MTP_long ( i - > media . id ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > media . filename ) ) : MTP_inputFile ( MTP_long ( i - > media . id ) , MTP_int ( i - > docPartsCount ) , MTP_string ( i - > media . filename ) , MTP_string ( audioMd5 ) ) ;
emit audioReady ( uploading , audio ) ;
2014-05-30 08:53:19 +00:00
}
queue . remove ( uploading ) ;
uploading = 0 ;
sendNext ( ) ;
}
return ;
}
QByteArray toSend ;
if ( i - > media . data . isEmpty ( ) ) {
if ( ! i - > docFile ) {
i - > docFile . reset ( new QFile ( i - > media . file ) ) ;
if ( ! i - > docFile - > open ( QIODevice : : ReadOnly ) ) {
currentFailed ( ) ;
return ;
}
}
toSend = i - > docFile - > read ( i - > docPartSize ) ;
if ( i - > docSize < = UseBigFilesFrom ) {
2015-05-29 18:52:43 +00:00
i - > md5Hash . feed ( toSend . constData ( ) , toSend . size ( ) ) ;
2014-05-30 08:53:19 +00:00
}
} else {
toSend = i - > media . data . mid ( i - > docSentParts * i - > docPartSize , i - > docPartSize ) ;
2015-05-29 18:52:43 +00:00
if ( ( i - > media . type = = ToPrepareDocument | | i - > media . type = = ToPrepareAudio ) & & i - > docSentParts < = UseBigFilesFrom ) {
i - > md5Hash . feed ( toSend . constData ( ) , toSend . size ( ) ) ;
2014-07-04 11:12:54 +00:00
}
2014-05-30 08:53:19 +00:00
}
2014-06-16 09:31:10 +00:00
if ( toSend . size ( ) > i - > docPartSize | | ( toSend . size ( ) < i - > docPartSize & & i - > docSentParts + 1 ! = i - > docPartsCount ) ) {
2014-05-30 08:53:19 +00:00
currentFailed ( ) ;
return ;
}
mtpRequestId requestId ;
if ( i - > docSize > UseBigFilesFrom ) {
2014-10-30 16:23:44 +00:00
requestId = MTP : : send ( MTPupload_SaveBigFilePart ( MTP_long ( i - > media . id ) , MTP_int ( i - > docSentParts ) , MTP_int ( i - > docPartsCount ) , MTP_string ( toSend ) ) , rpcDone ( & FileUploader : : partLoaded ) , rpcFail ( & FileUploader : : partFailed ) , MTP : : upl [ todc ] ) ;
2014-05-30 08:53:19 +00:00
} else {
2014-10-30 16:23:44 +00:00
requestId = MTP : : send ( MTPupload_SaveFilePart ( MTP_long ( i - > media . id ) , MTP_int ( i - > docSentParts ) , MTP_string ( toSend ) ) , rpcDone ( & FileUploader : : partLoaded ) , rpcFail ( & FileUploader : : partFailed ) , MTP : : upl [ todc ] ) ;
2014-05-30 08:53:19 +00:00
}
docRequestsSent . insert ( requestId , i - > docSentParts ) ;
2014-10-30 16:23:44 +00:00
dcMap . insert ( requestId , todc ) ;
2014-05-30 08:53:19 +00:00
sentSize + = i - > docPartSize ;
2014-10-30 16:23:44 +00:00
sentSizes [ todc ] + = i - > docPartSize ;
2014-05-30 08:53:19 +00:00
i - > docSentParts + + ;
} else {
LocalFileParts : : iterator part = i - > media . parts . begin ( ) ;
2015-01-05 20:17:33 +00:00
mtpRequestId requestId = MTP : : send ( MTPupload_SaveFilePart ( MTP_long ( i - > media . thumbId ) , MTP_int ( part . key ( ) ) , MTP_string ( part . value ( ) ) ) , rpcDone ( & FileUploader : : partLoaded ) , rpcFail ( & FileUploader : : partFailed ) , MTP : : upl [ todc ] ) ;
2014-05-30 08:53:19 +00:00
requestsSent . insert ( requestId , part . value ( ) ) ;
2014-10-30 16:23:44 +00:00
dcMap . insert ( requestId , todc ) ;
2014-05-30 08:53:19 +00:00
sentSize + = part . value ( ) . size ( ) ;
2014-10-30 16:23:44 +00:00
sentSizes [ todc ] + = part . value ( ) . size ( ) ;
2014-05-30 08:53:19 +00:00
i - > media . parts . erase ( part ) ;
}
nextTimer . start ( UploadRequestInterval ) ;
}
void FileUploader : : cancel ( MsgId msgId ) {
uploaded . remove ( msgId ) ;
if ( uploading = = msgId ) {
currentFailed ( ) ;
} else {
queue . remove ( msgId ) ;
}
}
void FileUploader : : confirm ( MsgId msgId ) {
}
void FileUploader : : clear ( ) {
uploaded . clear ( ) ;
queue . clear ( ) ;
for ( QMap < mtpRequestId , QByteArray > : : const_iterator i = requestsSent . cbegin ( ) , e = requestsSent . cend ( ) ; i ! = e ; + + i ) {
MTP : : cancel ( i . key ( ) ) ;
}
requestsSent . clear ( ) ;
for ( QMap < mtpRequestId , int32 > : : const_iterator i = docRequestsSent . cbegin ( ) , e = docRequestsSent . cend ( ) ; i ! = e ; + + i ) {
MTP : : cancel ( i . key ( ) ) ;
}
docRequestsSent . clear ( ) ;
2014-10-30 16:23:44 +00:00
dcMap . clear ( ) ;
2014-05-30 08:53:19 +00:00
sentSize = 0 ;
2014-10-30 16:23:44 +00:00
for ( int32 i = 0 ; i < MTPUploadSessionsCount ; + + i ) {
2014-12-05 13:44:27 +00:00
MTP : : stopSession ( MTP : : upl [ i ] ) ;
2014-10-30 16:23:44 +00:00
sentSizes [ i ] = 0 ;
}
killSessionsTimer . stop ( ) ;
2014-05-30 08:53:19 +00:00
}
void FileUploader : : partLoaded ( const MTPBool & result , mtpRequestId requestId ) {
QMap < mtpRequestId , int32 > : : iterator j = docRequestsSent . end ( ) ;
QMap < mtpRequestId , QByteArray > : : iterator i = requestsSent . find ( requestId ) ;
if ( i = = requestsSent . cend ( ) ) {
j = docRequestsSent . find ( requestId ) ;
}
if ( i ! = requestsSent . cend ( ) | | j ! = docRequestsSent . cend ( ) ) {
if ( ! result . v ) { // failed to upload current file
currentFailed ( ) ;
return ;
} else {
2014-10-30 16:23:44 +00:00
QMap < mtpRequestId , int32 > : : iterator dcIt = dcMap . find ( requestId ) ;
if ( dcIt = = dcMap . cend ( ) ) { // must not happen
currentFailed ( ) ;
return ;
}
int32 dc = dcIt . value ( ) ;
dcMap . erase ( dcIt ) ;
2014-05-30 08:53:19 +00:00
Queue : : const_iterator k = queue . constFind ( uploading ) ;
if ( i ! = requestsSent . cend ( ) ) {
sentSize - = i . value ( ) . size ( ) ;
2014-10-30 16:23:44 +00:00
sentSizes [ dc ] - = i . value ( ) . size ( ) ;
2014-05-30 08:53:19 +00:00
requestsSent . erase ( i ) ;
} else {
sentSize - = k - > docPartSize ;
2014-10-30 16:23:44 +00:00
sentSizes [ dc ] - = k - > docPartSize ;
2014-05-30 08:53:19 +00:00
docRequestsSent . erase ( j ) ;
}
if ( k - > media . type = = ToPreparePhoto ) {
emit photoProgress ( k . key ( ) ) ;
} else if ( k - > media . type = = ToPrepareDocument ) {
DocumentData * doc = App : : document ( k - > media . id ) ;
if ( doc - > status = = FileUploading ) {
doc - > uploadOffset = ( k - > docSentParts - docRequestsSent . size ( ) ) * k - > docPartSize ;
if ( doc - > uploadOffset > doc - > size ) {
doc - > uploadOffset = doc - > size ;
}
}
emit documentProgress ( k . key ( ) ) ;
2015-05-29 18:52:43 +00:00
} else if ( k - > media . type = = ToPrepareAudio ) {
AudioData * audio = App : : audio ( k - > media . id ) ;
if ( audio - > status = = FileUploading ) {
audio - > uploadOffset = ( k - > docSentParts - docRequestsSent . size ( ) ) * k - > docPartSize ;
if ( audio - > uploadOffset > audio - > size ) {
audio - > uploadOffset = audio - > size ;
}
}
2014-05-30 08:53:19 +00:00
}
}
}
sendNext ( ) ;
}
2015-04-04 20:01:34 +00:00
bool FileUploader : : partFailed ( const RPCError & error , mtpRequestId requestId ) {
if ( error . type ( ) . startsWith ( qsl ( " FLOOD_WAIT_ " ) ) ) return false ;
2014-05-30 08:53:19 +00:00
if ( requestsSent . constFind ( requestId ) ! = requestsSent . cend ( ) | | docRequestsSent . constFind ( requestId ) ! = docRequestsSent . cend ( ) ) { // failed to upload current file
currentFailed ( ) ;
}
sendNext ( ) ;
return true ;
}