Skip to content

Commit 42360f5

Browse files
writing more tests for multimedia. Writing factory functions for test standardization.
1 parent 22f57bd commit 42360f5

File tree

38 files changed

+5553
-314
lines changed

38 files changed

+5553
-314
lines changed

ipfs_datasets_py/multimedia/media_processor.py

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515

1616
def make_media_processor(
17-
default_output_dir: Optional[str] = None,
17+
default_output_dir: Optional[str|Path] = None,
1818
enable_logging: bool = True,
1919
logger: logging.Logger = logging.getLogger(__name__),
2020
ytdlp: Optional[YtDlpWrapper] = None,
@@ -112,7 +112,7 @@ class MediaProcessor:
112112
113113
# Check available capabilities
114114
capabilities = processor.get_capabilities()
115-
if capabilities["supported_operations"]["download"]:
115+
if capabilities["download"]:
116116
print("Video downloading is available")
117117
118118
# Custom output directory and logging
@@ -130,7 +130,7 @@ class MediaProcessor:
130130
"""
131131

132132
def __init__(self,
133-
default_output_dir: Optional[str] = None,
133+
default_output_dir: Optional[str|Path] = None,
134134
enable_logging: bool = True,
135135
logger: logging.Logger = logging.getLogger(__name__),
136136
ytdlp: Optional[YtDlpWrapper] = None,
@@ -145,7 +145,7 @@ def __init__(self,
145145
and configures the processor accordingly.
146146
147147
Args:
148-
default_output_dir (Optional[str], optional): Default directory path for output files.
148+
default_output_dir (Optional[str|Path], optional): Default directory path for output files.
149149
Can be relative or absolute path. If relative, it will be resolved relative
150150
to the current working directory. If None, uses current working directory.
151151
The directory will be created if it doesn't exist. Defaults to None.
@@ -317,7 +317,8 @@ async def download_and_convert(self,
317317
"error": str(e)
318318
}
319319

320-
def get_capabilities(self) -> Dict[str, Any]:
320+
@classmethod
321+
def get_capabilities(cls) -> Dict[str, Any]:
321322
"""
322323
Get comprehensive information about available capabilities and supported operations.
323324
@@ -332,35 +333,30 @@ def get_capabilities(self) -> Dict[str, Any]:
332333
333334
Returns:
334335
Dict[str, Any]: Comprehensive capabilities information with the following structure:
335-
{
336-
"ytdlp_available": bool, # Whether yt-dlp backend is available
337-
"ffmpeg_available": bool, # Whether FFmpeg backend is available
338-
"supported_operations": {
336+
{
339337
"download": bool, # Video downloading capability
340338
"convert": bool, # Media conversion capability
341-
"download_and_convert": bool # Complete workflow capability
342339
}
343-
}
344340
345341
Examples:
346-
>>> capabilities = processor.get_capabilities()
342+
>>> processor_can = processor.get_capabilities()
347343
>>>
348344
>>> # Check if downloading is supported
349-
>>> if capabilities["supported_operations"]["download"]:
345+
>>> if processor_can["download"]:
350346
... print("Video downloading is available")
351347
... else:
352348
... print("yt-dlp is required for video downloading")
353349
>>>
354350
>>> # Adapt UI based on capabilities
355-
>>> if capabilities["ytdlp_available"] and capabilities["ffmpeg_available"]:
351+
>>> if processor_can["download"] and processor_can["convert"]:
356352
... show_full_interface()
357-
... elif capabilities["ytdlp_available"]:
353+
... elif processor_can["download"]:
358354
... show_download_only_interface()
359355
... else:
360356
... show_no_capabilities_message()
361357
>>>
362358
>>> # Validate operation before attempting
363-
>>> if not capabilities["supported_operations"]["convert"]:
359+
>>> if not processor_can["convert"]:
364360
... raise RuntimeError("Conversion not supported - FFmpeg required")
365361
366362
Note:
@@ -371,11 +367,7 @@ def get_capabilities(self) -> Dict[str, Any]:
371367
- This method is synchronous and performs no external operations
372368
"""
373369
return {
374-
"ytdlp_available": YTDLP_AVAILABLE,
375-
"ffmpeg_available": FFMPEG_AVAILABLE,
376-
"supported_operations": {
377-
"download": YTDLP_AVAILABLE,
378-
"convert": FFMPEG_AVAILABLE,
379-
"download_and_convert": YTDLP_AVAILABLE and FFMPEG_AVAILABLE
380-
}
370+
"download": YTDLP_AVAILABLE,
371+
"convert": FFMPEG_AVAILABLE,
381372
}
373+

ipfs_datasets_py/multimedia/ytdlp_wrapper.py

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import time
1212
import uuid
1313
from pathlib import Path
14-
from typing import Any, Callable, Dict, List, Optional
14+
from typing import Any, Callable, Dict, List, Optional, Literal
1515

1616

1717
logger = logging.getLogger(__name__)
@@ -24,6 +24,25 @@
2424
logger.warning("yt-dlp not available. Install with: pip install yt-dlp")
2525

2626

27+
def make_ytdlp_wrapper(
28+
default_output_dir: Optional[str] = None,
29+
enable_logging: bool = True,
30+
default_quality: Literal['best', 'worst'] = "best",
31+
logger: logging.Logger = logging.getLogger(__name__),
32+
) -> 'YtDlpWrapper':
33+
34+
resources = {
35+
"default_output_dir": default_output_dir,
36+
"enable_logging": enable_logging,
37+
"default_quality": default_quality,
38+
logger: logger
39+
}
40+
41+
return YtDlpWrapper(**resources)
42+
43+
def type_name(obj: Any) -> str:
44+
return type(obj).__name__
45+
2746
class YtDlpWrapper:
2847
"""
2948
YT-DLP Wrapper for Multi-Platform Media Downloads
@@ -69,6 +88,7 @@ class YtDlpWrapper:
6988
default_quality (str): Default quality setting for downloads
7089
downloads (Dict[str, Dict[str, Any]]): Registry tracking all download operations,
7190
keyed by unique download IDs containing status, timing, and result information
91+
logger (logging.Logger): Logger instance for outputting messages
7292
7393
Public Methods:
7494
download_video(url, output_path=None, quality=None, **kwargs) -> Dict[str, Any]:
@@ -161,7 +181,9 @@ def progress_callback(download_id, progress_data):
161181
def __init__(self,
162182
default_output_dir: Optional[str] = None,
163183
enable_logging: bool = True,
164-
default_quality: str = "best"):
184+
default_quality: Literal['best', 'worst'] = "best",
185+
logger: logging.Logger = logging.getLogger(__name__)
186+
):
165187
"""
166188
Initialize YT-DLP wrapper with configuration options and dependency validation.
167189
@@ -182,8 +204,6 @@ def __init__(self,
182204
not explicitly specified. Supported values include:
183205
- 'best': Highest available quality
184206
- 'worst': Lowest available quality
185-
- Resolution strings: '720p', '1080p', '480p'
186-
- Custom format selectors: 'best[height<=720]'
187207
Defaults to "best".
188208
189209
Attributes initialized:
@@ -207,14 +227,14 @@ def __init__(self,
207227
>>> wrapper = YtDlpWrapper(
208228
... default_output_dir="/home/user/downloads",
209229
... enable_logging=False,
210-
... default_quality="720p"
230+
... default_quality="best"
211231
... )
212232
213233
>>> # Production configuration
214234
>>> wrapper = YtDlpWrapper(
215235
... default_output_dir="/var/media/downloads",
216236
... enable_logging=True,
217-
... default_quality="best[height<=1080]"
237+
... default_quality="best"
218238
... )
219239
220240
Notes:
@@ -223,14 +243,30 @@ def __init__(self,
223243
- Download tracking dictionary starts empty and accumulates data over time
224244
- All paths are converted to Path objects for cross-platform compatibility
225245
"""
226-
self.default_output_dir = Path(default_output_dir or tempfile.gettempdir())
227-
self.enable_logging = enable_logging
228-
self.default_quality = default_quality
229-
self.downloads = {} # Track active downloads
230-
231246
if not YTDLP_AVAILABLE:
232247
raise ImportError("yt-dlp is required but not installed. Install with: pip install yt-dlp")
233-
248+
249+
if default_output_dir is not None and not isinstance(default_output_dir, str):
250+
raise TypeError(f"default_output_dir must be a string, got {type_name(default_output_dir)}")
251+
252+
if not isinstance(default_quality, str):
253+
raise TypeError(f"default_quality must be a string, got {type_name(default_quality)}")
254+
255+
if default_quality not in ['best', 'worst']:
256+
raise ValueError(f"default_quality must be 'best' or 'worst', got {default_quality}")
257+
258+
if not isinstance(enable_logging, bool):
259+
raise TypeError(f"enable_logging must be a boolean, got {type_name(enable_logging)}")
260+
261+
if logger is not None and not isinstance(logger, logging.Logger):
262+
raise TypeError(f"logger must be a logging.Logger instance, got {type_name(logger)}")
263+
264+
self.default_output_dir: Path = Path(default_output_dir or tempfile.gettempdir())
265+
self.enable_logging: bool = enable_logging
266+
self.default_quality: Literal['best','worst'] = default_quality
267+
self.logger: logging.Logger = logger
268+
self.downloads = {} # Track active downloads
269+
234270
async def download_video(self,
235271
url: str,
236272
output_path: Optional[str] = None,

tests/_test_utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
from typing import Literal
1919

2020

21+
try:
22+
from nltk import word_tokenize, sent_tokenize
23+
except ImportError:
24+
raise ImportError(
25+
"The 'nltk' package is required for this module. "
26+
"Please install it using 'pip install nltk'."
27+
)
28+
29+
30+
31+
32+
33+
2134
class FalsifiedCodeError(NotImplementedError):
2235
"""Custom exception for falsified code."""
2336
pass

0 commit comments

Comments
 (0)