Skip to content
This repository was archived by the owner on Jun 24, 2025. It is now read-only.

Commit 2e89b7e

Browse files
author
Bash06
committed
feat: improve ograph embeds and fix some issues
1 parent b63e449 commit 2e89b7e

File tree

6 files changed

+115
-123
lines changed

6 files changed

+115
-123
lines changed

api/share.go

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import (
2222
"bash06/vxinstagram/utils"
2323
"log/slog"
2424
"net/http"
25-
"net/http/httputil"
26-
"net/url"
2725
"strings"
2826

2927
"github.com/getsentry/sentry-go"
@@ -83,6 +81,7 @@ func (h *Handler) FollowShare(c *gin.Context) {
8381
}
8482

8583
var videoUrl string
84+
var data *utils.ExtractedData
8685
create := false
8786

8887
err = h.Db.Where("post_id = ?", postId).
@@ -92,7 +91,9 @@ func (h *Handler) FollowShare(c *gin.Context) {
9291
if err != nil {
9392
create = true
9493
// 1: Try to scrape the HTML
95-
videoUrl, err = utils.ScrapeFromHTML(postId)
94+
data, err = utils.ScrapeFromHTML(postId)
95+
videoUrl = data.VideoURL
96+
9697
if err != nil || videoUrl == "" {
9798
slog.Error("Failed to scrape video URL from HTML. Trying to make an API request...", slog.Any("err", err))
9899

@@ -125,35 +126,10 @@ func (h *Handler) FollowShare(c *gin.Context) {
125126
})
126127
}
127128

128-
remote, err := url.Parse(videoUrl)
129-
if err != nil {
130-
slog.Error("Failed to parse CDN video URL", slog.Any("err", err))
131-
sentry.CaptureException(err)
132-
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
133-
Title: "VxInstagram - Server Error",
134-
Description: "VxInstagram encountered a server side error while processing your request. Request ID:`" + span.SpanID.String() + "`",
135-
})
136-
return
137-
}
138-
139-
proxy := httputil.NewSingleHostReverseProxy(remote)
140-
proxy.Director = func(r *http.Request) {
141-
r.Header = c.Request.Header
142-
r.Host = remote.Host
143-
r.URL = remote
144-
r.Header = c.Request.Header.Clone()
145-
146-
hopHeaders := []string{
147-
"Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "Te", "Trailer", "Transfer-Encoding",
148-
}
149-
for _, h := range hopHeaders {
150-
r.Header.Del(h)
151-
}
152-
}
153-
154-
slog.Debug("Success!")
155-
c.Header("Cache-Control", "max-age=43200")
156-
proxy.ServeHTTP(c.Writer, c.Request)
129+
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
130+
Title: "❤️ " + data.Likes + "💬 " + data.Comments + "👀 " + data.Views,
131+
VideoURL: videoUrl,
132+
})
157133

158134
if create {
159135
err := h.Db.Model(&utils.PostMemory{}).Create(&utils.PostMemory{

api/video.go

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ import (
2323
"context"
2424
"log/slog"
2525
"net/http"
26-
"net/http/httputil"
27-
"net/url"
2826
"strings"
2927

3028
"github.com/getsentry/sentry-go"
@@ -38,7 +36,6 @@ type HtmlOpenGraphData struct {
3836
Description string
3937
VideoURL string
4038
PostURL string
41-
Color string
4239
}
4340

4441
func (h *Handler) ServeVideo(c *gin.Context) {
@@ -62,12 +59,13 @@ func (h *Handler) ServeVideo(c *gin.Context) {
6259
userAgent := strings.ToLower(c.Request.Header.Get("User-Agent"))
6360

6461
if !strings.Contains(userAgent, "discord") {
65-
c.Redirect(http.StatusPermanentRedirect, "https://instagram.com/reel/"+postId)
62+
c.Redirect(http.StatusPermanentRedirect, "https://instagram.com/"+c.Request.URL.String())
6663
return
6764
}
6865
}
6966

7067
var videoUrl string
68+
var data *utils.ExtractedData
7169
create := false
7270

7371
err := h.Db.Where("post_id = ?", postId).
@@ -78,7 +76,9 @@ func (h *Handler) ServeVideo(c *gin.Context) {
7876
slog.Debug("Reading from database failed")
7977
create = true
8078
// 1: Try to scrape the HTML
81-
videoUrl, err = utils.ScrapeFromHTML(postId)
79+
data, err = utils.ScrapeFromHTML(postId)
80+
videoUrl = data.VideoURL
81+
8282
if err != nil || videoUrl == "" {
8383
slog.Error("Failed to scrape video URL from HTML. Trying to make an API request...", slog.Any("err", err))
8484

@@ -92,14 +92,15 @@ func (h *Handler) ServeVideo(c *gin.Context) {
9292
slog.Error("Failed to fetch video URL from API. Critial failure!", slog.Any("err", err))
9393
sentry.CaptureException(err)
9494

95-
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
96-
Title: "VxInstagram - Server Error",
97-
Description: "VxInstagram encountered a server side error while processing your request. Request ID:`" + span.SpanID.String() + "`",
98-
})
99-
return
95+
if err.Error() != "no instagram cookie provided" {
96+
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
97+
Title: "VxInstagram - Server Error",
98+
Description: "VxInstagram encountered a server side error while processing your request. Request ID:`" + span.SpanID.String() + "`",
99+
})
100+
}
101+
} else {
102+
videoUrl = data.Items[0].VideoVersions[0].URL
100103
}
101-
102-
videoUrl = data.Items[0].VideoVersions[0].URL
103104
}
104105
}
105106

@@ -111,36 +112,11 @@ func (h *Handler) ServeVideo(c *gin.Context) {
111112
})
112113
}
113114

114-
remote, err := url.Parse(videoUrl)
115-
if err != nil {
116-
slog.Error("Failed to parse CDN video URL", slog.Any("err", err))
117-
sentry.CaptureException(err)
118-
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
119-
Title: "VxInstagram - Server Error",
120-
Description: "VxInstagram encountered a server side error while processing your request. Request ID:`" + span.SpanID.String() + "`",
121-
})
122-
return
123-
}
124-
125-
proxy := httputil.NewSingleHostReverseProxy(remote)
126-
proxy.Director = func(r *http.Request) {
127-
r.Header = c.Request.Header
128-
r.Host = remote.Host
129-
r.URL = remote
130-
r.Header = c.Request.Header.Clone()
131-
132-
hopHeaders := []string{
133-
"Connection", "Keep-Alive", "Proxy-Authenticate", "Proxy-Authorization", "Te", "Trailer", "Transfer-Encoding",
134-
}
135-
136-
for _, h := range hopHeaders {
137-
r.Header.Del(h)
138-
}
139-
}
140-
141-
slog.Debug("Success!")
142-
c.Header("Cache-Control", "max-age=43200")
143-
proxy.ServeHTTP(c.Writer, c.Request)
115+
c.HTML(http.StatusOK, "embed.html", &HtmlOpenGraphData{
116+
Title: "@" + data.Username,
117+
VideoURL: videoUrl,
118+
PostURL: "https://instagram.com/" + c.Request.URL.String(),
119+
})
144120

145121
if create {
146122
err := h.Db.Model(&utils.PostMemory{}).Create(&utils.PostMemory{

middleware/cors.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import (
2626
func CorsMiddleware() gin.HandlerFunc {
2727
return func(c *gin.Context) {
2828
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
29+
c.Writer.Header().Set("Access-Control-Max-Age", "30000000")
2930
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
3031
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
32+
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // Optional
3133

3234
if c.Request.Method == http.MethodOptions {
3335
c.AbortWithStatus(http.StatusNoContent)

templates/embed.html

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
1+
</html>
2+
13
<!DOCTYPE html>
24
<html lang="en">
3-
45
<head>
5-
<meta property="og:title" content="{{.Title}}" />
6-
<meta property="og:description" content="{{.Description}}" />
6+
<meta property="og:title" content="{{.Title}}" />
7+
<meta property="og:description" content="{{.Description}}" />
8+
<meta property="og:type" content="video.other" />
79

8-
<meta property="og:video" content="{{.VideoURL}}" />
9-
<meta property="og:video:secure_url" content="{{.VideoURL}}" />
10-
<meta property="og:video:type" content="video/mp4" />
11-
<meta property="og:video:width" content="1280" />
12-
<meta property="og:video:height" content="720" />
10+
<meta property="og:video" content="{{.VideoURL}}" />
11+
<meta property="og:video:secure_url" content="{{.VideoURL}}" />
12+
<meta property="og:video:type" content="video/mp4" />
1313

14-
<meta property="twitter:player:width" content="1280" />
15-
<meta property="twitter:player:height" content="720" />
16-
<meta property="twitter:player:stream" content="{{.VideoURL}}" />
17-
<meta property="twitter:player:stream:content_type" content="video/mp4" />
14+
<meta name="twitter:card" content="player" />
15+
<meta name="twitter:title" content="{{.Title}}" />
16+
<meta name="twitter:description" content="{{.Description}}" />
17+
<meta name="twitter:player" content="{{.VideoURL}}" />
1818

19-
<meta property="theme-color" content="{{.Color}}" />
20-
<title>Vxinstgram</title>
19+
<meta property="theme-color" content="#2b2d31" />
20+
<title>{{.Title}}</title>
2121
</head>
22-
<a href="{{.PostURL}}">
23-
<p>If redirects don't work in your browser, click here to go to the original post</p>
24-
</a>
2522
<body>
23+
<video controls width="1280" height="720">
24+
<source src="{{.VideoURL}}" type="video/mp4">
25+
Your browser does not support the video tag.
26+
</video>
2627
</body>
27-
28-
</html>
28+
</html>

utils/extractUrl.go

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,76 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
package utils
1919

2020
import (
21-
"fmt"
2221
"strings"
2322
)
2423

24+
type ExtractedData struct {
25+
VideoURL string
26+
ThumbnailURL string
27+
IsVideo string
28+
Title string
29+
Views string
30+
Comments string
31+
Likes string
32+
Username string
33+
}
34+
35+
var prefixes = map[string]string{
36+
"VideoURL": `\"video_url\":`,
37+
"ThumbnailURL": `\"display_url\":`,
38+
"IsVideo": `\"is_video\":`,
39+
"Title": `\"title\":`,
40+
"Views": `\"video_views\":`,
41+
"Comments": `\"commenter_count\":`,
42+
"Likes": `\"likes_count\":`,
43+
"Username": `\"username\":`,
44+
}
45+
2546
const (
26-
prefix = `\"video_url\":`
27-
quote = `\"`
28-
prefixLen = len(prefix) + 1
47+
quote = `\"`
2948
)
3049

3150
// Extracts the video URL from response
32-
func ExtractUrl(s string) (string, bool) {
33-
// Thanks a lot for this tyler
34-
// Find the first prefix
35-
startIdx := strings.Index(s, prefix)
36-
if startIdx == -1 {
37-
return "", false
38-
}
51+
func ExtractUrl(s string) (*ExtractedData, bool) {
52+
data := &ExtractedData{}
53+
ok := false
3954

40-
// Offset start by prefix len
41-
start := startIdx + prefixLen
55+
for key, prefix := range prefixes {
56+
startIdx := strings.Index(s, prefix)
57+
if startIdx == -1 {
58+
continue
59+
}
60+
start := startIdx + len(prefix) + 1
61+
end := strings.Index(s[start:], quote)
62+
if end == -1 {
63+
continue
64+
}
4265

43-
end := strings.Index(s[start:], quote)
44-
if end == -1 {
45-
return "", false
46-
}
66+
value := s[start : start+end]
67+
value = UnescapeJSONString(value)
68+
value = strings.ReplaceAll(value, `\/`, `/`)
4769

48-
result := s[start : start+end]
49-
result = UnescapeJSONString(result)
50-
result = strings.ReplaceAll(result, `\/`, `/`)
70+
switch key {
71+
case "ThumbnailURL":
72+
data.ThumbnailURL = value[1:]
73+
case "Title":
74+
data.Title = value[1:]
75+
case "Views":
76+
data.Views = value[1:]
77+
case "Comments":
78+
data.Comments = value[1:]
79+
case "Likes":
80+
data.Likes = value[1:]
81+
case "Username":
82+
data.Username = value[1:]
83+
case "VideoURL":
84+
data.VideoURL = value[1:]
85+
case "IsVideo":
86+
data.IsVideo = value[1:]
87+
}
5188

52-
fmt.Println(result)
89+
ok = true
90+
}
5391

54-
return result[1:], true
92+
return data, ok
5593
}

utils/scrape.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ import (
2626
"time"
2727
)
2828

29-
func ScrapeFromHTML(postId string) (string, error) {
29+
func ScrapeFromHTML(postId string) (*ExtractedData, error) {
3030
origin := "https://instagram.com/p/" + postId + "/embed/captioned"
3131

3232
slog.Debug("Preparing request", slog.String("origin", origin))
3333
req, err := http.NewRequest("GET", origin, nil)
3434
if err != nil {
35-
return "", fmt.Errorf("failed to prepare HTTP request: %v", err)
35+
return nil, fmt.Errorf("failed to prepare HTTP request: %v", err)
3636
}
3737

3838
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")
@@ -49,7 +49,7 @@ func ScrapeFromHTML(postId string) (string, error) {
4949

5050
res, err := client.Do(req)
5151
if err != nil {
52-
return "", fmt.Errorf("HTTP request failed: %v", err)
52+
return nil, fmt.Errorf("HTTP request failed: %v", err)
5353
}
5454

5555
defer res.Body.Close()
@@ -60,15 +60,15 @@ func ScrapeFromHTML(postId string) (string, error) {
6060
slog.Debug("Scanning response body for video url")
6161
for scanner.Scan() {
6262
line := scanner.Text()
63-
if url, found := ExtractUrl(line); found && url != "" {
64-
slog.Debug("Video URL found!")
65-
return url, nil
63+
if data, ok := ExtractUrl(line); ok {
64+
slog.Debug("Data found!")
65+
return data, nil
6666
}
6767
}
6868

6969
if err := scanner.Err(); err != nil {
70-
return "", fmt.Errorf("error scanning response: %v", err)
70+
return nil, fmt.Errorf("error scanning response: %v", err)
7171
}
7272

73-
return "", nil
73+
return nil, nil
7474
}

0 commit comments

Comments
 (0)