package task_pack_release import ( "bytes" "fmt" "os" "path/filepath" "slices" "strings" "text/template" ) type NfoCreatorSettings struct { Title string Infos []*KeyValue Notes string } type KeyValue struct { Key string Value string } type nfoTemplateData struct { Title1 string Title2 string Infos []string Notes []string } func CreateNfo(dstPath string, settings *NfoCreatorSettings) (string, error) { maxAllowedWidth := 65 // Prepare nfoTemplateData := &nfoTemplateData{} // Handle the title title := strings.ReplaceAll(settings.Title, " ", ".") title = strings.ReplaceAll(title, "(", "") title = strings.ReplaceAll(title, ")", "") title = strings.ReplaceAll(title, ",", "") title = strings.ReplaceAll(title, "&", "") title = strings.ReplaceAll(title, "..", ".") nfoTemplateData.Title1 = title nfoTemplateData.Title2 = "" if len(title) > maxAllowedWidth { splitIndex := strings.LastIndexAny(title[:maxAllowedWidth], ".-") nfoTemplateData.Title1 = title[:splitIndex] nfoTemplateData.Title2 = title[splitIndex:] } nfoTemplateData.Title1 = fmt.Sprintf("%-*s", maxAllowedWidth, nfoTemplateData.Title1) nfoTemplateData.Title2 = fmt.Sprintf("%-*s", maxAllowedWidth, nfoTemplateData.Title2) // Handle Infos maxKey := slices.MaxFunc(settings.Infos, func(a, b *KeyValue) int { if len(a.Key) < len(b.Key) { return -1 } else if len(a.Key) > len(b.Key) { return 1 } return 0 }) maxKeyLen := len(maxKey.Key) for _, info := range settings.Infos { nfoTemplateData.Infos = append(nfoTemplateData.Infos, fmt.Sprintf("%-*s : %-*s", maxKeyLen, info.Key, maxAllowedWidth-maxKeyLen-3, info.Value)) } // Handle Notes noteLines := strings.SplitSeq(settings.Notes, "\n") for note := range noteLines { if len(note) > maxAllowedWidth { splitIndex := strings.LastIndexAny(note[:maxAllowedWidth], " ") nfoTemplateData.Notes = append(nfoTemplateData.Notes, fmt.Sprintf("%-*s", maxAllowedWidth, note[:splitIndex])) nfoTemplateData.Notes = append(nfoTemplateData.Notes, fmt.Sprintf("%-*s", maxAllowedWidth, note[splitIndex+1:])) } else { nfoTemplateData.Notes = append(nfoTemplateData.Notes, fmt.Sprintf("%-*s", maxAllowedWidth, note)) } } // Parse the template file t1, err := template.New("info.nfo.tmpl").ParseFiles("./task_pack_release/info.nfo.tmpl") if err != nil { return "", err } // Execute the template to a memory buffer var buf bytes.Buffer if err := t1.Execute(&buf, nfoTemplateData); err != nil { return "", err } // Convert the buffer to string content := buf.String() os.MkdirAll(dstPath, os.ModePerm) nfoFilePath := filepath.Join(dstPath, fmt.Sprintf("%s.nfo", title)) if err := os.WriteFile(nfoFilePath, []byte(content), os.ModePerm); err != nil { return "", err } return nfoFilePath, nil } const ( WebDL = "WEB-DL" DvdRip = "DVDRip" Bluray = "BluRay" Dvd = "DVD" YouTube = "YouTube" ) type TitleBuilder struct { title string season string language string quality string resolution string codec string version string dubbed bool dualLanguage bool enhanced bool complete bool extras bool year string episodes string } // Constructor func NewTitleBuilder(title string) *TitleBuilder { return &TitleBuilder{title: title} } func (t *TitleBuilder) Year(value int) *TitleBuilder { t.year = fmt.Sprintf("(%d)", value) return t } func (t *TitleBuilder) Season(value int) *TitleBuilder { t.season = fmt.Sprintf("S%02d", value) return t } func (t *TitleBuilder) EpisodeRange(first, last int) *TitleBuilder { t.episodes = fmt.Sprintf("E%02d-E%02d", first, last) return t } func (t *TitleBuilder) German() *TitleBuilder { t.language = "German" return t } func (t *TitleBuilder) Complete() *TitleBuilder { t.complete = true return t } func (t *TitleBuilder) Dubbed() *TitleBuilder { t.dubbed = true return t } func (t *TitleBuilder) Dual() *TitleBuilder { t.dualLanguage = true return t } func (t *TitleBuilder) Enhanced() *TitleBuilder { t.enhanced = true return t } func (t *TitleBuilder) Extras() *TitleBuilder { t.extras = true return t } func (t *TitleBuilder) WebDL() *TitleBuilder { t.quality = WebDL return t } func (t *TitleBuilder) DVDRip() *TitleBuilder { t.quality = DvdRip return t } func (t *TitleBuilder) Bluray() *TitleBuilder { t.quality = Bluray return t } func (t *TitleBuilder) R480() *TitleBuilder { t.resolution = "480p" return t } func (t *TitleBuilder) R576() *TitleBuilder { t.resolution = "576p" return t } func (t *TitleBuilder) R720p() *TitleBuilder { t.resolution = "720p" return t } func (t *TitleBuilder) R1080p() *TitleBuilder { t.resolution = "1080p" return t } func (t *TitleBuilder) X264() *TitleBuilder { t.codec = "x264" return t } func (t *TitleBuilder) ReleaseVersion(version string) *TitleBuilder { t.version = fmt.Sprintf("v%s", version) return t } func (t *TitleBuilder) Build() string { var sb strings.Builder // Replace German umlauts title := strings.NewReplacer( "ö", "oe", "ä", "ae", "ü", "ue", ).Replace(t.title) sb.WriteString(title) addIfTrue(&sb, t.extras, "Extras") addIfNotEmpty(&sb, t.year, false) hasSeason := addIfNotEmpty(&sb, t.season, false) addIfNotEmpty(&sb, t.episodes, hasSeason) addIfTrue(&sb, t.complete, "Complete") addIfNotEmpty(&sb, t.language, false) addIfTrue(&sb, t.dubbed, "Dubbed") addIfTrue(&sb, t.dualLanguage, "DL") addIfNotEmpty(&sb, t.quality, false) addIfNotEmpty(&sb, t.resolution, false) addIfTrue(&sb, t.enhanced, "Enhanced") addIfNotEmpty(&sb, t.codec, false) addIfNotEmpty(&sb, t.version, false) sb.WriteString("-Roemer") return sb.String() } func addIfNotEmpty(sb *strings.Builder, value string, skipDot bool) bool { if strings.TrimSpace(value) != "" { if !skipDot { sb.WriteString(".") } sb.WriteString(value) return true } return false } func addIfTrue(sb *strings.Builder, value bool, text string) bool { if value { sb.WriteString(".") sb.WriteString(text) return true } return false }