Skip to content

Commit dd12833

Browse files
authored
Merge pull request #26 from xdevplatform/migrate-media-upload
Refactor media upload API functionality
2 parents 0cc3d7e + ce68446 commit dd12833

File tree

5 files changed

+170
-110
lines changed

5 files changed

+170
-110
lines changed

.github/workflows/go.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ jobs:
2525
run: go test -v ./...
2626

2727
- name: Run lint
28-
run: go fmt ./...
28+
run: gofmt -l .

api/media.go

Lines changed: 98 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,17 @@ type MediaUploader struct {
2727
authType string
2828
username string
2929
headers []string
30+
trace bool
31+
}
32+
33+
type InitRequest struct {
34+
TotalBytes int64 `json:"total_bytes"`
35+
MediaType string `json:"media_type"`
36+
MediaCategory string `json:"media_category"`
3037
}
3138

3239
// NewMediaUploader creates a new MediaUploader
33-
func NewMediaUploader(client Client, filePath string, verbose bool, authType string, username string, headers []string) (*MediaUploader, error) {
40+
func NewMediaUploader(client Client, filePath string, verbose, trace bool, authType string, username string, headers []string) (*MediaUploader, error) {
3441
fileInfo, err := os.Stat(filePath)
3542
if err != nil {
3643
return nil, fmt.Errorf("error accessing file: %v", err)
@@ -49,16 +56,18 @@ func NewMediaUploader(client Client, filePath string, verbose bool, authType str
4956
authType: authType,
5057
username: username,
5158
headers: headers,
59+
trace: trace,
5260
}, nil
5361
}
5462

55-
func NewMediaUploaderWithoutFile(client Client, verbose bool, authType string, username string, headers []string) *MediaUploader {
63+
func NewMediaUploaderWithoutFile(client Client, verbose, trace bool, authType string, username string, headers []string) *MediaUploader {
5664
return &MediaUploader{
5765
client: client,
5866
verbose: verbose,
5967
authType: authType,
6068
username: username,
6169
headers: headers,
70+
trace: trace,
6271
}
6372
}
6473

@@ -69,19 +78,27 @@ func (m *MediaUploader) Init(mediaType string, mediaCategory string) error {
6978
}
7079

7180
finalUrl := MediaEndpoint +
72-
"?command=INIT" +
73-
"&total_bytes=" + strconv.FormatInt(m.fileSize, 10) +
74-
"&media_type=" + mediaType +
75-
"&media_category=" + mediaCategory
81+
"/initialize"
82+
83+
body := InitRequest{
84+
TotalBytes: m.fileSize,
85+
MediaType: mediaType,
86+
MediaCategory: mediaCategory,
87+
}
88+
jsonData, err := json.Marshal(body)
89+
if err != nil {
90+
return fmt.Errorf("error marshalling body: %v", err)
91+
}
7692

7793
requestOptions := RequestOptions{
7894
Method: "POST",
7995
Endpoint: finalUrl,
8096
Headers: m.headers,
81-
Data: "",
97+
Data: string(jsonData),
8298
AuthType: m.authType,
8399
Username: m.username,
84100
Verbose: m.verbose,
101+
Trace: m.trace,
85102
}
86103

87104
response, clientErr := m.client.SendRequest(requestOptions)
@@ -142,27 +159,27 @@ func (m *MediaUploader) Append() error {
142159
return fmt.Errorf("error reading file: %v", err)
143160
}
144161

162+
finalUrl := MediaEndpoint + fmt.Sprintf("/%s/append", m.mediaID)
163+
145164
// Prepare form fields
146165
formFields := map[string]string{
147-
"command": "APPEND",
148-
"media_id": m.mediaID,
149166
"segment_index": strconv.Itoa(segmentIndex),
150167
}
151168

152169
requestOptions := RequestOptions{
153170
Method: "POST",
154-
Endpoint: MediaEndpoint,
171+
Endpoint: finalUrl,
155172
Headers: m.headers,
156173
Data: "",
157174
AuthType: m.authType,
158175
Username: m.username,
159176
Verbose: m.verbose,
177+
Trace: m.trace,
160178
}
161179
multipartOptions := MultipartOptions{
162180
RequestOptions: requestOptions,
163181
FormFields: formFields,
164182
FileField: "media",
165-
FilePath: m.filePath,
166183
FileName: filepath.Base(m.filePath),
167184
FileData: buffer[:bytesRead],
168185
}
@@ -199,7 +216,7 @@ func (m *MediaUploader) Finalize() (json.RawMessage, error) {
199216
fmt.Printf("\033[32mFinalizing media upload...\033[0m\n")
200217
}
201218

202-
finalUrl := MediaEndpoint + "?command=FINALIZE&media_id=" + m.mediaID
219+
finalUrl := MediaEndpoint + fmt.Sprintf("/%s/finalize", m.mediaID)
203220
requestOptions := RequestOptions{
204221
Method: "POST",
205222
Endpoint: finalUrl,
@@ -208,6 +225,7 @@ func (m *MediaUploader) Finalize() (json.RawMessage, error) {
208225
AuthType: m.authType,
209226
Username: m.username,
210227
Verbose: m.verbose,
228+
Trace: m.trace,
211229
}
212230
response, clientErr := m.client.SendRequest(requestOptions)
213231
if clientErr != nil {
@@ -237,6 +255,7 @@ func (m *MediaUploader) CheckStatus() (json.RawMessage, error) {
237255
AuthType: m.authType,
238256
Username: m.username,
239257
Verbose: m.verbose,
258+
Trace: m.trace,
240259
}
241260
response, clientErr := m.client.SendRequest(requestOptions)
242261
if clientErr != nil {
@@ -316,8 +335,8 @@ func (m *MediaUploader) SetMediaID(mediaID string) {
316335
}
317336

318337
// ExecuteMediaUpload handles the media upload command execution
319-
func ExecuteMediaUpload(filePath, mediaType, mediaCategory, authType, username string, verbose, waitForProcessing bool, headers []string, client Client) error {
320-
uploader, err := NewMediaUploader(client, filePath, verbose, authType, username, headers)
338+
func ExecuteMediaUpload(filePath, mediaType, mediaCategory, authType, username string, verbose, waitForProcessing, trace bool, headers []string, client Client) error {
339+
uploader, err := NewMediaUploader(client, filePath, verbose, trace, authType, username, headers)
321340
if err != nil {
322341
return fmt.Errorf("error: %v", err)
323342
}
@@ -352,8 +371,8 @@ func ExecuteMediaUpload(filePath, mediaType, mediaCategory, authType, username s
352371
}
353372

354373
// ExecuteMediaStatus handles the media status command execution
355-
func ExecuteMediaStatus(mediaID, authType, username string, verbose, wait bool, headers []string, client Client) error {
356-
uploader := NewMediaUploaderWithoutFile(client, verbose, authType, username, headers)
374+
func ExecuteMediaStatus(mediaID, authType, username string, verbose, wait, trace bool, headers []string, client Client) error {
375+
uploader := NewMediaUploaderWithoutFile(client, verbose, trace, authType, username, headers)
357376

358377
uploader.SetMediaID(mediaID)
359378

@@ -386,19 +405,25 @@ func ExecuteMediaStatus(mediaID, authType, username string, verbose, wait bool,
386405

387406
// HandleMediaAppendRequest handles a media append request with a file
388407
func HandleMediaAppendRequest(options RequestOptions, mediaFile string, client Client) (json.RawMessage, error) {
389-
mediaID := ExtractMediaID(options.Endpoint, options.Data)
408+
// TODO: This function is in a weird state since append accepts either a multipart request or a json request
409+
// Right now, this function takes in segment_index from the json request and sends a multipart request
410+
// We should refactor this to handle both cases (by adding curl-like multipart request support)
411+
// example usage:
412+
// xurl -X POST "/2/media/upload/{id}/append" \
413+
// -H "Content-Type: multipart/form-data" \
414+
// -F "media=@/path/to/your/file.mp4" \
415+
// -F "segment_index=0"
416+
mediaID := ExtractMediaID(options.Endpoint)
390417
if mediaID == "" {
391-
return nil, fmt.Errorf("media_id is required for APPEND command")
418+
return nil, fmt.Errorf("media_id is required for append endpoint")
392419
}
393420

394-
segmentIndex := ExtractSegmentIndex(options.Endpoint, options.Data)
421+
segmentIndex := ExtractSegmentIndex(options.Data)
395422
if segmentIndex == "" {
396423
segmentIndex = "0"
397424
}
398425

399426
formFields := map[string]string{
400-
"command": "APPEND",
401-
"media_id": mediaID,
402427
"segment_index": segmentIndex,
403428
}
404429

@@ -421,62 +446,83 @@ func HandleMediaAppendRequest(options RequestOptions, mediaFile string, client C
421446
}
422447

423448
// ExtractMediaID extracts media_id from URL or data
424-
func ExtractMediaID(url string, data string) string {
425-
if strings.Contains(url, "media_id=") {
426-
parts := strings.Split(url, "media_id=")
449+
func ExtractMediaID(url string) string {
450+
if url == "" {
451+
return ""
452+
}
453+
454+
if !strings.Contains(url, "/2/media/upload") {
455+
return ""
456+
}
457+
458+
if strings.HasSuffix(url, "/2/media/upload/initialize") {
459+
return ""
460+
}
461+
462+
// Extract media ID from path for append/finalize endpoints
463+
if strings.Contains(url, "/2/media/upload/") {
464+
parts := strings.Split(url, "/2/media/upload/")
427465
if len(parts) > 1 {
428-
mediaID := parts[1]
429-
if idx := strings.Index(mediaID, "&"); idx != -1 {
430-
mediaID = mediaID[:idx]
466+
path := parts[1]
467+
for _, suffix := range []string{"/append", "/finalize"} {
468+
if idx := strings.Index(path, suffix); idx != -1 {
469+
return path[:idx]
470+
}
431471
}
432-
return mediaID
433472
}
434473
}
435474

436-
if strings.Contains(data, "media_id=") {
437-
parts := strings.Split(data, "media_id=")
438-
if len(parts) > 1 {
439-
mediaID := parts[1]
440-
if idx := strings.Index(mediaID, "&"); idx != -1 {
441-
mediaID = mediaID[:idx]
475+
if strings.Contains(url, "?") {
476+
queryParams := strings.Split(url, "?")
477+
if len(queryParams) > 1 {
478+
params := strings.Split(queryParams[1], "&")
479+
for _, param := range params {
480+
if strings.HasPrefix(param, "media_id=") {
481+
return strings.Split(param, "=")[1]
482+
}
442483
}
443-
return mediaID
444484
}
445485
}
446486

447487
return ""
448488
}
449489

450-
// ExtractSegmentIndex extracts segment_index from URL or data
451-
func ExtractSegmentIndex(url string, data string) string {
452-
if strings.Contains(url, "segment_index=") {
453-
parts := strings.Split(url, "segment_index=")
490+
// extracts command from URL
491+
func ExtractCommand(url string) string {
492+
if strings.Contains(url, "/2/media/upload/") {
493+
parts := strings.Split(url, "/2/media/upload/")
454494
if len(parts) > 1 {
455-
segmentIndex := parts[1]
456-
if idx := strings.Index(segmentIndex, "&"); idx != -1 {
457-
segmentIndex = segmentIndex[:idx]
495+
path := parts[1]
496+
if strings.Contains(path, "/append") {
497+
return "append"
498+
}
499+
if strings.Contains(path, "/finalize") {
500+
return "finalize"
501+
}
502+
if path == "initialize" {
503+
return "initialize"
458504
}
459-
return segmentIndex
460505
}
506+
return "status"
461507
}
462508

463-
if strings.Contains(data, "segment_index=") {
464-
parts := strings.Split(data, "segment_index=")
465-
if len(parts) > 1 {
466-
segmentIndex := parts[1]
467-
if idx := strings.Index(segmentIndex, "&"); idx != -1 {
468-
segmentIndex = segmentIndex[:idx]
469-
}
509+
return ""
510+
}
511+
512+
// ExtractSegmentIndex extracts segment_index from URL or data
513+
func ExtractSegmentIndex(data string) string {
514+
var jsonData map[string]string
515+
if err := json.Unmarshal([]byte(data), &jsonData); err == nil {
516+
if segmentIndex, ok := jsonData["segment_index"]; ok {
470517
return segmentIndex
471518
}
472519
}
473-
474520
return ""
475521
}
476522

477523
// IsMediaAppendRequest checks if the request is a media append request
478524
func IsMediaAppendRequest(url string, mediaFile string) bool {
479525
return strings.Contains(url, "/2/media/upload") &&
480-
strings.Contains(url, "command=APPEND") &&
526+
strings.Contains(url, "append") &&
481527
mediaFile != ""
482528
}

0 commit comments

Comments
 (0)