televault 0.1.0__py3-none-any.whl → 2.0.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.
televault/compress.py CHANGED
@@ -1,8 +1,6 @@
1
1
  """Compression utilities for TeleVault - zstd for speed and ratio."""
2
2
 
3
- import io
4
3
  from pathlib import Path
5
- from typing import BinaryIO
6
4
 
7
5
  import zstandard as zstd
8
6
 
@@ -15,17 +13,51 @@ DEFAULT_LEVEL = 3
15
13
  # File extensions that are already compressed (skip compression)
16
14
  INCOMPRESSIBLE_EXTENSIONS = {
17
15
  # Images
18
- ".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic", ".heif", ".avif",
16
+ ".jpg",
17
+ ".jpeg",
18
+ ".png",
19
+ ".gif",
20
+ ".webp",
21
+ ".heic",
22
+ ".heif",
23
+ ".avif",
19
24
  # Video
20
- ".mp4", ".mkv", ".avi", ".mov", ".webm", ".m4v", ".wmv", ".flv",
25
+ ".mp4",
26
+ ".mkv",
27
+ ".avi",
28
+ ".mov",
29
+ ".webm",
30
+ ".m4v",
31
+ ".wmv",
32
+ ".flv",
21
33
  # Audio
22
- ".mp3", ".aac", ".ogg", ".opus", ".flac", ".m4a", ".wma",
34
+ ".mp3",
35
+ ".aac",
36
+ ".ogg",
37
+ ".opus",
38
+ ".flac",
39
+ ".m4a",
40
+ ".wma",
23
41
  # Archives
24
- ".zip", ".gz", ".bz2", ".xz", ".7z", ".rar", ".zst", ".lz4", ".lzma",
42
+ ".zip",
43
+ ".gz",
44
+ ".bz2",
45
+ ".xz",
46
+ ".7z",
47
+ ".rar",
48
+ ".zst",
49
+ ".lz4",
50
+ ".lzma",
25
51
  # Documents (already compressed)
26
- ".pdf", ".docx", ".xlsx", ".pptx", ".odt",
52
+ ".pdf",
53
+ ".docx",
54
+ ".xlsx",
55
+ ".pptx",
56
+ ".odt",
27
57
  # Other
28
- ".woff", ".woff2", ".br",
58
+ ".woff",
59
+ ".woff2",
60
+ ".br",
29
61
  }
30
62
 
31
63
 
@@ -49,53 +81,57 @@ def decompress_data(data: bytes, max_output_size: int = 0) -> bytes:
49
81
  return dctx.decompress(data, max_output_size=max_output_size)
50
82
 
51
83
 
52
- def compress_file(input_path: str | Path, output_path: str | Path, level: int = DEFAULT_LEVEL) -> float:
84
+ def compress_file(
85
+ input_path: str | Path,
86
+ output_path: str | Path,
87
+ level: int = DEFAULT_LEVEL,
88
+ ) -> float:
53
89
  """
54
90
  Compress a file using zstd.
55
-
91
+
56
92
  Returns compression ratio (compressed_size / original_size).
57
93
  """
58
94
  cctx = zstd.ZstdCompressor(level=level)
59
-
95
+
60
96
  with open(input_path, "rb") as fin, open(output_path, "wb") as fout:
61
97
  cctx.copy_stream(fin, fout)
62
-
98
+
63
99
  original_size = Path(input_path).stat().st_size
64
100
  compressed_size = Path(output_path).stat().st_size
65
-
101
+
66
102
  return compressed_size / original_size if original_size > 0 else 1.0
67
103
 
68
104
 
69
105
  def decompress_file(input_path: str | Path, output_path: str | Path) -> None:
70
106
  """Decompress a zstd file."""
71
107
  dctx = zstd.ZstdDecompressor()
72
-
108
+
73
109
  with open(input_path, "rb") as fin, open(output_path, "wb") as fout:
74
110
  dctx.copy_stream(fin, fout)
75
111
 
76
112
 
77
113
  class StreamingCompressor:
78
114
  """Streaming compressor for pipeline integration."""
79
-
115
+
80
116
  def __init__(self, level: int = DEFAULT_LEVEL):
81
117
  self.cctx = zstd.ZstdCompressor(level=level)
82
118
  self.compressor = self.cctx.compressobj()
83
119
  self.total_in = 0
84
120
  self.total_out = 0
85
-
121
+
86
122
  def compress(self, data: bytes) -> bytes:
87
123
  """Compress a chunk of data."""
88
124
  self.total_in += len(data)
89
125
  compressed = self.compressor.compress(data)
90
126
  self.total_out += len(compressed)
91
127
  return compressed
92
-
128
+
93
129
  def flush(self) -> bytes:
94
130
  """Flush remaining data and finalize compression."""
95
131
  final = self.compressor.flush()
96
132
  self.total_out += len(final)
97
133
  return final
98
-
134
+
99
135
  @property
100
136
  def ratio(self) -> float:
101
137
  """Current compression ratio."""
@@ -106,11 +142,11 @@ class StreamingCompressor:
106
142
 
107
143
  class StreamingDecompressor:
108
144
  """Streaming decompressor for pipeline integration."""
109
-
145
+
110
146
  def __init__(self):
111
147
  self.dctx = zstd.ZstdDecompressor()
112
148
  self.decompressor = self.dctx.decompressobj()
113
-
149
+
114
150
  def decompress(self, data: bytes) -> bytes:
115
151
  """Decompress a chunk of data."""
116
152
  return self.decompressor.decompress(data)
@@ -119,15 +155,15 @@ class StreamingDecompressor:
119
155
  def estimate_compressed_size(original_size: int, filename: str) -> int:
120
156
  """
121
157
  Estimate compressed size based on file type.
122
-
158
+
123
159
  Returns estimated size in bytes.
124
160
  """
125
161
  if not should_compress(filename):
126
162
  return original_size
127
-
163
+
128
164
  # Typical compression ratios by type
129
165
  suffix = Path(filename).suffix.lower()
130
-
166
+
131
167
  if suffix in {".txt", ".log", ".csv", ".json", ".xml", ".html", ".md"}:
132
168
  return int(original_size * 0.2) # Text compresses well
133
169
  elif suffix in {".sql", ".py", ".js", ".ts", ".go", ".rs", ".c", ".cpp", ".h"}:
televault/config.py CHANGED
@@ -1,10 +1,9 @@
1
1
  """Configuration management for TeleVault."""
2
2
 
3
3
  import json
4
- from pathlib import Path
5
- from dataclasses import dataclass, field, asdict
6
- from typing import Optional
7
4
  import os
5
+ from dataclasses import asdict, dataclass
6
+ from pathlib import Path
8
7
 
9
8
 
10
9
  def get_config_dir() -> Path:
@@ -13,7 +12,7 @@ def get_config_dir() -> Path:
13
12
  base = Path(os.environ.get("APPDATA", "~"))
14
13
  else: # Unix
15
14
  base = Path(os.environ.get("XDG_CONFIG_HOME", "~/.config"))
16
-
15
+
17
16
  config_dir = base.expanduser() / "televault"
18
17
  config_dir.mkdir(parents=True, exist_ok=True)
19
18
  return config_dir
@@ -25,7 +24,7 @@ def get_data_dir() -> Path:
25
24
  base = Path(os.environ.get("LOCALAPPDATA", "~"))
26
25
  else:
27
26
  base = Path(os.environ.get("XDG_DATA_HOME", "~/.local/share"))
28
-
27
+
29
28
  data_dir = base.expanduser() / "televault"
30
29
  data_dir.mkdir(parents=True, exist_ok=True)
31
30
  return data_dir
@@ -34,44 +33,44 @@ def get_data_dir() -> Path:
34
33
  @dataclass
35
34
  class Config:
36
35
  """TeleVault configuration."""
37
-
36
+
38
37
  # Telegram settings
39
- channel_id: Optional[int] = None
40
-
38
+ channel_id: int | None = None
39
+
41
40
  # Chunking
42
41
  chunk_size: int = 100 * 1024 * 1024 # 100MB
43
-
42
+
44
43
  # Processing options
45
44
  compression: bool = True
46
45
  encryption: bool = True
47
-
46
+
48
47
  # Concurrency
49
48
  parallel_uploads: int = 3
50
49
  parallel_downloads: int = 5
51
-
50
+
52
51
  # Retry settings
53
52
  max_retries: int = 3
54
53
  retry_delay: float = 1.0
55
-
54
+
56
55
  def save(self) -> None:
57
56
  """Save config to file."""
58
57
  config_path = get_config_dir() / "config.json"
59
58
  with open(config_path, "w") as f:
60
59
  json.dump(asdict(self), f, indent=2)
61
-
60
+
62
61
  @classmethod
63
62
  def load(cls) -> "Config":
64
63
  """Load config from file."""
65
64
  config_path = get_config_dir() / "config.json"
66
-
65
+
67
66
  if not config_path.exists():
68
67
  return cls()
69
-
68
+
70
69
  with open(config_path) as f:
71
70
  data = json.load(f)
72
-
71
+
73
72
  return cls(**data)
74
-
73
+
75
74
  @classmethod
76
75
  def load_or_create(cls) -> "Config":
77
76
  """Load config or create default."""