func (media *FeedMedia) SetID(id interface{}) {
	media.NextID = id
}
SetID sets media ID
this value can be int64 or string
package goinsta
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	neturl "net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
)
type StoryReelMention struct {
	X        float64 `json:"x"`
	Y        float64 `json:"y"`
	Z        int     `json:"z"`
	Width    float64 `json:"width"`
	Height   float64 `json:"height"`
	Rotation float64 `json:"rotation"`
	IsPinned int     `json:"is_pinned"`
	IsHidden int     `json:"is_hidden"`
	User     User
}
type StoryCTA struct {
	Links []struct {
		LinkType                                int         `json:"linkType"`
		WebURI                                  string      `json:"webUri"`
		AndroidClass                            string      `json:"androidClass"`
		Package                                 string      `json:"package"`
		DeeplinkURI                             string      `json:"deeplinkUri"`
		CallToActionTitle                       string      `json:"callToActionTitle"`
		RedirectURI                             interface{} `json:"redirectUri"`
		LeadGenFormID                           string      `json:"leadGenFormId"`
		IgUserID                                string      `json:"igUserId"`
		AppInstallObjectiveInvalidationBehavior interface{} `json:"appInstallObjectiveInvalidationBehavior"`
	} `json:"links"`
}
type Item struct {
	media    Media
	Comments *Comments `json:"-"`
	TakenAt          int64   `json:"taken_at"`
	Pk               int64   `json:"pk"`
	ID               string  `json:"id"`
	CommentsDisabled bool    `json:"comments_disabled"`
	DeviceTimestamp  int64   `json:"device_timestamp"`
	MediaType        int     `json:"media_type"`
	Code             string  `json:"code"`
	ClientCacheKey   string  `json:"client_cache_key"`
	FilterType       int     `json:"filter_type"`
	CarouselParentID string  `json:"carousel_parent_id"`
	CarouselMedia    []Item  `json:"carousel_media,omitempty"`
	User             User    `json:"user"`
	CanViewerReshare bool    `json:"can_viewer_reshare"`
	Caption          Caption `json:"caption"`
	CaptionIsEdited  bool    `json:"caption_is_edited"`
	Likes            int     `json:"like_count"`
	HasLiked         bool    `json:"has_liked"`
	Toplikers                    interface{} `json:"top_likers"`
	Likers                       []User      `json:"likers"`
	CommentLikesEnabled          bool        `json:"comment_likes_enabled"`
	CommentThreadingEnabled      bool        `json:"comment_threading_enabled"`
	HasMoreComments              bool        `json:"has_more_comments"`
	MaxNumVisiblePreviewComments int         `json:"max_num_visible_preview_comments"`
	Previewcomments interface{} `json:"preview_comments,omitempty"`
	CommentCount    int         `json:"comment_count"`
	PhotoOfYou      bool        `json:"photo_of_you"`
	Tags struct {
		In []Tag `json:"in"`
	} `json:"usertags,omitempty"`
	FbUserTags           Tag    `json:"fb_user_tags"`
	CanViewerSave        bool   `json:"can_viewer_save"`
	OrganicTrackingToken string `json:"organic_tracking_token"`
	Images          Images   `json:"image_versions2,omitempty"`
	OriginalWidth   int      `json:"original_width,omitempty"`
	OriginalHeight  int      `json:"original_height,omitempty"`
	ImportedTakenAt int64    `json:"imported_taken_at,omitempty"`
	Location        Location `json:"location,omitempty"`
	Lat             float64  `json:"lat,omitempty"`
	Lng             float64  `json:"lng,omitempty"`
	Videos            []Video `json:"video_versions,omitempty"`
	HasAudio          bool    `json:"has_audio,omitempty"`
	VideoDuration     float64 `json:"video_duration,omitempty"`
	ViewCount         float64 `json:"view_count,omitempty"`
	IsDashEligible    int     `json:"is_dash_eligible,omitempty"`
	VideoDashManifest string  `json:"video_dash_manifest,omitempty"`
	NumberOfQualities int     `json:"number_of_qualities,omitempty"`
	StoryEvents              []interface{}      `json:"story_events"`
	StoryHashtags            []interface{}      `json:"story_hashtags"`
	StoryPolls               []interface{}      `json:"story_polls"`
	StoryFeedMedia           []interface{}      `json:"story_feed_media"`
	StorySoundOn             []interface{}      `json:"story_sound_on"`
	CreativeConfig           interface{}        `json:"creative_config"`
	StoryLocations           []interface{}      `json:"story_locations"`
	StorySliders             []interface{}      `json:"story_sliders"`
	StoryQuestions           []interface{}      `json:"story_questions"`
	StoryProductItems        []interface{}      `json:"story_product_items"`
	StoryCTA                 []StoryCTA         `json:"story_cta"`
	ReelMentions             []StoryReelMention `json:"reel_mentions"`
	SupportsReelReactions    bool               `json:"supports_reel_reactions"`
	ShowOneTapFbShareTooltip bool               `json:"show_one_tap_fb_share_tooltip"`
	HasSharedToFb            int64              `json:"has_shared_to_fb"`
	Mentions                 []Mentions
	Audience                 string `json:"audience,omitempty"`
	StoryMusicStickers       []struct {
		X              float64 `json:"x"`
		Y              float64 `json:"y"`
		Z              int     `json:"z"`
		Width          float64 `json:"width"`
		Height         float64 `json:"height"`
		Rotation       float64 `json:"rotation"`
		IsPinned       int     `json:"is_pinned"`
		IsHidden       int     `json:"is_hidden"`
		IsSticker      int     `json:"is_sticker"`
		MusicAssetInfo struct {
			ID                       string `json:"id"`
			Title                    string `json:"title"`
			Subtitle                 string `json:"subtitle"`
			DisplayArtist            string `json:"display_artist"`
			CoverArtworkURI          string `json:"cover_artwork_uri"`
			CoverArtworkThumbnailURI string `json:"cover_artwork_thumbnail_uri"`
			ProgressiveDownloadURL   string `json:"progressive_download_url"`
			HighlightStartTimesInMs  []int  `json:"highlight_start_times_in_ms"`
			IsExplicit               bool   `json:"is_explicit"`
			DashManifest             string `json:"dash_manifest"`
			HasLyrics                bool   `json:"has_lyrics"`
			AudioAssetID             string `json:"audio_asset_id"`
			IgArtist                 struct {
				Pk            int    `json:"pk"`
				Username      string `json:"username"`
				FullName      string `json:"full_name"`
				IsPrivate     bool   `json:"is_private"`
				ProfilePicURL string `json:"profile_pic_url"`
				ProfilePicID  string `json:"profile_pic_id"`
				IsVerified    bool   `json:"is_verified"`
			} `json:"ig_artist"`
			PlaceholderProfilePicURL string `json:"placeholder_profile_pic_url"`
			ShouldMuteAudio          bool   `json:"should_mute_audio"`
			ShouldMuteAudioReason    string `json:"should_mute_audio_reason"`
			OverlapDurationInMs      int    `json:"overlap_duration_in_ms"`
			AudioAssetStartTimeInMs  int    `json:"audio_asset_start_time_in_ms"`
		} `json:"music_asset_info"`
	} `json:"story_music_stickers,omitempty"`
}
func (item *Item) Comment(text string) error {
	var opt *reqOptions
	var err error
	insta := item.media.instagram()
	switch item.media.(type) {
	case *StoryMedia:
		to, err := prepareRecipients(item)
		if err != nil {
			return err
		}
		query := insta.prepareDataQuery(
			map[string]interface{}{
				"recipient_users": to,
				"action":          "send_item",
				"media_id":        item.ID,
				"client_context":  generateUUID(),
				"text":            text,
				"entry":           "reel",
				"reel_id":         item.User.ID,
			},
		)
		opt = &reqOptions{
			Connection: "keep-alive",
			Endpoint:   fmt.Sprintf("%s?media_type=%s", urlReplyStory, item.MediaToString()),
			Query:      query,
			IsPost:     true,
		}
	case *FeedMedia:  
		var data string
		data, err = insta.prepareData(
			map[string]interface{}{
				"comment_text": text,
			},
		)
		opt = &reqOptions{
			Endpoint: fmt.Sprintf(urlCommentAdd, item.Pk),
			Query:    generateSignature(data),
			IsPost:   true,
		}
	}
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(opt)
	return err
}
func (item *Item) MediaToString() string {
	switch item.MediaType {
	case 1:
		return "photo"
	case 2:
		return "video"
	case 8:
		return "carousel"
	}
	return ""
}
func setToItem(item *Item, media Media) {
	item.media = media
	item.User.inst = media.instagram()
	item.Comments = newComments(item)
	for i := range item.CarouselMedia {
		item.CarouselMedia[i].User = item.User
		setToItem(&item.CarouselMedia[i], media)
	}
}
func setToMediaItem(item *MediaItem, media Media) {
	item.Media.media = media
	item.Media.User.inst = media.instagram()
	item.Media.Comments = newComments(&item.Media)
	for i := range item.Media.CarouselMedia {
		item.Media.CarouselMedia[i].User = item.Media.User
		setToItem(&item.Media.CarouselMedia[i], media)
	}
}
func getname(name string) string {
	nname := name
	i := 1
	for {
		ext := path.Ext(name)
		_, err := os.Stat(name)
		if err != nil {
			break
		}
		if ext != "" {
			nname = strings.Replace(nname, ext, "", -1)
		}
		name = fmt.Sprintf("%s.%d%s", nname, i, ext)
		i++
	}
	return name
}
func download(inst *Instagram, url, dst string) (string, error) {
	file, err := os.Create(dst)
	if err != nil {
		return "", err
	}
	defer file.Close()
	resp, err := inst.c.Get(url)
	if err != nil {
		return "", err
	}
	_, err = io.Copy(file, resp.Body)
	return dst, err
}
type bestMedia struct {
	w, h int
	url  string
}
func GetBest(obj interface{}) string {
	m := bestMedia{}
	switch t := obj.(type) {
	case []Video:
		for _, video := range t {
			if m.w < video.Width && video.Height > m.h && video.URL != "" {
				m.w = video.Width
				m.h = video.Height
				m.url = video.URL
			}
		}
	case []Candidate:
		for _, image := range t {
			if m.w < image.Width && image.Height > m.h && image.URL != "" {
				m.w = image.Width
				m.h = image.Height
				m.url = image.URL
			}
		}
	}
	return m.url
}
var rxpTags = regexp.MustCompile(`#\w+`)
func (item *Item) Hashtags() []Hashtag {
	tags := rxpTags.FindAllString(item.Caption.Text, -1)
	hsh := make([]Hashtag, len(tags))
	i := 0
	for _, tag := range tags {
		hsh[i].Name = tag[1:]
		i++
	}
	for _, comment := range item.PreviewComments() {
		tags := rxpTags.FindAllString(comment.Text, -1)
		for _, tag := range tags {
			hsh = append(hsh, Hashtag{Name: tag[1:]})
		}
	}
	return hsh
}
func (item *Item) Delete() error {
	insta := item.media.instagram()
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": item.ID,
		},
	)
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlMediaDelete, item.ID),
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	return err
}
func (item *Item) SyncLikers() error {
	resp := respLikers{}
	insta := item.media.instagram()
	body, err := insta.sendSimpleRequest(urlMediaLikers, item.ID)
	if err != nil {
		return err
	}
	err = json.Unmarshal(body, &resp)
	if err == nil {
		item.Likers = resp.Users
	}
	return err
}
func (item *Item) Unlike() error {
	insta := item.media.instagram()
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": item.ID,
		},
	)
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlMediaUnlike, item.ID),
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	return err
}
func (item *Item) Like() error {
	insta := item.media.instagram()
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": item.ID,
		},
	)
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlMediaLike, item.ID),
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	return err
}
func (item *Item) Save() error {
	insta := item.media.instagram()
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": item.ID,
		},
	)
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlMediaSave, item.ID),
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	return err
}
func (item *Item) Unsave() error {
	insta := item.media.instagram()
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": item.ID,
		},
	)
	if err != nil {
		return err
	}
	_, err = insta.sendRequest(
		&reqOptions{
			Endpoint: fmt.Sprintf(urlMediaUnsave, item.ID),
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	return err
}
func (item *Item) Download(folder, name string) (imgs, vds string, err error) {
	var u *neturl.URL
	var nname string
	imgFolder := path.Join(folder, "images")
	vidFolder := path.Join(folder, "videos")
	inst := item.media.instagram()
	os.MkdirAll(folder, 0777)
	os.MkdirAll(imgFolder, 0777)
	os.MkdirAll(vidFolder, 0777)
	vds = GetBest(item.Videos)
	if vds != "" {
		if name == "" {
			u, err = neturl.Parse(vds)
			if err != nil {
				return
			}
			nname = path.Join(vidFolder, path.Base(u.Path))
		} else {
			nname = path.Join(vidFolder, name)
		}
		nname = getname(nname)
		vds, err = download(inst, vds, nname)
		return "", vds, err
	}
	imgs = GetBest(item.Images.Versions)
	if imgs != "" {
		if name == "" {
			u, err = neturl.Parse(imgs)
			if err != nil {
				return
			}
			nname = path.Join(imgFolder, path.Base(u.Path))
		} else {
			nname = path.Join(imgFolder, name)
		}
		nname = getname(nname)
		imgs, err = download(inst, imgs, nname)
		return imgs, "", err
	}
	return imgs, vds, fmt.Errorf("cannot find any image or video")
}
func (item *Item) TopLikers() []string {
	switch s := item.Toplikers.(type) {
	case string:
		return []string{s}
	case []string:
		return s
	}
	return nil
}
func (item *Item) PreviewComments() []Comment {
	switch s := item.Previewcomments.(type) {
	case []interface{}:
		if len(s) == 0 {
			return nil
		}
		switch s[0].(type) {
		case interface{}:
			comments := make([]Comment, 0)
			for i := range s {
				if buf, err := json.Marshal(s[i]); err != nil {
					return nil
				} else {
					comment := &Comment{}
					if err = json.Unmarshal(buf, comment); err != nil {
						return nil
					} else {
						comments = append(comments, *comment)
					}
				}
			}
			return comments
		case string:
			comments := make([]Comment, 0)
			for i := range s {
				comments = append(comments, Comment{
					Text: s[i].(string),
				})
			}
			return comments
		}
	case string:
		comments := []Comment{
			{
				Text: s,
			},
		}
		return comments
	}
	return nil
}
func (item *Item) StoryIsCloseFriends() bool {
	return item.Audience == "besties"
}
type Media interface {
	Next(...interface{}) bool
	Error() error
	ID() string
	Delete() error
	instagram() *Instagram
}
type StoryMedia struct {
	inst     *Instagram
	endpoint string
	uid      int64
	err error
	Pk              interface{} `json:"id"`
	LatestReelMedia int64       `json:"latest_reel_media"`
	ExpiringAt      float64     `json:"expiring_at"`
	HaveBeenSeen    float64     `json:"seen"`
	CanReply        bool        `json:"can_reply"`
	Title           string      `json:"title"`
	CanReshare      bool        `json:"can_reshare"`
	ReelType        string      `json:"reel_type"`
	User            User        `json:"user"`
	Items           []Item      `json:"items"`
	ReelMentions    []string    `json:"reel_mentions"`
	PrefetchCount   int         `json:"prefetch_count"`
	HasBestiesMedia      interface{} `json:"has_besties_media"`
	StoryRankingToken    string      `json:"story_ranking_token"`
	Broadcasts           []Broadcast `json:"broadcasts"`
	FaceFilterNuxVersion int         `json:"face_filter_nux_version"`
	HasNewNuxStory       bool        `json:"has_new_nux_story"`
	Status               string      `json:"status"`
}
func (media *StoryMedia) Delete() error {
	insta := media.inst
	data, err := insta.prepareData(
		map[string]interface{}{
			"media_id": media.ID(),
		},
	)
	if err == nil {
		_, err = insta.sendRequest(
			&reqOptions{
				Endpoint: fmt.Sprintf(urlMediaDelete, media.ID()),
				Query:    generateSignature(data),
				IsPost:   true,
			},
		)
	}
	return err
}
func (media *StoryMedia) ID() string {
	switch id := media.Pk.(type) {
	case int64:
		return strconv.FormatInt(id, 10)
	case string:
		return id
	}
	return ""
}
func (media *StoryMedia) instagram() *Instagram {
	return media.inst
}
func (media *StoryMedia) setValues() {
	for i := range media.Items {
		setToItem(&media.Items[i], media)
	}
}
func (media StoryMedia) Error() error {
	return media.err
}
type trayRequest struct {
	Name  string `json:"name"`
	Value string `json:"value"`
}
func (media *StoryMedia) Sync() error {
	insta := media.inst
	query := []trayRequest{
		{"SUPPORTED_SDK_VERSIONS", "9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,21.0,22.0,23.0,24.0"},
		{"FACE_TRACKER_VERSION", "10"},
		{"segmentation", "segmentation_enabled"},
		{"COMPRESSION", "ETC2_COMPRESSION"},
	}
	qjson, err := json.Marshal(query)
	if err != nil {
		return err
	}
	id := media.Pk.(string)
	data, err := insta.prepareData(
		map[string]interface{}{
			"user_ids":                   []string{id},
			"supported_capabilities_new": b2s(qjson),
		},
	)
	if err != nil {
		return err
	}
	body, err := insta.sendRequest(
		&reqOptions{
			Endpoint: urlReelMedia,
			Query:    generateSignature(data),
			IsPost:   true,
		},
	)
	if err == nil {
		resp := trayResp{}
		err = json.Unmarshal(body, &resp)
		if err == nil {
			m, ok := resp.Reels[id]
			if ok {
				media.Items = m.Items
				media.setValues()
				return nil
			}
			err = fmt.Errorf("cannot find %s structure in response", id)
		}
	}
	return err
}
func (media *StoryMedia) Next(params ...interface{}) bool {
	if media.err != nil {
		return false
	}
	insta := media.inst
	endpoint := media.endpoint
	if media.uid != 0 {
		endpoint = fmt.Sprintf(endpoint, media.uid)
	}
	body, err := insta.sendSimpleRequest(endpoint)
	if err == nil {
		m := StoryMedia{}
		err = json.Unmarshal(body, &m)
		if err == nil {
			*media = m
			media.inst = insta
			media.endpoint = endpoint
			media.err = ErrNoMore  
			media.setValues()
			return true
		}
	}
	media.err = err
	return false
}
type FeedMedia struct {
	inst *Instagram
	err error
	uid       int64
	endpoint  string
	timestamp string
	Items               []Item `json:"items"`
	NumResults          int    `json:"num_results"`
	MoreAvailable       bool   `json:"more_available"`
	AutoLoadMoreEnabled bool   `json:"auto_load_more_enabled"`
	Status              string `json:"status"`
	NextID interface{} `json:"next_max_id"`
}
func (media *FeedMedia) Delete() error {
	for i := range media.Items {
		media.Items[i].Delete()
	}
	return nil
}
func (media *FeedMedia) instagram() *Instagram {
	return media.inst
}
func (media *FeedMedia) SetInstagram(inst *Instagram) {
	media.inst = inst
}