ファイル名の上限を拡大し、動画の配信・削除を os.Root に置き換え(エピソード ID は 64 バイト上限)
This commit is contained in:
+36
-15
@@ -1,23 +1,41 @@
|
||||
package media
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const maxComponentRunes = 80
|
||||
const maxComponentBytes = 60
|
||||
const (
|
||||
maxSeriesBytes = 90
|
||||
maxEpisodeBytes = 140
|
||||
maxEpisodeIDBytes = 64
|
||||
maxFilenameBytes = 255
|
||||
)
|
||||
|
||||
// RelativeVideoPath は worker と web が共有する保存先の相対パスを返す。
|
||||
func RelativeVideoPath(seriesTitle, episodeTitle, episodeID string) string {
|
||||
series := sanitizeComponent(seriesTitle)
|
||||
episode := sanitizeComponent(episodeTitle)
|
||||
id := sanitizeComponent(episodeID)
|
||||
return filepath.Join(series, series+" "+episode+" ["+id+"].mp4")
|
||||
func RelativeVideoPath(seriesTitle, episodeTitle, episodeID string) (string, error) {
|
||||
series := sanitizeComponent(seriesTitle, maxSeriesBytes)
|
||||
episode := sanitizeComponent(episodeTitle, maxEpisodeBytes)
|
||||
id := sanitizeComponent(episodeID, -1)
|
||||
if len(id) > maxEpisodeIDBytes {
|
||||
return "", fmt.Errorf("エピソードIDはサニタイズ後%dバイト以下である必要があります: %dバイト", maxEpisodeIDBytes, len(id))
|
||||
}
|
||||
|
||||
// A directory entry must fit within the NFS namlen limit. Keep the episode
|
||||
// ID intact and use the remaining bytes for the episode title.
|
||||
const separators = " [].mp4"
|
||||
seriesInFilename := series
|
||||
titleBytes := maxFilenameBytes - len(seriesInFilename) - len(id) - len(separators)
|
||||
if titleBytes < 0 {
|
||||
titleBytes = 0
|
||||
}
|
||||
episode = truncateComponent(episode, titleBytes, false)
|
||||
return filepath.Join(series, seriesInFilename+" "+episode+" ["+id+"].mp4"), nil
|
||||
}
|
||||
|
||||
func sanitizeComponent(value string) string {
|
||||
func sanitizeComponent(value string, maxBytes int) string {
|
||||
var result []rune
|
||||
lastReplacement := false
|
||||
for _, r := range value {
|
||||
@@ -39,22 +57,25 @@ func sanitizeComponent(value string) string {
|
||||
if clean == "" || clean == "." || clean == ".." {
|
||||
return "名称未設定"
|
||||
}
|
||||
runes := []rune(clean)
|
||||
if len(runes) > maxComponentRunes {
|
||||
runes = runes[:maxComponentRunes]
|
||||
return truncateComponent(clean, maxBytes, true)
|
||||
}
|
||||
|
||||
func truncateComponent(value string, maxBytes int, fallback bool) string {
|
||||
if maxBytes < 0 {
|
||||
return value
|
||||
}
|
||||
var limited []rune
|
||||
bytes := 0
|
||||
for _, r := range runes {
|
||||
for _, r := range value {
|
||||
size := len(string(r))
|
||||
if bytes+size > maxComponentBytes {
|
||||
if bytes+size > maxBytes {
|
||||
break
|
||||
}
|
||||
limited = append(limited, r)
|
||||
bytes += size
|
||||
}
|
||||
clean = strings.Trim(string(limited), " .")
|
||||
if clean == "" || clean == "." || clean == ".." {
|
||||
clean := strings.Trim(string(limited), " .")
|
||||
if fallback && (clean == "" || clean == "." || clean == "..") {
|
||||
return "名称未設定"
|
||||
}
|
||||
return clean
|
||||
|
||||
Reference in New Issue
Block a user