package ass import ( "bufio" "fmt" "os" "strconv" "strings" "sub-cli/internal/model" ) // 常量定义 const ( ASSHeader = "[Script Info]" ASSStylesHeader = "[V4+ Styles]" ASSEventsHeader = "[Events]" DefaultFormat = "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text" ) // Parse 解析ASS文件为ASSFile结构 func Parse(filePath string) (model.ASSFile, error) { file, err := os.Open(filePath) if err != nil { return model.ASSFile{}, fmt.Errorf("打开ASS文件失败: %w", err) } defer file.Close() result := model.NewASSFile() scanner := bufio.NewScanner(file) // 当前解析的区块 currentSection := "" var styleFormat, eventFormat []string for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, ";") { // 跳过空行和注释行 continue } // 检查章节标题 if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { currentSection = line continue } switch currentSection { case ASSHeader: // 解析脚本信息 if strings.Contains(line, ":") { parts := strings.SplitN(line, ":", 2) key := strings.TrimSpace(parts[0]) value := strings.TrimSpace(parts[1]) result.ScriptInfo[key] = value } case ASSStylesHeader: // 解析样式格式行和样式定义 if strings.HasPrefix(line, "Format:") { formatStr := strings.TrimPrefix(line, "Format:") styleFormat = parseFormatLine(formatStr) } else if strings.HasPrefix(line, "Style:") { styleValues := parseStyleLine(line) if len(styleFormat) > 0 && len(styleValues) > 0 { style := model.ASSStyle{ Name: styleValues[0], // 第一个值通常是样式名称 Properties: make(map[string]string), } // 将原始格式行保存下来 style.Properties["Format"] = "Name, " + strings.Join(styleFormat[1:], ", ") style.Properties["Style"] = strings.Join(styleValues, ", ") // 解析各个样式属性 for i := 0; i < len(styleFormat) && i < len(styleValues); i++ { style.Properties[styleFormat[i]] = styleValues[i] } result.Styles = append(result.Styles, style) } } case ASSEventsHeader: // 解析事件格式行和对话行 if strings.HasPrefix(line, "Format:") { formatStr := strings.TrimPrefix(line, "Format:") eventFormat = parseFormatLine(formatStr) } else if len(eventFormat) > 0 && (strings.HasPrefix(line, "Dialogue:") || strings.HasPrefix(line, "Comment:")) { eventType := "Dialogue" if strings.HasPrefix(line, "Comment:") { eventType = "Comment" line = strings.TrimPrefix(line, "Comment:") } else { line = strings.TrimPrefix(line, "Dialogue:") } values := parseEventLine(line) if len(values) >= len(eventFormat) { event := model.NewASSEvent() event.Type = eventType // 填充事件属性 for i, format := range eventFormat { value := values[i] switch strings.TrimSpace(format) { case "Layer": layer, _ := strconv.Atoi(value) event.Layer = layer case "Start": event.StartTime = parseASSTimestamp(value) case "End": event.EndTime = parseASSTimestamp(value) case "Style": event.Style = value case "Name": event.Name = value case "MarginL": marginL, _ := strconv.Atoi(value) event.MarginL = marginL case "MarginR": marginR, _ := strconv.Atoi(value) event.MarginR = marginR case "MarginV": marginV, _ := strconv.Atoi(value) event.MarginV = marginV case "Effect": event.Effect = value case "Text": // 文本可能包含逗号,所以需要特殊处理 textStartIndex := strings.Index(line, value) if textStartIndex >= 0 { event.Text = line[textStartIndex:] } else { event.Text = value } } } result.Events = append(result.Events, event) } } } } return result, nil }