{"record":{"id":"870384aee73356a1","repo":"tonhowtf/omniget","slug":"cole-o-link-de-um-vod-ou-de-um-clipe-da-twitch","errorCode":null,"errorMessage":"cole o link de um VOD ou de um clipe da Twitch: {}","messagePattern":"cole o link de um VOD ou de um clipe da Twitch: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/tools/twitch/gql.rs","lineNumber":265,"sourceCode":"            escape(slug)\n        );\n        let data = self.query(&q).await?;\n        let c = data.get(\"clip\").filter(|v| !v.is_null());\n        let c = c.ok_or_else(|| anyhow!(\"clipe não encontrado: {}\", slug))?;\n        let vid = c\n            .pointer(\"/video/id\")\n            .and_then(|x| x.as_str())\n            .ok_or_else(|| anyhow!(\"esse clipe não tem VOD de origem, então não há chat\"))?;\n        let mut info = self.video(vid).await?;\n        info.clip_offset = c.get(\"videoOffsetSeconds\").and_then(num);\n        info.clip_duration = c.get(\"durationSeconds\").and_then(num);\n        Ok(info)\n    }\n\n    /// Resolve o que o usuário colou até chegar num VOD.\n    pub async fn resolve_chat_target(&self, input: &str) -> anyhow::Result<VideoInfo> {\n        let target = parse_video(input)\n            .ok_or_else(|| anyhow!(\"cole o link de um VOD ou de um clipe da Twitch: {}\", input))?;\n        match target {\n            ChatTarget::Video(id) => self.video(&id).await,\n            ChatTarget::Clip(slug) => self.clip_video(&slug).await,\n        }\n    }\n}\n\nfn num(v: &Value) -> Option<f64> {\n    v.as_f64()\n        .or_else(|| v.as_str().and_then(|s| s.parse().ok()))\n}\n\npub(super) fn str_at(v: &Value, key: &str) -> String {\n    v.get(key)\n        .and_then(|x| x.as_str())\n        .unwrap_or_default()\n        .to_string()\n}","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/tools/twitch/gql.rs#L247-L283","documentation":"resolve_chat_target parses a user-pasted string into a Twitch VOD or clip target. When parse_video cannot extract a video id or clip slug from the input, it throws this anyhow error embedding the original input. It is a user-input validation error, not an internal fault.","triggerScenarios":"Calling resolve_chat_target with a string that is not a Twitch VOD/clip URL (e.g. a channel URL, a plain video id without context, a YouTube link, or arbitrary text).","commonSituations":"Users paste a channel page (twitch.tv/name) instead of a VOD link; paste a video ID alone like '123456789'; clipboard contains a timestamped or shortened link variant the parser does not handle.","solutions":["Check that the input matches a supported Twitch VOD URL pattern (twitch.tv/videos/<id>) or clip URL (twitch.tv/<ch>/clip/<slug> or clips.twitch.tv/<slug>) before calling","Trim whitespace and strip query parameters (?t=...) from the pasted link","Ask the user to re-copy the link directly from the VOD/clip share button","If supporting more formats is desired, extend parse_video in gql.rs to handle them"],"exampleFix":"// before\nlet info = tool.resolve_chat_target(\"twitch.tv/somechannel\").await?;\n// after\nlet input = \"twitch.tv/somechannel\";\nif !input.contains(\"/videos/\") && !input.contains(\"/clip/\") && !input.contains(\"clips.twitch.tv\") {\n    anyhow::bail!(\"{} não é um link de VOD ou clipe da Twitch\", input);\n}\nlet info = tool.resolve_chat_target(input).await?;","handlingStrategy":"validation","validationCode":"fn is_twitch_vod_or_clip(input: &str) -> bool {\n    let u = input.trim();\n    u.contains(\"twitch.tv/videos/\") || u.contains(\"/clip/\") || u.contains(\"clips.twitch.tv/\")\n}","typeGuard":"fn looks_like_twitch_url(s: &str) -> bool {\n    s.starts_with(\"https://\") && (s.contains(\"twitch.tv/videos/\") || s.contains(\"clips.twitch.tv\"))\n}","tryCatchPattern":"match tool.resolve_chat_target(input).await {\n    Ok(info) => info,\n    Err(e) => { show_user(\"link inválido, use um VOD ou clipe da Twitch\"); return; }\n}","preventionTips":["Validate the pasted string against Twitch URL patterns before calling","Strip query strings and whitespace from user input","Show a clear UI hint of the accepted link formats"],"tags":["twitch","input-validation","url-parsing","rust"],"backgroundTag":"invalid-url-format","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"}