notionary 0.1.10__tar.gz → 0.1.11__tar.gz

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.
Files changed (59) hide show
  1. {notionary-0.1.10 → notionary-0.1.11}/PKG-INFO +1 -1
  2. notionary-0.1.11/notionary/core/converters/elements/audio_element.py +141 -0
  3. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/registry/block_element_registry.py +1 -5
  4. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/registry/block_element_registry_builder.py +2 -0
  5. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/notion_page_manager.py +4 -13
  6. {notionary-0.1.10 → notionary-0.1.11}/notionary.egg-info/PKG-INFO +1 -1
  7. {notionary-0.1.10 → notionary-0.1.11}/notionary.egg-info/SOURCES.txt +1 -0
  8. {notionary-0.1.10 → notionary-0.1.11}/setup.py +1 -1
  9. {notionary-0.1.10 → notionary-0.1.11}/LICENSE +0 -0
  10. {notionary-0.1.10 → notionary-0.1.11}/README.md +0 -0
  11. {notionary-0.1.10 → notionary-0.1.11}/notionary/__init__.py +0 -0
  12. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/__init__.py +0 -0
  13. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/bookmark_element.py +0 -0
  14. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/callout_element.py +0 -0
  15. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/code_block_element.py +0 -0
  16. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/column_element.py +0 -0
  17. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/divider_element.py +0 -0
  18. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/embed_element.py +0 -0
  19. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/heading_element.py +0 -0
  20. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/image_element.py +0 -0
  21. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/list_element.py +0 -0
  22. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/notion_block_element.py +0 -0
  23. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/paragraph_element.py +0 -0
  24. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/qoute_element.py +0 -0
  25. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/table_element.py +0 -0
  26. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/text_inline_formatter.py +0 -0
  27. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/todo_lists.py +0 -0
  28. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/toggle_element.py +0 -0
  29. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/elements/video_element.py +0 -0
  30. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/markdown_to_notion_converter.py +0 -0
  31. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/converters/notion_to_markdown_converter.py +0 -0
  32. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/database/database_info_service.py +0 -0
  33. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/database/models/page_result.py +0 -0
  34. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/database/notion_database_manager.py +0 -0
  35. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/database/notion_database_manager_factory.py +0 -0
  36. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/database/notion_database_schema.py +0 -0
  37. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/notion_client.py +0 -0
  38. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/content/page_content_manager.py +0 -0
  39. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/metadata/metadata_editor.py +0 -0
  40. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/metadata/notion_icon_manager.py +0 -0
  41. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/metadata/notion_page_cover_manager.py +0 -0
  42. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/properites/database_property_service.py +0 -0
  43. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/properites/page_property_manager.py +0 -0
  44. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/properites/property_formatter.py +0 -0
  45. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/properites/property_operation_result.py +0 -0
  46. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/properites/property_value_extractor.py +0 -0
  47. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/relations/notion_page_relation_manager.py +0 -0
  48. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/relations/notion_page_title_resolver.py +0 -0
  49. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/relations/page_database_relation.py +0 -0
  50. {notionary-0.1.10 → notionary-0.1.11}/notionary/core/page/relations/relation_operation_result.py +0 -0
  51. {notionary-0.1.10 → notionary-0.1.11}/notionary/exceptions/database_exceptions.py +0 -0
  52. {notionary-0.1.10 → notionary-0.1.11}/notionary/exceptions/page_creation_exception.py +0 -0
  53. {notionary-0.1.10 → notionary-0.1.11}/notionary/util/logging_mixin.py +0 -0
  54. {notionary-0.1.10 → notionary-0.1.11}/notionary/util/page_id_utils.py +0 -0
  55. {notionary-0.1.10 → notionary-0.1.11}/notionary/util/singleton_decorator.py +0 -0
  56. {notionary-0.1.10 → notionary-0.1.11}/notionary.egg-info/dependency_links.txt +0 -0
  57. {notionary-0.1.10 → notionary-0.1.11}/notionary.egg-info/requires.txt +0 -0
  58. {notionary-0.1.10 → notionary-0.1.11}/notionary.egg-info/top_level.txt +0 -0
  59. {notionary-0.1.10 → notionary-0.1.11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: A toolkit to convert between Markdown and Notion blocks
5
5
  Home-page: https://github.com/mathisarends/notionary
6
6
  Author: Mathis Arends
@@ -0,0 +1,141 @@
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 AudioElement(NotionBlockElement):
7
+ """
8
+ Handles conversion between Markdown audio embeds and Notion audio blocks.
9
+
10
+ Markdown audio syntax (custom format since standard Markdown doesn't support audio):
11
+ - $[Caption](https://example.com/audio.mp3) - Basic audio with caption
12
+ - $[](https://example.com/audio.mp3) - Audio without caption
13
+ - $[Caption](https://storage.googleapis.com/audio_summaries/example.mp3) - CDN hosted audio
14
+
15
+ Supports various audio URLs including direct audio file links from CDNs and other sources.
16
+ """
17
+
18
+ # Regex pattern for audio syntax
19
+ PATTERN = re.compile(
20
+ r"^\$\[(.*?)\]" # $[Caption] part
21
+ + r'\((https?://[^\s"]+)' # (URL part
22
+ + r"\)$" # closing parenthesis
23
+ )
24
+
25
+ # Audio file extensions
26
+ AUDIO_EXTENSIONS = [".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac"]
27
+
28
+ @staticmethod
29
+ def match_markdown(text: str) -> bool:
30
+ """Check if text is a markdown audio embed."""
31
+ text = text.strip()
32
+ return text.startswith("$[") and bool(AudioElement.PATTERN.match(text))
33
+
34
+ @staticmethod
35
+ def match_notion(block: Dict[str, Any]) -> bool:
36
+ """Check if block is a Notion audio."""
37
+ return block.get("type") == "audio"
38
+
39
+ @staticmethod
40
+ def is_audio_url(url: str) -> bool:
41
+ """Check if URL points to an audio file."""
42
+ return any(url.lower().endswith(ext) for ext in AudioElement.AUDIO_EXTENSIONS) or \
43
+ "audio" in url.lower() or \
44
+ "storage.googleapis.com/audio_summaries" in url.lower()
45
+
46
+ @staticmethod
47
+ def markdown_to_notion(text: str) -> Optional[Dict[str, Any]]:
48
+ """Convert markdown audio embed to Notion audio block."""
49
+ audio_match = AudioElement.PATTERN.match(text.strip())
50
+ if not audio_match:
51
+ return None
52
+
53
+ caption = audio_match.group(1)
54
+ url = audio_match.group(2)
55
+
56
+ if not url:
57
+ return None
58
+
59
+ # Make sure this is an audio URL
60
+ if not AudioElement.is_audio_url(url):
61
+ # If not obviously an audio URL, we'll still accept it as the user might know better
62
+ pass
63
+
64
+ # Prepare the audio block
65
+ audio_block = {
66
+ "type": "audio",
67
+ "audio": {"type": "external", "external": {"url": url}},
68
+ }
69
+
70
+ # Add caption if provided
71
+ if caption:
72
+ audio_block["audio"]["caption"] = [
73
+ {"type": "text", "text": {"content": caption}}
74
+ ]
75
+
76
+ return audio_block
77
+
78
+ @staticmethod
79
+ def notion_to_markdown(block: Dict[str, Any]) -> Optional[str]:
80
+ """Convert Notion audio block to markdown audio embed."""
81
+ if block.get("type") != "audio":
82
+ return None
83
+
84
+ audio_data = block.get("audio", {})
85
+
86
+ # Handle both external and file (uploaded) audios
87
+ if audio_data.get("type") == "external":
88
+ url = audio_data.get("external", {}).get("url", "")
89
+ elif audio_data.get("type") == "file":
90
+ url = audio_data.get("file", {}).get("url", "")
91
+ else:
92
+ return None
93
+
94
+ if not url:
95
+ return None
96
+
97
+ # Extract caption if available
98
+ caption = ""
99
+ caption_rich_text = audio_data.get("caption", [])
100
+ if caption_rich_text:
101
+ caption = AudioElement._extract_text_content(caption_rich_text)
102
+
103
+ return f"$[{caption}]({url})"
104
+
105
+ @staticmethod
106
+ def is_multiline() -> bool:
107
+ """Audio embeds are single-line elements."""
108
+ return False
109
+
110
+ @staticmethod
111
+ def _extract_text_content(rich_text: List[Dict[str, Any]]) -> str:
112
+ """Extract plain text content from Notion rich_text elements."""
113
+ result = ""
114
+ for text_obj in rich_text:
115
+ if text_obj.get("type") == "text":
116
+ result += text_obj.get("text", {}).get("content", "")
117
+ elif "plain_text" in text_obj:
118
+ result += text_obj.get("plain_text", "")
119
+ return result
120
+
121
+ @classmethod
122
+ def get_llm_prompt_content(cls) -> dict:
123
+ """Returns information for LLM prompts about this element."""
124
+ return {
125
+ "description": "Embeds audio content from external sources like CDNs or direct audio URLs.",
126
+ "when_to_use": "Use audio embeds when you want to include audio content directly in your document. Audio embeds are useful for podcasts, music, voice recordings, or any content that benefits from audio explanation.",
127
+ "syntax": [
128
+ "$[](https://example.com/audio.mp3) - Audio without caption",
129
+ "$[Caption text](https://example.com/audio.mp3) - Audio with caption",
130
+ ],
131
+ "supported_sources": [
132
+ "Direct links to audio files (.mp3, .wav, .ogg, etc.)",
133
+ "Google Cloud Storage links (storage.googleapis.com)",
134
+ "Other audio hosting platforms supported by Notion",
135
+ ],
136
+ "examples": [
137
+ "$[Podcast Episode](https://storage.googleapis.com/audio_summaries/ep_ai_summary_127d02ec-ca12-4312-a5ed-cb14b185480c.mp3)",
138
+ "$[Voice recording](https://example.com/audio/recording.mp3)",
139
+ "$[](https://storage.googleapis.com/audio_summaries/example.mp3)",
140
+ ],
141
+ }
@@ -233,8 +233,4 @@ paragraphs, lists, quotes, etc.
233
233
  """
234
234
  element_docs = cls.generate_element_docs(element_classes)
235
235
 
236
- return cls.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
237
-
238
-
239
-
240
- # TODO: Testen ob der Inline Formatter hier überhaupt funktionieren tut:
236
+ return cls.SYSTEM_PROMPT_TEMPLATE.format(element_docs=element_docs)
@@ -1,6 +1,7 @@
1
1
  from typing import List, Type
2
2
  from collections import OrderedDict
3
3
 
4
+ from notionary.core.converters.elements.audio_element import AudioElement
4
5
  from notionary.core.converters.elements.embed_element import EmbedElement
5
6
  from notionary.core.converters.elements.notion_block_element import NotionBlockElement
6
7
  from notionary.core.converters.registry.block_element_registry import (
@@ -92,6 +93,7 @@ class BlockElementRegistryBuilder:
92
93
  .add_element(ImageElement)
93
94
  .add_element(VideoElement)
94
95
  .add_element(EmbedElement)
96
+ .add_element(AudioElement)
95
97
  .add_element(ParagraphElement)
96
98
  ) # Add paragraph last as fallback
97
99
 
@@ -311,20 +311,11 @@ async def demo2():
311
311
 
312
312
  page_manager = NotionPageManager(url=url)
313
313
 
314
- # Beispiel mit einem Embed-Element im Transcript-Toggle
315
314
  markdown = """
316
- ## 💪 Muskelaufbau und Kraft
317
- - Regelmäßiges Training ist essentiell für den Muskelerhalt im Alter
318
- - Richtige Ernährung unterstützt die Regeneration nach dem Training
319
-
320
- +++ Transcript
321
- <embed:Listen to this highlight>(https://share.snipd.com/snip/ad3d95b2-d648-4fa9-9036-bd7df653ea32)
322
- <!-- spacer -->
323
- ... In diesem Teil des Podcasts erklärt der Sprecher, wie das Henneman-Größenprinzip funktioniert und wie Muskelfasern rekrutiert werden, basierend auf der Schwere des zu bewegenden Objekts.
324
- """
325
-
326
- await page_manager.append_markdown(markdown)
327
- print("Markdown wurde zur Notion-Seite hinzugefügt.")
315
+ $[Podcast Zusammenfassung](https://storage.googleapis.com/audio_summaries/ep_ai_summary_127d02ec-ca12-4312-a5ed-cb14b185480c.mp3)
316
+ """
317
+
318
+ await page_manager.append_markdown(markdown=markdown)
328
319
 
329
320
  if __name__ == "__main__":
330
321
  asyncio.run(demo2())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: notionary
3
- Version: 0.1.10
3
+ Version: 0.1.11
4
4
  Summary: A toolkit to convert between Markdown and Notion blocks
5
5
  Home-page: https://github.com/mathisarends/notionary
6
6
  Author: Mathis Arends
@@ -11,6 +11,7 @@ notionary/core/notion_client.py
11
11
  notionary/core/converters/__init__.py
12
12
  notionary/core/converters/markdown_to_notion_converter.py
13
13
  notionary/core/converters/notion_to_markdown_converter.py
14
+ notionary/core/converters/elements/audio_element.py
14
15
  notionary/core/converters/elements/bookmark_element.py
15
16
  notionary/core/converters/elements/callout_element.py
16
17
  notionary/core/converters/elements/code_block_element.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_namespace_packages
2
2
 
3
3
  setup(
4
4
  name="notionary",
5
- version="0.1.10",
5
+ version="0.1.11",
6
6
  packages=find_namespace_packages(include=["notionary*"]),
7
7
  install_requires=[
8
8
  "notion-client>=2.0.0",
File without changes
File without changes
File without changes