slide-narrator 5.1.0__py3-none-any.whl → 5.2.0__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.

Potentially problematic release.


This version of slide-narrator might be problematic. Click here for more details.

narrator/__init__.py CHANGED
@@ -8,7 +8,7 @@ from .models.thread import Thread
8
8
  from .models.message import Message
9
9
  from .models.attachment import Attachment
10
10
 
11
- __version__ = "5.1.0"
11
+ __version__ = "5.2.0"
12
12
  __all__ = [
13
13
  "ThreadStore",
14
14
  "FileStore",
@@ -2,7 +2,8 @@ from typing import Dict, Optional, Any, Union, Literal
2
2
  from pydantic import BaseModel, computed_field
3
3
  import base64
4
4
  import io
5
- import magic
5
+ import filetype
6
+ import mimetypes
6
7
  from ..utils.logging import get_logger
7
8
  from pathlib import Path
8
9
  from ..storage.file_store import FileStore
@@ -61,7 +62,15 @@ class Attachment(BaseModel):
61
62
  content = file_path.read_bytes()
62
63
 
63
64
  # Detect MIME type
64
- mime_type = magic.from_buffer(content, mime=True)
65
+ mime_type = filetype.guess_mime(content)
66
+
67
+ if not mime_type:
68
+ # Fallback: extension-based detection
69
+ mime_type, _ = mimetypes.guess_type(str(file_path))
70
+
71
+ if not mime_type:
72
+ # Default: binary
73
+ mime_type = 'application/octet-stream'
65
74
 
66
75
  return cls(
67
76
  filename=file_path.name,
@@ -88,7 +97,15 @@ class Attachment(BaseModel):
88
97
  return
89
98
 
90
99
  # Detect MIME type
91
- detected_mime_type = magic.from_buffer(content_bytes, mime=True)
100
+ detected_mime_type = filetype.guess_mime(content_bytes)
101
+
102
+ if not detected_mime_type:
103
+ # Fallback: extension-based detection
104
+ detected_mime_type, _ = mimetypes.guess_type(self.filename)
105
+
106
+ if not detected_mime_type:
107
+ # Default: binary
108
+ detected_mime_type = 'application/octet-stream'
92
109
 
93
110
  if not self.mime_type:
94
111
  self.mime_type = detected_mime_type
@@ -209,7 +226,16 @@ class Attachment(BaseModel):
209
226
 
210
227
  # Detect/verify MIME type
211
228
  logger.debug("Detecting MIME type")
212
- detected_mime_type = magic.from_buffer(content_bytes, mime=True)
229
+ detected_mime_type = filetype.guess_mime(content_bytes)
230
+
231
+ if not detected_mime_type:
232
+ # Fallback: extension-based detection
233
+ detected_mime_type, _ = mimetypes.guess_type(self.filename)
234
+
235
+ if not detected_mime_type:
236
+ # Default: binary
237
+ detected_mime_type = 'application/octet-stream'
238
+
213
239
  logger.debug(f"Detected MIME type: {detected_mime_type}")
214
240
 
215
241
  if not self.mime_type:
@@ -11,7 +11,7 @@ import mimetypes
11
11
  from datetime import datetime, UTC
12
12
  from sqlalchemy import select
13
13
  from ..utils.logging import get_logger
14
- import magic
14
+ import filetype
15
15
  import base64
16
16
 
17
17
  # Get configured logger
@@ -282,11 +282,18 @@ class FileStore:
282
282
 
283
283
  # Detect or validate MIME type
284
284
  if not mime_type:
285
- mime_type = mimetypes.guess_type(filename)[0]
285
+ # Primary: content-based detection
286
+ mime_type = filetype.guess_mime(content)
287
+
288
+ if not mime_type:
289
+ # Fallback: extension-based detection
290
+ mime_type, _ = mimetypes.guess_type(filename)
291
+
286
292
  if not mime_type:
287
- # Try to detect from content
288
- mime_type = magic.from_buffer(content, mime=True)
289
- logger.debug(f"Detected MIME type for {filename}: {mime_type}")
293
+ # Default: binary
294
+ mime_type = 'application/octet-stream'
295
+
296
+ logger.debug(f"Detected MIME type for {filename}: {mime_type}")
290
297
 
291
298
  if mime_type not in self.allowed_mime_types:
292
299
  raise UnsupportedFileTypeError(f"Unsupported file type: {mime_type}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slide-narrator
3
- Version: 5.1.0
3
+ Version: 5.2.0
4
4
  Summary: Thread and file storage components for conversational AI - the companion to Tyler AI framework
5
5
  Project-URL: Homepage, https://github.com/adamwdraper/slide
6
6
  Project-URL: Repository, https://github.com/adamwdraper/slide
@@ -21,10 +21,10 @@ Requires-Dist: aiosqlite>=0.21.0
21
21
  Requires-Dist: alembic>=1.14.1
22
22
  Requires-Dist: asyncpg>=0.30.0
23
23
  Requires-Dist: click>=8.1.8
24
+ Requires-Dist: filetype>=1.2.0
24
25
  Requires-Dist: greenlet>=3.2.3
25
26
  Requires-Dist: pydantic>=2.10.4
26
27
  Requires-Dist: pypdf>=5.3.0
27
- Requires-Dist: python-magic>=0.4.0
28
28
  Requires-Dist: sqlalchemy>=2.0.36
29
29
  Requires-Dist: typing-extensions>=4.12.0
30
30
  Provides-Extra: dev
@@ -537,7 +537,6 @@ The test suite requires:
537
537
  - Python 3.13+
538
538
  - pytest with async support
539
539
  - Test coverage reporting
540
- - System dependencies (libmagic for file type detection)
541
540
 
542
541
  ## Contributing
543
542
 
@@ -1,4 +1,4 @@
1
- narrator/__init__.py,sha256=P_KknvRyp7LgtPRJqqxyJrQLTzi5G7taxDS1SVFMyzI,403
1
+ narrator/__init__.py,sha256=ofg1cMUxW2BhzY_5YI7kNLIhkW-XLrlVFM_agHZCKZU,403
2
2
  narrator/database/__init__.py,sha256=UngOnFqImCeJiMZlMasm72mC4-UnJDDvfu1MNQLkRA8,189
3
3
  narrator/database/cli.py,sha256=QvET17X5kLZ7GiOTw0b80-u4FuI-tOTu4SjAqCBkiSs,8355
4
4
  narrator/database/models.py,sha256=OyiufON_ZLNM7PUxXrUoFu1uu62SolHABwpedVnSsfk,2659
@@ -6,15 +6,15 @@ narrator/database/storage_backend.py,sha256=NZ0SKvEL6qKgdJrxuBw5qs6k-9mdWXWE0Ad3
6
6
  narrator/database/thread_store.py,sha256=vMIPDdwuSpTyPogEUmxGcILxM_r1wxoQBUOn8XJpdqM,11301
7
7
  narrator/database/migrations/__init__.py,sha256=IqoSL8eCcbcOtn96u2_TTrNG0KN1Jn1yreDZEO4RsnM,173
8
8
  narrator/models/__init__.py,sha256=J8Rsv2lmfGR5QmUjoAPEFTSQt5TGtyrBynnp17HdZnU,179
9
- narrator/models/attachment.py,sha256=6fZnGla_Ahgc4Kro2bHBTWoF_Kr-mUBHzONizVH73oc,16129
9
+ narrator/models/attachment.py,sha256=Y-vR9ujWXZp2E3w6Q-pB7RBlLB62k6Fody3v2SE4Y_k,17032
10
10
  narrator/models/message.py,sha256=Wm6M-zwqRFjuf__jCJ0pL5rrgqOfMMjmja1PRX-Y8UU,21482
11
11
  narrator/models/thread.py,sha256=4HKnCW8MkF52vYA6FQga1awxMA7OPjxOZL4QBcXpYOo,19218
12
12
  narrator/storage/__init__.py,sha256=K4cxGITSQoQiw32QOWZsCBm11fwDTbsyzHGeAqcL6yY,101
13
- narrator/storage/file_store.py,sha256=m2btUQcbqpHbWm-htPe1_zwcGRmFXatmS_m9rB9ac2U,19858
13
+ narrator/storage/file_store.py,sha256=H2F08e_DYjO-L782s1DgSctJ_2x-7EjrTFxLxV3oWuk,20062
14
14
  narrator/utils/__init__.py,sha256=P4BhLvBJbBvb8qha2tTZPlYbjCRXth_K97f4vNc77UI,109
15
15
  narrator/utils/logging.py,sha256=Hm6D4VX03e28UCkNS1pCOXnYQKHQ2nz_PvZX8h-wLgg,1807
16
- slide_narrator-5.1.0.dist-info/METADATA,sha256=wCwnTRX2T74TAHhQyjTIjkIWBVnhyTBDFVXOyGS1DTw,16555
17
- slide_narrator-5.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- slide_narrator-5.1.0.dist-info/entry_points.txt,sha256=5Oa53AERvPVdrEvsdWbY85xfzAGayOqq_P4KEmf1khA,56
19
- slide_narrator-5.1.0.dist-info/licenses/LICENSE,sha256=g6cGasroU9sqSOjThWg14w0BMlwZhgmOQQVTiu036ks,1068
20
- slide_narrator-5.1.0.dist-info/RECORD,,
16
+ slide_narrator-5.2.0.dist-info/METADATA,sha256=_qiBcYax5gj_HYclLzBC3nCXfWFU6K02-t2aExiMwZo,16494
17
+ slide_narrator-5.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ slide_narrator-5.2.0.dist-info/entry_points.txt,sha256=5Oa53AERvPVdrEvsdWbY85xfzAGayOqq_P4KEmf1khA,56
19
+ slide_narrator-5.2.0.dist-info/licenses/LICENSE,sha256=g6cGasroU9sqSOjThWg14w0BMlwZhgmOQQVTiu036ks,1068
20
+ slide_narrator-5.2.0.dist-info/RECORD,,