示例#1
0
    def collection_medias(self,
                          collection_pk: str,
                          amount: int = 21,
                          last_media_pk: int = 0) -> List[Media]:
        """
        Get media in a collection by collection_pk

        Parameters
        ----------
        collection_pk: str
            Unique identifier of a Collection
        amount: int, optional
            Maximum number of media to return, default is 21
        last_media_pk: int, optional
            Last PK user has seen, function will return medias after this pk. Default is 0

        Returns
        -------
        List[Media]
            A list of objects of Media
        """
        if isinstance(collection_pk, int) or collection_pk.isdigit():
            private_request_endpoint = f"feed/collection/{collection_pk}/"
        elif collection_pk.lower() == "liked":
            private_request_endpoint = "feed/liked/"
        else:
            private_request_endpoint = "feed/saved/posts/"

        last_media_pk = last_media_pk and int(last_media_pk)
        total_items = []
        next_max_id = ""
        amount = int(amount)
        found_last_media_pk = False
        while True:
            try:
                result = self.private_request(
                    private_request_endpoint,
                    params={
                        "include_igtv_preview": "false",
                        "max_id": next_max_id
                    },
                )
            except Exception as e:
                self.logger.exception(e)
                break
            for item in result["items"]:
                if last_media_pk and last_media_pk == item["media"]["pk"]:
                    found_last_media_pk = True
                    break
                total_items.append(extract_media_v1(item.get("media", item)))
            if (amount and len(total_items) >= amount) or found_last_media_pk:
                break
            if not result.get("more_available"):
                break
            next_max_id = result.get("next_max_id", "")
        return total_items[:amount] if amount else total_items
示例#2
0
    def hashtag_medias_v1_chunk(
        self, name: str, max_amount: int = 27, tab_key: str = "", max_id: str = None
    ) -> Tuple[List[Media], str]:
        """
        Get chunk of medias for a hashtag and max_id (cursor) by Private Mobile API

        Parameters
        ----------
        name: str
            Name of the hashtag
        max_amount: int, optional
            Maximum number of media to return, default is 27
        tab_key: str, optional
            Tab Key, default value is ""
        max_id: str
            Max ID, default value is None

        Returns
        -------
        Tuple[List[Media], str]
            List of objects of Media and max_id
        """
        assert tab_key in ("top", "recent"), \
            'You must specify one of the options for "tab_key" ("top" or "recent")'
        data = {
            "supported_tabs": dumps([tab_key]),
            # 'lat': 59.8626416,
            # 'lng': 30.5126682,
            "include_persistent": "true",
            "rank_token": self.rank_token,
            "count": 10000,
        }
        medias = []
        while True:
            result = self.private_request(
                f"tags/{name}/sections/",
                params={"max_id": max_id} if max_id else {},
                data=self.with_default_data(data),
            )
            for section in result["sections"]:
                layout_content = section.get("layout_content") or {}
                nodes = layout_content.get("medias") or []
                for node in nodes:
                    if max_amount and len(medias) >= max_amount:
                        break
                    media = extract_media_v1(node["media"])
                    # check contains hashtag in caption
                    if f"#{name}" not in media.caption_text:
                        continue
                    medias.append(media)
            if not result["more_available"]:
                break
            if max_amount and len(medias) >= max_amount:
                break
            max_id = result["next_max_id"]
        return medias, max_id
示例#3
0
    def hashtag_medias_v1(self,
                          name: str,
                          amount: int = 27,
                          tab_key: str = "") -> List[Media]:
        """
        Get medias for a hashtag

        Parameters
        ----------
        name: str
            Name of the hashtag
        amount: int, optional
            Maximum number of media to return, default is 27
        tab_key: str, optional
            Tab Key, default value is ""

        Returns
        -------
        List[Media]
            List of objects of Media
        """
        data = {
            "supported_tabs": dumps([tab_key]),
            # 'lat': 59.8626416,
            # 'lng': 30.5126682,
            "include_persistent": "true",
            "rank_token": self.rank_token,
        }
        max_id = None
        medias = []
        while True:
            result = self.private_request(
                f"tags/{name}/sections/",
                params={"max_id": max_id} if max_id else {},
                data=self.with_default_data(data),
            )
            for section in result["sections"]:
                layout_content = section.get("layout_content") or {}
                nodes = layout_content.get("medias") or []
                for node in nodes:
                    if amount and len(medias) >= amount:
                        break
                    media = extract_media_v1(node["media"])
                    # check contains hashtag in caption
                    if f"#{name}" not in media.caption_text:
                        continue
                    medias.append(media)
            if not result["more_available"]:
                break
            if amount and len(medias) >= amount:
                break
            max_id = result["next_max_id"]
        if amount:
            medias = medias[:amount]
        return medias
示例#4
0
    def photo_upload(
        self,
        path: Path,
        caption: str,
        upload_id: str = "",
        usertags: List[Usertag] = [],
        location: Location = None,
        links: List[StoryLink] = [],
        configure_timeout: int = 3,
        configure_handler=None,
        configure_exception=None,
    ) -> Media:
        """
        Upload photo and configure to feed

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        upload_id: str, optional
            Unique upload_id (String). When None, then generate automatically. Example from video.video_configure
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is None
        links: List[StoryLink]
            URLs for Swipe Up
        configure_timeout: int
            Timeout between attempt to configure media (set caption, etc), default is 3
        configure_handler
            Configure handler method, default is None
        configure_exception
            Configure exception class, default is None

        Returns
        -------
        Media
            An object of Media class
        """
        path = Path(path)
        upload_id, width, height = self.photo_rupload(path, upload_id)
        for attempt in range(10):
            self.logger.debug(f"Attempt #{attempt} to configure Photo: {path}")
            time.sleep(configure_timeout)
            if (configure_handler or self.photo_configure)(
                upload_id, width, height, caption, usertags, location, links
            ):
                media = self.last_json.get("media")
                self.expose()
                return extract_media_v1(media)
        raise (configure_exception or PhotoConfigureError)(
            response=self.last_response, **self.last_json
        )
示例#5
0
    def reels_timeline_media(self,
                             collection_pk: str,
                             amount: int = 10,
                             last_media_pk: int = 0) -> List[Media]:
        """
        Get reels timeline media in a collection

        Parameters
        ----------
        collection_pk: str
            Unique identifier of a timeline
        amount: int, optional
            Maximum number of media to return, default is 10
        last_media_pk: int, optional
            Last PK user has seen, function will return medias after this pk. Default is 0

        Returns
        -------
        List[Media]
            A list of objects of Media
        """

        if collection_pk == "reels":
            private_request_endpoint = "clips/connected/"
        elif collection_pk == 'explore_reels':
            private_request_endpoint = "clips/discover/"

        last_media_pk = last_media_pk and int(last_media_pk)
        total_items = []
        next_max_id = ""
        while True:
            if len(total_items) >= float(amount):
                return total_items[:amount]
            try:
                result = self.private_request(
                    private_request_endpoint,
                    data=' ',
                    params={"max_id": next_max_id},
                )
            except Exception as e:
                self.logger.exception(e)
                return total_items

            for item in result["items"]:
                if last_media_pk and last_media_pk == item["media"]["pk"]:
                    return total_items
                total_items.append(extract_media_v1(item.get("media")))

            if not result.get('paging_info', {}).get("more_available"):
                return total_items

            next_max_id = result.get('paging_info', {}).get("more_available")
示例#6
0
    def location_medias_v1_chunk(
            self,
            location_pk: int,
            max_amount: int = 63,
            tab_key: str = "",
            max_id: str = None) -> Tuple[List[Media], str]:
        """
        Get chunk of medias for a location and max_id (cursor) by Private Mobile API

        Parameters
        ----------
        location_pk: int
            Unique identifier for a location
        max_amount: int, optional
            Maximum number of media to return, default is 27
        tab_key: str, optional
            Tab Key, default value is ""
        max_id: str
            Max ID, default value is None

        Returns
        -------
        Tuple[List[Media], str]
            List of objects of Media and max_id
        """
        assert tab_key in tab_keys_v1, f'You must specify one of the options for "tab_key" {tab_keys_a1}'
        data = {
            "_uuid": self.uuid,
            "session_id": self.client_session_id,
            "tab": tab_key
        }
        medias = []
        while True:
            result = self.private_request(
                f"locations/{location_pk}/sections/",
                params={"max_id": max_id} if max_id else {},
                data=data,
            )
            for section in result["sections"]:
                layout_content = section.get("layout_content") or {}
                nodes = layout_content.get("medias") or []
                for node in nodes:
                    if max_amount and len(medias) >= max_amount:
                        break
                    media = extract_media_v1(node["media"])
                    medias.append(media)
            if not result["more_available"]:
                break
            if max_amount and len(medias) >= max_amount:
                break
            max_id = result["next_max_id"]
        return medias, max_id
示例#7
0
    def collection_medias(self,
                          collection_pk: int,
                          amount: int = 21,
                          last_media_pk: int = 0) -> List[Media]:
        """
        Get media in a collection by collection pk

        Parameters
        ----------
        collection_pk: int
            Unique identifier of a Collection
        amount: int, optional
            Maximum number of media to return, default is 21
        last_media_pk: int, optional
            Last PK user has seen, function will return medias after this pk. Default is 0

        Returns
        -------
        List[Media]
            A list of objects of Media
        """
        collection_pk = int(collection_pk)
        last_media_pk = last_media_pk and int(last_media_pk)
        total_items = []
        next_max_id = ""
        while True:
            if len(total_items) >= float(amount):
                return total_items[:amount]
            try:
                result = self.private_request(
                    f"feed/collection/{collection_pk}/",
                    params={
                        "include_igtv_preview": "false",
                        "max_id": next_max_id
                    },
                )
            except Exception as e:
                self.logger.exception(e)
                return total_items
            for item in result["items"]:
                if last_media_pk and last_media_pk == item["media"]["pk"]:
                    return total_items
                total_items.append(extract_media_v1(item["media"]))
            if not result.get("more_available"):
                return total_items
            next_max_id = result.get("next_max_id", "")
        return total_items
示例#8
0
    def photo_upload(
        self,
        path: Path,
        caption: str,
        upload_id: str = "",
        usertags: List[Usertag] = [],
        location: Location = None,
    ) -> Media:
        """
        Upload photo and configure to feed

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        upload_id: str, optional
            Unique upload_id (String). When None, then generate automatically. Example from video.video_configure
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is None

        Returns
        -------
        Media
            An object of Media class
        """
        path = Path(path)
        upload_id, width, height = self.photo_rupload(path, upload_id)
        for attempt in range(10):
            self.logger.debug(f"Attempt #{attempt} to configure Photo: {path}")
            time.sleep(3)
            if self.photo_configure(
                    upload_id,
                    width,
                    height,
                    caption,
                    usertags,
                    location,
            ):
                media = self.last_json.get("media")
                self.expose()
                return extract_media_v1(media)
        raise PhotoConfigureError(response=self.last_response,
                                  **self.last_json)
示例#9
0
    def user_medias_paginated_v1(
            self,
            user_id: int,
            amount: int = 0,
            end_cursor: str = "") -> Tuple[List[Media], str]:
        """
        Get a page of user's media by Private Mobile API

        Parameters
        ----------
        user_id: int
        amount: int, optional
            Maximum number of media to return, default is 0 (all medias)
        end_cursor: str, optional
            Cursor value to start at, obtained from previous call to this method

        Returns
        -------
        Tuple[List[Media], str]
            A tuple containing a list of medias and the next end_cursor value
        """
        amount = int(amount)
        user_id = int(user_id)
        medias = []
        next_max_id = end_cursor
        min_timestamp = None
        try:
            items = self.private_request(
                f"feed/user/{user_id}/",
                params={
                    "max_id": next_max_id,
                    "count": 1000,
                    "min_timestamp": min_timestamp,
                    "rank_token": self.rank_token,
                    "ranked_content": "true",
                },
            )["items"]
        except Exception as e:
            self.logger.exception(e)
            return [], None
        medias.extend(items)
        next_max_id = self.last_json.get("next_max_id", "")
        if amount:
            medias = medias[:amount]
        return ([extract_media_v1(media) for media in medias], next_max_id)
示例#10
0
    def user_medias_v1(self, user_id: int, amount: int = 18) -> List[Media]:
        """
        Get a user's media

        Parameters
        ----------
        user_id: int
        amount: int, optional
            Maximum number of media to return, default is 18

        Returns
        -------
        List[Media]
            A list of objects of Media
        """
        amount = int(amount)
        user_id = int(user_id)
        medias = []
        next_max_id = ""
        min_timestamp = None
        while True:
            try:
                items = self.private_request(
                    f"feed/user/{user_id}/",
                    params={
                        "max_id": next_max_id,
                        "min_timestamp": min_timestamp,
                        "rank_token": self.rank_token,
                        "ranked_content": "true",
                    },
                )["items"]
            except Exception as e:
                self.logger.exception(e)
                break
            medias.extend(items)
            if not self.last_json.get("more_available"):
                break
            if len(medias) >= amount:
                break
            next_max_id = self.last_json.get("next_max_id", "")
        return [extract_media_v1(media) for media in medias[:amount]]
示例#11
0
    def media_info_v1(self, media_pk: int) -> Media:
        """
        Get Media from PK

        Parameters
        ----------
        media_pk: int
            Unique identifier of the media

        Returns
        -------
        Media
            An object of Media type
        """
        try:
            result = self.private_request(f"media/{media_pk}/info/")
        except ClientNotFoundError as e:
            raise MediaNotFound(e, media_pk=media_pk, **self.last_json)
        except ClientError as e:
            if "Media not found" in str(e):
                raise MediaNotFound(e, media_pk=media_pk, **self.last_json)
            raise e
        return extract_media_v1(result["items"].pop())
示例#12
0
    def usertag_medias_v1(self, user_id: int, amount: int = 0) -> List[Media]:
        """
        Get medias where a user is tagged (by Private Mobile API)

        Parameters
        ----------
        user_id: int
        amount: int, optional
            Maximum number of media to return, default is 0 (all medias)

        Returns
        -------
        List[Media]
            A list of objects of Media
        """
        amount = int(amount)
        user_id = int(user_id)
        medias = []
        next_max_id = ""
        while True:
            try:
                items = self.private_request(f"usertags/{user_id}/feed/",
                                             params={"max_id":
                                                     next_max_id})["items"]
            except Exception as e:
                self.logger.exception(e)
                break
            medias.extend(items)
            if not self.last_json.get("more_available"):
                break
            if amount and len(medias) >= amount:
                break
            next_max_id = self.last_json.get("next_max_id", "")
        if amount:
            medias = medias[:amount]
        return [extract_media_v1(media) for media in medias]
示例#13
0
    def album_upload(
        self,
        paths: List[Path],
        caption: str,
        usertags: List[Usertag] = [],
        location: Location = None,
        configure_timeout: int = 3,
        configure_handler=None,
        configure_exception=None,
        to_story=False,
    ) -> Media:
        """
        Upload album to feed

        Parameters
        ----------
        paths: List[Path]
            List of paths for media to upload
        caption: str
            Media caption
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is none
        configure_timeout: int
            Timeout between attempt to configure media (set caption, etc), default is 3
        configure_handler
            Configure handler method, default is None
        configure_exception
            Configure exception class, default is None
        to_story: bool
            Currently not used, default is False

        Returns
        -------
        Media
            An object of Media class
        """
        children = []
        for path in paths:
            path = Path(path)
            if path.suffix == ".jpg":
                upload_id, width, height = self.photo_rupload(path, to_album=True)
                children.append(
                    {
                        "upload_id": upload_id,
                        "edits": dumps(
                            {
                                "crop_original_size": [width, height],
                                "crop_center": [0.0, -0.0],
                                "crop_zoom": 1.0,
                            }
                        ),
                        "extra": dumps(
                            {"source_width": width, "source_height": height}
                        ),
                        "scene_capture_type": "",
                        "scene_type": None,
                    }
                )
            elif path.suffix == ".mp4":
                upload_id, width, height, duration, thumbnail = self.video_rupload(
                    path, to_album=True
                )
                children.append(
                    {
                        "upload_id": upload_id,
                        "clips": dumps([{"length": duration, "source_type": "4"}]),
                        "extra": dumps(
                            {"source_width": width, "source_height": height}
                        ),
                        "length": duration,
                        "poster_frame_index": "0",
                        "filter_type": "0",
                        "video_result": "",
                        "date_time_original": time.strftime(
                            "%Y%m%dT%H%M%S.000Z", time.localtime()
                        ),
                        "audio_muted": "false",
                    }
                )
                self.photo_rupload(thumbnail, upload_id)
            else:
                raise AlbumUnknownFormat()

        for attempt in range(20):
            self.logger.debug(f"Attempt #{attempt} to configure Album: {paths}")
            time.sleep(configure_timeout)
            try:
                configured = (configure_handler or self.album_configure)(
                    children, caption, usertags, location
                )
            except Exception as e:
                if "Transcode not finished yet" in str(e):
                    """
                    Response 202 status:
                    {"message": "Transcode not finished yet.", "status": "fail"}
                    """
                    time.sleep(10)
                    continue
                raise e
            else:
                if configured:
                    media = configured.get("media")
                    self.expose()
                    return extract_media_v1(media)
        raise (configure_exception or AlbumConfigureError)(
            response=self.last_response, **self.last_json
        )
示例#14
0
    def video_upload(
        self,
        path: Path,
        caption: str,
        thumbnail: Path = None,
        usertags: List[Usertag] = [],
        location: Location = None,
    ) -> Media:
        """
        Upload video and configure to feed

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        thumbnail: str
            Path to thumbnail for video. When None, then thumbnail is generate automatically
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is None

        Returns
        -------
        Media
            An object of Media class
        """
        path = Path(path)
        if thumbnail is not None:
            thumbnail = Path(thumbnail)
        upload_id, width, height, duration, thumbnail = self.video_rupload(
            path, thumbnail, to_story=False)
        for attempt in range(20):
            self.logger.debug(f"Attempt #{attempt} to configure Video: {path}")
            time.sleep(3)
            try:
                configured = self.video_configure(
                    upload_id,
                    width,
                    height,
                    duration,
                    thumbnail,
                    caption,
                    usertags,
                    location,
                )
            except Exception as e:
                if "Transcode not finished yet" in str(e):
                    """
                    Response 202 status:
                    {"message": "Transcode not finished yet.", "status": "fail"}
                    """
                    time.sleep(10)
                    continue
                raise e
            else:
                if configured:
                    media = configured.get("media")
                    self.expose()
                    return extract_media_v1(media)
        raise VideoConfigureError(response=self.last_response,
                                  **self.last_json)
示例#15
0
    def video_upload(
        self,
        path: Path,
        caption: str,
        thumbnail: Path = None,
        usertags: List[Usertag] = [],
        location: Location = None,
        links: List[StoryLink] = [],
        configure_timeout: int = 3,
        configure_handler=None,
        configure_exception=None,
        to_story: bool = False,
    ) -> Media:
        """
        Upload video and configure to feed

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        thumbnail: str
            Path to thumbnail for video. When None, then thumbnail is generate automatically
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is None
        links: List[StoryLink]
            URLs for Swipe Up
        configure_timeout: int
            Timeout between attempt to configure media (set caption, etc), default is 3
        configure_handler
            Configure handler method, default is None
        configure_exception
            Configure exception class, default is None
        to_story: bool, optional

        Returns
        -------
        Media
            An object of Media class
        """
        path = Path(path)
        if thumbnail is not None:
            thumbnail = Path(thumbnail)
        upload_id, width, height, duration, thumbnail = self.video_rupload(
            path, thumbnail, to_story=to_story)
        for attempt in range(20):
            self.logger.debug(f"Attempt #{attempt} to configure Video: {path}")
            time.sleep(configure_timeout)
            try:
                configured = (configure_handler or self.video_configure)(
                    upload_id,
                    width,
                    height,
                    duration,
                    thumbnail,
                    caption,
                    usertags,
                    location,
                    links,
                )
            except Exception as e:
                if "Transcode not finished yet" in str(e):
                    """
                    Response 202 status:
                    {"message": "Transcode not finished yet.", "status": "fail"}
                    """
                    time.sleep(10)
                    continue
                raise e
            else:
                if configured:
                    media = configured.get("media")
                    self.expose()
                    return extract_media_v1(media)
        raise (configure_exception
               or VideoConfigureError)(response=self.last_response,
                                       **self.last_json)
示例#16
0
    def video_upload_to_story(
        self,
        path: Path,
        caption: str,
        thumbnail: Path = None,
        mentions: List[StoryMention] = [],
        locations: List[StoryLocation] = [],
        links: List[StoryLink] = [],
        hashtags: List[StoryHashtag] = [],
    ) -> Story:
        """
        Upload video as a story and configure it

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        thumbnail: str
            Path to thumbnail for video. When None, then thumbnail is generate automatically
        mentions: List[StoryMention], optional
            List of mentions to be tagged on this upload, default is empty list.
        locations: List[StoryLocation], optional
            List of locations to be tagged on this upload, default is empty list.
        links: List[StoryLink]
            URLs for Swipe Up
        hashtags: List[StoryHashtag], optional
            List of hashtags to be tagged on this upload, default is empty list.

        Returns
        -------
        Story
            An object of Media class
        """
        path = Path(path)
        if thumbnail is not None:
            thumbnail = Path(thumbnail)
        upload_id, width, height, duration, thumbnail = self.video_rupload(
            path, thumbnail, to_story=True)
        for attempt in range(20):
            self.logger.debug(f"Attempt #{attempt} to configure Video: {path}")
            time.sleep(3)
            try:
                configured = self.video_configure_to_story(
                    upload_id,
                    width,
                    height,
                    duration,
                    thumbnail,
                    caption,
                    mentions,
                    locations,
                    links,
                    hashtags,
                )
            except Exception as e:
                if "Transcode not finished yet" in str(e):
                    """
                    Response 202 status:
                    {"message": "Transcode not finished yet.", "status": "fail"}
                    """
                    time.sleep(10)
                    continue
                raise e
            if configured:
                media = configured.get("media")
                self.expose()
                return Story(links=links,
                             mentions=mentions,
                             hashtags=hashtags,
                             locations=locations,
                             **extract_media_v1(media).dict())
        raise VideoConfigureStoryError(response=self.last_response,
                                       **self.last_json)
示例#17
0
    def igtv_upload(
        self,
        path: Path,
        title: str,
        caption: str,
        thumbnail: Path = None,
        usertags: List[Usertag] = [],
        location: Location = None,
        configure_timeout: int = 10,
    ) -> Media:
        """
        Upload IGTV to Instagram

        Parameters
        ----------
        path: Path
            Path to IGTV file
        title: str
            Title of the video
        caption: str
            Media caption
        thumbnail: Path, optional
            Path to thumbnail for IGTV. Default value is None, and it generates a thumbnail
        usertags: List[Usertag], optional
            List of users to be tagged on this upload, default is empty list.
        location: Location, optional
            Location tag for this upload, default is none
        configure_timeout: int
            Timeout between attempt to configure media (set caption, etc), default is 10

        Returns
        -------
        Media
            An object of Media class
        """
        path = Path(path)
        if thumbnail is not None:
            thumbnail = Path(thumbnail)
        upload_id = str(int(time.time() * 1000))
        thumbnail, width, height, duration = analyze_video(path, thumbnail)
        waterfall_id = str(uuid4())
        # upload_name example: '1576102477530_0_7823256191'
        upload_name = "{upload_id}_0_{rand}".format(upload_id=upload_id,
                                                    rand=random.randint(
                                                        1000000000,
                                                        9999999999))
        # by segments bb2c1d0c127384453a2122e79e4c9a85-0-6498763
        # upload_name = "{hash}-0-{rand}".format(
        #     hash="bb2c1d0c127384453a2122e79e4c9a85", rand=random.randint(1111111, 9999999)
        # )
        rupload_params = {
            "is_igtv_video": "1",
            "retry_context":
            '{"num_step_auto_retry":0,"num_reupload":0,"num_step_manual_retry":0}',
            "media_type": "2",
            "xsharing_user_ids": json.dumps([self.user_id]),
            "upload_id": upload_id,
            "upload_media_duration_ms": str(int(duration * 1000)),
            "upload_media_width": str(width),
            "upload_media_height": str(height),
        }
        headers = {
            "Accept-Encoding": "gzip",
            "X-Instagram-Rupload-Params": json.dumps(rupload_params),
            "X_FB_VIDEO_WATERFALL_ID": waterfall_id,
            "X-Entity-Type": "video/mp4",
        }
        response = self.private.get(
            "https://{domain}/rupload_igvideo/{name}".format(
                domain=config.API_DOMAIN, name=upload_name),
            headers=headers,
        )
        self.request_log(response)
        if response.status_code != 200:
            raise IGTVNotUpload(response=self.last_response, **self.last_json)
        igtv_data = open(path, "rb").read()
        igtv_len = str(len(igtv_data))
        headers = {
            "Offset": "0",
            "X-Entity-Name": upload_name,
            "X-Entity-Length": igtv_len,
            "Content-Type": "application/octet-stream",
            "Content-Length": igtv_len,
            **headers,
        }
        response = self.private.post(
            "https://{domain}/rupload_igvideo/{name}".format(
                domain=config.API_DOMAIN, name=upload_name),
            data=igtv_data,
            headers=headers,
        )
        self.request_log(response)
        if response.status_code != 200:
            raise IGTVNotUpload(response=self.last_response, **self.last_json)
        # CONFIGURE
        self.igtv_composer_session_id = self.generate_uuid()
        for attempt in range(20):
            self.logger.debug(f"Attempt #{attempt} to configure IGTV: {path}")
            time.sleep(configure_timeout)
            try:
                configured = self.igtv_configure(
                    upload_id,
                    thumbnail,
                    width,
                    height,
                    duration,
                    title,
                    caption,
                    usertags,
                    location,
                )
            except ClientError as e:
                if "Transcode not finished yet" in str(e):
                    """
                    Response 202 status:
                    {"message": "Transcode not finished yet.", "status": "fail"}
                    """
                    time.sleep(10)
                    continue
                raise e
            else:
                if configured:
                    media = self.last_json.get("media")
                    self.expose()
                    return extract_media_v1(media)
        raise IGTVConfigureError(response=self.last_response, **self.last_json)
示例#18
0
    def photo_upload_to_story(
        self,
        path: Path,
        caption: str,
        upload_id: str = "",
        mentions: List[StoryMention] = [],
        locations: List[StoryLocation] = [],
        links: List[StoryLink] = [],
        hashtags: List[StoryHashtag] = [],
        stickers: List[StorySticker] = [],
    ) -> Story:
        """
        Upload photo as a story and configure it

        Parameters
        ----------
        path: Path
            Path to the media
        caption: str
            Media caption
        upload_id: str, optional
            Unique upload_id (String). When None, then generate automatically. Example from video.video_configure
        mentions: List[StoryMention], optional
            List of mentions to be tagged on this upload, default is empty list.
        locations: List[StoryLocation], optional
            List of locations to be tagged on this upload, default is empty list.
        links: List[StoryLink]
            URLs for Swipe Up
        hashtags: List[StoryHashtag], optional
            List of hashtags to be tagged on this upload, default is empty list.
        stickers: List[StorySticker], optional
            List of stickers to be tagged on this upload, default is empty list.

        Returns
        -------
        Story
            An object of Media class
        """
        path = Path(path)
        upload_id, width, height = self.photo_rupload(path, upload_id)
        for attempt in range(10):
            self.logger.debug(f"Attempt #{attempt} to configure Photo: {path}")
            time.sleep(3)
            if self.photo_configure_to_story(
                    upload_id,
                    width,
                    height,
                    caption,
                    mentions,
                    locations,
                    links,
                    hashtags,
                    stickers,
            ):
                media = self.last_json.get("media")
                self.expose()
                return Story(links=links,
                             mentions=mentions,
                             hashtags=hashtags,
                             locations=locations,
                             stickers=stickers,
                             **extract_media_v1(media).dict())
        raise PhotoConfigureStoryError(response=self.last_response,
                                       **self.last_json)