notionary 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notionary/__init__.py +9 -0
- notionary/core/__init__.py +0 -0
- notionary/core/converters/__init__.py +50 -0
- notionary/core/converters/elements/__init__.py +0 -0
- notionary/core/converters/elements/bookmark_element.py +224 -0
- notionary/core/converters/elements/callout_element.py +179 -0
- notionary/core/converters/elements/code_block_element.py +153 -0
- notionary/core/converters/elements/column_element.py +294 -0
- notionary/core/converters/elements/divider_element.py +73 -0
- notionary/core/converters/elements/heading_element.py +84 -0
- notionary/core/converters/elements/image_element.py +130 -0
- notionary/core/converters/elements/list_element.py +130 -0
- notionary/core/converters/elements/notion_block_element.py +51 -0
- notionary/core/converters/elements/paragraph_element.py +73 -0
- notionary/core/converters/elements/qoute_element.py +242 -0
- notionary/core/converters/elements/table_element.py +306 -0
- notionary/core/converters/elements/text_inline_formatter.py +294 -0
- notionary/core/converters/elements/todo_lists.py +114 -0
- notionary/core/converters/elements/toggle_element.py +205 -0
- notionary/core/converters/elements/video_element.py +159 -0
- notionary/core/converters/markdown_to_notion_converter.py +482 -0
- notionary/core/converters/notion_to_markdown_converter.py +45 -0
- notionary/core/converters/registry/__init__.py +0 -0
- notionary/core/converters/registry/block_element_registry.py +234 -0
- notionary/core/converters/registry/block_element_registry_builder.py +280 -0
- notionary/core/database/database_info_service.py +43 -0
- notionary/core/database/database_query_service.py +73 -0
- notionary/core/database/database_schema_service.py +57 -0
- notionary/core/database/models/page_result.py +10 -0
- notionary/core/database/notion_database_manager.py +332 -0
- notionary/core/database/notion_database_manager_factory.py +233 -0
- notionary/core/database/notion_database_schema.py +415 -0
- notionary/core/database/notion_database_writer.py +390 -0
- notionary/core/database/page_service.py +161 -0
- notionary/core/notion_client.py +134 -0
- notionary/core/page/meta_data/metadata_editor.py +37 -0
- notionary/core/page/notion_page_manager.py +110 -0
- notionary/core/page/page_content_manager.py +85 -0
- notionary/core/page/property_formatter.py +97 -0
- notionary/exceptions/database_exceptions.py +76 -0
- notionary/exceptions/page_creation_exception.py +9 -0
- notionary/util/logging_mixin.py +47 -0
- notionary/util/singleton_decorator.py +20 -0
- notionary/util/uuid_utils.py +24 -0
- {notionary-0.1.1.dist-info → notionary-0.1.3.dist-info}/METADATA +1 -1
- notionary-0.1.3.dist-info/RECORD +49 -0
- notionary-0.1.3.dist-info/top_level.txt +1 -0
- notionary-0.1.1.dist-info/RECORD +0 -5
- notionary-0.1.1.dist-info/top_level.txt +0 -1
- {notionary-0.1.1.dist-info → notionary-0.1.3.dist-info}/WHEEL +0 -0
- {notionary-0.1.1.dist-info → notionary-0.1.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,114 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Any, Optional
|
3
|
+
from typing_extensions import override
|
4
|
+
from notionary.core.converters.elements.notion_block_element import NotionBlockElement
|
5
|
+
from notionary.core.converters.elements.text_inline_formatter import TextInlineFormatter
|
6
|
+
|
7
|
+
|
8
|
+
class TodoElement(NotionBlockElement):
|
9
|
+
"""
|
10
|
+
Handles conversion between Markdown todo items and Notion to_do blocks.
|
11
|
+
|
12
|
+
Markdown syntax examples:
|
13
|
+
- [ ] Unchecked todo item
|
14
|
+
- [x] Checked todo item
|
15
|
+
* [ ] Also works with asterisk
|
16
|
+
+ [ ] Also works with plus sign
|
17
|
+
"""
|
18
|
+
|
19
|
+
# Patterns for detecting Markdown todo items
|
20
|
+
TODO_PATTERN = re.compile(r"^\s*[-*+]\s+\[\s?\]\s+(.+)$")
|
21
|
+
DONE_PATTERN = re.compile(r"^\s*[-*+]\s+\[x\]\s+(.+)$")
|
22
|
+
|
23
|
+
@override
|
24
|
+
@staticmethod
|
25
|
+
def match_markdown(text: str) -> bool:
|
26
|
+
"""Check if text is a markdown todo item."""
|
27
|
+
return bool(
|
28
|
+
TodoElement.TODO_PATTERN.match(text) or TodoElement.DONE_PATTERN.match(text)
|
29
|
+
)
|
30
|
+
|
31
|
+
@override
|
32
|
+
@staticmethod
|
33
|
+
def match_notion(block: Dict[str, Any]) -> bool:
|
34
|
+
"""Check if block is a Notion to_do block."""
|
35
|
+
return block.get("type") == "to_do"
|
36
|
+
|
37
|
+
@override
|
38
|
+
@staticmethod
|
39
|
+
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
40
|
+
"""Convert markdown todo item to Notion to_do block."""
|
41
|
+
done_match = TodoElement.DONE_PATTERN.match(text)
|
42
|
+
if done_match:
|
43
|
+
content = done_match.group(1)
|
44
|
+
return TodoElement._create_todo_block(content, True)
|
45
|
+
|
46
|
+
todo_match = TodoElement.TODO_PATTERN.match(text)
|
47
|
+
if todo_match:
|
48
|
+
content = todo_match.group(1)
|
49
|
+
return TodoElement._create_todo_block(content, False)
|
50
|
+
|
51
|
+
return None
|
52
|
+
|
53
|
+
@override
|
54
|
+
@staticmethod
|
55
|
+
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
56
|
+
"""Convert Notion to_do block to markdown todo item."""
|
57
|
+
if block.get("type") != "to_do":
|
58
|
+
return None
|
59
|
+
|
60
|
+
todo_data = block.get("to_do", {})
|
61
|
+
checked = todo_data.get("checked", False)
|
62
|
+
|
63
|
+
# Extract text content
|
64
|
+
rich_text = todo_data.get("rich_text", [])
|
65
|
+
content = TextInlineFormatter.extract_text_with_formatting(rich_text)
|
66
|
+
|
67
|
+
# Format as markdown todo item
|
68
|
+
checkbox = "[x]" if checked else "[ ]"
|
69
|
+
return f"- {checkbox} {content}"
|
70
|
+
|
71
|
+
@staticmethod
|
72
|
+
def _create_todo_block(content: str, checked: bool) -> Dict[str, Any]:
|
73
|
+
"""
|
74
|
+
Create a Notion to_do block.
|
75
|
+
|
76
|
+
Args:
|
77
|
+
content: The text content of the todo item
|
78
|
+
checked: Whether the todo item is checked
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
Notion to_do block dictionary
|
82
|
+
"""
|
83
|
+
return {
|
84
|
+
"type": "to_do",
|
85
|
+
"to_do": {
|
86
|
+
"rich_text": TextInlineFormatter.parse_inline_formatting(content),
|
87
|
+
"checked": checked,
|
88
|
+
"color": "default",
|
89
|
+
},
|
90
|
+
}
|
91
|
+
|
92
|
+
@override
|
93
|
+
@staticmethod
|
94
|
+
def is_multiline() -> bool:
|
95
|
+
return False
|
96
|
+
|
97
|
+
@classmethod
|
98
|
+
def get_llm_prompt_content(cls) -> dict:
|
99
|
+
"""Returns information for LLM prompts about this element."""
|
100
|
+
return {
|
101
|
+
"description": "Creates interactive to-do items with checkboxes that can be marked as complete.",
|
102
|
+
"when_to_use": "Use to-do items for task lists, checklists, or tracking progress on items that need to be completed. Todo items are interactive in Notion and can be checked/unchecked directly.",
|
103
|
+
"syntax": ["- [ ] Unchecked to-do item", "- [x] Checked to-do item"],
|
104
|
+
"notes": [
|
105
|
+
"Can use any list indicator (-, *, +) before the checkbox",
|
106
|
+
"Space in brackets [ ] indicates unchecked status",
|
107
|
+
"x in brackets [x] indicates checked status",
|
108
|
+
"To-do items support inline formatting like **bold** and *italic*",
|
109
|
+
],
|
110
|
+
"examples": [
|
111
|
+
"- [ ] Draft project proposal\n- [ ] Schedule kickoff meeting\n- [x] Create initial timeline",
|
112
|
+
"* [ ] Review code changes\n* [x] Write documentation\n* [ ] Deploy to production",
|
113
|
+
],
|
114
|
+
}
|
@@ -0,0 +1,205 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Any, Optional, List, Tuple, Callable
|
3
|
+
|
4
|
+
from notionary.core.converters.elements.notion_block_element import NotionBlockElement
|
5
|
+
|
6
|
+
|
7
|
+
class ToggleElement(NotionBlockElement):
|
8
|
+
"""
|
9
|
+
Handles conversion between Markdown toggle blocks and Notion toggle blocks.
|
10
|
+
|
11
|
+
Markdown toggle syntax:
|
12
|
+
+++ Toggle title
|
13
|
+
Indented content that belongs to the toggle
|
14
|
+
More indented content
|
15
|
+
|
16
|
+
Non-indented content marks the end of the toggle block.
|
17
|
+
"""
|
18
|
+
|
19
|
+
TOGGLE_PATTERN = re.compile(r"^[+]{3}\s+(.+)$")
|
20
|
+
|
21
|
+
INDENT_PATTERN = re.compile(r"^(\s{2,}|\t+)(.+)$")
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def match_markdown(text: str) -> bool:
|
25
|
+
"""Check if text is a markdown toggle."""
|
26
|
+
return bool(ToggleElement.TOGGLE_PATTERN.match(text.strip()))
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def match_notion(block: Dict[str, Any]) -> bool:
|
30
|
+
"""Check if block is a Notion toggle."""
|
31
|
+
return block.get("type") == "toggle"
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
35
|
+
"""Convert markdown toggle to Notion toggle block.
|
36
|
+
|
37
|
+
Note: This method only converts the toggle title line.
|
38
|
+
The nested content needs to be processed separately.
|
39
|
+
"""
|
40
|
+
toggle_match = ToggleElement.TOGGLE_PATTERN.match(text.strip())
|
41
|
+
if not toggle_match:
|
42
|
+
return None
|
43
|
+
|
44
|
+
# Extract content
|
45
|
+
title = toggle_match.group(1)
|
46
|
+
|
47
|
+
return {
|
48
|
+
"type": "toggle",
|
49
|
+
"toggle": {
|
50
|
+
"rich_text": [{"type": "text", "text": {"content": title}}],
|
51
|
+
"color": "default",
|
52
|
+
"children": [], # Will be populated with nested content
|
53
|
+
},
|
54
|
+
}
|
55
|
+
|
56
|
+
@staticmethod
|
57
|
+
def extract_nested_content(
|
58
|
+
lines: List[str], start_index: int
|
59
|
+
) -> Tuple[List[str], int]:
|
60
|
+
"""
|
61
|
+
Extract the nested content of a toggle element.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
lines: All lines of text
|
65
|
+
start_index: Starting index to look for nested content
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
Tuple of (nested_content_lines, next_line_index)
|
69
|
+
"""
|
70
|
+
nested_content = []
|
71
|
+
current_index = start_index
|
72
|
+
|
73
|
+
while current_index < len(lines):
|
74
|
+
line = lines[current_index]
|
75
|
+
|
76
|
+
# Empty line is still part of toggle content
|
77
|
+
if not line.strip():
|
78
|
+
nested_content.append("")
|
79
|
+
current_index += 1
|
80
|
+
continue
|
81
|
+
|
82
|
+
# Check if line is indented (part of toggle content)
|
83
|
+
if line.startswith(" ") or line.startswith("\t"):
|
84
|
+
# Extract content with indentation removed
|
85
|
+
content_line = ToggleElement._remove_indentation(line)
|
86
|
+
nested_content.append(content_line)
|
87
|
+
current_index += 1
|
88
|
+
continue
|
89
|
+
|
90
|
+
# Non-indented, non-empty line marks the end of toggle content
|
91
|
+
break
|
92
|
+
|
93
|
+
return nested_content, current_index
|
94
|
+
|
95
|
+
@staticmethod
|
96
|
+
def _remove_indentation(line: str) -> str:
|
97
|
+
"""Remove indentation from a line, handling both spaces and tabs."""
|
98
|
+
if line.startswith("\t"):
|
99
|
+
return line[1:]
|
100
|
+
else:
|
101
|
+
# Find number of leading spaces
|
102
|
+
leading_spaces = len(line) - len(line.lstrip(" "))
|
103
|
+
# Remove at least 2 spaces, but not more than what's there
|
104
|
+
return line[min(2, leading_spaces) :]
|
105
|
+
|
106
|
+
@staticmethod
|
107
|
+
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
108
|
+
"""Convert Notion toggle block to markdown toggle."""
|
109
|
+
if block.get("type") != "toggle":
|
110
|
+
return None
|
111
|
+
|
112
|
+
toggle_data = block.get("toggle", {})
|
113
|
+
|
114
|
+
# Extract title from rich_text
|
115
|
+
title = ToggleElement._extract_text_content(toggle_data.get("rich_text", []))
|
116
|
+
|
117
|
+
# Create the toggle line
|
118
|
+
toggle_line = f"+++ {title}"
|
119
|
+
|
120
|
+
# Process children if any
|
121
|
+
children = toggle_data.get("children", [])
|
122
|
+
if children:
|
123
|
+
child_lines = []
|
124
|
+
for child_block in children:
|
125
|
+
# This would need to be handled by a full converter that can dispatch
|
126
|
+
# to the appropriate element type for each child block
|
127
|
+
child_markdown = " [Nested content]" # Placeholder
|
128
|
+
child_lines.append(f" {child_markdown}")
|
129
|
+
|
130
|
+
return toggle_line + "\n" + "\n".join(child_lines)
|
131
|
+
|
132
|
+
return toggle_line
|
133
|
+
|
134
|
+
@staticmethod
|
135
|
+
def is_multiline() -> bool:
|
136
|
+
"""Toggle blocks can span multiple lines due to their nested content."""
|
137
|
+
return True
|
138
|
+
|
139
|
+
@staticmethod
|
140
|
+
def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
|
141
|
+
"""Extract plain text content from Notion rich_text elements."""
|
142
|
+
result = ""
|
143
|
+
for text_obj in rich_text:
|
144
|
+
if text_obj.get("type") == "text":
|
145
|
+
result += text_obj.get("text", {}).get("content", "")
|
146
|
+
elif "plain_text" in text_obj:
|
147
|
+
result += text_obj.get("plain_text", "")
|
148
|
+
return result
|
149
|
+
|
150
|
+
@classmethod
|
151
|
+
def find_matches(
|
152
|
+
cls, text: str, process_nested_content: Callable = None
|
153
|
+
) -> List[Tuple[int, int, Dict[str, Any]]]:
|
154
|
+
"""
|
155
|
+
Find all toggle elements in the text and process them.
|
156
|
+
|
157
|
+
Args:
|
158
|
+
text: The text to search in
|
159
|
+
process_nested_content: Optional callback function to process nested content
|
160
|
+
It should accept a string and return a list of Notion blocks
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
List of (start_pos, end_pos, block) tuples
|
164
|
+
"""
|
165
|
+
if not text:
|
166
|
+
return []
|
167
|
+
|
168
|
+
toggle_blocks = []
|
169
|
+
lines = text.split("\n")
|
170
|
+
|
171
|
+
i = 0
|
172
|
+
while i < len(lines):
|
173
|
+
line = lines[i]
|
174
|
+
|
175
|
+
# Check if line is a toggle
|
176
|
+
if not cls.match_markdown(line):
|
177
|
+
i += 1
|
178
|
+
continue
|
179
|
+
|
180
|
+
start_pos = 0
|
181
|
+
for j in range(i):
|
182
|
+
start_pos += len(lines[j]) + 1
|
183
|
+
|
184
|
+
toggle_block = cls.markdown_to_notion(line)
|
185
|
+
if not toggle_block:
|
186
|
+
i += 1
|
187
|
+
continue
|
188
|
+
|
189
|
+
# Extract nested content
|
190
|
+
nested_content, next_index = cls.extract_nested_content(lines, i + 1)
|
191
|
+
|
192
|
+
# Calculate ending position
|
193
|
+
end_pos = start_pos + len(line) + sum(len(l) + 1 for l in nested_content)
|
194
|
+
|
195
|
+
if nested_content and process_nested_content:
|
196
|
+
nested_text = "\n".join(nested_content)
|
197
|
+
nested_blocks = process_nested_content(nested_text)
|
198
|
+
if nested_blocks:
|
199
|
+
toggle_block["toggle"]["children"] = nested_blocks
|
200
|
+
|
201
|
+
toggle_blocks.append((start_pos, end_pos, toggle_block))
|
202
|
+
|
203
|
+
i = next_index
|
204
|
+
|
205
|
+
return toggle_blocks
|
@@ -0,0 +1,159 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Dict, Any, Optional, List
|
3
|
+
from notionary.core.converters.elements.notion_block_element import NotionBlockElement
|
4
|
+
|
5
|
+
|
6
|
+
class VideoElement(NotionBlockElement):
|
7
|
+
"""
|
8
|
+
Handles conversion between Markdown video embeds and Notion video blocks.
|
9
|
+
|
10
|
+
Markdown video syntax (custom format since standard Markdown doesn't support videos):
|
11
|
+
- @[Caption](https://example.com/video.mp4) - Basic video with caption
|
12
|
+
- @[](https://example.com/video.mp4) - Video without caption
|
13
|
+
- @[Caption](https://www.youtube.com/watch?v=dQw4w9WgXcQ) - YouTube video
|
14
|
+
- @[Caption](https://youtu.be/dQw4w9WgXcQ) - YouTube shortened URL
|
15
|
+
|
16
|
+
Supports various video URLs including YouTube, Vimeo, and direct video file links.
|
17
|
+
"""
|
18
|
+
|
19
|
+
# Regex pattern for video syntax
|
20
|
+
PATTERN = re.compile(
|
21
|
+
r"^\@\[(.*?)\]" # @[Caption] part
|
22
|
+
+ r'\((https?://[^\s"]+)' # (URL part
|
23
|
+
+ r"\)$" # closing parenthesis
|
24
|
+
)
|
25
|
+
|
26
|
+
# YouTube specific patterns
|
27
|
+
YOUTUBE_PATTERNS = [
|
28
|
+
re.compile(
|
29
|
+
r"(?:https?://)?(?:www\.)?youtube\.com/watch\?v=([a-zA-Z0-9_-]{11})"
|
30
|
+
),
|
31
|
+
re.compile(r"(?:https?://)?(?:www\.)?youtu\.be/([a-zA-Z0-9_-]{11})"),
|
32
|
+
]
|
33
|
+
|
34
|
+
@staticmethod
|
35
|
+
def match_markdown(text: str) -> bool:
|
36
|
+
"""Check if text is a markdown video embed."""
|
37
|
+
text = text.strip()
|
38
|
+
return text.startswith("@[") and bool(VideoElement.PATTERN.match(text))
|
39
|
+
|
40
|
+
@staticmethod
|
41
|
+
def match_notion(block: Dict[str, Any]) -> bool:
|
42
|
+
"""Check if block is a Notion video."""
|
43
|
+
return block.get("type") == "video"
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def is_youtube_url(url: str) -> bool:
|
47
|
+
"""Check if URL is a YouTube video and return video ID if it is."""
|
48
|
+
for pattern in VideoElement.YOUTUBE_PATTERNS:
|
49
|
+
match = pattern.match(url)
|
50
|
+
if match:
|
51
|
+
return True
|
52
|
+
return False
|
53
|
+
|
54
|
+
@staticmethod
|
55
|
+
def get_youtube_id(url: str) -> Optional[str]:
|
56
|
+
"""Extract YouTube video ID from URL."""
|
57
|
+
for pattern in VideoElement.YOUTUBE_PATTERNS:
|
58
|
+
match = pattern.match(url)
|
59
|
+
if match:
|
60
|
+
return match.group(1)
|
61
|
+
return None
|
62
|
+
|
63
|
+
@staticmethod
|
64
|
+
def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
|
65
|
+
"""Convert markdown video embed to Notion video block."""
|
66
|
+
video_match = VideoElement.PATTERN.match(text.strip())
|
67
|
+
if not video_match:
|
68
|
+
return None
|
69
|
+
|
70
|
+
caption = video_match.group(1)
|
71
|
+
url = video_match.group(2)
|
72
|
+
|
73
|
+
if not url:
|
74
|
+
return None
|
75
|
+
|
76
|
+
# For YouTube videos, ensure we use the full embed URL
|
77
|
+
youtube_id = VideoElement.get_youtube_id(url)
|
78
|
+
if youtube_id:
|
79
|
+
url = f"https://www.youtube.com/watch?v={youtube_id}"
|
80
|
+
|
81
|
+
# Prepare the video block
|
82
|
+
video_block = {
|
83
|
+
"type": "video",
|
84
|
+
"video": {"type": "external", "external": {"url": url}},
|
85
|
+
}
|
86
|
+
|
87
|
+
# Add caption if provided
|
88
|
+
if caption:
|
89
|
+
video_block["video"]["caption"] = [
|
90
|
+
{"type": "text", "text": {"content": caption}}
|
91
|
+
]
|
92
|
+
|
93
|
+
return video_block
|
94
|
+
|
95
|
+
@staticmethod
|
96
|
+
def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
|
97
|
+
"""Convert Notion video block to markdown video embed."""
|
98
|
+
if block.get("type") != "video":
|
99
|
+
return None
|
100
|
+
|
101
|
+
video_data = block.get("video", {})
|
102
|
+
|
103
|
+
# Handle both external and file (uploaded) videos
|
104
|
+
if video_data.get("type") == "external":
|
105
|
+
url = video_data.get("external", {}).get("url", "")
|
106
|
+
elif video_data.get("type") == "file":
|
107
|
+
url = video_data.get("file", {}).get("url", "")
|
108
|
+
else:
|
109
|
+
return None
|
110
|
+
|
111
|
+
if not url:
|
112
|
+
return None
|
113
|
+
|
114
|
+
# Extract caption if available
|
115
|
+
caption = ""
|
116
|
+
caption_rich_text = video_data.get("caption", [])
|
117
|
+
if caption_rich_text:
|
118
|
+
caption = VideoElement._extract_text_content(caption_rich_text)
|
119
|
+
|
120
|
+
return f"@[{caption}]({url})"
|
121
|
+
|
122
|
+
@staticmethod
|
123
|
+
def is_multiline() -> bool:
|
124
|
+
"""Videos are single-line elements."""
|
125
|
+
return False
|
126
|
+
|
127
|
+
@staticmethod
|
128
|
+
def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
|
129
|
+
"""Extract plain text content from Notion rich_text elements."""
|
130
|
+
result = ""
|
131
|
+
for text_obj in rich_text:
|
132
|
+
if text_obj.get("type") == "text":
|
133
|
+
result += text_obj.get("text", {}).get("content", "")
|
134
|
+
elif "plain_text" in text_obj:
|
135
|
+
result += text_obj.get("plain_text", "")
|
136
|
+
return result
|
137
|
+
|
138
|
+
@classmethod
|
139
|
+
def get_llm_prompt_content(cls) -> dict:
|
140
|
+
"""Returns information for LLM prompts about this element."""
|
141
|
+
return {
|
142
|
+
"description": "Embeds video content from external sources like YouTube or direct video URLs.",
|
143
|
+
"when_to_use": "Use video embeds when you want to include multimedia content directly in your document. Videos are useful for tutorials, demonstrations, presentations, or any content that benefits from visual explanation.",
|
144
|
+
"syntax": [
|
145
|
+
"@[](https://example.com/video.mp4) - Video without caption",
|
146
|
+
"@[Caption text](https://example.com/video.mp4) - Video with caption",
|
147
|
+
],
|
148
|
+
"supported_sources": [
|
149
|
+
"YouTube videos (https://youtube.com/watch?v=ID or https://youtu.be/ID)",
|
150
|
+
"Vimeo videos",
|
151
|
+
"Direct links to video files (.mp4, .mov, etc.)",
|
152
|
+
"Other video hosting platforms supported by Notion",
|
153
|
+
],
|
154
|
+
"examples": [
|
155
|
+
"@[How to use this feature](https://www.youtube.com/watch?v=dQw4w9WgXcQ)",
|
156
|
+
"@[Product demo](https://example.com/videos/demo.mp4)",
|
157
|
+
"@[](https://youtu.be/dQw4w9WgXcQ)",
|
158
|
+
],
|
159
|
+
}
|