Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated "download_master_playlist" to return JSON-formatted available resolutions #23

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Version History

- 0.1.7: Restructured Code + Add. Test Cases (latest)
- 0.1.8: Updated "download_master_playlist" to return JSON-formatted available resolutions (latest)
- 0.1.7: Restructured Code + Add. Test Cases
- 0.1.6: Fixed bug with SSL error
- 0.1.5: Introduced Support for Master Playlist
- 0.1.4: Added Additional Files Required for Master Playlist Tests
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,23 @@ downloader.download_master_playlist(name='720')

### `M3U8DownloaderError`

This error class is employed to signal any issues or errors encountered during the execution of M3U8Downloader methods.
This error class is employed to signal any issues or errors encountered during the execution of `M3U8Downloader`
methods.

#### Constructors

- `M3U8DownloaderError(message: str)`: Initialize a M3U8DownloaderError.

### `M3U8DownloaderWarning`

This warning class is employed to signal any issues encountered during the execution of the `download_master_playlist`
method of the `M3U8Downloader` class, especially when no parameters are passed. Along with the warning message,
the `json_data` variable is returned with all available resolution formats (variants) in JSON-structured format.

#### Constructors

- `M3U8DownloaderWarning(message: str, json_data: Optional[list] = None)`: Initialize a M3U8DownloaderWarning.

# Troubleshooting Guide

For detailed troubleshooting guide, please refer to [TROUBLESHOOTING](TROUBLESHOOTING.md).
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ version, which is currently:

| Version | Supported |
|---------|--------------------|
| 0.1.7 | :white_check_mark: |
| 0.1.8 | :white_check_mark: |

If you are using an older version, we highly recommend upgrading to the latest version to ensure you have the latest
security updates.
Expand Down
24 changes: 9 additions & 15 deletions TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This document provides comprehensive guidance on troubleshooting issues related
**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: Internet connection required.
pym3u8downloader.exceptions.M3U8DownloaderError: Internet connection required.
```

**Description:**
Expand All @@ -24,7 +24,7 @@ To resolve this issue, ensure that you are connected to the internet and then re
**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: Identified file "***" as master playlist. Please use "download_master_playlist" instead.
pym3u8downloader.exceptions.M3U8DownloaderError: Identified file "***" as master playlist. Please use "download_master_playlist" instead.
```

**Description:**
Expand Down Expand Up @@ -52,7 +52,7 @@ downloader.download_master_playlist(name='720', bandwidth='2048', resolution='12
**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: Identified file "***" as playlist. Please use "download_playlist" instead.
pym3u8downloader.exceptions.M3U8DownloaderError: Identified file "***" as playlist. Please use "download_playlist" instead.
```

**Description:**
Expand Down Expand Up @@ -80,7 +80,7 @@ downloader.download_playlist()
**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: File "***" is not identified as either playlist or master.
pym3u8downloader.exceptions.M3U8DownloaderError: File "***" is not identified as either playlist or master.
```

**Description:**
Expand All @@ -97,7 +97,7 @@ file link from someone else, contact them to ensure that they provide the correc
**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: Unable to download "***" file.
pym3u8downloader.exceptions.M3U8DownloaderError: Unable to download "***" file.
```

**Description:**
Expand All @@ -114,14 +114,8 @@ check that the file is accessible. Reattempt the download once these conditions
**Error Message:**

```html
UserWarning: Identified * variants in the master playlist. To download the desired playlist, please provide additional parameters, such as NAME, BANDWIDTH, or RESOLUTION, to identify the specific variant.
pym3u8downloader.exceptions.M3U8DownloaderWarning: Identified * variants in the master playlist. To download the desired playlist, please provide additional parameters, such as NAME, BANDWIDTH, or RESOLUTION, to identify the specific variant.
For example: use "download_master_playlist(name='720', bandwidth='2149280', resolution='1280x720')".

You can view the available options using the following list:
[
{'bandwidth': '***', 'name': '***', 'resolution': '***'},
...
]
```

**Description:**
Expand All @@ -132,13 +126,13 @@ package analyzes the master playlist and lists all available variants to assist

**Solution:**

Review the list of available variants provided in the warning message. Choose the appropriate values for name,
bandwidth, and resolution, and then provide these parameters to download the desired video segments.
Review the list of available variants provided in the JSON-formatted return data. Choose the appropriate values for
name, bandwidth, and resolution, and then provide these parameters to download the desired video segments.

**Error Message:**

```html
pym3u8downloader.__main__.M3U8DownloaderError: Selected variant, name="***", bandwidth="***", resolution="***" not found.
pym3u8downloader.exceptions.M3U8DownloaderError: Selected variant, name="***", bandwidth="***", resolution="***" not found.
```

**Description:**
Expand Down
5 changes: 3 additions & 2 deletions pym3u8downloader/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
__all__ = [
"M3U8Downloader",
"M3U8DownloaderError",
"M3U8DownloaderWarning",
"__author__",
"__description__",
"__name__",
Expand All @@ -13,7 +14,7 @@
into a single video file, and manage various error conditions.
"""
__name__ = "pym3u8downloader"
__version__ = "0.1.7"
__version__ = "0.1.8"

from pym3u8downloader.downloader import M3U8Downloader
from pym3u8downloader.exceptions import M3U8DownloaderError
from pym3u8downloader.exceptions import M3U8DownloaderError, M3U8DownloaderWarning
28 changes: 10 additions & 18 deletions pym3u8downloader/downloader.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import json
import os
import re
import shutil
Expand All @@ -7,7 +8,7 @@
from concurrent.futures import ThreadPoolExecutor
from typing import Optional, TextIO, Union

from pym3u8downloader.exceptions import M3U8DownloaderError
from pym3u8downloader.exceptions import M3U8DownloaderError, M3U8DownloaderWarning
from pym3u8downloader.utility_class import UtilityClass
from pym3u8downloader.validations import validate_type

Expand Down Expand Up @@ -830,23 +831,14 @@ def download_master_playlist(
{k: v for k, v in variant.items() if k != 'uri'}
for variant in variants
]
formatted_variants = '[\n'
for variant in display_variants:
formatted_variant = ' {'
for key, value in variant.items():
formatted_variant += f"'{key}': '{value}', "
formatted_variant = formatted_variant.rstrip(', ')
formatted_variant += '},\n'
formatted_variants += formatted_variant
formatted_variants = formatted_variants.rstrip(',\n') + '\n]'

raise UserWarning(
f'Identified {len(variants)} variants in the master playlist. '
f'To download the desired playlist, please provide additional parameters, such as NAME, BANDWIDTH, '
f'or RESOLUTION, to identify the specific variant.'
f'\nFor example: use "download_master_playlist(name=\'720\', bandwidth=\'2149280\', '
f'resolution=\'1280x720\')".\n\n'
f'You can view the available options using the following list: \n{formatted_variants}'
formatted_variants = json.dumps(display_variants, indent=4)

raise M3U8DownloaderWarning(
message=f'Identified {len(variants)} variants in the master playlist. To download the desired '
f'playlist, please provide additional parameters, such as NAME, BANDWIDTH, or RESOLUTION, '
f'to identify the specific variant.\nFor example: use "download_master_playlist('
f'name=\'720\', bandwidth=\'2149280\', resolution=\'1280x720\')".',
json_data=json.loads(formatted_variants)
)
else:
selected_variant = self._search_playlist(variants, name, bandwidth, resolution)
Expand Down
28 changes: 27 additions & 1 deletion pym3u8downloader/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Optional, Union

from pym3u8downloader.validations import validate_type


Expand All @@ -13,9 +15,33 @@ def __init__(self, message: str) -> None:
"""
Initialize a new instance of M3U8DownloaderError.

:param message: A human-readable error message describing the exception
:param message: A human-readable error message describing the exception.
:type message: str
"""
validate_type(message, str, 'message should be a string.')
self.message = message
super().__init__(self.message)


class M3U8DownloaderWarning(Warning):
"""
A warning raised by the M3U8Downloader class or its methods.

This warning is used to indicate an issue with master playlist resolution that occurs during the operation of the
M3U8Downloader class or its methods.
"""

def __init__(self, message: str, json_data: Optional[list] = None) -> None:
"""
Initialize a new instance of M3U8DownloaderWarning.

:param message: A human-readable warning message describing the issue.
:type message: str
:param json_data: The JSON-formatted data of all available resolutions from master playlist to choose from.
:type json_data: list
"""
validate_type(message, str, 'message should be a string.')
validate_type(json_data, Union[list, None], 'json_data should be a list.')
self.message = message
self.json_data = json_data
super().__init__(self.message)
5 changes: 4 additions & 1 deletion tests/test_m3u8downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from commonclass import CommonClass
from pym3u8downloader import M3U8Downloader, M3U8DownloaderError
from pym3u8downloader.exceptions import M3U8DownloaderWarning


class TestM3U8Downloader(unittest.TestCase):
Expand Down Expand Up @@ -606,8 +607,10 @@ def test_download_master_playlist_with_no_params(self):
self.input_file_path = f'{CommonClass.get_git_test_parent_url()}/master.m3u8'
downloader = M3U8Downloader(self.input_file_path, self.output_file_path)
try:
with self.assertRaises(UserWarning):
with self.assertRaises(M3U8DownloaderWarning) as w:
downloader.download_master_playlist()
self.assertIsNotNone(w.json_data)
self.assertGreater(len(w.json_data), 0)
finally:
try:
downloader._remove_temp_directory()
Expand Down