Example #1
0
    def download_contact(message_media_contact, file_path, add_extension=True):
        """Downloads a media contact using the vCard 4.0 format"""

        first_name = message_media_contact.first_name
        last_name = message_media_contact.last_name
        phone_number = message_media_contact.phone_number

        # The only way we can save a contact in an understandable
        # way by phones is by using the .vCard format
        if add_extension:
            file_path += '.vcard'

        # Ensure that we'll be able to download the contact
        utils.ensure_parent_dir_exists(file_path)

        with open(file_path, 'w', encoding='utf-8') as file:
            file.write('BEGIN:VCARD\n')
            file.write('VERSION:4.0\n')
            file.write('N:{};{};;;\n'.format(first_name,
                                             last_name if last_name else ''))
            file.write('FN:{}\n'.format(' '.join((first_name, last_name))))
            file.write(
                'TEL;TYPE=cell;VALUE=uri:tel:+{}\n'.format(phone_number))
            file.write('END:VCARD\n')

        return file_path
Example #2
0
    def download_file_loc(self,
                          input_location,
                          file_path,
                          part_size_kb=64,
                          file_size=None,
                          progress_callback=None):
        """Downloads media from the given input_file_location to the specified file_path.
           If a progress_callback function is given, it will be called taking two
           arguments (downloaded bytes count and total file size)"""

        if not part_size_kb:
            if not file_size:
                raise ValueError('A part size value must be provided')
            else:
                part_size_kb = get_appropiate_part_size(file_size)

        part_size = int(part_size_kb * 1024)
        if part_size % 1024 != 0:
            raise ValueError('The part size must be evenly divisible by 1024')

        # Ensure that we'll be able to download the media
        utils.ensure_parent_dir_exists(file_path)

        # Start with an offset index of 0
        offset_index = 0
        with open(file_path, 'wb') as file:
            while True:
                # The current offset equals the offset_index multiplied by the part size
                offset = offset_index * part_size
                result = self.invoke(
                    GetFileRequest(input_location, offset, part_size))
                offset_index += 1

                # If we have received no data (0 bytes), the file is over
                # So there is nothing left to download and write
                if not result.bytes:
                    return result.type  # Return some extra information

                file.write(result.bytes)
                if progress_callback:
                    progress_callback(file.tell(), file_size)
Example #3
0
    def download_file(self,
                      input_location,
                      file=None,
                      part_size_kb=None,
                      file_size=None,
                      progress_callback=None):

        """
        Downloads the given input location to a file.

        Args:
            input_location (:tl:`InputFileLocation`):
                The file location from which the file will be downloaded.

            file (`str` | `file`):
                The output file path, directory, or stream-like object.
                If the path exists and is a file, it will be overwritten.

            part_size_kb (`int`, optional):
                Chunk size when downloading files. The larger, the less
                requests will be made (up to 512KB maximum).

            file_size (`int`, optional):
                The file size that is about to be downloaded, if known.
                Only used if ``progress_callback`` is specified.

            progress_callback (`callable`, optional):
                A callback function accepting two parameters:
                ``(downloaded bytes, total)``. Note that the
                ``total`` is the provided ``file_size``.
        """
        if not part_size_kb:
            if not file_size:
                part_size_kb = 64  # Reasonable default
            else:
                part_size_kb = utils.get_appropriated_part_size(file_size)

        part_size = int(part_size_kb * 1024)
        # https://core.telegram.org/api/files says:
        # > part_size % 1024 = 0 (divisible by 1KB)
        #
        # But https://core.telegram.org/cdn (more recent) says:
        # > limit must be divisible by 4096 bytes
        # So we just stick to the 4096 limit.
        if part_size % 4096 != 0:
            raise ValueError(
                'The part size must be evenly divisible by 4096.')

        in_memory = file is None
        if in_memory:
            f = io.BytesIO()
        elif isinstance(file, str):
            # Ensure that we'll be able to download the media
            helpers.ensure_parent_dir_exists(file)
            f = open(file, 'wb')
        else:
            f = file

        # The used client will change if FileMigrateError occurs
        client = self
        cdn_decrypter = None
        input_location = utils.get_input_location(input_location)
        download_thread = []
        q_request = []

        __log__.info('Downloading file in chunks of %d bytes', part_size)
        threads_count = 2 + int((self._download_threads_count - 2) * float(file_size) / (1024 * 1024 * 10))
        threads_count = min(threads_count, self._download_threads_count)
        # threads_count = 1
        # threads_count = min(part_count, threads_count)
        try:
            offset = 0
            result = None
            try:
                request = GetFileRequest(input_location, offset, part_size)
                result = client(request)
                if isinstance(result, FileCdnRedirect):
                    __log__.info('File lives in a CDN')
                    cdn_decrypter, result = CdnDecrypter.prepare_decrypter(client, self._get_cdn_client(result), result)
                else:
                    f.write(result.bytes)
                    offset += part_size
            except FileMigrateError as e:
                __log__.info('File lives in another DC')
                client = self._get_exported_client(e.new_dc)

            # if cdn_decrypter:
            #     result = cdn_decrypter.get_file()

            # if not result.bytes:
            #     return getattr(result, 'type', '')
            # f.write(result.bytes)
            __log__.debug('Saved %d more bytes', len(result.bytes))
            if progress_callback:
                progress_callback(f.tell(), file_size)

            # spawn threads
            for i in range(threads_count):
                q_request.append(Queue())
                thread_dl = self.ProcessDownload('thread {0}'.format(i), self, q_request[i])
                thread_dl.start()
                download_thread.append(thread_dl)
            # offset += part_size
            while True:
                for i in range(threads_count):
                    if cdn_decrypter:
                        q_request[i].put(cdn_decrypter)
                    else:
                        request = GetFileRequest(input_location, offset, part_size)
                        q_request[i].put(request)
                    offset += part_size
                for q in q_request:
                    q.join()
                for th in download_thread:
                    if th.result and th.result.bytes:
                        f.write(th.result.bytes)
                        if progress_callback:
                            progress_callback(f.tell(), file_size)
                    else:
                        for i in range(threads_count):
                            q_request[i].put(None)
                        for th in download_thread:
                            th.join()
                        return getattr(th.result, 'type', '')
        finally:
            if client != self:
                client.disconnect()

            if cdn_decrypter:
                try:
                    cdn_decrypter.client.disconnect()
                except:
                    pass
            if isinstance(file, str):
                f.close()