High-level method to download set of profiles.

        :param profiles: Set of profiles to download.
        :param profile_pic: not :option:`--no-profile-pic`.
        :param posts: not :option:`--no-posts`.
        :param tagged: :option:`--tagged`.
        :param highlights: :option:`--highlights`.
        :param stories: :option:`--stories`.
        :param fast_update: :option:`--fast-update`.
        :param post_filter: :option:`--post-filter`.
        :param storyitem_filter: :option:`--post-filter`.
        :param raise_errors:
           Whether :exc:`LoginRequiredException` and :exc:`PrivateProfileNotFollowedException` should be raised or
           catched and printed with :meth:`InstaloaderContext.error_catcher`.

        .. versionadded:: 4.1

def download_profiles(self, profiles: Set[Profile],
                          profile_pic: bool = True, posts: bool = True,
                          tagged: bool = False, highlights: bool = False, stories: bool = False,
                          fast_update: bool = False,
                          post_filter: Optional[Callable[[Post], bool]] = None,
                          storyitem_filter: Optional[Callable[[Post], bool]] = None,
                          raise_errors: bool = False):
        """High-level method to download set of profiles.

        :param profiles: Set of profiles to download.
        :param profile_pic: not :option:`--no-profile-pic`.
        :param posts: not :option:`--no-posts`.
        :param tagged: :option:`--tagged`.
        :param highlights: :option:`--highlights`.
        :param stories: :option:`--stories`.
        :param fast_update: :option:`--fast-update`.
        :param post_filter: :option:`--post-filter`.
        :param storyitem_filter: :option:`--post-filter`.
        :param raise_errors:
           Whether :exc:`LoginRequiredException` and :exc:`PrivateProfileNotFollowedException` should be raised or
           catched and printed with :meth:`InstaloaderContext.error_catcher`.

        .. versionadded:: 4.1"""

        def _error_raiser(_str):
            yield

        error_handler = _error_raiser if raise_errors else self.context.error_catcher

        for profile in profiles:
            with error_handler(profile.username):
                profile_name = profile.username

                # Download profile picture
                if profile_pic:
                    with self.context.error_catcher('Download profile picture of {}'.format(profile_name)):
                        self.download_profilepic(profile)

                # Save metadata as JSON if desired.
                if self.save_metadata:
                    json_filename = '{0}/{1}_{2}'.format(self.dirname_pattern.format(profile=profile_name,
                                                                                     target=profile_name),
                                                         profile_name, profile.userid)
                    self.save_metadata_json(json_filename, profile)

                # Catch some errors
                if profile.is_private and (tagged or highlights or posts):
                    if not self.context.is_logged_in:
                        raise LoginRequiredException("--login=USERNAME required.")
                    if not profile.followed_by_viewer and self.context.username != profile.username:
                        raise PrivateProfileNotFollowedException("Private but not followed.")

                # Download tagged, if requested
                if tagged:
                    with self.context.error_catcher('Download tagged of {}'.format(profile_name)):
                        self.download_tagged(profile, fast_update=fast_update, post_filter=post_filter)

                # Download highlights, if requested
                if highlights:
                    with self.context.error_catcher('Download highlights of {}'.format(profile_name)):
                        self.download_highlights(profile, fast_update=fast_update, storyitem_filter=storyitem_filter)

                # Iterate over pictures and download them
                if posts:
                    self.context.log("Retrieving posts from profile {}.".format(profile_name))
                    totalcount = profile.mediacount
                    count = 1
                    for post in profile.get_posts():
                        self.context.log("[%3i/%3i] " % (count, totalcount), end="", flush=True)
                        count += 1
                        if post_filter is not None and not post_filter(post):
                            self.context.log('<skipped>')
                            continue
                        with self.context.error_catcher("Download {} of {}".format(post, profile_name)):
                            # The PostChangedException gets raised if the Post's id/shortcode changed while obtaining
                            # additional metadata. This is most likely the case if a HTTP redirect takes place while
                            # resolving the shortcode URL.
                            # The `post_changed` variable keeps the fast-update functionality alive: A Post which is
                            # obained after a redirect has probably already been downloaded as a previous Post of the
                            # same Profile.
                            # Observed in issue #225: https://github.com/instaloader/instaloader/issues/225
                            post_changed = False
                            while True:
                                try:
                                    downloaded = self.download_post(post, target=profile_name)
                                    break
                                except PostChangedException:
                                    post_changed = True
                                    continue
                            if fast_update and not downloaded and not post_changed:
                                break

        if stories and profiles:
            with self.context.error_catcher("Download stories"):
                self.context.log("Downloading stories")
                self.download_stories(userids=list(profiles), fast_update=fast_update, filename_target=None,
                                      storyitem_filter=storyitem_filter)