{"record":{"id":"cc981feff83018a2","repo":"tonhowtf/omniget","slug":"trecho-cap-tulo-precisa-de-um-t-tulo","errorCode":null,"errorMessage":"trecho {}: capítulo precisa de um título","messagePattern":"trecho (.+?): capítulo precisa de um título","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/sponsorblock.rs","lineNumber":240,"sourceCode":"                s.category\n            ));\n        }\n        if !ACTION_TYPES.contains(&s.action_type.as_str()) {\n            return Err(anyhow!(\n                \"trecho {}: tipo de ação desconhecido ({})\",\n                n,\n                s.action_type\n            ));\n        }\n        if !s.start.is_finite() || !s.end.is_finite() || s.start < 0.0 || s.end < 0.0 {\n            return Err(anyhow!(\"trecho {}: tempo inválido\", n));\n        }\n        let pontual = s.action_type == \"poi\" || s.action_type == \"full\";\n        if !pontual && s.end <= s.start {\n            return Err(anyhow!(\"trecho {}: o fim tem de vir depois do começo\", n));\n        }\n        if s.action_type == \"chapter\" && s.description.trim().is_empty() {\n            return Err(anyhow!(\"trecho {}: capítulo precisa de um título\", n));\n        }\n        if duration > 0.0 && s.end > duration + 1.0 {\n            return Err(anyhow!(\"trecho {}: passa do fim do vídeo\", n));\n        }\n    }\n    // Sobreposição só importa entre trechos que pulam pedaço.\n    let mut cortes: Vec<&NewSegment> = segments\n        .iter()\n        .filter(|s| s.action_type == \"skip\" || s.action_type == \"mute\")\n        .collect();\n    cortes.sort_by(|a, b| a.start.total_cmp(&b.start));\n    for par in cortes.windows(2) {\n        if par[1].start < par[0].end {\n            return Err(anyhow!(\"dois trechos se sobrepõem em {:.1}s\", par[1].start));\n        }\n    }\n    Ok(())\n}","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/sponsorblock.rs#L222-L258","documentation":"validate_segments requires every segment with action_type \"chapter\" to carry a non-empty (after trim) description, because a YouTube chapter is displayed by its title. A chapter with a blank title would be invisible/useless on the SponsorBlock API, so the library refuses to build the payload.","triggerScenarios":"Calling build_payload/submit with a chapter segment whose description is \"\", \"   \", or only whitespace/newlines.","commonSituations":"A UI where the title field is optional but the user picked the 'chapter' category; batch imports where chapter titles were in a column that was never mapped; pasting timestamps without titles.","solutions":["Set a non-empty description on every chapter segment before calling build_payload.","Fall back to a generated title (e.g. the formatted timestamp) when the user leaves it blank.","If the segment is not really a chapter, change its action_type instead of leaving it as \"chapter\".","Validate in the UI: require the title field when the chapter category is selected."],"exampleFix":"// before\nNewSegment { action_type: \"chapter\".into(), description: \"\".into(), .. }\n// after\nNewSegment { action_type: \"chapter\".into(), description: \"Introdução\".into(), .. }","handlingStrategy":"validation","validationCode":"fn chapter_has_title(s: &NewSegment) -> bool {\n    s.action_type != \"chapter\" || !s.description.trim().is_empty()\n}","typeGuard":"fn is_titled_chapter(s: &NewSegment) -> bool {\n    !(s.action_type == \"chapter\" && s.description.trim().is_empty())\n}","tryCatchPattern":"match build_payload(&id, &user, dur, &segs) {\n    Err(e) if e.to_string().contains(\"capítulo precisa de um título\") => prompt_for_missing_title()?,\n    other => other?,\n}","preventionTips":["Make the title field mandatory in the UI when the chapter category is selected.","Generate a default title from the timestamp when the user leaves it blank.","Trim descriptions before storing segments."],"tags":["validation","sponsorblock","rust","empty-field"],"backgroundTag":"empty-required-field","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}