Skip to content

Commit 99b8aaa

Browse files
authored
feat: improve doc media extension inference (#364)
Change-Id: Ifc7c0e7844908b88e2d527e0933d080b140a50eb
1 parent b4a26b2 commit 99b8aaa

File tree

4 files changed

+318
-48
lines changed

4 files changed

+318
-48
lines changed

shortcuts/doc/doc_media_download.go

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"context"
88
"fmt"
99
"net/http"
10-
"path/filepath"
11-
"strings"
1210

1311
larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
1412

@@ -18,17 +16,6 @@ import (
1816
"github.com/larksuite/cli/shortcuts/common"
1917
)
2018

21-
var mimeToExt = map[string]string{
22-
"image/png": ".png",
23-
"image/jpeg": ".jpg",
24-
"image/gif": ".gif",
25-
"image/webp": ".webp",
26-
"image/svg+xml": ".svg",
27-
"application/pdf": ".pdf",
28-
"video/mp4": ".mp4",
29-
"text/plain": ".txt",
30-
}
31-
3219
var DocMediaDownload = common.Shortcut{
3320
Service: "docs",
3421
Command: "+media-download",
@@ -90,19 +77,11 @@ var DocMediaDownload = common.Shortcut{
9077
}
9178
defer resp.Body.Close()
9279

93-
// Auto-detect extension from Content-Type
94-
finalPath := outputPath
95-
currentExt := filepath.Ext(outputPath)
96-
if currentExt == "" {
97-
contentType := resp.Header.Get("Content-Type")
98-
mimeType := strings.Split(contentType, ";")[0]
99-
mimeType = strings.TrimSpace(mimeType)
100-
if ext, ok := mimeToExt[mimeType]; ok {
101-
finalPath = outputPath + ext
102-
} else if mediaType == "whiteboard" {
103-
finalPath = outputPath + ".png"
104-
}
80+
fallbackExt := ""
81+
if mediaType == "whiteboard" {
82+
fallbackExt = ".png"
10583
}
84+
finalPath, _ := autoAppendDocMediaExtension(outputPath, resp.Header, fallbackExt)
10685

10786
// Validate final path after extension append
10887
if finalPath != outputPath {

shortcuts/doc/doc_media_ext.go

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) 2026 Lark Technologies Pte. Ltd.
2+
// SPDX-License-Identifier: MIT
3+
4+
package doc
5+
6+
import (
7+
"mime"
8+
"net/http"
9+
"path/filepath"
10+
"strings"
11+
12+
larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
13+
)
14+
15+
type docMediaExtensionResolution struct {
16+
Ext string
17+
Source string
18+
Detail string
19+
}
20+
21+
var docMediaMimeToExt = map[string]string{
22+
"application/msword": ".doc",
23+
"application/pdf": ".pdf",
24+
"application/vnd.ms-excel": ".xls",
25+
"application/vnd.ms-powerpoint": ".ppt",
26+
"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
27+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": ".xlsx",
28+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
29+
"application/xml": ".xml",
30+
"application/zip": ".zip",
31+
"image/bmp": ".bmp",
32+
"image/gif": ".gif",
33+
"image/jpeg": ".jpg",
34+
"image/png": ".png",
35+
"image/svg+xml": ".svg",
36+
"image/webp": ".webp",
37+
"text/csv": ".csv",
38+
"text/html": ".html",
39+
"text/plain": ".txt",
40+
"text/xml": ".xml",
41+
"video/mp4": ".mp4",
42+
}
43+
44+
func autoAppendDocMediaExtension(outputPath string, header http.Header, fallbackExt string) (string, *docMediaExtensionResolution) {
45+
if docMediaHasExplicitExtension(outputPath) {
46+
return outputPath, nil
47+
}
48+
normalizedPath := outputPath
49+
if filepath.Ext(outputPath) == "." {
50+
normalizedPath = strings.TrimSuffix(outputPath, ".")
51+
}
52+
if resolution := docMediaExtensionByContentType(header.Get("Content-Type")); resolution != nil {
53+
return normalizedPath + resolution.Ext, resolution
54+
}
55+
if resolution := docMediaExtensionByContentDisposition(header); resolution != nil {
56+
return normalizedPath + resolution.Ext, resolution
57+
}
58+
if fallbackExt != "" {
59+
return normalizedPath + fallbackExt, &docMediaExtensionResolution{
60+
Ext: fallbackExt,
61+
Source: "fallback",
62+
Detail: "default fallback",
63+
}
64+
}
65+
return outputPath, nil
66+
}
67+
68+
func docMediaHasExplicitExtension(path string) bool {
69+
ext := filepath.Ext(path)
70+
return ext != "" && ext != "."
71+
}
72+
73+
func docMediaExtensionByContentType(contentType string) *docMediaExtensionResolution {
74+
if contentType == "" {
75+
return nil
76+
}
77+
mediaType, _, err := mime.ParseMediaType(contentType)
78+
if err != nil {
79+
mediaType = strings.TrimSpace(strings.Split(contentType, ";")[0])
80+
}
81+
if ext, ok := docMediaMimeToExt[strings.ToLower(mediaType)]; ok {
82+
return &docMediaExtensionResolution{
83+
Ext: ext,
84+
Source: "Content-Type",
85+
Detail: contentType,
86+
}
87+
}
88+
return nil
89+
}
90+
91+
func docMediaExtensionByContentDisposition(header http.Header) *docMediaExtensionResolution {
92+
filename := strings.TrimSpace(larkcore.FileNameByHeader(header))
93+
if filename == "" {
94+
return nil
95+
}
96+
ext := filepath.Ext(filename)
97+
if ext == "" || ext == "." {
98+
return nil
99+
}
100+
return &docMediaExtensionResolution{
101+
Ext: ext,
102+
Source: "Content-Disposition",
103+
Detail: filename,
104+
}
105+
}

shortcuts/doc/doc_media_preview.go

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import (
77
"context"
88
"fmt"
99
"net/http"
10-
"path/filepath"
11-
"strings"
1210

1311
larkcore "github.com/larksuite/oapi-sdk-go/v3/core"
1412

@@ -18,17 +16,6 @@ import (
1816
"github.com/larksuite/cli/shortcuts/common"
1917
)
2018

21-
var previewMimeToExt = map[string]string{
22-
"image/png": ".png",
23-
"image/jpeg": ".jpg",
24-
"image/gif": ".gif",
25-
"image/webp": ".webp",
26-
"image/svg+xml": ".svg",
27-
"application/pdf": ".pdf",
28-
"video/mp4": ".mp4",
29-
"text/plain": ".txt",
30-
}
31-
3219
const PreviewType_SOURCE_FILE = "16"
3320

3421
var DocMediaPreview = common.Shortcut{
@@ -82,16 +69,7 @@ var DocMediaPreview = common.Shortcut{
8269
}
8370
defer resp.Body.Close()
8471

85-
finalPath := outputPath
86-
currentExt := filepath.Ext(outputPath)
87-
if currentExt == "" {
88-
contentType := resp.Header.Get("Content-Type")
89-
mimeType := strings.Split(contentType, ";")[0]
90-
mimeType = strings.TrimSpace(mimeType)
91-
if ext, ok := previewMimeToExt[mimeType]; ok {
92-
finalPath = outputPath + ext
93-
}
94-
}
72+
finalPath, _ := autoAppendDocMediaExtension(outputPath, resp.Header, "")
9573

9674
// Validate final path after extension append
9775
if finalPath != outputPath {

0 commit comments

Comments
 (0)