ass_editor/commands/karaoke_commands/
adjust_impl.rs1use super::{AdjustKaraokeCommand, TimingAdjustment};
4use crate::commands::{CommandResult, EditorCommand};
5use crate::core::{EditorDocument, Position, Range, Result};
6
7#[cfg(not(feature = "std"))]
8use alloc::{
9 format,
10 string::{String, ToString},
11 vec::Vec,
12};
13
14impl EditorCommand for AdjustKaraokeCommand {
15 fn execute(&self, document: &mut EditorDocument) -> Result<CommandResult> {
16 let original_text = document.text_range(self.range)?;
17 let adjusted_text = self.adjust_karaoke_timing(&original_text)?;
18
19 document.replace_raw(self.range, &adjusted_text)?;
20
21 let end_pos = Position::new(self.range.start.offset + adjusted_text.len());
22 let range = Range::new(self.range.start, end_pos);
23
24 Ok(CommandResult::success_with_change(range, end_pos))
25 }
26
27 fn description(&self) -> &str {
28 match self.adjustment {
29 TimingAdjustment::Scale(_) => "Scale karaoke timing",
30 TimingAdjustment::Offset(_) => "Offset karaoke timing",
31 TimingAdjustment::SetAll(_) => "Set karaoke timing",
32 TimingAdjustment::Custom(_) => "Apply custom karaoke timing",
33 }
34 }
35
36 fn memory_usage(&self) -> usize {
37 let adjustment_size = match &self.adjustment {
38 TimingAdjustment::Custom(vec) => vec.len() * core::mem::size_of::<u32>(),
39 _ => 0,
40 };
41 core::mem::size_of::<Self>() + adjustment_size
42 }
43}
44
45impl AdjustKaraokeCommand {
46 fn adjust_karaoke_timing(&self, text: &str) -> Result<String> {
48 use ass_core::analysis::events::tags::parse_override_block_with_registry;
49 use ass_core::plugin::{tags::karaoke::create_karaoke_handlers, ExtensionRegistry};
50
51 let mut registry = ExtensionRegistry::new();
53 for handler in create_karaoke_handlers() {
54 registry.register_tag_handler(handler).map_err(|e| {
55 crate::core::errors::EditorError::ValidationError {
56 message: format!("Failed to register karaoke handler: {e:?}"),
57 }
58 })?;
59 }
60
61 let mut result = String::new();
62 let mut chars = text.chars().peekable();
63 let mut custom_index = 0;
64
65 while let Some(ch) = chars.next() {
66 if ch == '{' {
67 let mut override_content = String::new();
69 let mut brace_count = 1;
70
71 for inner_ch in chars.by_ref() {
72 if inner_ch == '{' {
73 brace_count += 1;
74 } else if inner_ch == '}' {
75 brace_count -= 1;
76 if brace_count == 0 {
77 break;
78 }
79 }
80 override_content.push(inner_ch);
81 }
82
83 let mut tags = Vec::new();
85 let mut diagnostics = Vec::new();
86 parse_override_block_with_registry(
87 &override_content,
88 0,
89 &mut tags,
90 &mut diagnostics,
91 Some(®istry),
92 );
93
94 let processed_content = self.adjust_karaoke_tags_with_registry(
96 &override_content,
97 &tags,
98 &mut custom_index,
99 )?;
100
101 result.push('{');
102 result.push_str(&processed_content);
103 result.push('}');
104 } else {
105 result.push(ch);
106 }
107 }
108
109 Ok(result)
110 }
111
112 fn adjust_karaoke_tags_with_registry(
114 &self,
115 original_content: &str,
116 tags: &[ass_core::analysis::events::tags::OverrideTag],
117 custom_index: &mut usize,
118 ) -> Result<String> {
119 let mut result = original_content.to_string();
120
121 for tag in tags.iter().rev() {
123 if tag.name().starts_with('k') {
124 let tag_name = tag.name();
126 let args = tag.args();
127
128 let current_duration: u32 = args.trim().parse().unwrap_or(0);
130
131 let new_duration = match &self.adjustment {
133 TimingAdjustment::Scale(factor) => {
134 ((current_duration as f32 * factor) as u32).max(1)
135 }
136 TimingAdjustment::Offset(offset) => {
137 ((current_duration as i32 + offset).max(1)) as u32
138 }
139 TimingAdjustment::SetAll(duration) => *duration,
140 TimingAdjustment::Custom(timings) => {
141 if *custom_index < timings.len() {
142 let timing = timings[*custom_index];
143 *custom_index += 1;
144 timing
145 } else {
146 current_duration
147 }
148 }
149 };
150
151 let old_tag = format!("\\{tag_name}{current_duration}");
153 let new_tag = format!("\\{tag_name}{new_duration}");
154 result = result.replace(&old_tag, &new_tag);
155 }
156 }
157
158 Ok(result)
159 }
160}