From 1984caa91b47b7c12aa34b096ef2720888b02770 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Tue, 21 May 2024 23:56:54 -0500 Subject: [PATCH 01/41] update --- PYPI.md | 2 ++ README.md | 1 + functional_test.py | 26 +++++++++++++++++++++----- pyxtream/pyxtream.py | 5 +++-- pyxtream/version.py | 2 +- setup.py | 6 +++++- 6 files changed, 33 insertions(+), 9 deletions(-) diff --git a/PYPI.md b/PYPI.md index 5416f75..e0c515a 100644 --- a/PYPI.md +++ b/PYPI.md @@ -1,11 +1,13 @@ # Build PIP Module python3 setup.py sdist bdist_wheel +python3 -m build # Upload to PYPI twine upload dist/pyxtream-0.7* # Optional Local Install python3 -m pip install dist/pyxtream-0.7 +python3 -m pip install --editable dist/pyxtream-0.7 # GitHub Documentation diff --git a/README.md b/README.md index c816f68..79a049f 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ xTream.movies[{},{},...] | Date | Version | Description | | ----------- | -----| ----------- | +| 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test | 2023-11-08 | 0.7.0 | - Added Schema Validator
- Added Channel Age
- Added list of movies added in the last 30 and 7 days
- Updated code based on PyLint
- Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| | 2023-02-06 | 0.6.0 | - Added methods to change connection header, to turn off reload timer, and to enable/disable Flask debug mode
- Added a loop when attempting to connect to the provider
- Cleaned up some print lines| | 2021-08-19 | 0.5.0 | - Added method to gracefully handle connection errors
- Added setting to not load adult content
- Added sorting by stream name
- Changed the handling of special characters in streams
- Changed print formatting
- Changed index.html webpage to HTML5 and Bootstrap 5| diff --git a/functional_test.py b/functional_test.py index 7e2b16f..4fb2320 100755 --- a/functional_test.py +++ b/functional_test.py @@ -5,10 +5,10 @@ from pyxtream import XTream, __version__ -PROVIDER_NAME = "" -PROVIDER_URL = "" -PROVIDER_USERNAME = "" -PROVIDER_PASSWORD = "" +PROVIDER_NAME = "Alibaba" +PROVIDER_URL = "http://megamegeric.xyz:80" +PROVIDER_USERNAME = "6580771576" +PROVIDER_PASSWORD = "1839762243" if PROVIDER_URL == "" or PROVIDER_USERNAME == "" or PROVIDER_PASSWORD == "": print("Please edit this file with the provider credentials") @@ -40,7 +40,7 @@ def str2list(input_string: str) -> list: print(f"pyxtream version {__version__}") xt = XTream( - "YourProvider", + PROVIDER_NAME, PROVIDER_USERNAME, PROVIDER_PASSWORD, PROVIDER_URL, @@ -66,6 +66,8 @@ def str2list(input_string: str) -> list: (3) Search Streams Text (4) Download Video (stream_id) (5) Download Video Impl (URL, filename) + (6) Show how many movies added in past 30 days + (7) Show how many movies added in past 7 days ---------- (0) Quit """ @@ -123,3 +125,17 @@ def str2list(input_string: str) -> list: url = input("Enter URL to download: ") filename = input("Enter Fullpath Filename: ") xt._download_video_impl(url,filename) + + elif choice == 6: + num_movies = len(xt.movies_30days) + print(f"Found {num_movies} new movies in the past 30 days") + if num_movies < 20: + for i in range(0,num_movies): + print(xt.movies_30days[i].title) + + elif choice == 7: + num_movies = len(xt.movies_7days) + print(f"Found {num_movies} new movies in the past 7 days") + if num_movies < 20: + for i in range(0,num_movies): + print(xt.movies_7days[i].title) diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py index c51374c..b1b24fb 100755 --- a/pyxtream/pyxtream.py +++ b/pyxtream/pyxtream.py @@ -583,6 +583,7 @@ def authenticate(self): r = None # Prepare the authentication url url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + print(f"Attempting connection: ", end='') while i < 30: try: # Request authentication, wait 4 seconds maximum @@ -590,7 +591,7 @@ def authenticate(self): i = 31 except requests.exceptions.ConnectionError: time.sleep(1) - print(i) + print(f"{i} ", end='',flush=True) i += 1 if r is not None: @@ -612,7 +613,7 @@ def authenticate(self): else: print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") else: - print(f"{self.name}: Provider refused the connection") + print(f"\n{self.name}: Provider refused the connection") def _load_from_file(self, filename) -> dict: """Try to load the dictionary from file diff --git a/pyxtream/version.py b/pyxtream/version.py index 39cdad1..cc06b4b 100644 --- a/pyxtream/version.py +++ b/pyxtream/version.py @@ -1,4 +1,4 @@ -__version__ = '0.7.0' +__version__ = '0.7.1' __author__ = 'Claudio Olmi' __author_email__ = 'superolmo2@gmail.com' diff --git a/setup.py b/setup.py index 4f55a92..919b03b 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,8 @@ -from setuptools import setup, find_packages from distutils.util import convert_path +from setuptools import find_packages, setup + with open("README.md", "r") as fh: long_description = fh.read() @@ -30,6 +31,9 @@ "Operating System :: OS Independent", "Natural Language :: English" ], + install_require=[ + 'jsonschema' + ], extras_require={ "REST_API": ["Flask>=1.1.2",], } From 4d50da641db928ab0c3a7320d68364dad4e3a080 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Tue, 21 May 2024 23:58:23 -0500 Subject: [PATCH 02/41] Removed test --- functional_test.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/functional_test.py b/functional_test.py index 4fb2320..1111916 100755 --- a/functional_test.py +++ b/functional_test.py @@ -5,10 +5,10 @@ from pyxtream import XTream, __version__ -PROVIDER_NAME = "Alibaba" -PROVIDER_URL = "http://megamegeric.xyz:80" -PROVIDER_USERNAME = "6580771576" -PROVIDER_PASSWORD = "1839762243" +PROVIDER_NAME = "" +PROVIDER_URL = "" +PROVIDER_USERNAME = "" +PROVIDER_PASSWORD = "" if PROVIDER_URL == "" or PROVIDER_USERNAME == "" or PROVIDER_PASSWORD == "": print("Please edit this file with the provider credentials") From e3b177481314b81c9c0fcb0a53da37bb12b846da Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 00:01:40 -0500 Subject: [PATCH 03/41] Update --- README.md | 2 +- setup.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 79a049f..94def52 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ xTream.movies[{},{},...] | Date | Version | Description | | ----------- | -----| ----------- | -| 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test +| 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test
- Improved print out of connection attempts | 2023-11-08 | 0.7.0 | - Added Schema Validator
- Added Channel Age
- Added list of movies added in the last 30 and 7 days
- Updated code based on PyLint
- Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| | 2023-02-06 | 0.6.0 | - Added methods to change connection header, to turn off reload timer, and to enable/disable Flask debug mode
- Added a loop when attempting to connect to the provider
- Cleaned up some print lines| | 2021-08-19 | 0.5.0 | - Added method to gracefully handle connection errors
- Added setting to not load adult content
- Added sorting by stream name
- Changed the handling of special characters in streams
- Changed print formatting
- Changed index.html webpage to HTML5 and Bootstrap 5| diff --git a/setup.py b/setup.py index 919b03b..70f5138 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ -from distutils.util import convert_path - from setuptools import find_packages, setup +from distutils.util import convert_path with open("README.md", "r") as fh: long_description = fh.read() From 0e198f6d58d8a8644644cb019dd64a6991528857 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 00:06:30 -0500 Subject: [PATCH 04/41] Update --- README.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94def52..b869167 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ xTream.movies[{},{},...] | Date | Version | Description | | ----------- | -----| ----------- | -| 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test
- Improved print out of connection attempts +| 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test
- Improved print out of connection attempts
- Added method to read latest changes in functional_test | 2023-11-08 | 0.7.0 | - Added Schema Validator
- Added Channel Age
- Added list of movies added in the last 30 and 7 days
- Updated code based on PyLint
- Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| | 2023-02-06 | 0.6.0 | - Added methods to change connection header, to turn off reload timer, and to enable/disable Flask debug mode
- Added a loop when attempting to connect to the provider
- Cleaned up some print lines| | 2021-08-19 | 0.5.0 | - Added method to gracefully handle connection errors
- Added setting to not load adult content
- Added sorting by stream name
- Changed the handling of special characters in streams
- Changed print formatting
- Changed index.html webpage to HTML5 and Bootstrap 5| diff --git a/setup.py b/setup.py index 70f5138..4824f89 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ -from setuptools import find_packages, setup +from setuptools import setup, find_packages from distutils.util import convert_path with open("README.md", "r") as fh: From 337576736739b60d8945c93dcbbaf582f4dbdd97 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 00:35:14 -0500 Subject: [PATCH 05/41] Update --- PYPI.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/PYPI.md b/PYPI.md index e0c515a..5416f75 100644 --- a/PYPI.md +++ b/PYPI.md @@ -1,13 +1,11 @@ # Build PIP Module python3 setup.py sdist bdist_wheel -python3 -m build # Upload to PYPI twine upload dist/pyxtream-0.7* # Optional Local Install python3 -m pip install dist/pyxtream-0.7 -python3 -m pip install --editable dist/pyxtream-0.7 # GitHub Documentation From cfe9b19bf1edc3a8b0dd9c33358a59d778d19b8e Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 00:39:12 -0500 Subject: [PATCH 06/41] Updated docs --- PYPI.md | 5 +- doc/pyxtream/pyxtream.html | 2783 ++++++++++++++++++------------------ doc/pyxtream/version.html | 2 +- 3 files changed, 1396 insertions(+), 1394 deletions(-) diff --git a/PYPI.md b/PYPI.md index 5416f75..24ecfb7 100644 --- a/PYPI.md +++ b/PYPI.md @@ -10,9 +10,8 @@ python3 -m pip install dist/pyxtream-0.7 # GitHub Documentation ## Build docs -pdoc --html pyxtream/ --force -mv html/pyxtream/*.html doc -rm -rf html +rm -rf doc +pdoc pyxtream # Record TS Video ffmpeg -y -i "(iptv url)" -c:v copy -c:a copy -map 0:v -map 0:a -t 00:00:30 "myrecording.ts" >"mylog.log" 2>&1 diff --git a/doc/pyxtream/pyxtream.html b/doc/pyxtream/pyxtream.html index 1978b50..da43222 100644 --- a/doc/pyxtream/pyxtream.html +++ b/doc/pyxtream/pyxtream.html @@ -1022,547 +1022,548 @@

583 r = None 584 # Prepare the authentication url 585 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 586 while i < 30: - 587 try: - 588 # Request authentication, wait 4 seconds maximum - 589 r = requests.get(url, timeout=(4), headers=self.connection_headers) - 590 i = 31 - 591 except requests.exceptions.ConnectionError: - 592 time.sleep(1) - 593 print(i) - 594 i += 1 - 595 - 596 if r is not None: - 597 # If the answer is ok, process data and change state - 598 if r.ok: - 599 self.auth_data = r.json() - 600 self.authorization = { - 601 "username": self.auth_data["user_info"]["username"], - 602 "password": self.auth_data["user_info"]["password"] - 603 } - 604 # Mark connection authorized - 605 self.state["authenticated"] = True - 606 # Construct the base url for all requests - 607 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 608 # If there is a secure server connection, construct the base url SSL for all requests - 609 if "https_port" in self.auth_data["server_info"]: - 610 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ - 611 f"/player_api.php?username={self.username}&password={self.password}" - 612 else: - 613 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") - 614 else: - 615 print(f"{self.name}: Provider refused the connection") - 616 - 617 def _load_from_file(self, filename) -> dict: - 618 """Try to load the dictionary from file - 619 - 620 Args: - 621 filename ([type]): File name containing the data - 622 - 623 Returns: - 624 dict: Dictionary if found and no errors, None if file does not exists - 625 """ - 626 # Build the full path - 627 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 628 - 629 # If the cached file exists, attempt to load it - 630 if osp.isfile(full_filename): - 631 - 632 my_data = None - 633 - 634 # Get the enlapsed seconds since last file update - 635 file_age_sec = time.time() - osp.getmtime(full_filename) - 636 # If the file was updated less than the threshold time, - 637 # it means that the file is still fresh, we can load it. - 638 # Otherwise skip and return None to force a re-download - 639 if self.threshold_time_sec > file_age_sec: - 640 # Load the JSON data - 641 try: - 642 with open(full_filename, mode="r", encoding="utf-8") as myfile: - 643 my_data = json.load(myfile) - 644 if len(my_data) == 0: - 645 my_data = None - 646 except Exception as e: - 647 print(f" - Could not load from file `{full_filename}`: e=`{e}`") - 648 return my_data - 649 - 650 return None - 651 - 652 def _save_to_file(self, data_list: dict, filename: str) -> bool: - 653 """Save a dictionary to file - 654 - 655 This function will overwrite the file if already exists - 656 - 657 Args: - 658 data_list (dict): Dictionary to save - 659 filename (str): Name of the file - 660 - 661 Returns: - 662 bool: True if successfull, False if error - 663 """ - 664 if data_list is not None: - 665 - 666 #Build the full path - 667 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 668 # If the path makes sense, save the file - 669 json_data = json.dumps(data_list, ensure_ascii=False) - 670 try: - 671 with open(full_filename, mode="wt", encoding="utf-8") as myfile: - 672 myfile.write(json_data) - 673 except Exception as e: - 674 print(f" - Could not save to file `{full_filename}`: e=`{e}`") - 675 return False - 676 - 677 return True - 678 else: - 679 return False - 680 - 681 def load_iptv(self) -> bool: - 682 """Load XTream IPTV - 683 - 684 - Add all Live TV to XTream.channels - 685 - Add all VOD to XTream.movies - 686 - Add all Series to XTream.series - 687 Series contains Seasons and Episodes. Those are not automatically - 688 retrieved from the server to reduce the loading time. - 689 - Add all groups to XTream.groups - 690 Groups are for all three channel types, Live TV, VOD, and Series - 691 - 692 Returns: - 693 bool: True if successfull, False if error - 694 """ - 695 # If pyxtream has not authenticated the connection, return empty - 696 if self.state["authenticated"] is False: - 697 print("Warning, cannot load steams since authorization failed") - 698 return False - 699 - 700 # If pyxtream has already loaded the data, skip and return success - 701 if self.state["loaded"] is True: - 702 print("Warning, data has already been loaded.") - 703 return True - 704 - 705 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): - 706 ## Get GROUPS - 707 - 708 # Try loading local file - 709 dt = 0 - 710 start = timer() - 711 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") - 712 # If file empty or does not exists, download it from remote - 713 if all_cat is None: - 714 # Load all Groups and save file locally - 715 all_cat = self._load_categories_from_provider(loading_stream_type) - 716 if all_cat is not None: - 717 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") - 718 dt = timer() - start - 719 - 720 # If we got the GROUPS data, show the statistics and load GROUPS - 721 if all_cat is not None: - 722 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") - 723 ## Add GROUPS to dictionaries - 724 - 725 # Add the catch-all-errors group - 726 if loading_stream_type == self.live_type: - 727 self.groups.append(self.live_catch_all_group) - 728 elif loading_stream_type == self.vod_type: - 729 self.groups.append(self.vod_catch_all_group) - 730 elif loading_stream_type == self.series_type: - 731 self.groups.append(self.series_catch_all_group) - 732 - 733 for cat_obj in all_cat: - 734 if schemaValidator(cat_obj, SchemaType.GROUP): - 735 # Create Group (Category) - 736 new_group = Group(cat_obj, loading_stream_type) - 737 # Add to xtream class - 738 self.groups.append(new_group) - 739 else: - 740 # Save what did not pass schema validation - 741 print(cat_obj) - 742 - 743 # Sort Categories - 744 self.groups.sort(key=lambda x: x.name) - 745 else: - 746 print(f" - Could not load {loading_stream_type} Groups") - 747 break - 748 - 749 ## Get Streams - 750 - 751 # Try loading local file - 752 dt = 0 - 753 start = timer() - 754 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") - 755 # If file empty or does not exists, download it from remote - 756 if all_streams is None: - 757 # Load all Streams and save file locally - 758 all_streams = self._load_streams_from_provider(loading_stream_type) - 759 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") - 760 dt = timer() - start - 761 - 762 # If we got the STREAMS data, show the statistics and load Streams - 763 if all_streams is not None: - 764 print( - 765 f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \ - 766 f"in {dt:.3f} seconds" - 767 ) - 768 ## Add Streams to dictionaries - 769 - 770 skipped_adult_content = 0 - 771 skipped_no_name_content = 0 - 772 - 773 number_of_streams = len(all_streams) - 774 current_stream_number = 0 - 775 # Calculate 1% of total number of streams - 776 # This is used to slow down the progress bar - 777 one_percent_number_of_streams = number_of_streams/100 - 778 start = timer() - 779 for stream_channel in all_streams: - 780 skip_stream = False - 781 current_stream_number += 1 - 782 - 783 # Show download progress every 1% of total number of streams - 784 if current_stream_number < one_percent_number_of_streams: - 785 progress( - 786 current_stream_number, - 787 number_of_streams, - 788 f"Processing {loading_stream_type} Streams" - 789 ) - 790 one_percent_number_of_streams *= 2 - 791 - 792 # Validate JSON scheme - 793 if self.validate_json: - 794 if loading_stream_type == self.series_type: - 795 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): - 796 print(stream_channel) - 797 elif loading_stream_type == self.live_type: - 798 if not schemaValidator(stream_channel, SchemaType.LIVE): - 799 print(stream_channel) - 800 else: - 801 # vod_type - 802 if not schemaValidator(stream_channel, SchemaType.VOD): - 803 print(stream_channel) - 804 - 805 # Skip if the name of the stream is empty - 806 if stream_channel["name"] == "": - 807 skip_stream = True - 808 skipped_no_name_content = skipped_no_name_content + 1 - 809 self._save_to_file_skipped_streams(stream_channel) - 810 - 811 # Skip if the user chose to hide adult streams - 812 if self.hide_adult_content and loading_stream_type == self.live_type: - 813 if "is_adult" in stream_channel: - 814 if stream_channel["is_adult"] == "1": - 815 skip_stream = True - 816 skipped_adult_content = skipped_adult_content + 1 - 817 self._save_to_file_skipped_streams(stream_channel) - 818 - 819 if not skip_stream: - 820 # Some channels have no group, - 821 # so let's add them to the catch all group - 822 if stream_channel["category_id"] is None: - 823 stream_channel["category_id"] = "9999" - 824 elif stream_channel["category_id"] != "1": - 825 pass - 826 - 827 # Find the first occurence of the group that the - 828 # Channel or Stream is pointing to - 829 the_group = next( - 830 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), - 831 None - 832 ) - 833 - 834 # Set group title - 835 if the_group is not None: - 836 group_title = the_group.name - 837 else: - 838 if loading_stream_type == self.live_type: - 839 group_title = self.live_catch_all_group.name - 840 the_group = self.live_catch_all_group - 841 elif loading_stream_type == self.vod_type: - 842 group_title = self.vod_catch_all_group.name - 843 the_group = self.vod_catch_all_group - 844 elif loading_stream_type == self.series_type: - 845 group_title = self.series_catch_all_group.name - 846 the_group = self.series_catch_all_group - 847 + 586 print(f"Attempting connection: ", end='') + 587 while i < 30: + 588 try: + 589 # Request authentication, wait 4 seconds maximum + 590 r = requests.get(url, timeout=(4), headers=self.connection_headers) + 591 i = 31 + 592 except requests.exceptions.ConnectionError: + 593 time.sleep(1) + 594 print(f"{i} ", end='',flush=True) + 595 i += 1 + 596 + 597 if r is not None: + 598 # If the answer is ok, process data and change state + 599 if r.ok: + 600 self.auth_data = r.json() + 601 self.authorization = { + 602 "username": self.auth_data["user_info"]["username"], + 603 "password": self.auth_data["user_info"]["password"] + 604 } + 605 # Mark connection authorized + 606 self.state["authenticated"] = True + 607 # Construct the base url for all requests + 608 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 609 # If there is a secure server connection, construct the base url SSL for all requests + 610 if "https_port" in self.auth_data["server_info"]: + 611 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ + 612 f"/player_api.php?username={self.username}&password={self.password}" + 613 else: + 614 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") + 615 else: + 616 print(f"\n{self.name}: Provider refused the connection") + 617 + 618 def _load_from_file(self, filename) -> dict: + 619 """Try to load the dictionary from file + 620 + 621 Args: + 622 filename ([type]): File name containing the data + 623 + 624 Returns: + 625 dict: Dictionary if found and no errors, None if file does not exists + 626 """ + 627 # Build the full path + 628 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 629 + 630 # If the cached file exists, attempt to load it + 631 if osp.isfile(full_filename): + 632 + 633 my_data = None + 634 + 635 # Get the enlapsed seconds since last file update + 636 file_age_sec = time.time() - osp.getmtime(full_filename) + 637 # If the file was updated less than the threshold time, + 638 # it means that the file is still fresh, we can load it. + 639 # Otherwise skip and return None to force a re-download + 640 if self.threshold_time_sec > file_age_sec: + 641 # Load the JSON data + 642 try: + 643 with open(full_filename, mode="r", encoding="utf-8") as myfile: + 644 my_data = json.load(myfile) + 645 if len(my_data) == 0: + 646 my_data = None + 647 except Exception as e: + 648 print(f" - Could not load from file `{full_filename}`: e=`{e}`") + 649 return my_data + 650 + 651 return None + 652 + 653 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 654 """Save a dictionary to file + 655 + 656 This function will overwrite the file if already exists + 657 + 658 Args: + 659 data_list (dict): Dictionary to save + 660 filename (str): Name of the file + 661 + 662 Returns: + 663 bool: True if successfull, False if error + 664 """ + 665 if data_list is not None: + 666 + 667 #Build the full path + 668 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 669 # If the path makes sense, save the file + 670 json_data = json.dumps(data_list, ensure_ascii=False) + 671 try: + 672 with open(full_filename, mode="wt", encoding="utf-8") as myfile: + 673 myfile.write(json_data) + 674 except Exception as e: + 675 print(f" - Could not save to file `{full_filename}`: e=`{e}`") + 676 return False + 677 + 678 return True + 679 else: + 680 return False + 681 + 682 def load_iptv(self) -> bool: + 683 """Load XTream IPTV + 684 + 685 - Add all Live TV to XTream.channels + 686 - Add all VOD to XTream.movies + 687 - Add all Series to XTream.series + 688 Series contains Seasons and Episodes. Those are not automatically + 689 retrieved from the server to reduce the loading time. + 690 - Add all groups to XTream.groups + 691 Groups are for all three channel types, Live TV, VOD, and Series + 692 + 693 Returns: + 694 bool: True if successfull, False if error + 695 """ + 696 # If pyxtream has not authenticated the connection, return empty + 697 if self.state["authenticated"] is False: + 698 print("Warning, cannot load steams since authorization failed") + 699 return False + 700 + 701 # If pyxtream has already loaded the data, skip and return success + 702 if self.state["loaded"] is True: + 703 print("Warning, data has already been loaded.") + 704 return True + 705 + 706 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): + 707 ## Get GROUPS + 708 + 709 # Try loading local file + 710 dt = 0 + 711 start = timer() + 712 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") + 713 # If file empty or does not exists, download it from remote + 714 if all_cat is None: + 715 # Load all Groups and save file locally + 716 all_cat = self._load_categories_from_provider(loading_stream_type) + 717 if all_cat is not None: + 718 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") + 719 dt = timer() - start + 720 + 721 # If we got the GROUPS data, show the statistics and load GROUPS + 722 if all_cat is not None: + 723 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") + 724 ## Add GROUPS to dictionaries + 725 + 726 # Add the catch-all-errors group + 727 if loading_stream_type == self.live_type: + 728 self.groups.append(self.live_catch_all_group) + 729 elif loading_stream_type == self.vod_type: + 730 self.groups.append(self.vod_catch_all_group) + 731 elif loading_stream_type == self.series_type: + 732 self.groups.append(self.series_catch_all_group) + 733 + 734 for cat_obj in all_cat: + 735 if schemaValidator(cat_obj, SchemaType.GROUP): + 736 # Create Group (Category) + 737 new_group = Group(cat_obj, loading_stream_type) + 738 # Add to xtream class + 739 self.groups.append(new_group) + 740 else: + 741 # Save what did not pass schema validation + 742 print(cat_obj) + 743 + 744 # Sort Categories + 745 self.groups.sort(key=lambda x: x.name) + 746 else: + 747 print(f" - Could not load {loading_stream_type} Groups") + 748 break + 749 + 750 ## Get Streams + 751 + 752 # Try loading local file + 753 dt = 0 + 754 start = timer() + 755 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") + 756 # If file empty or does not exists, download it from remote + 757 if all_streams is None: + 758 # Load all Streams and save file locally + 759 all_streams = self._load_streams_from_provider(loading_stream_type) + 760 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") + 761 dt = timer() - start + 762 + 763 # If we got the STREAMS data, show the statistics and load Streams + 764 if all_streams is not None: + 765 print( + 766 f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \ + 767 f"in {dt:.3f} seconds" + 768 ) + 769 ## Add Streams to dictionaries + 770 + 771 skipped_adult_content = 0 + 772 skipped_no_name_content = 0 + 773 + 774 number_of_streams = len(all_streams) + 775 current_stream_number = 0 + 776 # Calculate 1% of total number of streams + 777 # This is used to slow down the progress bar + 778 one_percent_number_of_streams = number_of_streams/100 + 779 start = timer() + 780 for stream_channel in all_streams: + 781 skip_stream = False + 782 current_stream_number += 1 + 783 + 784 # Show download progress every 1% of total number of streams + 785 if current_stream_number < one_percent_number_of_streams: + 786 progress( + 787 current_stream_number, + 788 number_of_streams, + 789 f"Processing {loading_stream_type} Streams" + 790 ) + 791 one_percent_number_of_streams *= 2 + 792 + 793 # Validate JSON scheme + 794 if self.validate_json: + 795 if loading_stream_type == self.series_type: + 796 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): + 797 print(stream_channel) + 798 elif loading_stream_type == self.live_type: + 799 if not schemaValidator(stream_channel, SchemaType.LIVE): + 800 print(stream_channel) + 801 else: + 802 # vod_type + 803 if not schemaValidator(stream_channel, SchemaType.VOD): + 804 print(stream_channel) + 805 + 806 # Skip if the name of the stream is empty + 807 if stream_channel["name"] == "": + 808 skip_stream = True + 809 skipped_no_name_content = skipped_no_name_content + 1 + 810 self._save_to_file_skipped_streams(stream_channel) + 811 + 812 # Skip if the user chose to hide adult streams + 813 if self.hide_adult_content and loading_stream_type == self.live_type: + 814 if "is_adult" in stream_channel: + 815 if stream_channel["is_adult"] == "1": + 816 skip_stream = True + 817 skipped_adult_content = skipped_adult_content + 1 + 818 self._save_to_file_skipped_streams(stream_channel) + 819 + 820 if not skip_stream: + 821 # Some channels have no group, + 822 # so let's add them to the catch all group + 823 if stream_channel["category_id"] is None: + 824 stream_channel["category_id"] = "9999" + 825 elif stream_channel["category_id"] != "1": + 826 pass + 827 + 828 # Find the first occurence of the group that the + 829 # Channel or Stream is pointing to + 830 the_group = next( + 831 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), + 832 None + 833 ) + 834 + 835 # Set group title + 836 if the_group is not None: + 837 group_title = the_group.name + 838 else: + 839 if loading_stream_type == self.live_type: + 840 group_title = self.live_catch_all_group.name + 841 the_group = self.live_catch_all_group + 842 elif loading_stream_type == self.vod_type: + 843 group_title = self.vod_catch_all_group.name + 844 the_group = self.vod_catch_all_group + 845 elif loading_stream_type == self.series_type: + 846 group_title = self.series_catch_all_group.name + 847 the_group = self.series_catch_all_group 848 - 849 if loading_stream_type == self.series_type: - 850 # Load all Series - 851 new_series = Serie(self, stream_channel) - 852 # To get all the Episodes for every Season of each - 853 # Series is very time consuming, we will only - 854 # populate the Series once the user click on the - 855 # Series, the Seasons and Episodes will be loaded - 856 # using x.getSeriesInfoByID() function - 857 - 858 else: - 859 new_channel = Channel( - 860 self, - 861 group_title, - 862 stream_channel - 863 ) - 864 - 865 if new_channel.group_id == "9999": - 866 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") - 867 - 868 # Save the new channel to the local list of channels - 869 if loading_stream_type == self.live_type: - 870 self.channels.append(new_channel) - 871 elif loading_stream_type == self.vod_type: - 872 self.movies.append(new_channel) - 873 if new_channel.age_days_from_added < 31: - 874 self.movies_30days.append(new_channel) - 875 if new_channel.age_days_from_added < 7: - 876 self.movies_7days.append(new_channel) - 877 else: - 878 self.series.append(new_series) - 879 - 880 # Add stream to the specific Group - 881 if the_group is not None: - 882 if loading_stream_type != self.series_type: - 883 the_group.channels.append(new_channel) - 884 else: - 885 the_group.series.append(new_series) - 886 else: - 887 print(f" - Group not found `{stream_channel['name']}`") - 888 print("\n") - 889 # Print information of which streams have been skipped - 890 if self.hide_adult_content: - 891 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") - 892 if skipped_no_name_content > 0: - 893 print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams") - 894 else: - 895 print(f" - Could not load {loading_stream_type} Streams") - 896 - 897 self.state["loaded"] = True - 898 - 899 def _save_to_file_skipped_streams(self, stream_channel: Channel): - 900 - 901 # Build the full path - 902 full_filename = osp.join(self.cache_path, "skipped_streams.json") - 903 - 904 # If the path makes sense, save the file - 905 json_data = json.dumps(stream_channel, ensure_ascii=False) - 906 try: - 907 with open(full_filename, mode="a", encoding="utf-8") as myfile: - 908 myfile.writelines(json_data) - 909 return True - 910 except Exception as e: - 911 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") - 912 return False - 913 - 914 def get_series_info_by_id(self, get_series: dict): - 915 """Get Seasons and Episodes for a Series - 916 - 917 Args: - 918 get_series (dict): Series dictionary - 919 """ - 920 - 921 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) - 922 - 923 if series_seasons["seasons"] is None: - 924 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] - 925 - 926 for series_info in series_seasons["seasons"]: - 927 season_name = series_info["name"] - 928 season_key = series_info['season_number'] - 929 season = Season(season_name) - 930 get_series.seasons[season_name] = season - 931 if "episodes" in series_seasons.keys(): - 932 for series_season in series_seasons["episodes"].keys(): - 933 for episode_info in series_seasons["episodes"][str(series_season)]: - 934 new_episode_channel = Episode( - 935 self, series_info, "Testing", episode_info - 936 ) - 937 season.episodes[episode_info["title"]] = new_episode_channel - 938 - 939 def _get_request(self, url: str, timeout: Tuple = (2, 15)): - 940 """Generic GET Request with Error handling - 941 - 942 Args: - 943 URL (str): The URL where to GET content - 944 timeout (Tuple, optional): Connection and Downloading Timeout. Defaults to (2,15). - 945 - 946 Returns: - 947 [type]: JSON dictionary of the loaded data, or None - 948 """ - 949 i = 0 - 950 while i < 10: - 951 time.sleep(1) - 952 try: - 953 r = requests.get(url, timeout=timeout, headers=self.connection_headers) - 954 i = 20 - 955 if r.status_code == 200: - 956 return r.json() - 957 except requests.exceptions.ConnectionError: - 958 print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") - 959 i += 1 - 960 - 961 except requests.exceptions.HTTPError: - 962 print(" - HTTP Error") - 963 i += 1 - 964 - 965 except requests.exceptions.TooManyRedirects: - 966 print(" - TooManyRedirects") - 967 i += 1 - 968 - 969 except requests.exceptions.ReadTimeout: - 970 print(" - Timeout while loading data") - 971 i += 1 - 972 - 973 return None - 974 - 975 # GET Stream Categories - 976 def _load_categories_from_provider(self, stream_type: str): - 977 """Get from provider all category for specific stream type from provider - 978 - 979 Args: - 980 stream_type (str): Stream type can be Live, VOD, Series - 981 - 982 Returns: - 983 [type]: JSON if successfull, otherwise None - 984 """ - 985 url = "" - 986 if stream_type == self.live_type: - 987 url = self.get_live_categories_URL() - 988 elif stream_type == self.vod_type: - 989 url = self.get_vod_cat_URL() - 990 elif stream_type == self.series_type: - 991 url = self.get_series_cat_URL() - 992 else: - 993 url = "" - 994 - 995 return self._get_request(url) - 996 - 997 # GET Streams - 998 def _load_streams_from_provider(self, stream_type: str): - 999 """Get from provider all streams for specific stream type -1000 -1001 Args: -1002 stream_type (str): Stream type can be Live, VOD, Series -1003 -1004 Returns: -1005 [type]: JSON if successfull, otherwise None -1006 """ -1007 url = "" -1008 if stream_type == self.live_type: -1009 url = self.get_live_streams_URL() -1010 elif stream_type == self.vod_type: -1011 url = self.get_vod_streams_URL() -1012 elif stream_type == self.series_type: -1013 url = self.get_series_URL() -1014 else: -1015 url = "" -1016 -1017 return self._get_request(url) -1018 -1019 # GET Streams by Category -1020 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): -1021 """Get from provider all streams for specific stream type with category/group ID -1022 -1023 Args: -1024 stream_type (str): Stream type can be Live, VOD, Series -1025 category_id ([type]): Category/Group ID. -1026 -1027 Returns: -1028 [type]: JSON if successfull, otherwise None -1029 """ -1030 url = "" -1031 -1032 if stream_type == self.live_type: -1033 url = self.get_live_streams_URL_by_category(category_id) -1034 elif stream_type == self.vod_type: -1035 url = self.get_vod_streams_URL_by_category(category_id) -1036 elif stream_type == self.series_type: -1037 url = self.get_series_URL_by_category(category_id) -1038 else: -1039 url = "" -1040 -1041 return self._get_request(url) -1042 -1043 # GET SERIES Info -1044 def _load_series_info_by_id_from_provider(self, series_id: str): -1045 """Gets informations about a Serie -1046 -1047 Args: -1048 series_id (str): Serie ID as described in Group -1049 -1050 Returns: -1051 [type]: JSON if successfull, otherwise None -1052 """ -1053 return self._get_request(self.get_series_info_URL_by_ID(series_id)) -1054 -1055 # The seasons array, might be filled or might be completely empty. -1056 # If it is not empty, it will contain the cover, overview and the air date -1057 # of the selected season. -1058 # In your APP if you want to display the series, you have to take that -1059 # from the episodes array. -1060 -1061 # GET VOD Info -1062 def vodInfoByID(self, vod_id): -1063 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) -1064 -1065 # GET short_epg for LIVE Streams (same as stalker portal, -1066 # prints the next X EPG that will play soon) -1067 def liveEpgByStream(self, stream_id): -1068 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) -1069 -1070 def liveEpgByStreamAndLimit(self, stream_id, limit): -1071 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) -1072 -1073 # GET ALL EPG for LIVE Streams (same as stalker portal, -1074 # but it will print all epg listings regardless of the day) -1075 def allLiveEpgByStream(self, stream_id): -1076 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) -1077 -1078 # Full EPG List for all Streams -1079 def allEpg(self): -1080 return self._get_request(self.get_all_epg_URL()) -1081 -1082 ## URL-builder methods -1083 def get_live_categories_URL(self) -> str: -1084 return f"{self.base_url}&action=get_live_categories" -1085 -1086 def get_live_streams_URL(self) -> str: -1087 return f"{self.base_url}&action=get_live_streams" -1088 -1089 def get_live_streams_URL_by_category(self, category_id) -> str: -1090 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" -1091 -1092 def get_vod_cat_URL(self) -> str: -1093 return f"{self.base_url}&action=get_vod_categories" -1094 -1095 def get_vod_streams_URL(self) -> str: -1096 return f"{self.base_url}&action=get_vod_streams" -1097 -1098 def get_vod_streams_URL_by_category(self, category_id) -> str: -1099 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" -1100 -1101 def get_series_cat_URL(self) -> str: -1102 return f"{self.base_url}&action=get_series_categories" -1103 -1104 def get_series_URL(self) -> str: -1105 return f"{self.base_url}&action=get_series" -1106 -1107 def get_series_URL_by_category(self, category_id) -> str: -1108 return f"{self.base_url}&action=get_series&category_id={category_id}" -1109 -1110 def get_series_info_URL_by_ID(self, series_id) -> str: -1111 return f"{self.base_url}&action=get_series_info&series_id={series_id}" -1112 -1113 def get_VOD_info_URL_by_ID(self, vod_id) -> str: -1114 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" -1115 -1116 def get_live_epg_URL_by_stream(self, stream_id) -> str: -1117 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" -1118 -1119 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: -1120 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" -1121 -1122 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: -1123 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" -1124 -1125 def get_all_epg_URL(self) -> str: -1126 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" + 849 + 850 if loading_stream_type == self.series_type: + 851 # Load all Series + 852 new_series = Serie(self, stream_channel) + 853 # To get all the Episodes for every Season of each + 854 # Series is very time consuming, we will only + 855 # populate the Series once the user click on the + 856 # Series, the Seasons and Episodes will be loaded + 857 # using x.getSeriesInfoByID() function + 858 + 859 else: + 860 new_channel = Channel( + 861 self, + 862 group_title, + 863 stream_channel + 864 ) + 865 + 866 if new_channel.group_id == "9999": + 867 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") + 868 + 869 # Save the new channel to the local list of channels + 870 if loading_stream_type == self.live_type: + 871 self.channels.append(new_channel) + 872 elif loading_stream_type == self.vod_type: + 873 self.movies.append(new_channel) + 874 if new_channel.age_days_from_added < 31: + 875 self.movies_30days.append(new_channel) + 876 if new_channel.age_days_from_added < 7: + 877 self.movies_7days.append(new_channel) + 878 else: + 879 self.series.append(new_series) + 880 + 881 # Add stream to the specific Group + 882 if the_group is not None: + 883 if loading_stream_type != self.series_type: + 884 the_group.channels.append(new_channel) + 885 else: + 886 the_group.series.append(new_series) + 887 else: + 888 print(f" - Group not found `{stream_channel['name']}`") + 889 print("\n") + 890 # Print information of which streams have been skipped + 891 if self.hide_adult_content: + 892 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") + 893 if skipped_no_name_content > 0: + 894 print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams") + 895 else: + 896 print(f" - Could not load {loading_stream_type} Streams") + 897 + 898 self.state["loaded"] = True + 899 + 900 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 901 + 902 # Build the full path + 903 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 904 + 905 # If the path makes sense, save the file + 906 json_data = json.dumps(stream_channel, ensure_ascii=False) + 907 try: + 908 with open(full_filename, mode="a", encoding="utf-8") as myfile: + 909 myfile.writelines(json_data) + 910 return True + 911 except Exception as e: + 912 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") + 913 return False + 914 + 915 def get_series_info_by_id(self, get_series: dict): + 916 """Get Seasons and Episodes for a Series + 917 + 918 Args: + 919 get_series (dict): Series dictionary + 920 """ + 921 + 922 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) + 923 + 924 if series_seasons["seasons"] is None: + 925 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] + 926 + 927 for series_info in series_seasons["seasons"]: + 928 season_name = series_info["name"] + 929 season_key = series_info['season_number'] + 930 season = Season(season_name) + 931 get_series.seasons[season_name] = season + 932 if "episodes" in series_seasons.keys(): + 933 for series_season in series_seasons["episodes"].keys(): + 934 for episode_info in series_seasons["episodes"][str(series_season)]: + 935 new_episode_channel = Episode( + 936 self, series_info, "Testing", episode_info + 937 ) + 938 season.episodes[episode_info["title"]] = new_episode_channel + 939 + 940 def _get_request(self, url: str, timeout: Tuple = (2, 15)): + 941 """Generic GET Request with Error handling + 942 + 943 Args: + 944 URL (str): The URL where to GET content + 945 timeout (Tuple, optional): Connection and Downloading Timeout. Defaults to (2,15). + 946 + 947 Returns: + 948 [type]: JSON dictionary of the loaded data, or None + 949 """ + 950 i = 0 + 951 while i < 10: + 952 time.sleep(1) + 953 try: + 954 r = requests.get(url, timeout=timeout, headers=self.connection_headers) + 955 i = 20 + 956 if r.status_code == 200: + 957 return r.json() + 958 except requests.exceptions.ConnectionError: + 959 print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") + 960 i += 1 + 961 + 962 except requests.exceptions.HTTPError: + 963 print(" - HTTP Error") + 964 i += 1 + 965 + 966 except requests.exceptions.TooManyRedirects: + 967 print(" - TooManyRedirects") + 968 i += 1 + 969 + 970 except requests.exceptions.ReadTimeout: + 971 print(" - Timeout while loading data") + 972 i += 1 + 973 + 974 return None + 975 + 976 # GET Stream Categories + 977 def _load_categories_from_provider(self, stream_type: str): + 978 """Get from provider all category for specific stream type from provider + 979 + 980 Args: + 981 stream_type (str): Stream type can be Live, VOD, Series + 982 + 983 Returns: + 984 [type]: JSON if successfull, otherwise None + 985 """ + 986 url = "" + 987 if stream_type == self.live_type: + 988 url = self.get_live_categories_URL() + 989 elif stream_type == self.vod_type: + 990 url = self.get_vod_cat_URL() + 991 elif stream_type == self.series_type: + 992 url = self.get_series_cat_URL() + 993 else: + 994 url = "" + 995 + 996 return self._get_request(url) + 997 + 998 # GET Streams + 999 def _load_streams_from_provider(self, stream_type: str): +1000 """Get from provider all streams for specific stream type +1001 +1002 Args: +1003 stream_type (str): Stream type can be Live, VOD, Series +1004 +1005 Returns: +1006 [type]: JSON if successfull, otherwise None +1007 """ +1008 url = "" +1009 if stream_type == self.live_type: +1010 url = self.get_live_streams_URL() +1011 elif stream_type == self.vod_type: +1012 url = self.get_vod_streams_URL() +1013 elif stream_type == self.series_type: +1014 url = self.get_series_URL() +1015 else: +1016 url = "" +1017 +1018 return self._get_request(url) +1019 +1020 # GET Streams by Category +1021 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1022 """Get from provider all streams for specific stream type with category/group ID +1023 +1024 Args: +1025 stream_type (str): Stream type can be Live, VOD, Series +1026 category_id ([type]): Category/Group ID. +1027 +1028 Returns: +1029 [type]: JSON if successfull, otherwise None +1030 """ +1031 url = "" +1032 +1033 if stream_type == self.live_type: +1034 url = self.get_live_streams_URL_by_category(category_id) +1035 elif stream_type == self.vod_type: +1036 url = self.get_vod_streams_URL_by_category(category_id) +1037 elif stream_type == self.series_type: +1038 url = self.get_series_URL_by_category(category_id) +1039 else: +1040 url = "" +1041 +1042 return self._get_request(url) +1043 +1044 # GET SERIES Info +1045 def _load_series_info_by_id_from_provider(self, series_id: str): +1046 """Gets informations about a Serie +1047 +1048 Args: +1049 series_id (str): Serie ID as described in Group +1050 +1051 Returns: +1052 [type]: JSON if successfull, otherwise None +1053 """ +1054 return self._get_request(self.get_series_info_URL_by_ID(series_id)) +1055 +1056 # The seasons array, might be filled or might be completely empty. +1057 # If it is not empty, it will contain the cover, overview and the air date +1058 # of the selected season. +1059 # In your APP if you want to display the series, you have to take that +1060 # from the episodes array. +1061 +1062 # GET VOD Info +1063 def vodInfoByID(self, vod_id): +1064 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) +1065 +1066 # GET short_epg for LIVE Streams (same as stalker portal, +1067 # prints the next X EPG that will play soon) +1068 def liveEpgByStream(self, stream_id): +1069 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) +1070 +1071 def liveEpgByStreamAndLimit(self, stream_id, limit): +1072 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) +1073 +1074 # GET ALL EPG for LIVE Streams (same as stalker portal, +1075 # but it will print all epg listings regardless of the day) +1076 def allLiveEpgByStream(self, stream_id): +1077 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) +1078 +1079 # Full EPG List for all Streams +1080 def allEpg(self): +1081 return self._get_request(self.get_all_epg_URL()) +1082 +1083 ## URL-builder methods +1084 def get_live_categories_URL(self) -> str: +1085 return f"{self.base_url}&action=get_live_categories" +1086 +1087 def get_live_streams_URL(self) -> str: +1088 return f"{self.base_url}&action=get_live_streams" +1089 +1090 def get_live_streams_URL_by_category(self, category_id) -> str: +1091 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" +1092 +1093 def get_vod_cat_URL(self) -> str: +1094 return f"{self.base_url}&action=get_vod_categories" +1095 +1096 def get_vod_streams_URL(self) -> str: +1097 return f"{self.base_url}&action=get_vod_streams" +1098 +1099 def get_vod_streams_URL_by_category(self, category_id) -> str: +1100 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" +1101 +1102 def get_series_cat_URL(self) -> str: +1103 return f"{self.base_url}&action=get_series_categories" +1104 +1105 def get_series_URL(self) -> str: +1106 return f"{self.base_url}&action=get_series" +1107 +1108 def get_series_URL_by_category(self, category_id) -> str: +1109 return f"{self.base_url}&action=get_series&category_id={category_id}" +1110 +1111 def get_series_info_URL_by_ID(self, series_id) -> str: +1112 return f"{self.base_url}&action=get_series_info&series_id={series_id}" +1113 +1114 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1115 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" +1116 +1117 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1118 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" +1119 +1120 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1121 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" +1122 +1123 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1124 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" +1125 +1126 def get_all_epg_URL(self) -> str: +1127 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" @@ -3066,547 +3067,548 @@

584 r = None 585 # Prepare the authentication url 586 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 587 while i < 30: - 588 try: - 589 # Request authentication, wait 4 seconds maximum - 590 r = requests.get(url, timeout=(4), headers=self.connection_headers) - 591 i = 31 - 592 except requests.exceptions.ConnectionError: - 593 time.sleep(1) - 594 print(i) - 595 i += 1 - 596 - 597 if r is not None: - 598 # If the answer is ok, process data and change state - 599 if r.ok: - 600 self.auth_data = r.json() - 601 self.authorization = { - 602 "username": self.auth_data["user_info"]["username"], - 603 "password": self.auth_data["user_info"]["password"] - 604 } - 605 # Mark connection authorized - 606 self.state["authenticated"] = True - 607 # Construct the base url for all requests - 608 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 609 # If there is a secure server connection, construct the base url SSL for all requests - 610 if "https_port" in self.auth_data["server_info"]: - 611 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ - 612 f"/player_api.php?username={self.username}&password={self.password}" - 613 else: - 614 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") - 615 else: - 616 print(f"{self.name}: Provider refused the connection") - 617 - 618 def _load_from_file(self, filename) -> dict: - 619 """Try to load the dictionary from file - 620 - 621 Args: - 622 filename ([type]): File name containing the data - 623 - 624 Returns: - 625 dict: Dictionary if found and no errors, None if file does not exists - 626 """ - 627 # Build the full path - 628 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 629 - 630 # If the cached file exists, attempt to load it - 631 if osp.isfile(full_filename): - 632 - 633 my_data = None - 634 - 635 # Get the enlapsed seconds since last file update - 636 file_age_sec = time.time() - osp.getmtime(full_filename) - 637 # If the file was updated less than the threshold time, - 638 # it means that the file is still fresh, we can load it. - 639 # Otherwise skip and return None to force a re-download - 640 if self.threshold_time_sec > file_age_sec: - 641 # Load the JSON data - 642 try: - 643 with open(full_filename, mode="r", encoding="utf-8") as myfile: - 644 my_data = json.load(myfile) - 645 if len(my_data) == 0: - 646 my_data = None - 647 except Exception as e: - 648 print(f" - Could not load from file `{full_filename}`: e=`{e}`") - 649 return my_data - 650 - 651 return None - 652 - 653 def _save_to_file(self, data_list: dict, filename: str) -> bool: - 654 """Save a dictionary to file - 655 - 656 This function will overwrite the file if already exists - 657 - 658 Args: - 659 data_list (dict): Dictionary to save - 660 filename (str): Name of the file - 661 - 662 Returns: - 663 bool: True if successfull, False if error - 664 """ - 665 if data_list is not None: - 666 - 667 #Build the full path - 668 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 669 # If the path makes sense, save the file - 670 json_data = json.dumps(data_list, ensure_ascii=False) - 671 try: - 672 with open(full_filename, mode="wt", encoding="utf-8") as myfile: - 673 myfile.write(json_data) - 674 except Exception as e: - 675 print(f" - Could not save to file `{full_filename}`: e=`{e}`") - 676 return False - 677 - 678 return True - 679 else: - 680 return False - 681 - 682 def load_iptv(self) -> bool: - 683 """Load XTream IPTV - 684 - 685 - Add all Live TV to XTream.channels - 686 - Add all VOD to XTream.movies - 687 - Add all Series to XTream.series - 688 Series contains Seasons and Episodes. Those are not automatically - 689 retrieved from the server to reduce the loading time. - 690 - Add all groups to XTream.groups - 691 Groups are for all three channel types, Live TV, VOD, and Series - 692 - 693 Returns: - 694 bool: True if successfull, False if error - 695 """ - 696 # If pyxtream has not authenticated the connection, return empty - 697 if self.state["authenticated"] is False: - 698 print("Warning, cannot load steams since authorization failed") - 699 return False - 700 - 701 # If pyxtream has already loaded the data, skip and return success - 702 if self.state["loaded"] is True: - 703 print("Warning, data has already been loaded.") - 704 return True - 705 - 706 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): - 707 ## Get GROUPS - 708 - 709 # Try loading local file - 710 dt = 0 - 711 start = timer() - 712 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") - 713 # If file empty or does not exists, download it from remote - 714 if all_cat is None: - 715 # Load all Groups and save file locally - 716 all_cat = self._load_categories_from_provider(loading_stream_type) - 717 if all_cat is not None: - 718 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") - 719 dt = timer() - start - 720 - 721 # If we got the GROUPS data, show the statistics and load GROUPS - 722 if all_cat is not None: - 723 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") - 724 ## Add GROUPS to dictionaries - 725 - 726 # Add the catch-all-errors group - 727 if loading_stream_type == self.live_type: - 728 self.groups.append(self.live_catch_all_group) - 729 elif loading_stream_type == self.vod_type: - 730 self.groups.append(self.vod_catch_all_group) - 731 elif loading_stream_type == self.series_type: - 732 self.groups.append(self.series_catch_all_group) - 733 - 734 for cat_obj in all_cat: - 735 if schemaValidator(cat_obj, SchemaType.GROUP): - 736 # Create Group (Category) - 737 new_group = Group(cat_obj, loading_stream_type) - 738 # Add to xtream class - 739 self.groups.append(new_group) - 740 else: - 741 # Save what did not pass schema validation - 742 print(cat_obj) - 743 - 744 # Sort Categories - 745 self.groups.sort(key=lambda x: x.name) - 746 else: - 747 print(f" - Could not load {loading_stream_type} Groups") - 748 break - 749 - 750 ## Get Streams - 751 - 752 # Try loading local file - 753 dt = 0 - 754 start = timer() - 755 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") - 756 # If file empty or does not exists, download it from remote - 757 if all_streams is None: - 758 # Load all Streams and save file locally - 759 all_streams = self._load_streams_from_provider(loading_stream_type) - 760 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") - 761 dt = timer() - start - 762 - 763 # If we got the STREAMS data, show the statistics and load Streams - 764 if all_streams is not None: - 765 print( - 766 f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \ - 767 f"in {dt:.3f} seconds" - 768 ) - 769 ## Add Streams to dictionaries - 770 - 771 skipped_adult_content = 0 - 772 skipped_no_name_content = 0 - 773 - 774 number_of_streams = len(all_streams) - 775 current_stream_number = 0 - 776 # Calculate 1% of total number of streams - 777 # This is used to slow down the progress bar - 778 one_percent_number_of_streams = number_of_streams/100 - 779 start = timer() - 780 for stream_channel in all_streams: - 781 skip_stream = False - 782 current_stream_number += 1 - 783 - 784 # Show download progress every 1% of total number of streams - 785 if current_stream_number < one_percent_number_of_streams: - 786 progress( - 787 current_stream_number, - 788 number_of_streams, - 789 f"Processing {loading_stream_type} Streams" - 790 ) - 791 one_percent_number_of_streams *= 2 - 792 - 793 # Validate JSON scheme - 794 if self.validate_json: - 795 if loading_stream_type == self.series_type: - 796 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): - 797 print(stream_channel) - 798 elif loading_stream_type == self.live_type: - 799 if not schemaValidator(stream_channel, SchemaType.LIVE): - 800 print(stream_channel) - 801 else: - 802 # vod_type - 803 if not schemaValidator(stream_channel, SchemaType.VOD): - 804 print(stream_channel) - 805 - 806 # Skip if the name of the stream is empty - 807 if stream_channel["name"] == "": - 808 skip_stream = True - 809 skipped_no_name_content = skipped_no_name_content + 1 - 810 self._save_to_file_skipped_streams(stream_channel) - 811 - 812 # Skip if the user chose to hide adult streams - 813 if self.hide_adult_content and loading_stream_type == self.live_type: - 814 if "is_adult" in stream_channel: - 815 if stream_channel["is_adult"] == "1": - 816 skip_stream = True - 817 skipped_adult_content = skipped_adult_content + 1 - 818 self._save_to_file_skipped_streams(stream_channel) - 819 - 820 if not skip_stream: - 821 # Some channels have no group, - 822 # so let's add them to the catch all group - 823 if stream_channel["category_id"] is None: - 824 stream_channel["category_id"] = "9999" - 825 elif stream_channel["category_id"] != "1": - 826 pass - 827 - 828 # Find the first occurence of the group that the - 829 # Channel or Stream is pointing to - 830 the_group = next( - 831 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), - 832 None - 833 ) - 834 - 835 # Set group title - 836 if the_group is not None: - 837 group_title = the_group.name - 838 else: - 839 if loading_stream_type == self.live_type: - 840 group_title = self.live_catch_all_group.name - 841 the_group = self.live_catch_all_group - 842 elif loading_stream_type == self.vod_type: - 843 group_title = self.vod_catch_all_group.name - 844 the_group = self.vod_catch_all_group - 845 elif loading_stream_type == self.series_type: - 846 group_title = self.series_catch_all_group.name - 847 the_group = self.series_catch_all_group - 848 + 587 print(f"Attempting connection: ", end='') + 588 while i < 30: + 589 try: + 590 # Request authentication, wait 4 seconds maximum + 591 r = requests.get(url, timeout=(4), headers=self.connection_headers) + 592 i = 31 + 593 except requests.exceptions.ConnectionError: + 594 time.sleep(1) + 595 print(f"{i} ", end='',flush=True) + 596 i += 1 + 597 + 598 if r is not None: + 599 # If the answer is ok, process data and change state + 600 if r.ok: + 601 self.auth_data = r.json() + 602 self.authorization = { + 603 "username": self.auth_data["user_info"]["username"], + 604 "password": self.auth_data["user_info"]["password"] + 605 } + 606 # Mark connection authorized + 607 self.state["authenticated"] = True + 608 # Construct the base url for all requests + 609 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 610 # If there is a secure server connection, construct the base url SSL for all requests + 611 if "https_port" in self.auth_data["server_info"]: + 612 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ + 613 f"/player_api.php?username={self.username}&password={self.password}" + 614 else: + 615 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") + 616 else: + 617 print(f"\n{self.name}: Provider refused the connection") + 618 + 619 def _load_from_file(self, filename) -> dict: + 620 """Try to load the dictionary from file + 621 + 622 Args: + 623 filename ([type]): File name containing the data + 624 + 625 Returns: + 626 dict: Dictionary if found and no errors, None if file does not exists + 627 """ + 628 # Build the full path + 629 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 630 + 631 # If the cached file exists, attempt to load it + 632 if osp.isfile(full_filename): + 633 + 634 my_data = None + 635 + 636 # Get the enlapsed seconds since last file update + 637 file_age_sec = time.time() - osp.getmtime(full_filename) + 638 # If the file was updated less than the threshold time, + 639 # it means that the file is still fresh, we can load it. + 640 # Otherwise skip and return None to force a re-download + 641 if self.threshold_time_sec > file_age_sec: + 642 # Load the JSON data + 643 try: + 644 with open(full_filename, mode="r", encoding="utf-8") as myfile: + 645 my_data = json.load(myfile) + 646 if len(my_data) == 0: + 647 my_data = None + 648 except Exception as e: + 649 print(f" - Could not load from file `{full_filename}`: e=`{e}`") + 650 return my_data + 651 + 652 return None + 653 + 654 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 655 """Save a dictionary to file + 656 + 657 This function will overwrite the file if already exists + 658 + 659 Args: + 660 data_list (dict): Dictionary to save + 661 filename (str): Name of the file + 662 + 663 Returns: + 664 bool: True if successfull, False if error + 665 """ + 666 if data_list is not None: + 667 + 668 #Build the full path + 669 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 670 # If the path makes sense, save the file + 671 json_data = json.dumps(data_list, ensure_ascii=False) + 672 try: + 673 with open(full_filename, mode="wt", encoding="utf-8") as myfile: + 674 myfile.write(json_data) + 675 except Exception as e: + 676 print(f" - Could not save to file `{full_filename}`: e=`{e}`") + 677 return False + 678 + 679 return True + 680 else: + 681 return False + 682 + 683 def load_iptv(self) -> bool: + 684 """Load XTream IPTV + 685 + 686 - Add all Live TV to XTream.channels + 687 - Add all VOD to XTream.movies + 688 - Add all Series to XTream.series + 689 Series contains Seasons and Episodes. Those are not automatically + 690 retrieved from the server to reduce the loading time. + 691 - Add all groups to XTream.groups + 692 Groups are for all three channel types, Live TV, VOD, and Series + 693 + 694 Returns: + 695 bool: True if successfull, False if error + 696 """ + 697 # If pyxtream has not authenticated the connection, return empty + 698 if self.state["authenticated"] is False: + 699 print("Warning, cannot load steams since authorization failed") + 700 return False + 701 + 702 # If pyxtream has already loaded the data, skip and return success + 703 if self.state["loaded"] is True: + 704 print("Warning, data has already been loaded.") + 705 return True + 706 + 707 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): + 708 ## Get GROUPS + 709 + 710 # Try loading local file + 711 dt = 0 + 712 start = timer() + 713 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") + 714 # If file empty or does not exists, download it from remote + 715 if all_cat is None: + 716 # Load all Groups and save file locally + 717 all_cat = self._load_categories_from_provider(loading_stream_type) + 718 if all_cat is not None: + 719 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") + 720 dt = timer() - start + 721 + 722 # If we got the GROUPS data, show the statistics and load GROUPS + 723 if all_cat is not None: + 724 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") + 725 ## Add GROUPS to dictionaries + 726 + 727 # Add the catch-all-errors group + 728 if loading_stream_type == self.live_type: + 729 self.groups.append(self.live_catch_all_group) + 730 elif loading_stream_type == self.vod_type: + 731 self.groups.append(self.vod_catch_all_group) + 732 elif loading_stream_type == self.series_type: + 733 self.groups.append(self.series_catch_all_group) + 734 + 735 for cat_obj in all_cat: + 736 if schemaValidator(cat_obj, SchemaType.GROUP): + 737 # Create Group (Category) + 738 new_group = Group(cat_obj, loading_stream_type) + 739 # Add to xtream class + 740 self.groups.append(new_group) + 741 else: + 742 # Save what did not pass schema validation + 743 print(cat_obj) + 744 + 745 # Sort Categories + 746 self.groups.sort(key=lambda x: x.name) + 747 else: + 748 print(f" - Could not load {loading_stream_type} Groups") + 749 break + 750 + 751 ## Get Streams + 752 + 753 # Try loading local file + 754 dt = 0 + 755 start = timer() + 756 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") + 757 # If file empty or does not exists, download it from remote + 758 if all_streams is None: + 759 # Load all Streams and save file locally + 760 all_streams = self._load_streams_from_provider(loading_stream_type) + 761 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") + 762 dt = timer() - start + 763 + 764 # If we got the STREAMS data, show the statistics and load Streams + 765 if all_streams is not None: + 766 print( + 767 f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \ + 768 f"in {dt:.3f} seconds" + 769 ) + 770 ## Add Streams to dictionaries + 771 + 772 skipped_adult_content = 0 + 773 skipped_no_name_content = 0 + 774 + 775 number_of_streams = len(all_streams) + 776 current_stream_number = 0 + 777 # Calculate 1% of total number of streams + 778 # This is used to slow down the progress bar + 779 one_percent_number_of_streams = number_of_streams/100 + 780 start = timer() + 781 for stream_channel in all_streams: + 782 skip_stream = False + 783 current_stream_number += 1 + 784 + 785 # Show download progress every 1% of total number of streams + 786 if current_stream_number < one_percent_number_of_streams: + 787 progress( + 788 current_stream_number, + 789 number_of_streams, + 790 f"Processing {loading_stream_type} Streams" + 791 ) + 792 one_percent_number_of_streams *= 2 + 793 + 794 # Validate JSON scheme + 795 if self.validate_json: + 796 if loading_stream_type == self.series_type: + 797 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): + 798 print(stream_channel) + 799 elif loading_stream_type == self.live_type: + 800 if not schemaValidator(stream_channel, SchemaType.LIVE): + 801 print(stream_channel) + 802 else: + 803 # vod_type + 804 if not schemaValidator(stream_channel, SchemaType.VOD): + 805 print(stream_channel) + 806 + 807 # Skip if the name of the stream is empty + 808 if stream_channel["name"] == "": + 809 skip_stream = True + 810 skipped_no_name_content = skipped_no_name_content + 1 + 811 self._save_to_file_skipped_streams(stream_channel) + 812 + 813 # Skip if the user chose to hide adult streams + 814 if self.hide_adult_content and loading_stream_type == self.live_type: + 815 if "is_adult" in stream_channel: + 816 if stream_channel["is_adult"] == "1": + 817 skip_stream = True + 818 skipped_adult_content = skipped_adult_content + 1 + 819 self._save_to_file_skipped_streams(stream_channel) + 820 + 821 if not skip_stream: + 822 # Some channels have no group, + 823 # so let's add them to the catch all group + 824 if stream_channel["category_id"] is None: + 825 stream_channel["category_id"] = "9999" + 826 elif stream_channel["category_id"] != "1": + 827 pass + 828 + 829 # Find the first occurence of the group that the + 830 # Channel or Stream is pointing to + 831 the_group = next( + 832 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), + 833 None + 834 ) + 835 + 836 # Set group title + 837 if the_group is not None: + 838 group_title = the_group.name + 839 else: + 840 if loading_stream_type == self.live_type: + 841 group_title = self.live_catch_all_group.name + 842 the_group = self.live_catch_all_group + 843 elif loading_stream_type == self.vod_type: + 844 group_title = self.vod_catch_all_group.name + 845 the_group = self.vod_catch_all_group + 846 elif loading_stream_type == self.series_type: + 847 group_title = self.series_catch_all_group.name + 848 the_group = self.series_catch_all_group 849 - 850 if loading_stream_type == self.series_type: - 851 # Load all Series - 852 new_series = Serie(self, stream_channel) - 853 # To get all the Episodes for every Season of each - 854 # Series is very time consuming, we will only - 855 # populate the Series once the user click on the - 856 # Series, the Seasons and Episodes will be loaded - 857 # using x.getSeriesInfoByID() function - 858 - 859 else: - 860 new_channel = Channel( - 861 self, - 862 group_title, - 863 stream_channel - 864 ) - 865 - 866 if new_channel.group_id == "9999": - 867 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") - 868 - 869 # Save the new channel to the local list of channels - 870 if loading_stream_type == self.live_type: - 871 self.channels.append(new_channel) - 872 elif loading_stream_type == self.vod_type: - 873 self.movies.append(new_channel) - 874 if new_channel.age_days_from_added < 31: - 875 self.movies_30days.append(new_channel) - 876 if new_channel.age_days_from_added < 7: - 877 self.movies_7days.append(new_channel) - 878 else: - 879 self.series.append(new_series) - 880 - 881 # Add stream to the specific Group - 882 if the_group is not None: - 883 if loading_stream_type != self.series_type: - 884 the_group.channels.append(new_channel) - 885 else: - 886 the_group.series.append(new_series) - 887 else: - 888 print(f" - Group not found `{stream_channel['name']}`") - 889 print("\n") - 890 # Print information of which streams have been skipped - 891 if self.hide_adult_content: - 892 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") - 893 if skipped_no_name_content > 0: - 894 print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams") - 895 else: - 896 print(f" - Could not load {loading_stream_type} Streams") - 897 - 898 self.state["loaded"] = True - 899 - 900 def _save_to_file_skipped_streams(self, stream_channel: Channel): - 901 - 902 # Build the full path - 903 full_filename = osp.join(self.cache_path, "skipped_streams.json") - 904 - 905 # If the path makes sense, save the file - 906 json_data = json.dumps(stream_channel, ensure_ascii=False) - 907 try: - 908 with open(full_filename, mode="a", encoding="utf-8") as myfile: - 909 myfile.writelines(json_data) - 910 return True - 911 except Exception as e: - 912 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") - 913 return False - 914 - 915 def get_series_info_by_id(self, get_series: dict): - 916 """Get Seasons and Episodes for a Series - 917 - 918 Args: - 919 get_series (dict): Series dictionary - 920 """ - 921 - 922 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) - 923 - 924 if series_seasons["seasons"] is None: - 925 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] - 926 - 927 for series_info in series_seasons["seasons"]: - 928 season_name = series_info["name"] - 929 season_key = series_info['season_number'] - 930 season = Season(season_name) - 931 get_series.seasons[season_name] = season - 932 if "episodes" in series_seasons.keys(): - 933 for series_season in series_seasons["episodes"].keys(): - 934 for episode_info in series_seasons["episodes"][str(series_season)]: - 935 new_episode_channel = Episode( - 936 self, series_info, "Testing", episode_info - 937 ) - 938 season.episodes[episode_info["title"]] = new_episode_channel - 939 - 940 def _get_request(self, url: str, timeout: Tuple = (2, 15)): - 941 """Generic GET Request with Error handling - 942 - 943 Args: - 944 URL (str): The URL where to GET content - 945 timeout (Tuple, optional): Connection and Downloading Timeout. Defaults to (2,15). - 946 - 947 Returns: - 948 [type]: JSON dictionary of the loaded data, or None - 949 """ - 950 i = 0 - 951 while i < 10: - 952 time.sleep(1) - 953 try: - 954 r = requests.get(url, timeout=timeout, headers=self.connection_headers) - 955 i = 20 - 956 if r.status_code == 200: - 957 return r.json() - 958 except requests.exceptions.ConnectionError: - 959 print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") - 960 i += 1 - 961 - 962 except requests.exceptions.HTTPError: - 963 print(" - HTTP Error") - 964 i += 1 - 965 - 966 except requests.exceptions.TooManyRedirects: - 967 print(" - TooManyRedirects") - 968 i += 1 - 969 - 970 except requests.exceptions.ReadTimeout: - 971 print(" - Timeout while loading data") - 972 i += 1 - 973 - 974 return None - 975 - 976 # GET Stream Categories - 977 def _load_categories_from_provider(self, stream_type: str): - 978 """Get from provider all category for specific stream type from provider - 979 - 980 Args: - 981 stream_type (str): Stream type can be Live, VOD, Series - 982 - 983 Returns: - 984 [type]: JSON if successfull, otherwise None - 985 """ - 986 url = "" - 987 if stream_type == self.live_type: - 988 url = self.get_live_categories_URL() - 989 elif stream_type == self.vod_type: - 990 url = self.get_vod_cat_URL() - 991 elif stream_type == self.series_type: - 992 url = self.get_series_cat_URL() - 993 else: - 994 url = "" - 995 - 996 return self._get_request(url) - 997 - 998 # GET Streams - 999 def _load_streams_from_provider(self, stream_type: str): -1000 """Get from provider all streams for specific stream type -1001 -1002 Args: -1003 stream_type (str): Stream type can be Live, VOD, Series -1004 -1005 Returns: -1006 [type]: JSON if successfull, otherwise None -1007 """ -1008 url = "" -1009 if stream_type == self.live_type: -1010 url = self.get_live_streams_URL() -1011 elif stream_type == self.vod_type: -1012 url = self.get_vod_streams_URL() -1013 elif stream_type == self.series_type: -1014 url = self.get_series_URL() -1015 else: -1016 url = "" -1017 -1018 return self._get_request(url) -1019 -1020 # GET Streams by Category -1021 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): -1022 """Get from provider all streams for specific stream type with category/group ID -1023 -1024 Args: -1025 stream_type (str): Stream type can be Live, VOD, Series -1026 category_id ([type]): Category/Group ID. -1027 -1028 Returns: -1029 [type]: JSON if successfull, otherwise None -1030 """ -1031 url = "" -1032 -1033 if stream_type == self.live_type: -1034 url = self.get_live_streams_URL_by_category(category_id) -1035 elif stream_type == self.vod_type: -1036 url = self.get_vod_streams_URL_by_category(category_id) -1037 elif stream_type == self.series_type: -1038 url = self.get_series_URL_by_category(category_id) -1039 else: -1040 url = "" -1041 -1042 return self._get_request(url) -1043 -1044 # GET SERIES Info -1045 def _load_series_info_by_id_from_provider(self, series_id: str): -1046 """Gets informations about a Serie -1047 -1048 Args: -1049 series_id (str): Serie ID as described in Group -1050 -1051 Returns: -1052 [type]: JSON if successfull, otherwise None -1053 """ -1054 return self._get_request(self.get_series_info_URL_by_ID(series_id)) -1055 -1056 # The seasons array, might be filled or might be completely empty. -1057 # If it is not empty, it will contain the cover, overview and the air date -1058 # of the selected season. -1059 # In your APP if you want to display the series, you have to take that -1060 # from the episodes array. -1061 -1062 # GET VOD Info -1063 def vodInfoByID(self, vod_id): -1064 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) -1065 -1066 # GET short_epg for LIVE Streams (same as stalker portal, -1067 # prints the next X EPG that will play soon) -1068 def liveEpgByStream(self, stream_id): -1069 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) -1070 -1071 def liveEpgByStreamAndLimit(self, stream_id, limit): -1072 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) -1073 -1074 # GET ALL EPG for LIVE Streams (same as stalker portal, -1075 # but it will print all epg listings regardless of the day) -1076 def allLiveEpgByStream(self, stream_id): -1077 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) -1078 -1079 # Full EPG List for all Streams -1080 def allEpg(self): -1081 return self._get_request(self.get_all_epg_URL()) -1082 -1083 ## URL-builder methods -1084 def get_live_categories_URL(self) -> str: -1085 return f"{self.base_url}&action=get_live_categories" -1086 -1087 def get_live_streams_URL(self) -> str: -1088 return f"{self.base_url}&action=get_live_streams" -1089 -1090 def get_live_streams_URL_by_category(self, category_id) -> str: -1091 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" -1092 -1093 def get_vod_cat_URL(self) -> str: -1094 return f"{self.base_url}&action=get_vod_categories" -1095 -1096 def get_vod_streams_URL(self) -> str: -1097 return f"{self.base_url}&action=get_vod_streams" -1098 -1099 def get_vod_streams_URL_by_category(self, category_id) -> str: -1100 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" -1101 -1102 def get_series_cat_URL(self) -> str: -1103 return f"{self.base_url}&action=get_series_categories" -1104 -1105 def get_series_URL(self) -> str: -1106 return f"{self.base_url}&action=get_series" -1107 -1108 def get_series_URL_by_category(self, category_id) -> str: -1109 return f"{self.base_url}&action=get_series&category_id={category_id}" -1110 -1111 def get_series_info_URL_by_ID(self, series_id) -> str: -1112 return f"{self.base_url}&action=get_series_info&series_id={series_id}" -1113 -1114 def get_VOD_info_URL_by_ID(self, vod_id) -> str: -1115 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" -1116 -1117 def get_live_epg_URL_by_stream(self, stream_id) -> str: -1118 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" -1119 -1120 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: -1121 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" -1122 -1123 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: -1124 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" -1125 -1126 def get_all_epg_URL(self) -> str: -1127 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" + 850 + 851 if loading_stream_type == self.series_type: + 852 # Load all Series + 853 new_series = Serie(self, stream_channel) + 854 # To get all the Episodes for every Season of each + 855 # Series is very time consuming, we will only + 856 # populate the Series once the user click on the + 857 # Series, the Seasons and Episodes will be loaded + 858 # using x.getSeriesInfoByID() function + 859 + 860 else: + 861 new_channel = Channel( + 862 self, + 863 group_title, + 864 stream_channel + 865 ) + 866 + 867 if new_channel.group_id == "9999": + 868 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") + 869 + 870 # Save the new channel to the local list of channels + 871 if loading_stream_type == self.live_type: + 872 self.channels.append(new_channel) + 873 elif loading_stream_type == self.vod_type: + 874 self.movies.append(new_channel) + 875 if new_channel.age_days_from_added < 31: + 876 self.movies_30days.append(new_channel) + 877 if new_channel.age_days_from_added < 7: + 878 self.movies_7days.append(new_channel) + 879 else: + 880 self.series.append(new_series) + 881 + 882 # Add stream to the specific Group + 883 if the_group is not None: + 884 if loading_stream_type != self.series_type: + 885 the_group.channels.append(new_channel) + 886 else: + 887 the_group.series.append(new_series) + 888 else: + 889 print(f" - Group not found `{stream_channel['name']}`") + 890 print("\n") + 891 # Print information of which streams have been skipped + 892 if self.hide_adult_content: + 893 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") + 894 if skipped_no_name_content > 0: + 895 print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams") + 896 else: + 897 print(f" - Could not load {loading_stream_type} Streams") + 898 + 899 self.state["loaded"] = True + 900 + 901 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 902 + 903 # Build the full path + 904 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 905 + 906 # If the path makes sense, save the file + 907 json_data = json.dumps(stream_channel, ensure_ascii=False) + 908 try: + 909 with open(full_filename, mode="a", encoding="utf-8") as myfile: + 910 myfile.writelines(json_data) + 911 return True + 912 except Exception as e: + 913 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") + 914 return False + 915 + 916 def get_series_info_by_id(self, get_series: dict): + 917 """Get Seasons and Episodes for a Series + 918 + 919 Args: + 920 get_series (dict): Series dictionary + 921 """ + 922 + 923 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) + 924 + 925 if series_seasons["seasons"] is None: + 926 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] + 927 + 928 for series_info in series_seasons["seasons"]: + 929 season_name = series_info["name"] + 930 season_key = series_info['season_number'] + 931 season = Season(season_name) + 932 get_series.seasons[season_name] = season + 933 if "episodes" in series_seasons.keys(): + 934 for series_season in series_seasons["episodes"].keys(): + 935 for episode_info in series_seasons["episodes"][str(series_season)]: + 936 new_episode_channel = Episode( + 937 self, series_info, "Testing", episode_info + 938 ) + 939 season.episodes[episode_info["title"]] = new_episode_channel + 940 + 941 def _get_request(self, url: str, timeout: Tuple = (2, 15)): + 942 """Generic GET Request with Error handling + 943 + 944 Args: + 945 URL (str): The URL where to GET content + 946 timeout (Tuple, optional): Connection and Downloading Timeout. Defaults to (2,15). + 947 + 948 Returns: + 949 [type]: JSON dictionary of the loaded data, or None + 950 """ + 951 i = 0 + 952 while i < 10: + 953 time.sleep(1) + 954 try: + 955 r = requests.get(url, timeout=timeout, headers=self.connection_headers) + 956 i = 20 + 957 if r.status_code == 200: + 958 return r.json() + 959 except requests.exceptions.ConnectionError: + 960 print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") + 961 i += 1 + 962 + 963 except requests.exceptions.HTTPError: + 964 print(" - HTTP Error") + 965 i += 1 + 966 + 967 except requests.exceptions.TooManyRedirects: + 968 print(" - TooManyRedirects") + 969 i += 1 + 970 + 971 except requests.exceptions.ReadTimeout: + 972 print(" - Timeout while loading data") + 973 i += 1 + 974 + 975 return None + 976 + 977 # GET Stream Categories + 978 def _load_categories_from_provider(self, stream_type: str): + 979 """Get from provider all category for specific stream type from provider + 980 + 981 Args: + 982 stream_type (str): Stream type can be Live, VOD, Series + 983 + 984 Returns: + 985 [type]: JSON if successfull, otherwise None + 986 """ + 987 url = "" + 988 if stream_type == self.live_type: + 989 url = self.get_live_categories_URL() + 990 elif stream_type == self.vod_type: + 991 url = self.get_vod_cat_URL() + 992 elif stream_type == self.series_type: + 993 url = self.get_series_cat_URL() + 994 else: + 995 url = "" + 996 + 997 return self._get_request(url) + 998 + 999 # GET Streams +1000 def _load_streams_from_provider(self, stream_type: str): +1001 """Get from provider all streams for specific stream type +1002 +1003 Args: +1004 stream_type (str): Stream type can be Live, VOD, Series +1005 +1006 Returns: +1007 [type]: JSON if successfull, otherwise None +1008 """ +1009 url = "" +1010 if stream_type == self.live_type: +1011 url = self.get_live_streams_URL() +1012 elif stream_type == self.vod_type: +1013 url = self.get_vod_streams_URL() +1014 elif stream_type == self.series_type: +1015 url = self.get_series_URL() +1016 else: +1017 url = "" +1018 +1019 return self._get_request(url) +1020 +1021 # GET Streams by Category +1022 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1023 """Get from provider all streams for specific stream type with category/group ID +1024 +1025 Args: +1026 stream_type (str): Stream type can be Live, VOD, Series +1027 category_id ([type]): Category/Group ID. +1028 +1029 Returns: +1030 [type]: JSON if successfull, otherwise None +1031 """ +1032 url = "" +1033 +1034 if stream_type == self.live_type: +1035 url = self.get_live_streams_URL_by_category(category_id) +1036 elif stream_type == self.vod_type: +1037 url = self.get_vod_streams_URL_by_category(category_id) +1038 elif stream_type == self.series_type: +1039 url = self.get_series_URL_by_category(category_id) +1040 else: +1041 url = "" +1042 +1043 return self._get_request(url) +1044 +1045 # GET SERIES Info +1046 def _load_series_info_by_id_from_provider(self, series_id: str): +1047 """Gets informations about a Serie +1048 +1049 Args: +1050 series_id (str): Serie ID as described in Group +1051 +1052 Returns: +1053 [type]: JSON if successfull, otherwise None +1054 """ +1055 return self._get_request(self.get_series_info_URL_by_ID(series_id)) +1056 +1057 # The seasons array, might be filled or might be completely empty. +1058 # If it is not empty, it will contain the cover, overview and the air date +1059 # of the selected season. +1060 # In your APP if you want to display the series, you have to take that +1061 # from the episodes array. +1062 +1063 # GET VOD Info +1064 def vodInfoByID(self, vod_id): +1065 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) +1066 +1067 # GET short_epg for LIVE Streams (same as stalker portal, +1068 # prints the next X EPG that will play soon) +1069 def liveEpgByStream(self, stream_id): +1070 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) +1071 +1072 def liveEpgByStreamAndLimit(self, stream_id, limit): +1073 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) +1074 +1075 # GET ALL EPG for LIVE Streams (same as stalker portal, +1076 # but it will print all epg listings regardless of the day) +1077 def allLiveEpgByStream(self, stream_id): +1078 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) +1079 +1080 # Full EPG List for all Streams +1081 def allEpg(self): +1082 return self._get_request(self.get_all_epg_URL()) +1083 +1084 ## URL-builder methods +1085 def get_live_categories_URL(self) -> str: +1086 return f"{self.base_url}&action=get_live_categories" +1087 +1088 def get_live_streams_URL(self) -> str: +1089 return f"{self.base_url}&action=get_live_streams" +1090 +1091 def get_live_streams_URL_by_category(self, category_id) -> str: +1092 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" +1093 +1094 def get_vod_cat_URL(self) -> str: +1095 return f"{self.base_url}&action=get_vod_categories" +1096 +1097 def get_vod_streams_URL(self) -> str: +1098 return f"{self.base_url}&action=get_vod_streams" +1099 +1100 def get_vod_streams_URL_by_category(self, category_id) -> str: +1101 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" +1102 +1103 def get_series_cat_URL(self) -> str: +1104 return f"{self.base_url}&action=get_series_categories" +1105 +1106 def get_series_URL(self) -> str: +1107 return f"{self.base_url}&action=get_series" +1108 +1109 def get_series_URL_by_category(self, category_id) -> str: +1110 return f"{self.base_url}&action=get_series&category_id={category_id}" +1111 +1112 def get_series_info_URL_by_ID(self, series_id) -> str: +1113 return f"{self.base_url}&action=get_series_info&series_id={series_id}" +1114 +1115 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1116 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" +1117 +1118 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1119 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" +1120 +1121 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1122 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" +1123 +1124 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1125 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" +1126 +1127 def get_all_epg_URL(self) -> str: +1128 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" @@ -4190,36 +4192,37 @@

584 r = None 585 # Prepare the authentication url 586 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" -587 while i < 30: -588 try: -589 # Request authentication, wait 4 seconds maximum -590 r = requests.get(url, timeout=(4), headers=self.connection_headers) -591 i = 31 -592 except requests.exceptions.ConnectionError: -593 time.sleep(1) -594 print(i) -595 i += 1 -596 -597 if r is not None: -598 # If the answer is ok, process data and change state -599 if r.ok: -600 self.auth_data = r.json() -601 self.authorization = { -602 "username": self.auth_data["user_info"]["username"], -603 "password": self.auth_data["user_info"]["password"] -604 } -605 # Mark connection authorized -606 self.state["authenticated"] = True -607 # Construct the base url for all requests -608 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" -609 # If there is a secure server connection, construct the base url SSL for all requests -610 if "https_port" in self.auth_data["server_info"]: -611 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ -612 f"/player_api.php?username={self.username}&password={self.password}" -613 else: -614 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") -615 else: -616 print(f"{self.name}: Provider refused the connection") +587 print(f"Attempting connection: ", end='') +588 while i < 30: +589 try: +590 # Request authentication, wait 4 seconds maximum +591 r = requests.get(url, timeout=(4), headers=self.connection_headers) +592 i = 31 +593 except requests.exceptions.ConnectionError: +594 time.sleep(1) +595 print(f"{i} ", end='',flush=True) +596 i += 1 +597 +598 if r is not None: +599 # If the answer is ok, process data and change state +600 if r.ok: +601 self.auth_data = r.json() +602 self.authorization = { +603 "username": self.auth_data["user_info"]["username"], +604 "password": self.auth_data["user_info"]["password"] +605 } +606 # Mark connection authorized +607 self.state["authenticated"] = True +608 # Construct the base url for all requests +609 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" +610 # If there is a secure server connection, construct the base url SSL for all requests +611 if "https_port" in self.auth_data["server_info"]: +612 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ +613 f"/player_api.php?username={self.username}&password={self.password}" +614 else: +615 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") +616 else: +617 print(f"\n{self.name}: Provider refused the connection") @@ -4239,223 +4242,223 @@

-
682    def load_iptv(self) -> bool:
-683        """Load XTream IPTV
-684
-685        - Add all Live TV to XTream.channels
-686        - Add all VOD to XTream.movies
-687        - Add all Series to XTream.series
-688          Series contains Seasons and Episodes. Those are not automatically
-689          retrieved from the server to reduce the loading time.
-690        - Add all groups to XTream.groups
-691          Groups are for all three channel types, Live TV, VOD, and Series
-692
-693        Returns:
-694            bool: True if successfull, False if error
-695        """
-696        # If pyxtream has not authenticated the connection, return empty
-697        if self.state["authenticated"] is False:
-698            print("Warning, cannot load steams since authorization failed")
-699            return False
-700
-701        # If pyxtream has already loaded the data, skip and return success
-702        if self.state["loaded"] is True:
-703            print("Warning, data has already been loaded.")
-704            return True
-705
-706        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
-707            ## Get GROUPS
-708
-709            # Try loading local file
-710            dt = 0
-711            start = timer()
-712            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
-713            # If file empty or does not exists, download it from remote
-714            if all_cat is None:
-715                # Load all Groups and save file locally
-716                all_cat = self._load_categories_from_provider(loading_stream_type)
-717                if all_cat is not None:
-718                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
-719            dt = timer() - start
-720
-721            # If we got the GROUPS data, show the statistics and load GROUPS
-722            if all_cat is not None:
-723                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
-724                ## Add GROUPS to dictionaries
-725
-726                # Add the catch-all-errors group
-727                if loading_stream_type == self.live_type:
-728                    self.groups.append(self.live_catch_all_group)
-729                elif loading_stream_type == self.vod_type:
-730                    self.groups.append(self.vod_catch_all_group)
-731                elif loading_stream_type == self.series_type:
-732                    self.groups.append(self.series_catch_all_group)
-733
-734                for cat_obj in all_cat:
-735                    if schemaValidator(cat_obj, SchemaType.GROUP):
-736                        # Create Group (Category)
-737                        new_group = Group(cat_obj, loading_stream_type)
-738                        #  Add to xtream class
-739                        self.groups.append(new_group)
-740                    else:
-741                        # Save what did not pass schema validation
-742                        print(cat_obj)
-743
-744                # Sort Categories
-745                self.groups.sort(key=lambda x: x.name)
-746            else:
-747                print(f" - Could not load {loading_stream_type} Groups")
-748                break
-749
-750            ## Get Streams
-751
-752            # Try loading local file
-753            dt = 0
-754            start = timer()
-755            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
-756            # If file empty or does not exists, download it from remote
-757            if all_streams is None:
-758                # Load all Streams and save file locally
-759                all_streams = self._load_streams_from_provider(loading_stream_type)
-760                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
-761            dt = timer() - start
-762
-763            # If we got the STREAMS data, show the statistics and load Streams
-764            if all_streams is not None:
-765                print(
-766                    f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \
-767                    f"in {dt:.3f} seconds"
-768                    )
-769                ## Add Streams to dictionaries
-770
-771                skipped_adult_content = 0
-772                skipped_no_name_content = 0
-773
-774                number_of_streams = len(all_streams)
-775                current_stream_number = 0
-776                # Calculate 1% of total number of streams
-777                # This is used to slow down the progress bar
-778                one_percent_number_of_streams = number_of_streams/100
-779                start = timer()
-780                for stream_channel in all_streams:
-781                    skip_stream = False
-782                    current_stream_number += 1
-783
-784                    # Show download progress every 1% of total number of streams
-785                    if current_stream_number < one_percent_number_of_streams:
-786                        progress(
-787                            current_stream_number,
-788                            number_of_streams,
-789                            f"Processing {loading_stream_type} Streams"
-790                            )
-791                        one_percent_number_of_streams *= 2
-792
-793                    # Validate JSON scheme
-794                    if self.validate_json:
-795                        if loading_stream_type == self.series_type:
-796                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
-797                                print(stream_channel)
-798                        elif loading_stream_type == self.live_type:
-799                            if not schemaValidator(stream_channel, SchemaType.LIVE):
-800                                print(stream_channel)
-801                        else:
-802                            # vod_type
-803                            if not schemaValidator(stream_channel, SchemaType.VOD):
-804                                print(stream_channel)
-805
-806                    # Skip if the name of the stream is empty
-807                    if stream_channel["name"] == "":
-808                        skip_stream = True
-809                        skipped_no_name_content = skipped_no_name_content + 1
-810                        self._save_to_file_skipped_streams(stream_channel)
-811
-812                    # Skip if the user chose to hide adult streams
-813                    if self.hide_adult_content and loading_stream_type == self.live_type:
-814                        if "is_adult" in stream_channel:
-815                            if stream_channel["is_adult"] == "1":
-816                                skip_stream = True
-817                                skipped_adult_content = skipped_adult_content + 1
-818                                self._save_to_file_skipped_streams(stream_channel)
-819
-820                    if not skip_stream:
-821                        # Some channels have no group,
-822                        # so let's add them to the catch all group
-823                        if stream_channel["category_id"] is None:
-824                            stream_channel["category_id"] = "9999"
-825                        elif stream_channel["category_id"] != "1":
-826                            pass
-827
-828                        # Find the first occurence of the group that the
-829                        # Channel or Stream is pointing to
-830                        the_group = next(
-831                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
-832                            None
-833                        )
-834
-835                        # Set group title
-836                        if the_group is not None:
-837                            group_title = the_group.name
-838                        else:
-839                            if loading_stream_type == self.live_type:
-840                                group_title = self.live_catch_all_group.name
-841                                the_group = self.live_catch_all_group
-842                            elif loading_stream_type == self.vod_type:
-843                                group_title = self.vod_catch_all_group.name
-844                                the_group = self.vod_catch_all_group
-845                            elif loading_stream_type == self.series_type:
-846                                group_title = self.series_catch_all_group.name
-847                                the_group = self.series_catch_all_group
-848
+            
683    def load_iptv(self) -> bool:
+684        """Load XTream IPTV
+685
+686        - Add all Live TV to XTream.channels
+687        - Add all VOD to XTream.movies
+688        - Add all Series to XTream.series
+689          Series contains Seasons and Episodes. Those are not automatically
+690          retrieved from the server to reduce the loading time.
+691        - Add all groups to XTream.groups
+692          Groups are for all three channel types, Live TV, VOD, and Series
+693
+694        Returns:
+695            bool: True if successfull, False if error
+696        """
+697        # If pyxtream has not authenticated the connection, return empty
+698        if self.state["authenticated"] is False:
+699            print("Warning, cannot load steams since authorization failed")
+700            return False
+701
+702        # If pyxtream has already loaded the data, skip and return success
+703        if self.state["loaded"] is True:
+704            print("Warning, data has already been loaded.")
+705            return True
+706
+707        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
+708            ## Get GROUPS
+709
+710            # Try loading local file
+711            dt = 0
+712            start = timer()
+713            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
+714            # If file empty or does not exists, download it from remote
+715            if all_cat is None:
+716                # Load all Groups and save file locally
+717                all_cat = self._load_categories_from_provider(loading_stream_type)
+718                if all_cat is not None:
+719                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
+720            dt = timer() - start
+721
+722            # If we got the GROUPS data, show the statistics and load GROUPS
+723            if all_cat is not None:
+724                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
+725                ## Add GROUPS to dictionaries
+726
+727                # Add the catch-all-errors group
+728                if loading_stream_type == self.live_type:
+729                    self.groups.append(self.live_catch_all_group)
+730                elif loading_stream_type == self.vod_type:
+731                    self.groups.append(self.vod_catch_all_group)
+732                elif loading_stream_type == self.series_type:
+733                    self.groups.append(self.series_catch_all_group)
+734
+735                for cat_obj in all_cat:
+736                    if schemaValidator(cat_obj, SchemaType.GROUP):
+737                        # Create Group (Category)
+738                        new_group = Group(cat_obj, loading_stream_type)
+739                        #  Add to xtream class
+740                        self.groups.append(new_group)
+741                    else:
+742                        # Save what did not pass schema validation
+743                        print(cat_obj)
+744
+745                # Sort Categories
+746                self.groups.sort(key=lambda x: x.name)
+747            else:
+748                print(f" - Could not load {loading_stream_type} Groups")
+749                break
+750
+751            ## Get Streams
+752
+753            # Try loading local file
+754            dt = 0
+755            start = timer()
+756            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
+757            # If file empty or does not exists, download it from remote
+758            if all_streams is None:
+759                # Load all Streams and save file locally
+760                all_streams = self._load_streams_from_provider(loading_stream_type)
+761                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
+762            dt = timer() - start
+763
+764            # If we got the STREAMS data, show the statistics and load Streams
+765            if all_streams is not None:
+766                print(
+767                    f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \
+768                    f"in {dt:.3f} seconds"
+769                    )
+770                ## Add Streams to dictionaries
+771
+772                skipped_adult_content = 0
+773                skipped_no_name_content = 0
+774
+775                number_of_streams = len(all_streams)
+776                current_stream_number = 0
+777                # Calculate 1% of total number of streams
+778                # This is used to slow down the progress bar
+779                one_percent_number_of_streams = number_of_streams/100
+780                start = timer()
+781                for stream_channel in all_streams:
+782                    skip_stream = False
+783                    current_stream_number += 1
+784
+785                    # Show download progress every 1% of total number of streams
+786                    if current_stream_number < one_percent_number_of_streams:
+787                        progress(
+788                            current_stream_number,
+789                            number_of_streams,
+790                            f"Processing {loading_stream_type} Streams"
+791                            )
+792                        one_percent_number_of_streams *= 2
+793
+794                    # Validate JSON scheme
+795                    if self.validate_json:
+796                        if loading_stream_type == self.series_type:
+797                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
+798                                print(stream_channel)
+799                        elif loading_stream_type == self.live_type:
+800                            if not schemaValidator(stream_channel, SchemaType.LIVE):
+801                                print(stream_channel)
+802                        else:
+803                            # vod_type
+804                            if not schemaValidator(stream_channel, SchemaType.VOD):
+805                                print(stream_channel)
+806
+807                    # Skip if the name of the stream is empty
+808                    if stream_channel["name"] == "":
+809                        skip_stream = True
+810                        skipped_no_name_content = skipped_no_name_content + 1
+811                        self._save_to_file_skipped_streams(stream_channel)
+812
+813                    # Skip if the user chose to hide adult streams
+814                    if self.hide_adult_content and loading_stream_type == self.live_type:
+815                        if "is_adult" in stream_channel:
+816                            if stream_channel["is_adult"] == "1":
+817                                skip_stream = True
+818                                skipped_adult_content = skipped_adult_content + 1
+819                                self._save_to_file_skipped_streams(stream_channel)
+820
+821                    if not skip_stream:
+822                        # Some channels have no group,
+823                        # so let's add them to the catch all group
+824                        if stream_channel["category_id"] is None:
+825                            stream_channel["category_id"] = "9999"
+826                        elif stream_channel["category_id"] != "1":
+827                            pass
+828
+829                        # Find the first occurence of the group that the
+830                        # Channel or Stream is pointing to
+831                        the_group = next(
+832                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
+833                            None
+834                        )
+835
+836                        # Set group title
+837                        if the_group is not None:
+838                            group_title = the_group.name
+839                        else:
+840                            if loading_stream_type == self.live_type:
+841                                group_title = self.live_catch_all_group.name
+842                                the_group = self.live_catch_all_group
+843                            elif loading_stream_type == self.vod_type:
+844                                group_title = self.vod_catch_all_group.name
+845                                the_group = self.vod_catch_all_group
+846                            elif loading_stream_type == self.series_type:
+847                                group_title = self.series_catch_all_group.name
+848                                the_group = self.series_catch_all_group
 849
-850                        if loading_stream_type == self.series_type:
-851                            # Load all Series
-852                            new_series = Serie(self, stream_channel)
-853                            # To get all the Episodes for every Season of each
-854                            # Series is very time consuming, we will only
-855                            # populate the Series once the user click on the
-856                            # Series, the Seasons and Episodes will be loaded
-857                            # using x.getSeriesInfoByID() function
-858
-859                        else:
-860                            new_channel = Channel(
-861                                self,
-862                                group_title,
-863                                stream_channel
-864                            )
-865
-866                        if new_channel.group_id == "9999":
-867                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
-868
-869                        # Save the new channel to the local list of channels
-870                        if loading_stream_type == self.live_type:
-871                            self.channels.append(new_channel)
-872                        elif loading_stream_type == self.vod_type:
-873                            self.movies.append(new_channel)
-874                            if new_channel.age_days_from_added < 31:
-875                                self.movies_30days.append(new_channel)
-876                            if new_channel.age_days_from_added < 7:
-877                                self.movies_7days.append(new_channel)
-878                        else:
-879                            self.series.append(new_series)
-880
-881                        # Add stream to the specific Group
-882                        if the_group is not None:
-883                            if loading_stream_type != self.series_type:
-884                                the_group.channels.append(new_channel)
-885                            else:
-886                                the_group.series.append(new_series)
-887                        else:
-888                            print(f" - Group not found `{stream_channel['name']}`")
-889                print("\n")
-890                # Print information of which streams have been skipped
-891                if self.hide_adult_content:
-892                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
-893                if skipped_no_name_content > 0:
-894                    print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams")
-895            else:
-896                print(f" - Could not load {loading_stream_type} Streams")
-897
-898            self.state["loaded"] = True
+850
+851                        if loading_stream_type == self.series_type:
+852                            # Load all Series
+853                            new_series = Serie(self, stream_channel)
+854                            # To get all the Episodes for every Season of each
+855                            # Series is very time consuming, we will only
+856                            # populate the Series once the user click on the
+857                            # Series, the Seasons and Episodes will be loaded
+858                            # using x.getSeriesInfoByID() function
+859
+860                        else:
+861                            new_channel = Channel(
+862                                self,
+863                                group_title,
+864                                stream_channel
+865                            )
+866
+867                        if new_channel.group_id == "9999":
+868                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
+869
+870                        # Save the new channel to the local list of channels
+871                        if loading_stream_type == self.live_type:
+872                            self.channels.append(new_channel)
+873                        elif loading_stream_type == self.vod_type:
+874                            self.movies.append(new_channel)
+875                            if new_channel.age_days_from_added < 31:
+876                                self.movies_30days.append(new_channel)
+877                            if new_channel.age_days_from_added < 7:
+878                                self.movies_7days.append(new_channel)
+879                        else:
+880                            self.series.append(new_series)
+881
+882                        # Add stream to the specific Group
+883                        if the_group is not None:
+884                            if loading_stream_type != self.series_type:
+885                                the_group.channels.append(new_channel)
+886                            else:
+887                                the_group.series.append(new_series)
+888                        else:
+889                            print(f" - Group not found `{stream_channel['name']}`")
+890                print("\n")
+891                # Print information of which streams have been skipped
+892                if self.hide_adult_content:
+893                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
+894                if skipped_no_name_content > 0:
+895                    print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams")
+896            else:
+897                print(f" - Could not load {loading_stream_type} Streams")
+898
+899            self.state["loaded"] = True
 
@@ -4488,30 +4491,30 @@

-
915    def get_series_info_by_id(self, get_series: dict):
-916        """Get Seasons and Episodes for a Series
-917
-918        Args:
-919            get_series (dict): Series dictionary
-920        """
-921
-922        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
-923
-924        if series_seasons["seasons"] is None:
-925            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
-926
-927        for series_info in series_seasons["seasons"]:
-928            season_name = series_info["name"]
-929            season_key = series_info['season_number']
-930            season = Season(season_name)
-931            get_series.seasons[season_name] = season
-932            if "episodes" in series_seasons.keys():
-933                for series_season in series_seasons["episodes"].keys():
-934                    for episode_info in series_seasons["episodes"][str(series_season)]:
-935                        new_episode_channel = Episode(
-936                            self, series_info, "Testing", episode_info
-937                        )
-938                        season.episodes[episode_info["title"]] = new_episode_channel
+            
916    def get_series_info_by_id(self, get_series: dict):
+917        """Get Seasons and Episodes for a Series
+918
+919        Args:
+920            get_series (dict): Series dictionary
+921        """
+922
+923        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
+924
+925        if series_seasons["seasons"] is None:
+926            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
+927
+928        for series_info in series_seasons["seasons"]:
+929            season_name = series_info["name"]
+930            season_key = series_info['season_number']
+931            season = Season(season_name)
+932            get_series.seasons[season_name] = season
+933            if "episodes" in series_seasons.keys():
+934                for series_season in series_seasons["episodes"].keys():
+935                    for episode_info in series_seasons["episodes"][str(series_season)]:
+936                        new_episode_channel = Episode(
+937                            self, series_info, "Testing", episode_info
+938                        )
+939                        season.episodes[episode_info["title"]] = new_episode_channel
 
@@ -4534,8 +4537,8 @@

-
1063    def vodInfoByID(self, vod_id):
-1064        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
+            
1064    def vodInfoByID(self, vod_id):
+1065        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
 
@@ -4553,8 +4556,8 @@

-
1068    def liveEpgByStream(self, stream_id):
-1069        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
+            
1069    def liveEpgByStream(self, stream_id):
+1070        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
 
@@ -4572,8 +4575,8 @@

-
1071    def liveEpgByStreamAndLimit(self, stream_id, limit):
-1072        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
+            
1072    def liveEpgByStreamAndLimit(self, stream_id, limit):
+1073        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
 
@@ -4591,8 +4594,8 @@

-
1076    def allLiveEpgByStream(self, stream_id):
-1077        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
+            
1077    def allLiveEpgByStream(self, stream_id):
+1078        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
 
@@ -4610,8 +4613,8 @@

-
1080    def allEpg(self):
-1081        return self._get_request(self.get_all_epg_URL())
+            
1081    def allEpg(self):
+1082        return self._get_request(self.get_all_epg_URL())
 
@@ -4629,8 +4632,8 @@

-
1084    def get_live_categories_URL(self) -> str:
-1085        return f"{self.base_url}&action=get_live_categories"
+            
1085    def get_live_categories_URL(self) -> str:
+1086        return f"{self.base_url}&action=get_live_categories"
 
@@ -4648,8 +4651,8 @@

-
1087    def get_live_streams_URL(self) -> str:
-1088        return f"{self.base_url}&action=get_live_streams"
+            
1088    def get_live_streams_URL(self) -> str:
+1089        return f"{self.base_url}&action=get_live_streams"
 
@@ -4667,8 +4670,8 @@

-
1090    def get_live_streams_URL_by_category(self, category_id) -> str:
-1091        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
+            
1091    def get_live_streams_URL_by_category(self, category_id) -> str:
+1092        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
 
@@ -4686,8 +4689,8 @@

-
1093    def get_vod_cat_URL(self) -> str:
-1094        return f"{self.base_url}&action=get_vod_categories"
+            
1094    def get_vod_cat_URL(self) -> str:
+1095        return f"{self.base_url}&action=get_vod_categories"
 
@@ -4705,8 +4708,8 @@

-
1096    def get_vod_streams_URL(self) -> str:
-1097        return f"{self.base_url}&action=get_vod_streams"
+            
1097    def get_vod_streams_URL(self) -> str:
+1098        return f"{self.base_url}&action=get_vod_streams"
 
@@ -4724,8 +4727,8 @@

-
1099    def get_vod_streams_URL_by_category(self, category_id) -> str:
-1100        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
+            
1100    def get_vod_streams_URL_by_category(self, category_id) -> str:
+1101        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
 
@@ -4743,8 +4746,8 @@

-
1102    def get_series_cat_URL(self) -> str:
-1103        return f"{self.base_url}&action=get_series_categories"
+            
1103    def get_series_cat_URL(self) -> str:
+1104        return f"{self.base_url}&action=get_series_categories"
 
@@ -4762,8 +4765,8 @@

-
1105    def get_series_URL(self) -> str:
-1106        return f"{self.base_url}&action=get_series"
+            
1106    def get_series_URL(self) -> str:
+1107        return f"{self.base_url}&action=get_series"
 
@@ -4781,8 +4784,8 @@

-
1108    def get_series_URL_by_category(self, category_id) -> str:
-1109        return f"{self.base_url}&action=get_series&category_id={category_id}"
+            
1109    def get_series_URL_by_category(self, category_id) -> str:
+1110        return f"{self.base_url}&action=get_series&category_id={category_id}"
 
@@ -4800,8 +4803,8 @@

-
1111    def get_series_info_URL_by_ID(self, series_id) -> str:
-1112        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
+            
1112    def get_series_info_URL_by_ID(self, series_id) -> str:
+1113        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
 
@@ -4819,8 +4822,8 @@

-
1114    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
-1115        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
+            
1115    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
+1116        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
 
@@ -4838,8 +4841,8 @@

-
1117    def get_live_epg_URL_by_stream(self, stream_id) -> str:
-1118        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
+            
1118    def get_live_epg_URL_by_stream(self, stream_id) -> str:
+1119        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
 
@@ -4857,8 +4860,8 @@

-
1120    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
-1121        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
+            
1121    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
+1122        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
 
@@ -4876,8 +4879,8 @@

-
1123    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
-1124        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
+            
1124    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
+1125        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
 
@@ -4895,8 +4898,8 @@

-
1126    def get_all_epg_URL(self) -> str:
-1127        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
+            
1127    def get_all_epg_URL(self) -> str:
+1128        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
 
diff --git a/doc/pyxtream/version.html b/doc/pyxtream/version.html index 1ee7185..e4e3f8b 100644 --- a/doc/pyxtream/version.html +++ b/doc/pyxtream/version.html @@ -51,7 +51,7 @@

-
1__version__ = '0.7.0'
+                        
1__version__ = '0.7.1'
 2__author__ = 'Claudio Olmi'
 3__author_email__ = 'superolmo2@gmail.com'
 
From 16d1235dc7820e7ec99c81e66602ff4805507f41 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 00:40:04 -0500 Subject: [PATCH 07/41] Update --- PYPI.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/PYPI.md b/PYPI.md index 24ecfb7..4baaad5 100644 --- a/PYPI.md +++ b/PYPI.md @@ -1,3 +1,7 @@ +# Build docs +rm -rf doc +pdoc pyxtream -o doc + # Build PIP Module python3 setup.py sdist bdist_wheel @@ -7,12 +11,6 @@ twine upload dist/pyxtream-0.7* # Optional Local Install python3 -m pip install dist/pyxtream-0.7 -# GitHub Documentation - -## Build docs -rm -rf doc -pdoc pyxtream - # Record TS Video ffmpeg -y -i "(iptv url)" -c:v copy -c:a copy -map 0:v -map 0:a -t 00:00:30 "myrecording.ts" >"mylog.log" 2>&1 From b8bff9b7084522af74878c26a9527f205f392362 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Wed, 22 May 2024 13:02:54 -0500 Subject: [PATCH 08/41] Moved doc to docs --- PYPI.md | 2 +- {doc => docs}/index.html | 0 {doc => docs}/pyxtream.html | 0 {doc => docs}/pyxtream/progress.html | 0 {doc => docs}/pyxtream/pyxtream.html | 0 {doc => docs}/pyxtream/rest_api.html | 0 {doc => docs}/pyxtream/schemaValidator.html | 0 {doc => docs}/pyxtream/version.html | 0 {doc => docs}/search.js | 0 9 files changed, 1 insertion(+), 1 deletion(-) rename {doc => docs}/index.html (100%) rename {doc => docs}/pyxtream.html (100%) rename {doc => docs}/pyxtream/progress.html (100%) rename {doc => docs}/pyxtream/pyxtream.html (100%) rename {doc => docs}/pyxtream/rest_api.html (100%) rename {doc => docs}/pyxtream/schemaValidator.html (100%) rename {doc => docs}/pyxtream/version.html (100%) rename {doc => docs}/search.js (100%) diff --git a/PYPI.md b/PYPI.md index 4baaad5..efdd5d0 100644 --- a/PYPI.md +++ b/PYPI.md @@ -1,6 +1,6 @@ # Build docs rm -rf doc -pdoc pyxtream -o doc +pdoc pyxtream -o docs # Build PIP Module python3 setup.py sdist bdist_wheel diff --git a/doc/index.html b/docs/index.html similarity index 100% rename from doc/index.html rename to docs/index.html diff --git a/doc/pyxtream.html b/docs/pyxtream.html similarity index 100% rename from doc/pyxtream.html rename to docs/pyxtream.html diff --git a/doc/pyxtream/progress.html b/docs/pyxtream/progress.html similarity index 100% rename from doc/pyxtream/progress.html rename to docs/pyxtream/progress.html diff --git a/doc/pyxtream/pyxtream.html b/docs/pyxtream/pyxtream.html similarity index 100% rename from doc/pyxtream/pyxtream.html rename to docs/pyxtream/pyxtream.html diff --git a/doc/pyxtream/rest_api.html b/docs/pyxtream/rest_api.html similarity index 100% rename from doc/pyxtream/rest_api.html rename to docs/pyxtream/rest_api.html diff --git a/doc/pyxtream/schemaValidator.html b/docs/pyxtream/schemaValidator.html similarity index 100% rename from doc/pyxtream/schemaValidator.html rename to docs/pyxtream/schemaValidator.html diff --git a/doc/pyxtream/version.html b/docs/pyxtream/version.html similarity index 100% rename from doc/pyxtream/version.html rename to docs/pyxtream/version.html diff --git a/doc/search.js b/docs/search.js similarity index 100% rename from doc/search.js rename to docs/search.js From 18e3818b0a60f65f6992fbe1e38b3cb0a330a411 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Mon, 2 Sep 2024 00:17:19 -0500 Subject: [PATCH 09/41] More adjustments --- .gitignore | 1 + README.md | 7 +- docs/pyxtream.html | 4 +- docs/pyxtream/progress.html | 4 +- docs/pyxtream/pyxtream.html | 4113 +++++++++++++++------------- docs/pyxtream/rest_api.html | 419 +-- docs/pyxtream/schemaValidator.html | 10 +- docs/pyxtream/version.html | 4 +- docs/search.js | 2 +- functional_test.py | 47 +- pyxtream/html/index.html | 246 +- pyxtream/pyxtream.py | 259 +- 12 files changed, 2769 insertions(+), 2347 deletions(-) diff --git a/.gitignore b/.gitignore index b6c4e54..14fa750 100644 --- a/.gitignore +++ b/.gitignore @@ -85,6 +85,7 @@ ipython_config.py # pyenv .python-version +myenv # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/README.md b/README.md index b869167..598626a 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,11 @@ python3 functional_test.py The functional test will allow you to authenticate on startup, load and search streams. If Flask is installed, a simple website will be available at http://localhost:5000 to allow you to search and play streams. -### Interesting Work by somebody else +## Interesting Work by somebody else + +- xtreamPOC - Project is a Proof of Concept (POC) that leverages pyxtream, MPV, and NiceGUI to demonstrate the use of Xtream Portal Codes. + +## So far there is no ready to use Transport Stream library for playing live stream. @@ -112,6 +116,7 @@ xTream.movies[{},{},...] | Date | Version | Description | | ----------- | -----| ----------- | +| 2024-09-02 | 0.7.2 | -
-
- | | 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
- Fixed provider name in functional_test
- Improved print out of connection attempts
- Added method to read latest changes in functional_test | 2023-11-08 | 0.7.0 | - Added Schema Validator
- Added Channel Age
- Added list of movies added in the last 30 and 7 days
- Updated code based on PyLint
- Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| | 2023-02-06 | 0.6.0 | - Added methods to change connection header, to turn off reload timer, and to enable/disable Flask debug mode
- Added a loop when attempting to connect to the provider
- Cleaned up some print lines| diff --git a/docs/pyxtream.html b/docs/pyxtream.html index 3c2fc21..135d34b 100644 --- a/docs/pyxtream.html +++ b/docs/pyxtream.html @@ -3,14 +3,14 @@ - + pyxtream API documentation - +
@@ -2826,789 +2907,870 @@

343 provider_url (str): URL of the IPTV provider 344 headers (dict): Requests Headers 345 hide_adult_content(bool, optional): When `True` hide stream that are marked for adult - 346 cache_path (str, optional): Location where to save loaded files. Defaults to empty string. - 347 reload_time_sec (int, optional): Number of seconds before automatic reloading (-1 to turn it OFF) - 348 debug_flask (bool, optional): Enable the debug mode in Flask - 349 validate_json (bool, optional): Check Xtream API provided JSON for validity - 350 - 351 Returns: XTream Class Instance + 346 cache_path (str, optional): Location where to save loaded files. + 347 Defaults to empty string. + 348 reload_time_sec (int, optional): Number of seconds before automatic reloading + 349 (-1 to turn it OFF) + 350 debug_flask (bool, optional): Enable the debug mode in Flask + 351 validate_json (bool, optional): Check Xtream API provided JSON for validity 352 - 353 - Note 1: If it fails to authorize with provided username and password, - 354 auth_data will be an empty dictionary. - 355 - Note 2: The JSON validation option will take considerable amount of time and it should be - 356 used only as a debug tool. The Xtream API JSON from the provider passes through a schema - 357 that represent the best available understanding of how the Xtream API works. - 358 """ - 359 self.server = provider_url - 360 self.username = provider_username - 361 self.password = provider_password - 362 self.name = provider_name - 363 self.cache_path = cache_path - 364 self.hide_adult_content = hide_adult_content - 365 self.threshold_time_sec = reload_time_sec - 366 self.validate_json = validate_json - 367 - 368 # get the pyxtream local path - 369 self.app_fullpath = osp.dirname(osp.realpath(__file__)) + 353 Returns: XTream Class Instance + 354 + 355 - Note 1: If it fails to authorize with provided username and password, + 356 auth_data will be an empty dictionary. + 357 - Note 2: The JSON validation option will take considerable amount of time and it should be + 358 used only as a debug tool. The Xtream API JSON from the provider passes through a + 359 schema that represent the best available understanding of how the Xtream API + 360 works. + 361 """ + 362 self.server = provider_url + 363 self.username = provider_username + 364 self.password = provider_password + 365 self.name = provider_name + 366 self.cache_path = cache_path + 367 self.hide_adult_content = hide_adult_content + 368 self.threshold_time_sec = reload_time_sec + 369 self.validate_json = validate_json 370 - 371 # prepare location of local html template - 372 self.html_template_folder = osp.join(self.app_fullpath,"html") + 371 # get the pyxtream local path + 372 self.app_fullpath = osp.dirname(osp.realpath(__file__)) 373 - 374 # if the cache_path is specified, test that it is a directory - 375 if self.cache_path != "": - 376 # If the cache_path is not a directory, clear it - 377 if not osp.isdir(self.cache_path): - 378 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") - 379 self.cache_path == "" - 380 - 381 # If the cache_path is still empty, use default - 382 if self.cache_path == "": - 383 self.cache_path = osp.expanduser("~/.xtream-cache/") - 384 if not osp.isdir(self.cache_path): - 385 makedirs(self.cache_path, exist_ok=True) - 386 print(f"pyxtream cache path located at {self.cache_path}") - 387 - 388 if headers is not None: - 389 self.connection_headers = headers - 390 else: - 391 self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"} - 392 - 393 self.authenticate() - 394 - 395 if self.threshold_time_sec > 0: - 396 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") - 397 else: - 398 print("Reload timer is OFF") - 399 - 400 if self.state['authenticated']: - 401 if USE_FLASK: - 402 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) - 403 self.flaskapp.start() - 404 - 405 def search_stream(self, keyword: str, ignore_case: bool = True, return_type: str = "LIST") -> List: - 406 """Search for streams + 374 # prepare location of local html template + 375 self.html_template_folder = osp.join(self.app_fullpath,"html") + 376 + 377 # if the cache_path is specified, test that it is a directory + 378 if self.cache_path != "": + 379 # If the cache_path is not a directory, clear it + 380 if not osp.isdir(self.cache_path): + 381 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") + 382 self.cache_path == "" + 383 + 384 # If the cache_path is still empty, use default + 385 if self.cache_path == "": + 386 self.cache_path = osp.expanduser("~/.xtream-cache/") + 387 if not osp.isdir(self.cache_path): + 388 makedirs(self.cache_path, exist_ok=True) + 389 print(f"pyxtream cache path located at {self.cache_path}") + 390 + 391 if headers is not None: + 392 self.connection_headers = headers + 393 else: + 394 self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"} + 395 + 396 self.authenticate() + 397 + 398 if self.threshold_time_sec > 0: + 399 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") + 400 else: + 401 print("Reload timer is OFF") + 402 + 403 if self.state['authenticated']: + 404 if USE_FLASK: + 405 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) + 406 self.flaskapp.start() 407 - 408 Args: - 409 keyword (str): Keyword to search for. Supports REGEX - 410 ignore_case (bool, optional): True to ignore case during search. Defaults to "True". - 411 return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". - 412 - 413 Returns: - 414 List: List with all the results, it could be empty. Each result - 415 """ - 416 - 417 search_result = [] - 418 - 419 if ignore_case: - 420 regex = re.compile(keyword, re.IGNORECASE) - 421 else: - 422 regex = re.compile(keyword) + 408 def search_stream(self, keyword: str, + 409 ignore_case: bool = True, + 410 return_type: str = "LIST", + 411 stream_type: list = ("series", "movies", "channels")) -> list: + 412 """Search for streams + 413 + 414 Args: + 415 keyword (str): Keyword to search for. Supports REGEX + 416 ignore_case (bool, optional): True to ignore case during search. Defaults to "True". + 417 return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". + 418 stream_type (list, optional): Search within specific stream type. + 419 + 420 Returns: + 421 list: List with all the results, it could be empty. + 422 """ 423 - 424 print(f"Checking {len(self.movies)} movies") - 425 for stream in self.movies: - 426 if re.match(regex, stream.name) is not None: - 427 search_result.append(stream.export_json()) - 428 - 429 print(f"Checking {len(self.channels)} channels") - 430 for stream in self.channels: - 431 if re.match(regex, stream.name) is not None: - 432 search_result.append(stream.export_json()) - 433 - 434 print(f"Checking {len(self.series)} series") - 435 for stream in self.series: - 436 if re.match(regex, stream.name) is not None: - 437 search_result.append(stream.export_json()) - 438 - 439 if return_type == "JSON": - 440 if search_result is not None: - 441 print(f"Found {len(search_result)} results `{keyword}`") - 442 return json.dumps(search_result, ensure_ascii=False) + 424 search_result = [] + 425 regex_flags = re.IGNORECASE if ignore_case else 0 + 426 regex = re.compile(keyword, regex_flags) + 427 # if ignore_case: + 428 # regex = re.compile(keyword, re.IGNORECASE) + 429 # else: + 430 # regex = re.compile(keyword) + 431 + 432 # if "movies" in stream_type: + 433 # print(f"Checking {len(self.movies)} movies") + 434 # for stream in self.movies: + 435 # if re.match(regex, stream.name) is not None: + 436 # search_result.append(stream.export_json()) + 437 + 438 # if "channels" in stream_type: + 439 # print(f"Checking {len(self.channels)} channels") + 440 # for stream in self.channels: + 441 # if re.match(regex, stream.name) is not None: + 442 # search_result.append(stream.export_json()) 443 - 444 return search_result - 445 - 446 def download_video(self, stream_id: int) -> str: - 447 """Download Video from Stream ID - 448 - 449 Args: - 450 stream_id (int): Stirng identifing the stream ID - 451 - 452 Returns: - 453 str: Absolute Path Filename where the file was saved. Empty if could not download - 454 """ - 455 url = "" - 456 filename = "" - 457 for stream in self.movies: - 458 if stream.id == stream_id: - 459 url = stream.url - 460 fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}" - 461 filename = osp.join(self.cache_path,fn) - 462 - 463 # If the url was correctly built and file does not exists, start downloading - 464 if url != "": - 465 if not osp.isfile(filename): - 466 if not self._download_video_impl(url,filename): - 467 return "Error" - 468 - 469 return filename + 444 # if "series" in stream_type: + 445 # print(f"Checking {len(self.series)} series") + 446 # for stream in self.series: + 447 # if re.match(regex, stream.name) is not None: + 448 # search_result.append(stream.export_json()) + 449 + 450 stream_collections = { + 451 "movies": self.movies, + 452 "channels": self.channels, + 453 "series": self.series + 454 } + 455 + 456 for stream_type_name in stream_type: + 457 if stream_type_name in stream_collections: + 458 collection = stream_collections[stream_type_name] + 459 print(f"Checking {len(collection)} {stream_type_name}") + 460 for stream in collection: + 461 if re.match(regex, stream.name) is not None: + 462 search_result.append(stream.export_json()) + 463 else: + 464 print(f"`{stream_type_name}` not found in collection") + 465 + 466 if return_type == "JSON": + 467 # if search_result is not None: + 468 print(f"Found {len(search_result)} results `{keyword}`") + 469 return json.dumps(search_result, ensure_ascii=False) 470 - 471 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: - 472 """Download a stream - 473 - 474 Args: - 475 url (str): Complete URL of the stream - 476 fullpath_filename (str): Complete File path where to save the stream - 477 - 478 Returns: - 479 bool: True if successful, False if error - 480 """ - 481 ret_code = False - 482 mb_size = 1024*1024 - 483 try: - 484 print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`") - 485 - 486 # Make the request to download - 487 response = requests.get(url, timeout=(5), stream=True, allow_redirects=True, headers=self.connection_headers) - 488 # If there is an answer from the remote server - 489 if response.status_code == 200: - 490 # Get content type Binary or Text - 491 content_type = response.headers.get('content-type',None) - 492 - 493 # Get total playlist byte size - 494 total_content_size = int(response.headers.get('content-length',None)) - 495 total_content_size_mb = total_content_size/mb_size - 496 - 497 # Set downloaded size - 498 downloaded_bytes = 0 - 499 - 500 # Set stream blocks - 501 block_bytes = int(4*mb_size) # 4 MB - 502 - 503 print(f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})") - 504 if content_type.split('/')[0] != "text": - 505 with open(fullpath_filename, "wb") as file: - 506 - 507 # Grab data by block_bytes - 508 for data in response.iter_content(block_bytes,decode_unicode=False): - 509 downloaded_bytes += block_bytes - 510 progress(downloaded_bytes,total_content_size,"Downloading") - 511 file.write(data) - 512 if downloaded_bytes < total_content_size: - 513 print("The file size is incorrect, deleting") - 514 remove(fullpath_filename) - 515 else: - 516 # Set the datatime when it was last retreived - 517 # self.settings.set_ - 518 print("") - 519 ret_code = True - 520 else: - 521 print(f"URL has a file with unexpected content-type {content_type}") - 522 else: - 523 print(f"HTTP error {response.status_code} while retrieving from {url}") - 524 except Exception as e: - 525 print(e) - 526 - 527 return ret_code - 528 - 529 def _slugify(self, string: str) -> str: - 530 """Normalize string - 531 - 532 Normalizes string, converts to lowercase, removes non-alpha characters, - 533 and converts spaces to hyphens. + 471 return search_result + 472 + 473 def download_video(self, stream_id: int) -> str: + 474 """Download Video from Stream ID + 475 + 476 Args: + 477 stream_id (int): Stirng identifing the stream ID + 478 + 479 Returns: + 480 str: Absolute Path Filename where the file was saved. Empty if could not download + 481 """ + 482 url = "" + 483 filename = "" + 484 for stream in self.movies: + 485 if stream.id == stream_id: + 486 url = stream.url + 487 fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}" + 488 filename = osp.join(self.cache_path,fn) + 489 + 490 # If the url was correctly built and file does not exists, start downloading + 491 if url != "": + 492 #if not osp.isfile(filename): + 493 if not self._download_video_impl(url,filename): + 494 return "Error" + 495 + 496 return filename + 497 + 498 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: + 499 """Download a stream + 500 + 501 Args: + 502 url (str): Complete URL of the stream + 503 fullpath_filename (str): Complete File path where to save the stream + 504 + 505 Returns: + 506 bool: True if successful, False if error + 507 """ + 508 ret_code = False + 509 mb_size = 1024*1024 + 510 try: + 511 print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`") + 512 + 513 # Check if the file already exists + 514 if osp.exists(fullpath_filename): + 515 # If the file exists, resume the download from where it left off + 516 file_size = osp.getsize(fullpath_filename) + 517 self.connection_headers['Range'] = f'bytes={file_size}-' + 518 mode = 'ab' # Append to the existing file + 519 print(f"Resuming from {file_size:_} bytes") + 520 else: + 521 # If the file does not exist, start a new download + 522 mode = 'wb' # Write a new file + 523 + 524 # Make the request to download + 525 response = requests.get(url, timeout=(10), stream=True, allow_redirects=True, headers=self.connection_headers) + 526 # If there is an answer from the remote server + 527 if response.status_code == 200 or response.status_code == 206: + 528 # Get content type Binary or Text + 529 content_type = response.headers.get('content-type',None) + 530 + 531 # Get total playlist byte size + 532 total_content_size = int(response.headers.get('content-length',None)) + 533 total_content_size_mb = total_content_size/mb_size 534 - 535 Args: - 536 string (str): String to be normalized + 535 # Set downloaded size + 536 downloaded_bytes = 0 537 - 538 Returns: - 539 str: Normalized String - 540 """ - 541 return "".join(x.lower() for x in string if x.isprintable()) - 542 - 543 def _validate_url(self, url: str) -> bool: - 544 regex = re.compile( - 545 r"^(?:http|ftp)s?://" # http:// or https:// - 546 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... - 547 r"localhost|" # localhost... - 548 r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip - 549 r"(?::\d+)?" # optional port - 550 r"(?:/?|[/?]\S+)$", - 551 re.IGNORECASE, - 552 ) + 538 # Set stream blocks + 539 block_bytes = int(4*mb_size) # 4 MB + 540 + 541 print(f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})") + 542 if content_type.split('/')[0] != "text": + 543 with open(fullpath_filename, mode) as file: + 544 + 545 # Grab data by block_bytes + 546 for data in response.iter_content(block_bytes,decode_unicode=False): + 547 downloaded_bytes += block_bytes + 548 progress(downloaded_bytes,total_content_size,"Downloading") + 549 file.write(data) + 550 + 551 if downloaded_bytes == total_content_size: + 552 ret_code = True 553 - 554 return re.match(regex, url) is not None - 555 - 556 def _get_logo_local_path(self, logo_url: str) -> str: - 557 """Convert the Logo URL to a local Logo Path - 558 - 559 Args: - 560 logoURL (str): The Logo URL - 561 - 562 Returns: - 563 [type]: The logo path as a string or None - 564 """ - 565 local_logo_path = None - 566 if logo_url is not None: - 567 if not self._validate_url(logo_url): - 568 logo_url = None - 569 else: - 570 local_logo_path = osp.join( - 571 self.cache_path, - 572 f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}" - 573 ) - 574 return local_logo_path - 575 - 576 def authenticate(self): - 577 """Login to provider""" - 578 # If we have not yet successfully authenticated, attempt authentication - 579 if self.state["authenticated"] is False: - 580 # Erase any previous data - 581 self.auth_data = {} - 582 # Loop through 30 seconds - 583 i = 0 - 584 r = None - 585 # Prepare the authentication url - 586 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 587 print(f"Attempting connection: ", end='') - 588 while i < 30: - 589 try: - 590 # Request authentication, wait 4 seconds maximum - 591 r = requests.get(url, timeout=(4), headers=self.connection_headers) - 592 i = 31 - 593 except requests.exceptions.ConnectionError: - 594 time.sleep(1) - 595 print(f"{i} ", end='',flush=True) - 596 i += 1 + 554 # Delete Range if it was added + 555 try: + 556 del self.connection_headers['Range'] + 557 except KeyError: + 558 pass + 559 else: + 560 print(f"URL has a file with unexpected content-type {content_type}") + 561 else: + 562 print(f"HTTP error {response.status_code} while retrieving from {url}") + 563 except Exception as e: + 564 print(e) + 565 + 566 return ret_code + 567 + 568 def _slugify(self, string: str) -> str: + 569 """Normalize string + 570 + 571 Normalizes string, converts to lowercase, removes non-alpha characters, + 572 and converts spaces to hyphens. + 573 + 574 Args: + 575 string (str): String to be normalized + 576 + 577 Returns: + 578 str: Normalized String + 579 """ + 580 return "".join(x.lower() for x in string if x.isprintable()) + 581 + 582 def _validate_url(self, url: str) -> bool: + 583 regex = re.compile( + 584 r"^(?:http|ftp)s?://" # http:// or https:// + 585 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... + 586 r"localhost|" # localhost... + 587 r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip + 588 r"(?::\d+)?" # optional port + 589 r"(?:/?|[/?]\S+)$", + 590 re.IGNORECASE, + 591 ) + 592 + 593 return re.match(regex, url) is not None + 594 + 595 def _get_logo_local_path(self, logo_url: str) -> str: + 596 """Convert the Logo URL to a local Logo Path 597 - 598 if r is not None: - 599 # If the answer is ok, process data and change state - 600 if r.ok: - 601 self.auth_data = r.json() - 602 self.authorization = { - 603 "username": self.auth_data["user_info"]["username"], - 604 "password": self.auth_data["user_info"]["password"] - 605 } - 606 # Mark connection authorized - 607 self.state["authenticated"] = True - 608 # Construct the base url for all requests - 609 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 610 # If there is a secure server connection, construct the base url SSL for all requests - 611 if "https_port" in self.auth_data["server_info"]: - 612 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ - 613 f"/player_api.php?username={self.username}&password={self.password}" - 614 else: - 615 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") - 616 else: - 617 print(f"\n{self.name}: Provider refused the connection") - 618 - 619 def _load_from_file(self, filename) -> dict: - 620 """Try to load the dictionary from file - 621 - 622 Args: - 623 filename ([type]): File name containing the data - 624 - 625 Returns: - 626 dict: Dictionary if found and no errors, None if file does not exists - 627 """ - 628 # Build the full path - 629 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 630 - 631 # If the cached file exists, attempt to load it - 632 if osp.isfile(full_filename): - 633 - 634 my_data = None - 635 - 636 # Get the enlapsed seconds since last file update - 637 file_age_sec = time.time() - osp.getmtime(full_filename) - 638 # If the file was updated less than the threshold time, - 639 # it means that the file is still fresh, we can load it. - 640 # Otherwise skip and return None to force a re-download - 641 if self.threshold_time_sec > file_age_sec: - 642 # Load the JSON data - 643 try: - 644 with open(full_filename, mode="r", encoding="utf-8") as myfile: - 645 my_data = json.load(myfile) - 646 if len(my_data) == 0: - 647 my_data = None - 648 except Exception as e: - 649 print(f" - Could not load from file `{full_filename}`: e=`{e}`") - 650 return my_data - 651 - 652 return None - 653 - 654 def _save_to_file(self, data_list: dict, filename: str) -> bool: - 655 """Save a dictionary to file - 656 - 657 This function will overwrite the file if already exists - 658 - 659 Args: - 660 data_list (dict): Dictionary to save - 661 filename (str): Name of the file - 662 - 663 Returns: - 664 bool: True if successfull, False if error - 665 """ - 666 if data_list is not None: - 667 - 668 #Build the full path - 669 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 670 # If the path makes sense, save the file - 671 json_data = json.dumps(data_list, ensure_ascii=False) - 672 try: - 673 with open(full_filename, mode="wt", encoding="utf-8") as myfile: - 674 myfile.write(json_data) - 675 except Exception as e: - 676 print(f" - Could not save to file `{full_filename}`: e=`{e}`") - 677 return False - 678 - 679 return True - 680 else: - 681 return False + 598 Args: + 599 logoURL (str): The Logo URL + 600 + 601 Returns: + 602 [type]: The logo path as a string or None + 603 """ + 604 local_logo_path = None + 605 if logo_url is not None: + 606 if not self._validate_url(logo_url): + 607 logo_url = None + 608 else: + 609 local_logo_path = osp.join( + 610 self.cache_path, + 611 f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}" + 612 ) + 613 return local_logo_path + 614 + 615 def authenticate(self): + 616 """Login to provider""" + 617 # If we have not yet successfully authenticated, attempt authentication + 618 if self.state["authenticated"] is False: + 619 # Erase any previous data + 620 self.auth_data = {} + 621 # Loop through 30 seconds + 622 i = 0 + 623 r = None + 624 # Prepare the authentication url + 625 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 626 print("Attempting connection... ", end='') + 627 while i < 30: + 628 try: + 629 # Request authentication, wait 4 seconds maximum + 630 r = requests.get(url, timeout=(4), headers=self.connection_headers) + 631 i = 31 + 632 except requests.exceptions.ConnectionError: + 633 time.sleep(1) + 634 print(f"{i} ", end='',flush=True) + 635 i += 1 + 636 + 637 if r is not None: + 638 # If the answer is ok, process data and change state + 639 if r.ok: + 640 print("Connected") + 641 self.auth_data = r.json() + 642 self.authorization = { + 643 "username": self.auth_data["user_info"]["username"], + 644 "password": self.auth_data["user_info"]["password"] + 645 } + 646 # Account expiration date + 647 self.account_expiration = timedelta( + 648 seconds=( + 649 int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp() + 650 ) + 651 ) + 652 # Mark connection authorized + 653 self.state["authenticated"] = True + 654 # Construct the base url for all requests + 655 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 656 # If there is a secure server connection, construct the base url SSL for all requests + 657 if "https_port" in self.auth_data["server_info"]: + 658 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ + 659 f"/player_api.php?username={self.username}&password={self.password}" + 660 print(f"Account expires in {str(self.account_expiration)}") + 661 else: + 662 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") + 663 else: + 664 print(f"\n{self.name}: Provider refused the connection") + 665 + 666 def _load_from_file(self, filename) -> dict: + 667 """Try to load the dictionary from file + 668 + 669 Args: + 670 filename ([type]): File name containing the data + 671 + 672 Returns: + 673 dict: Dictionary if found and no errors, None if file does not exists + 674 """ + 675 # Build the full path + 676 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 677 + 678 # If the cached file exists, attempt to load it + 679 if osp.isfile(full_filename): + 680 + 681 my_data = None 682 - 683 def load_iptv(self) -> bool: - 684 """Load XTream IPTV - 685 - 686 - Add all Live TV to XTream.channels - 687 - Add all VOD to XTream.movies - 688 - Add all Series to XTream.series - 689 Series contains Seasons and Episodes. Those are not automatically - 690 retrieved from the server to reduce the loading time. - 691 - Add all groups to XTream.groups - 692 Groups are for all three channel types, Live TV, VOD, and Series - 693 - 694 Returns: - 695 bool: True if successfull, False if error - 696 """ - 697 # If pyxtream has not authenticated the connection, return empty - 698 if self.state["authenticated"] is False: - 699 print("Warning, cannot load steams since authorization failed") - 700 return False - 701 - 702 # If pyxtream has already loaded the data, skip and return success - 703 if self.state["loaded"] is True: - 704 print("Warning, data has already been loaded.") - 705 return True - 706 - 707 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): - 708 ## Get GROUPS + 683 # Get the enlapsed seconds since last file update + 684 file_age_sec = time.time() - osp.getmtime(full_filename) + 685 # If the file was updated less than the threshold time, + 686 # it means that the file is still fresh, we can load it. + 687 # Otherwise skip and return None to force a re-download + 688 if self.threshold_time_sec > file_age_sec: + 689 # Load the JSON data + 690 try: + 691 with open(full_filename, mode="r", encoding="utf-8") as myfile: + 692 my_data = json.load(myfile) + 693 if len(my_data) == 0: + 694 my_data = None + 695 except Exception as e: + 696 print(f" - Could not load from file `{full_filename}`: e=`{e}`") + 697 return my_data + 698 + 699 return None + 700 + 701 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 702 """Save a dictionary to file + 703 + 704 This function will overwrite the file if already exists + 705 + 706 Args: + 707 data_list (dict): Dictionary to save + 708 filename (str): Name of the file 709 - 710 # Try loading local file - 711 dt = 0 - 712 start = timer() - 713 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") - 714 # If file empty or does not exists, download it from remote - 715 if all_cat is None: - 716 # Load all Groups and save file locally - 717 all_cat = self._load_categories_from_provider(loading_stream_type) - 718 if all_cat is not None: - 719 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") - 720 dt = timer() - start - 721 - 722 # If we got the GROUPS data, show the statistics and load GROUPS - 723 if all_cat is not None: - 724 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") - 725 ## Add GROUPS to dictionaries - 726 - 727 # Add the catch-all-errors group - 728 if loading_stream_type == self.live_type: - 729 self.groups.append(self.live_catch_all_group) - 730 elif loading_stream_type == self.vod_type: - 731 self.groups.append(self.vod_catch_all_group) - 732 elif loading_stream_type == self.series_type: - 733 self.groups.append(self.series_catch_all_group) - 734 - 735 for cat_obj in all_cat: - 736 if schemaValidator(cat_obj, SchemaType.GROUP): - 737 # Create Group (Category) - 738 new_group = Group(cat_obj, loading_stream_type) - 739 # Add to xtream class - 740 self.groups.append(new_group) - 741 else: - 742 # Save what did not pass schema validation - 743 print(cat_obj) - 744 - 745 # Sort Categories - 746 self.groups.sort(key=lambda x: x.name) - 747 else: - 748 print(f" - Could not load {loading_stream_type} Groups") - 749 break - 750 - 751 ## Get Streams - 752 - 753 # Try loading local file - 754 dt = 0 - 755 start = timer() - 756 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") - 757 # If file empty or does not exists, download it from remote - 758 if all_streams is None: - 759 # Load all Streams and save file locally - 760 all_streams = self._load_streams_from_provider(loading_stream_type) - 761 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") - 762 dt = timer() - start - 763 - 764 # If we got the STREAMS data, show the statistics and load Streams - 765 if all_streams is not None: - 766 print( - 767 f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \ - 768 f"in {dt:.3f} seconds" - 769 ) - 770 ## Add Streams to dictionaries - 771 - 772 skipped_adult_content = 0 - 773 skipped_no_name_content = 0 - 774 - 775 number_of_streams = len(all_streams) - 776 current_stream_number = 0 - 777 # Calculate 1% of total number of streams - 778 # This is used to slow down the progress bar - 779 one_percent_number_of_streams = number_of_streams/100 - 780 start = timer() - 781 for stream_channel in all_streams: - 782 skip_stream = False - 783 current_stream_number += 1 + 710 Returns: + 711 bool: True if successfull, False if error + 712 """ + 713 if data_list is None: + 714 return False + 715 + 716 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 717 try: + 718 with open(full_filename, mode="wt", encoding="utf-8") as file: + 719 json.dump(data_list, file, ensure_ascii=False) + 720 return True + 721 except Exception as e: + 722 print(f" - Could not save to file `{full_filename}`: e=`{e}`") + 723 return False + 724 # if data_list is not None: + 725 + 726 # #Build the full path + 727 # full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 728 # # If the path makes sense, save the file + 729 # json_data = json.dumps(data_list, ensure_ascii=False) + 730 # try: + 731 # with open(full_filename, mode="wt", encoding="utf-8") as myfile: + 732 # myfile.write(json_data) + 733 # except Exception as e: + 734 # print(f" - Could not save to file `{full_filename}`: e=`{e}`") + 735 # return False + 736 + 737 # return True + 738 # else: + 739 # return False + 740 + 741 def load_iptv(self) -> bool: + 742 """Load XTream IPTV + 743 + 744 - Add all Live TV to XTream.channels + 745 - Add all VOD to XTream.movies + 746 - Add all Series to XTream.series + 747 Series contains Seasons and Episodes. Those are not automatically + 748 retrieved from the server to reduce the loading time. + 749 - Add all groups to XTream.groups + 750 Groups are for all three channel types, Live TV, VOD, and Series + 751 + 752 Returns: + 753 bool: True if successfull, False if error + 754 """ + 755 # If pyxtream has not authenticated the connection, return empty + 756 if self.state["authenticated"] is False: + 757 print("Warning, cannot load steams since authorization failed") + 758 return False + 759 + 760 # If pyxtream has already loaded the data, skip and return success + 761 if self.state["loaded"] is True: + 762 print("Warning, data has already been loaded.") + 763 return True + 764 + 765 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): + 766 ## Get GROUPS + 767 + 768 # Try loading local file + 769 dt = 0 + 770 start = timer() + 771 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") + 772 # If file empty or does not exists, download it from remote + 773 if all_cat is None: + 774 # Load all Groups and save file locally + 775 all_cat = self._load_categories_from_provider(loading_stream_type) + 776 if all_cat is not None: + 777 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") + 778 dt = timer() - start + 779 + 780 # If we got the GROUPS data, show the statistics and load GROUPS + 781 if all_cat is not None: + 782 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") + 783 ## Add GROUPS to dictionaries 784 - 785 # Show download progress every 1% of total number of streams - 786 if current_stream_number < one_percent_number_of_streams: - 787 progress( - 788 current_stream_number, - 789 number_of_streams, - 790 f"Processing {loading_stream_type} Streams" - 791 ) - 792 one_percent_number_of_streams *= 2 - 793 - 794 # Validate JSON scheme - 795 if self.validate_json: - 796 if loading_stream_type == self.series_type: - 797 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): - 798 print(stream_channel) - 799 elif loading_stream_type == self.live_type: - 800 if not schemaValidator(stream_channel, SchemaType.LIVE): - 801 print(stream_channel) - 802 else: - 803 # vod_type - 804 if not schemaValidator(stream_channel, SchemaType.VOD): - 805 print(stream_channel) - 806 - 807 # Skip if the name of the stream is empty - 808 if stream_channel["name"] == "": - 809 skip_stream = True - 810 skipped_no_name_content = skipped_no_name_content + 1 - 811 self._save_to_file_skipped_streams(stream_channel) - 812 - 813 # Skip if the user chose to hide adult streams - 814 if self.hide_adult_content and loading_stream_type == self.live_type: - 815 if "is_adult" in stream_channel: - 816 if stream_channel["is_adult"] == "1": - 817 skip_stream = True - 818 skipped_adult_content = skipped_adult_content + 1 - 819 self._save_to_file_skipped_streams(stream_channel) - 820 - 821 if not skip_stream: - 822 # Some channels have no group, - 823 # so let's add them to the catch all group - 824 if stream_channel["category_id"] is None: - 825 stream_channel["category_id"] = "9999" - 826 elif stream_channel["category_id"] != "1": - 827 pass - 828 - 829 # Find the first occurence of the group that the - 830 # Channel or Stream is pointing to - 831 the_group = next( - 832 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), - 833 None - 834 ) - 835 - 836 # Set group title - 837 if the_group is not None: - 838 group_title = the_group.name - 839 else: - 840 if loading_stream_type == self.live_type: - 841 group_title = self.live_catch_all_group.name - 842 the_group = self.live_catch_all_group - 843 elif loading_stream_type == self.vod_type: - 844 group_title = self.vod_catch_all_group.name - 845 the_group = self.vod_catch_all_group - 846 elif loading_stream_type == self.series_type: - 847 group_title = self.series_catch_all_group.name - 848 the_group = self.series_catch_all_group - 849 - 850 + 785 # Add the catch-all-errors group + 786 if loading_stream_type == self.live_type: + 787 self.groups.append(self.live_catch_all_group) + 788 elif loading_stream_type == self.vod_type: + 789 self.groups.append(self.vod_catch_all_group) + 790 elif loading_stream_type == self.series_type: + 791 self.groups.append(self.series_catch_all_group) + 792 + 793 for cat_obj in all_cat: + 794 if schemaValidator(cat_obj, SchemaType.GROUP): + 795 # Create Group (Category) + 796 new_group = Group(cat_obj, loading_stream_type) + 797 # Add to xtream class + 798 self.groups.append(new_group) + 799 else: + 800 # Save what did not pass schema validation + 801 print(cat_obj) + 802 + 803 # Sort Categories + 804 self.groups.sort(key=lambda x: x.name) + 805 else: + 806 print(f" - Could not load {loading_stream_type} Groups") + 807 break + 808 + 809 ## Get Streams + 810 + 811 # Try loading local file + 812 dt = 0 + 813 start = timer() + 814 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") + 815 # If file empty or does not exists, download it from remote + 816 if all_streams is None: + 817 # Load all Streams and save file locally + 818 all_streams = self._load_streams_from_provider(loading_stream_type) + 819 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") + 820 dt = timer() - start + 821 + 822 # If we got the STREAMS data, show the statistics and load Streams + 823 if all_streams is not None: + 824 print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds") + 825 ## Add Streams to dictionaries + 826 + 827 skipped_adult_content = 0 + 828 skipped_no_name_content = 0 + 829 + 830 number_of_streams = len(all_streams) + 831 current_stream_number = 0 + 832 # Calculate 1% of total number of streams + 833 # This is used to slow down the progress bar + 834 one_percent_number_of_streams = number_of_streams/100 + 835 start = timer() + 836 for stream_channel in all_streams: + 837 skip_stream = False + 838 current_stream_number += 1 + 839 + 840 # Show download progress every 1% of total number of streams + 841 if current_stream_number < one_percent_number_of_streams: + 842 progress( + 843 current_stream_number, + 844 number_of_streams, + 845 f"Processing {loading_stream_type} Streams" + 846 ) + 847 one_percent_number_of_streams *= 2 + 848 + 849 # Validate JSON scheme + 850 if self.validate_json: 851 if loading_stream_type == self.series_type: - 852 # Load all Series - 853 new_series = Serie(self, stream_channel) - 854 # To get all the Episodes for every Season of each - 855 # Series is very time consuming, we will only - 856 # populate the Series once the user click on the - 857 # Series, the Seasons and Episodes will be loaded - 858 # using x.getSeriesInfoByID() function - 859 - 860 else: - 861 new_channel = Channel( - 862 self, - 863 group_title, - 864 stream_channel - 865 ) - 866 - 867 if new_channel.group_id == "9999": - 868 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") - 869 - 870 # Save the new channel to the local list of channels - 871 if loading_stream_type == self.live_type: - 872 self.channels.append(new_channel) - 873 elif loading_stream_type == self.vod_type: - 874 self.movies.append(new_channel) - 875 if new_channel.age_days_from_added < 31: - 876 self.movies_30days.append(new_channel) - 877 if new_channel.age_days_from_added < 7: - 878 self.movies_7days.append(new_channel) - 879 else: - 880 self.series.append(new_series) - 881 - 882 # Add stream to the specific Group - 883 if the_group is not None: - 884 if loading_stream_type != self.series_type: - 885 the_group.channels.append(new_channel) - 886 else: - 887 the_group.series.append(new_series) - 888 else: - 889 print(f" - Group not found `{stream_channel['name']}`") - 890 print("\n") - 891 # Print information of which streams have been skipped - 892 if self.hide_adult_content: - 893 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") - 894 if skipped_no_name_content > 0: - 895 print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams") - 896 else: - 897 print(f" - Could not load {loading_stream_type} Streams") - 898 - 899 self.state["loaded"] = True - 900 - 901 def _save_to_file_skipped_streams(self, stream_channel: Channel): - 902 - 903 # Build the full path - 904 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 852 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): + 853 print(stream_channel) + 854 elif loading_stream_type == self.live_type: + 855 if not schemaValidator(stream_channel, SchemaType.LIVE): + 856 print(stream_channel) + 857 else: + 858 # vod_type + 859 if not schemaValidator(stream_channel, SchemaType.VOD): + 860 print(stream_channel) + 861 + 862 # Skip if the name of the stream is empty + 863 if stream_channel["name"] == "": + 864 skip_stream = True + 865 skipped_no_name_content = skipped_no_name_content + 1 + 866 self._save_to_file_skipped_streams(stream_channel) + 867 + 868 # Skip if the user chose to hide adult streams + 869 if self.hide_adult_content and loading_stream_type == self.live_type: + 870 if "is_adult" in stream_channel: + 871 if stream_channel["is_adult"] == "1": + 872 skip_stream = True + 873 skipped_adult_content = skipped_adult_content + 1 + 874 self._save_to_file_skipped_streams(stream_channel) + 875 + 876 if not skip_stream: + 877 # Some channels have no group, + 878 # so let's add them to the catch all group + 879 if stream_channel["category_id"] == "": + 880 stream_channel["category_id"] = "9999" + 881 elif stream_channel["category_id"] != "1": + 882 pass + 883 + 884 # Find the first occurence of the group that the + 885 # Channel or Stream is pointing to + 886 the_group = next( + 887 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), + 888 None + 889 ) + 890 + 891 # Set group title + 892 if the_group is not None: + 893 group_title = the_group.name + 894 else: + 895 if loading_stream_type == self.live_type: + 896 group_title = self.live_catch_all_group.name + 897 the_group = self.live_catch_all_group + 898 elif loading_stream_type == self.vod_type: + 899 group_title = self.vod_catch_all_group.name + 900 the_group = self.vod_catch_all_group + 901 elif loading_stream_type == self.series_type: + 902 group_title = self.series_catch_all_group.name + 903 the_group = self.series_catch_all_group + 904 905 - 906 # If the path makes sense, save the file - 907 json_data = json.dumps(stream_channel, ensure_ascii=False) - 908 try: - 909 with open(full_filename, mode="a", encoding="utf-8") as myfile: - 910 myfile.writelines(json_data) - 911 return True - 912 except Exception as e: - 913 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") - 914 return False - 915 - 916 def get_series_info_by_id(self, get_series: dict): - 917 """Get Seasons and Episodes for a Series - 918 - 919 Args: - 920 get_series (dict): Series dictionary - 921 """ - 922 - 923 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) + 906 if loading_stream_type == self.series_type: + 907 # Load all Series + 908 new_series = Serie(self, stream_channel) + 909 # To get all the Episodes for every Season of each + 910 # Series is very time consuming, we will only + 911 # populate the Series once the user click on the + 912 # Series, the Seasons and Episodes will be loaded + 913 # using x.getSeriesInfoByID() function + 914 + 915 else: + 916 new_channel = Channel( + 917 self, + 918 group_title, + 919 stream_channel + 920 ) + 921 + 922 if new_channel.group_id == "9999": + 923 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") 924 - 925 if series_seasons["seasons"] is None: - 926 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] - 927 - 928 for series_info in series_seasons["seasons"]: - 929 season_name = series_info["name"] - 930 season_key = series_info['season_number'] - 931 season = Season(season_name) - 932 get_series.seasons[season_name] = season - 933 if "episodes" in series_seasons.keys(): - 934 for series_season in series_seasons["episodes"].keys(): - 935 for episode_info in series_seasons["episodes"][str(series_season)]: - 936 new_episode_channel = Episode( - 937 self, series_info, "Testing", episode_info - 938 ) - 939 season.episodes[episode_info["title"]] = new_episode_channel - 940 - 941 def _get_request(self, url: str, timeout: Tuple = (2, 15)): - 942 """Generic GET Request with Error handling - 943 - 944 Args: - 945 URL (str): The URL where to GET content - 946 timeout (Tuple, optional): Connection and Downloading Timeout. Defaults to (2,15). - 947 - 948 Returns: - 949 [type]: JSON dictionary of the loaded data, or None - 950 """ - 951 i = 0 - 952 while i < 10: - 953 time.sleep(1) - 954 try: - 955 r = requests.get(url, timeout=timeout, headers=self.connection_headers) - 956 i = 20 - 957 if r.status_code == 200: - 958 return r.json() - 959 except requests.exceptions.ConnectionError: - 960 print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") - 961 i += 1 - 962 - 963 except requests.exceptions.HTTPError: - 964 print(" - HTTP Error") - 965 i += 1 - 966 - 967 except requests.exceptions.TooManyRedirects: - 968 print(" - TooManyRedirects") - 969 i += 1 - 970 - 971 except requests.exceptions.ReadTimeout: - 972 print(" - Timeout while loading data") - 973 i += 1 + 925 # Save the new channel to the local list of channels + 926 if loading_stream_type == self.live_type: + 927 self.channels.append(new_channel) + 928 elif loading_stream_type == self.vod_type: + 929 self.movies.append(new_channel) + 930 if new_channel.age_days_from_added < 31: + 931 self.movies_30days.append(new_channel) + 932 if new_channel.age_days_from_added < 7: + 933 self.movies_7days.append(new_channel) + 934 else: + 935 self.series.append(new_series) + 936 + 937 # Add stream to the specific Group + 938 if the_group is not None: + 939 if loading_stream_type != self.series_type: + 940 the_group.channels.append(new_channel) + 941 else: + 942 the_group.series.append(new_series) + 943 else: + 944 print(f" - Group not found `{stream_channel['name']}`") + 945 print("\n") + 946 # Print information of which streams have been skipped + 947 if self.hide_adult_content: + 948 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") + 949 if skipped_no_name_content > 0: + 950 print(f" - Skipped {skipped_no_name_content} " + 951 "unprintable {loading_stream_type} streams") + 952 else: + 953 print(f" - Could not load {loading_stream_type} Streams") + 954 + 955 self.state["loaded"] = True + 956 + 957 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 958 + 959 # Build the full path + 960 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 961 + 962 # If the path makes sense, save the file + 963 json_data = json.dumps(stream_channel, ensure_ascii=False) + 964 try: + 965 with open(full_filename, mode="a", encoding="utf-8") as myfile: + 966 myfile.writelines(json_data) + 967 return True + 968 except Exception as e: + 969 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") + 970 return False + 971 + 972 def get_series_info_by_id(self, get_series: dict): + 973 """Get Seasons and Episodes for a Series 974 - 975 return None - 976 - 977 # GET Stream Categories - 978 def _load_categories_from_provider(self, stream_type: str): - 979 """Get from provider all category for specific stream type from provider + 975 Args: + 976 get_series (dict): Series dictionary + 977 """ + 978 + 979 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) 980 - 981 Args: - 982 stream_type (str): Stream type can be Live, VOD, Series + 981 if series_seasons["seasons"] is None: + 982 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] 983 - 984 Returns: - 985 [type]: JSON if successfull, otherwise None - 986 """ - 987 url = "" - 988 if stream_type == self.live_type: - 989 url = self.get_live_categories_URL() - 990 elif stream_type == self.vod_type: - 991 url = self.get_vod_cat_URL() - 992 elif stream_type == self.series_type: - 993 url = self.get_series_cat_URL() - 994 else: - 995 url = "" + 984 for series_info in series_seasons["seasons"]: + 985 season_name = series_info["name"] + 986 season_key = series_info['season_number'] + 987 season = Season(season_name) + 988 get_series.seasons[season_name] = season + 989 if "episodes" in series_seasons.keys(): + 990 for series_season in series_seasons["episodes"].keys(): + 991 for episode_info in series_seasons["episodes"][str(series_season)]: + 992 new_episode_channel = Episode( + 993 self, series_info, "Testing", episode_info + 994 ) + 995 season.episodes[episode_info["title"]] = new_episode_channel 996 - 997 return self._get_request(url) - 998 - 999 # GET Streams -1000 def _load_streams_from_provider(self, stream_type: str): -1001 """Get from provider all streams for specific stream type -1002 -1003 Args: -1004 stream_type (str): Stream type can be Live, VOD, Series -1005 -1006 Returns: -1007 [type]: JSON if successfull, otherwise None -1008 """ -1009 url = "" -1010 if stream_type == self.live_type: -1011 url = self.get_live_streams_URL() -1012 elif stream_type == self.vod_type: -1013 url = self.get_vod_streams_URL() -1014 elif stream_type == self.series_type: -1015 url = self.get_series_URL() -1016 else: -1017 url = "" + 997 def _handle_request_exception(self, exception: requests.exceptions.RequestException): + 998 """Handle different types of request exceptions.""" + 999 if isinstance(exception, requests.exceptions.ConnectionError): +1000 print(" - Connection Error: Possible network problem \ +1001 (e.g. DNS failure, refused connection, etc)") +1002 elif isinstance(exception, requests.exceptions.HTTPError): +1003 print(" - HTTP Error") +1004 elif isinstance(exception, requests.exceptions.TooManyRedirects): +1005 print(" - TooManyRedirects") +1006 elif isinstance(exception, requests.exceptions.ReadTimeout): +1007 print(" - Timeout while loading data") +1008 else: +1009 print(f" - An unexpected error occurred: {exception}") +1010 +1011 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: +1012 """Generic GET Request with Error handling +1013 +1014 Args: +1015 URL (str): The URL where to GET content +1016 timeout (Tuple[int, int], optional): Connection and Downloading Timeout. +1017 Defaults to (2,15). 1018 -1019 return self._get_request(url) -1020 -1021 # GET Streams by Category -1022 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): -1023 """Get from provider all streams for specific stream type with category/group ID -1024 -1025 Args: -1026 stream_type (str): Stream type can be Live, VOD, Series -1027 category_id ([type]): Category/Group ID. -1028 -1029 Returns: -1030 [type]: JSON if successfull, otherwise None -1031 """ -1032 url = "" -1033 -1034 if stream_type == self.live_type: -1035 url = self.get_live_streams_URL_by_category(category_id) -1036 elif stream_type == self.vod_type: -1037 url = self.get_vod_streams_URL_by_category(category_id) -1038 elif stream_type == self.series_type: -1039 url = self.get_series_URL_by_category(category_id) -1040 else: -1041 url = "" -1042 -1043 return self._get_request(url) -1044 -1045 # GET SERIES Info -1046 def _load_series_info_by_id_from_provider(self, series_id: str): -1047 """Gets informations about a Serie -1048 -1049 Args: -1050 series_id (str): Serie ID as described in Group +1019 Returns: +1020 Optional[dict]: JSON dictionary of the loaded data, or None +1021 """ +1022 for attempt in range(10): +1023 time.sleep(1) +1024 try: +1025 response = requests.get(url, timeout=timeout, headers=self.connection_headers) +1026 response.raise_for_status() # Raise an HTTPError for bad responses (4xx and 5xx) +1027 return response.json() +1028 except requests.exceptions.RequestException as e: +1029 self._handle_request_exception(e) +1030 +1031 return None +1032 # i = 0 +1033 # while i < 10: +1034 # time.sleep(1) +1035 # try: +1036 # r = requests.get(url, timeout=timeout, headers=self.connection_headers) +1037 # i = 20 +1038 # if r.status_code == 200: +1039 # return r.json() +1040 # except requests.exceptions.ConnectionError: +1041 # print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") +1042 # i += 1 +1043 +1044 # except requests.exceptions.HTTPError: +1045 # print(" - HTTP Error") +1046 # i += 1 +1047 +1048 # except requests.exceptions.TooManyRedirects: +1049 # print(" - TooManyRedirects") +1050 # i += 1 1051 -1052 Returns: -1053 [type]: JSON if successfull, otherwise None -1054 """ -1055 return self._get_request(self.get_series_info_URL_by_ID(series_id)) -1056 -1057 # The seasons array, might be filled or might be completely empty. -1058 # If it is not empty, it will contain the cover, overview and the air date -1059 # of the selected season. -1060 # In your APP if you want to display the series, you have to take that -1061 # from the episodes array. -1062 -1063 # GET VOD Info -1064 def vodInfoByID(self, vod_id): -1065 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) -1066 -1067 # GET short_epg for LIVE Streams (same as stalker portal, -1068 # prints the next X EPG that will play soon) -1069 def liveEpgByStream(self, stream_id): -1070 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) -1071 -1072 def liveEpgByStreamAndLimit(self, stream_id, limit): -1073 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) -1074 -1075 # GET ALL EPG for LIVE Streams (same as stalker portal, -1076 # but it will print all epg listings regardless of the day) -1077 def allLiveEpgByStream(self, stream_id): -1078 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) +1052 # except requests.exceptions.ReadTimeout: +1053 # print(" - Timeout while loading data") +1054 # i += 1 +1055 +1056 # return None +1057 +1058 # GET Stream Categories +1059 def _load_categories_from_provider(self, stream_type: str): +1060 """Get from provider all category for specific stream type from provider +1061 +1062 Args: +1063 stream_type (str): Stream type can be Live, VOD, Series +1064 +1065 Returns: +1066 [type]: JSON if successfull, otherwise None +1067 """ +1068 url = "" +1069 if stream_type == self.live_type: +1070 url = self.get_live_categories_URL() +1071 elif stream_type == self.vod_type: +1072 url = self.get_vod_cat_URL() +1073 elif stream_type == self.series_type: +1074 url = self.get_series_cat_URL() +1075 else: +1076 url = "" +1077 +1078 return self._get_request(url) 1079 -1080 # Full EPG List for all Streams -1081 def allEpg(self): -1082 return self._get_request(self.get_all_epg_URL()) +1080 # GET Streams +1081 def _load_streams_from_provider(self, stream_type: str): +1082 """Get from provider all streams for specific stream type 1083 -1084 ## URL-builder methods -1085 def get_live_categories_URL(self) -> str: -1086 return f"{self.base_url}&action=get_live_categories" -1087 -1088 def get_live_streams_URL(self) -> str: -1089 return f"{self.base_url}&action=get_live_streams" -1090 -1091 def get_live_streams_URL_by_category(self, category_id) -> str: -1092 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" -1093 -1094 def get_vod_cat_URL(self) -> str: -1095 return f"{self.base_url}&action=get_vod_categories" -1096 -1097 def get_vod_streams_URL(self) -> str: -1098 return f"{self.base_url}&action=get_vod_streams" +1084 Args: +1085 stream_type (str): Stream type can be Live, VOD, Series +1086 +1087 Returns: +1088 [type]: JSON if successfull, otherwise None +1089 """ +1090 url = "" +1091 if stream_type == self.live_type: +1092 url = self.get_live_streams_URL() +1093 elif stream_type == self.vod_type: +1094 url = self.get_vod_streams_URL() +1095 elif stream_type == self.series_type: +1096 url = self.get_series_URL() +1097 else: +1098 url = "" 1099 -1100 def get_vod_streams_URL_by_category(self, category_id) -> str: -1101 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" -1102 -1103 def get_series_cat_URL(self) -> str: -1104 return f"{self.base_url}&action=get_series_categories" +1100 return self._get_request(url) +1101 +1102 # GET Streams by Category +1103 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1104 """Get from provider all streams for specific stream type with category/group ID 1105 -1106 def get_series_URL(self) -> str: -1107 return f"{self.base_url}&action=get_series" -1108 -1109 def get_series_URL_by_category(self, category_id) -> str: -1110 return f"{self.base_url}&action=get_series&category_id={category_id}" -1111 -1112 def get_series_info_URL_by_ID(self, series_id) -> str: -1113 return f"{self.base_url}&action=get_series_info&series_id={series_id}" +1106 Args: +1107 stream_type (str): Stream type can be Live, VOD, Series +1108 category_id ([type]): Category/Group ID. +1109 +1110 Returns: +1111 [type]: JSON if successfull, otherwise None +1112 """ +1113 url = "" 1114 -1115 def get_VOD_info_URL_by_ID(self, vod_id) -> str: -1116 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" -1117 -1118 def get_live_epg_URL_by_stream(self, stream_id) -> str: -1119 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" -1120 -1121 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: -1122 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" +1115 if stream_type == self.live_type: +1116 url = self.get_live_streams_URL_by_category(category_id) +1117 elif stream_type == self.vod_type: +1118 url = self.get_vod_streams_URL_by_category(category_id) +1119 elif stream_type == self.series_type: +1120 url = self.get_series_URL_by_category(category_id) +1121 else: +1122 url = "" 1123 -1124 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: -1125 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" -1126 -1127 def get_all_epg_URL(self) -> str: -1128 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" +1124 return self._get_request(url) +1125 +1126 # GET SERIES Info +1127 def _load_series_info_by_id_from_provider(self, series_id: str): +1128 """Gets informations about a Serie +1129 +1130 Args: +1131 series_id (str): Serie ID as described in Group +1132 +1133 Returns: +1134 [type]: JSON if successfull, otherwise None +1135 """ +1136 return self._get_request(self.get_series_info_URL_by_ID(series_id)) +1137 +1138 # The seasons array, might be filled or might be completely empty. +1139 # If it is not empty, it will contain the cover, overview and the air date +1140 # of the selected season. +1141 # In your APP if you want to display the series, you have to take that +1142 # from the episodes array. +1143 +1144 # GET VOD Info +1145 def vodInfoByID(self, vod_id): +1146 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) +1147 +1148 # GET short_epg for LIVE Streams (same as stalker portal, +1149 # prints the next X EPG that will play soon) +1150 def liveEpgByStream(self, stream_id): +1151 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) +1152 +1153 def liveEpgByStreamAndLimit(self, stream_id, limit): +1154 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) +1155 +1156 # GET ALL EPG for LIVE Streams (same as stalker portal, +1157 # but it will print all epg listings regardless of the day) +1158 def allLiveEpgByStream(self, stream_id): +1159 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) +1160 +1161 # Full EPG List for all Streams +1162 def allEpg(self): +1163 return self._get_request(self.get_all_epg_URL()) +1164 +1165 ## URL-builder methods +1166 def get_live_categories_URL(self) -> str: +1167 return f"{self.base_url}&action=get_live_categories" +1168 +1169 def get_live_streams_URL(self) -> str: +1170 return f"{self.base_url}&action=get_live_streams" +1171 +1172 def get_live_streams_URL_by_category(self, category_id) -> str: +1173 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" +1174 +1175 def get_vod_cat_URL(self) -> str: +1176 return f"{self.base_url}&action=get_vod_categories" +1177 +1178 def get_vod_streams_URL(self) -> str: +1179 return f"{self.base_url}&action=get_vod_streams" +1180 +1181 def get_vod_streams_URL_by_category(self, category_id) -> str: +1182 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" +1183 +1184 def get_series_cat_URL(self) -> str: +1185 return f"{self.base_url}&action=get_series_categories" +1186 +1187 def get_series_URL(self) -> str: +1188 return f"{self.base_url}&action=get_series" +1189 +1190 def get_series_URL_by_category(self, category_id) -> str: +1191 return f"{self.base_url}&action=get_series&category_id={category_id}" +1192 +1193 def get_series_info_URL_by_ID(self, series_id) -> str: +1194 return f"{self.base_url}&action=get_series_info&series_id={series_id}" +1195 +1196 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1197 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" +1198 +1199 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1200 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" +1201 +1202 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1203 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" +1204 +1205 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1206 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" +1207 +1208 def get_all_epg_URL(self) -> str: +1209 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"

@@ -3646,64 +3808,67 @@

343 provider_url (str): URL of the IPTV provider 344 headers (dict): Requests Headers 345 hide_adult_content(bool, optional): When `True` hide stream that are marked for adult -346 cache_path (str, optional): Location where to save loaded files. Defaults to empty string. -347 reload_time_sec (int, optional): Number of seconds before automatic reloading (-1 to turn it OFF) -348 debug_flask (bool, optional): Enable the debug mode in Flask -349 validate_json (bool, optional): Check Xtream API provided JSON for validity -350 -351 Returns: XTream Class Instance +346 cache_path (str, optional): Location where to save loaded files. +347 Defaults to empty string. +348 reload_time_sec (int, optional): Number of seconds before automatic reloading +349 (-1 to turn it OFF) +350 debug_flask (bool, optional): Enable the debug mode in Flask +351 validate_json (bool, optional): Check Xtream API provided JSON for validity 352 -353 - Note 1: If it fails to authorize with provided username and password, -354 auth_data will be an empty dictionary. -355 - Note 2: The JSON validation option will take considerable amount of time and it should be -356 used only as a debug tool. The Xtream API JSON from the provider passes through a schema -357 that represent the best available understanding of how the Xtream API works. -358 """ -359 self.server = provider_url -360 self.username = provider_username -361 self.password = provider_password -362 self.name = provider_name -363 self.cache_path = cache_path -364 self.hide_adult_content = hide_adult_content -365 self.threshold_time_sec = reload_time_sec -366 self.validate_json = validate_json -367 -368 # get the pyxtream local path -369 self.app_fullpath = osp.dirname(osp.realpath(__file__)) +353 Returns: XTream Class Instance +354 +355 - Note 1: If it fails to authorize with provided username and password, +356 auth_data will be an empty dictionary. +357 - Note 2: The JSON validation option will take considerable amount of time and it should be +358 used only as a debug tool. The Xtream API JSON from the provider passes through a +359 schema that represent the best available understanding of how the Xtream API +360 works. +361 """ +362 self.server = provider_url +363 self.username = provider_username +364 self.password = provider_password +365 self.name = provider_name +366 self.cache_path = cache_path +367 self.hide_adult_content = hide_adult_content +368 self.threshold_time_sec = reload_time_sec +369 self.validate_json = validate_json 370 -371 # prepare location of local html template -372 self.html_template_folder = osp.join(self.app_fullpath,"html") +371 # get the pyxtream local path +372 self.app_fullpath = osp.dirname(osp.realpath(__file__)) 373 -374 # if the cache_path is specified, test that it is a directory -375 if self.cache_path != "": -376 # If the cache_path is not a directory, clear it -377 if not osp.isdir(self.cache_path): -378 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") -379 self.cache_path == "" -380 -381 # If the cache_path is still empty, use default -382 if self.cache_path == "": -383 self.cache_path = osp.expanduser("~/.xtream-cache/") -384 if not osp.isdir(self.cache_path): -385 makedirs(self.cache_path, exist_ok=True) -386 print(f"pyxtream cache path located at {self.cache_path}") -387 -388 if headers is not None: -389 self.connection_headers = headers -390 else: -391 self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"} -392 -393 self.authenticate() -394 -395 if self.threshold_time_sec > 0: -396 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") -397 else: -398 print("Reload timer is OFF") -399 -400 if self.state['authenticated']: -401 if USE_FLASK: -402 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) -403 self.flaskapp.start() +374 # prepare location of local html template +375 self.html_template_folder = osp.join(self.app_fullpath,"html") +376 +377 # if the cache_path is specified, test that it is a directory +378 if self.cache_path != "": +379 # If the cache_path is not a directory, clear it +380 if not osp.isdir(self.cache_path): +381 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") +382 self.cache_path == "" +383 +384 # If the cache_path is still empty, use default +385 if self.cache_path == "": +386 self.cache_path = osp.expanduser("~/.xtream-cache/") +387 if not osp.isdir(self.cache_path): +388 makedirs(self.cache_path, exist_ok=True) +389 print(f"pyxtream cache path located at {self.cache_path}") +390 +391 if headers is not None: +392 self.connection_headers = headers +393 else: +394 self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"} +395 +396 self.authenticate() +397 +398 if self.threshold_time_sec > 0: +399 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") +400 else: +401 print("Reload timer is OFF") +402 +403 if self.state['authenticated']: +404 if USE_FLASK: +405 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) +406 self.flaskapp.start() @@ -3716,8 +3881,10 @@

provider_url (str): URL of the IPTV provider headers (dict): Requests Headers hide_adult_content(bool, optional): When True hide stream that are marked for adult - cache_path (str, optional): Location where to save loaded files. Defaults to empty string. - reload_time_sec (int, optional): Number of seconds before automatic reloading (-1 to turn it OFF) + cache_path (str, optional): Location where to save loaded files. + Defaults to empty string. + reload_time_sec (int, optional): Number of seconds before automatic reloading + (-1 to turn it OFF) debug_flask (bool, optional): Enable the debug mode in Flask validate_json (bool, optional): Check Xtream API provided JSON for validity

@@ -3727,8 +3894,9 @@

  • Note 1: If it fails to authorize with provided username and password, auth_data will be an empty dictionary.
  • Note 2: The JSON validation option will take considerable amount of time and it should be -used only as a debug tool. The Xtream API JSON from the provider passes through a schema -that represent the best available understanding of how the Xtream API works.
  • +used only as a debug tool. The Xtream API JSON from the provider passes through a +schema that represent the best available understanding of how the Xtream API +works. @@ -4059,52 +4227,76 @@

    def - search_stream( self, keyword: str, ignore_case: bool = True, return_type: str = 'LIST') -> List: + search_stream( self, keyword: str, ignore_case: bool = True, return_type: str = 'LIST', stream_type: list = ('series', 'movies', 'channels')) -> list:
    -
    405    def search_stream(self, keyword: str, ignore_case: bool = True, return_type: str = "LIST") -> List:
    -406        """Search for streams
    -407
    -408        Args:
    -409            keyword (str): Keyword to search for. Supports REGEX
    -410            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    -411            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    -412
    -413        Returns:
    -414            List: List with all the results, it could be empty. Each result
    -415        """
    -416
    -417        search_result = []
    -418
    -419        if ignore_case:
    -420            regex = re.compile(keyword, re.IGNORECASE)
    -421        else:
    -422            regex = re.compile(keyword)
    +            
    408    def search_stream(self, keyword: str,
    +409                      ignore_case: bool = True,
    +410                      return_type: str = "LIST",
    +411                      stream_type: list = ("series", "movies", "channels")) -> list:
    +412        """Search for streams
    +413
    +414        Args:
    +415            keyword (str): Keyword to search for. Supports REGEX
    +416            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    +417            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    +418            stream_type (list, optional): Search within specific stream type.
    +419
    +420        Returns:
    +421            list: List with all the results, it could be empty.
    +422        """
     423
    -424        print(f"Checking {len(self.movies)} movies")
    -425        for stream in self.movies:
    -426            if re.match(regex, stream.name) is not None:
    -427                search_result.append(stream.export_json())
    -428
    -429        print(f"Checking {len(self.channels)} channels")
    -430        for stream in self.channels:
    -431            if re.match(regex, stream.name) is not None:
    -432                search_result.append(stream.export_json())
    -433
    -434        print(f"Checking {len(self.series)} series")
    -435        for stream in self.series:
    -436            if re.match(regex, stream.name) is not None:
    -437                search_result.append(stream.export_json())
    -438
    -439        if return_type == "JSON":
    -440            if search_result is not None:
    -441                print(f"Found {len(search_result)} results `{keyword}`")
    -442                return json.dumps(search_result, ensure_ascii=False)
    +424        search_result = []
    +425        regex_flags = re.IGNORECASE if ignore_case else 0
    +426        regex = re.compile(keyword, regex_flags)
    +427        # if ignore_case:
    +428        #     regex = re.compile(keyword, re.IGNORECASE)
    +429        # else:
    +430        #     regex = re.compile(keyword)
    +431
    +432        # if "movies" in stream_type:
    +433        #     print(f"Checking {len(self.movies)} movies")
    +434        #     for stream in self.movies:
    +435        #         if re.match(regex, stream.name) is not None:
    +436        #             search_result.append(stream.export_json())
    +437
    +438        # if "channels" in stream_type:
    +439        #     print(f"Checking {len(self.channels)} channels")
    +440        #     for stream in self.channels:
    +441        #         if re.match(regex, stream.name) is not None:
    +442        #             search_result.append(stream.export_json())
     443
    -444        return search_result
    +444        # if "series" in stream_type:
    +445        #     print(f"Checking {len(self.series)} series")
    +446        #     for stream in self.series:
    +447        #         if re.match(regex, stream.name) is not None:
    +448        #             search_result.append(stream.export_json())
    +449
    +450        stream_collections = {
    +451            "movies": self.movies,
    +452            "channels": self.channels,
    +453            "series": self.series
    +454        }
    +455
    +456        for stream_type_name in stream_type:
    +457            if stream_type_name in stream_collections:
    +458                collection = stream_collections[stream_type_name]
    +459                print(f"Checking {len(collection)} {stream_type_name}")
    +460                for stream in collection:
    +461                    if re.match(regex, stream.name) is not None:
    +462                        search_result.append(stream.export_json())
    +463            else:
    +464                print(f"`{stream_type_name}` not found in collection")
    +465
    +466        if return_type == "JSON":
    +467            # if search_result is not None:
    +468            print(f"Found {len(search_result)} results `{keyword}`")
    +469            return json.dumps(search_result, ensure_ascii=False)
    +470
    +471        return search_result
     
    @@ -4113,10 +4305,11 @@

    Args: keyword (str): Keyword to search for. Supports REGEX ignore_case (bool, optional): True to ignore case during search. Defaults to "True". - return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".

    + return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". + stream_type (list, optional): Search within specific stream type.

    Returns: - List: List with all the results, it could be empty. Each result

    + list: List with all the results, it could be empty.

    @@ -4132,30 +4325,30 @@

    -
    446    def download_video(self, stream_id: int) -> str:
    -447        """Download Video from Stream ID
    -448
    -449        Args:
    -450            stream_id (int): Stirng identifing the stream ID
    -451
    -452        Returns:
    -453            str: Absolute Path Filename where the file was saved. Empty if could not download
    -454        """
    -455        url = ""
    -456        filename = ""
    -457        for stream in self.movies:
    -458            if stream.id == stream_id:
    -459                url = stream.url
    -460                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    -461                filename = osp.join(self.cache_path,fn)
    -462
    -463        # If the url was correctly built and file does not exists, start downloading
    -464        if url != "":
    -465            if not osp.isfile(filename):
    -466                if not self._download_video_impl(url,filename):
    -467                    return "Error"
    -468
    -469        return filename
    +            
    473    def download_video(self, stream_id: int) -> str:
    +474        """Download Video from Stream ID
    +475
    +476        Args:
    +477            stream_id (int): Stirng identifing the stream ID
    +478
    +479        Returns:
    +480            str: Absolute Path Filename where the file was saved. Empty if could not download
    +481        """
    +482        url = ""
    +483        filename = ""
    +484        for stream in self.movies:
    +485            if stream.id == stream_id:
    +486                url = stream.url
    +487                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    +488                filename = osp.join(self.cache_path,fn)
    +489
    +490        # If the url was correctly built and file does not exists, start downloading
    +491        if url != "":
    +492            #if not osp.isfile(filename):
    +493            if not self._download_video_impl(url,filename):
    +494                return "Error"
    +495
    +496        return filename
     
    @@ -4181,48 +4374,56 @@

    -
    576    def authenticate(self):
    -577        """Login to provider"""
    -578        # If we have not yet successfully authenticated, attempt authentication
    -579        if self.state["authenticated"] is False:
    -580            # Erase any previous data
    -581            self.auth_data = {}
    -582            # Loop through 30 seconds
    -583            i = 0
    -584            r = None
    -585            # Prepare the authentication url
    -586            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    -587            print(f"Attempting connection: ", end='')
    -588            while i < 30:
    -589                try:
    -590                    # Request authentication, wait 4 seconds maximum
    -591                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    -592                    i = 31
    -593                except requests.exceptions.ConnectionError:
    -594                    time.sleep(1)
    -595                    print(f"{i} ", end='',flush=True)
    -596                    i += 1
    -597
    -598            if r is not None:
    -599                # If the answer is ok, process data and change state
    -600                if r.ok:
    -601                    self.auth_data = r.json()
    -602                    self.authorization = {
    -603                        "username": self.auth_data["user_info"]["username"],
    -604                        "password": self.auth_data["user_info"]["password"]
    -605                    }
    -606                    # Mark connection authorized
    -607                    self.state["authenticated"] = True
    -608                    # Construct the base url for all requests
    -609                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    -610                    # If there is a secure server connection, construct the base url SSL for all requests
    -611                    if "https_port" in self.auth_data["server_info"]:
    -612                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    -613                                            f"/player_api.php?username={self.username}&password={self.password}"
    -614                else:
    -615                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    -616            else:
    -617                print(f"\n{self.name}: Provider refused the connection")
    +            
    615    def authenticate(self):
    +616        """Login to provider"""
    +617        # If we have not yet successfully authenticated, attempt authentication
    +618        if self.state["authenticated"] is False:
    +619            # Erase any previous data
    +620            self.auth_data = {}
    +621            # Loop through 30 seconds
    +622            i = 0
    +623            r = None
    +624            # Prepare the authentication url
    +625            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    +626            print("Attempting connection... ", end='')
    +627            while i < 30:
    +628                try:
    +629                    # Request authentication, wait 4 seconds maximum
    +630                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    +631                    i = 31
    +632                except requests.exceptions.ConnectionError:
    +633                    time.sleep(1)
    +634                    print(f"{i} ", end='',flush=True)
    +635                    i += 1
    +636
    +637            if r is not None:
    +638                # If the answer is ok, process data and change state
    +639                if r.ok:
    +640                    print("Connected")
    +641                    self.auth_data = r.json()
    +642                    self.authorization = {
    +643                        "username": self.auth_data["user_info"]["username"],
    +644                        "password": self.auth_data["user_info"]["password"]
    +645                    }
    +646                    # Account expiration date
    +647                    self.account_expiration = timedelta(
    +648                        seconds=(
    +649                            int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp()
    +650                        )
    +651                    )
    +652                    # Mark connection authorized
    +653                    self.state["authenticated"] = True
    +654                    # Construct the base url for all requests
    +655                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    +656                    # If there is a secure server connection, construct the base url SSL for all requests
    +657                    if "https_port" in self.auth_data["server_info"]:
    +658                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    +659                                            f"/player_api.php?username={self.username}&password={self.password}"
    +660                    print(f"Account expires in {str(self.account_expiration)}")
    +661                else:
    +662                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    +663            else:
    +664                print(f"\n{self.name}: Provider refused the connection")
     
    @@ -4242,223 +4443,221 @@

    -
    683    def load_iptv(self) -> bool:
    -684        """Load XTream IPTV
    -685
    -686        - Add all Live TV to XTream.channels
    -687        - Add all VOD to XTream.movies
    -688        - Add all Series to XTream.series
    -689          Series contains Seasons and Episodes. Those are not automatically
    -690          retrieved from the server to reduce the loading time.
    -691        - Add all groups to XTream.groups
    -692          Groups are for all three channel types, Live TV, VOD, and Series
    -693
    -694        Returns:
    -695            bool: True if successfull, False if error
    -696        """
    -697        # If pyxtream has not authenticated the connection, return empty
    -698        if self.state["authenticated"] is False:
    -699            print("Warning, cannot load steams since authorization failed")
    -700            return False
    -701
    -702        # If pyxtream has already loaded the data, skip and return success
    -703        if self.state["loaded"] is True:
    -704            print("Warning, data has already been loaded.")
    -705            return True
    -706
    -707        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    -708            ## Get GROUPS
    -709
    -710            # Try loading local file
    -711            dt = 0
    -712            start = timer()
    -713            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    -714            # If file empty or does not exists, download it from remote
    -715            if all_cat is None:
    -716                # Load all Groups and save file locally
    -717                all_cat = self._load_categories_from_provider(loading_stream_type)
    -718                if all_cat is not None:
    -719                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
    -720            dt = timer() - start
    -721
    -722            # If we got the GROUPS data, show the statistics and load GROUPS
    -723            if all_cat is not None:
    -724                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    -725                ## Add GROUPS to dictionaries
    -726
    -727                # Add the catch-all-errors group
    -728                if loading_stream_type == self.live_type:
    -729                    self.groups.append(self.live_catch_all_group)
    -730                elif loading_stream_type == self.vod_type:
    -731                    self.groups.append(self.vod_catch_all_group)
    -732                elif loading_stream_type == self.series_type:
    -733                    self.groups.append(self.series_catch_all_group)
    -734
    -735                for cat_obj in all_cat:
    -736                    if schemaValidator(cat_obj, SchemaType.GROUP):
    -737                        # Create Group (Category)
    -738                        new_group = Group(cat_obj, loading_stream_type)
    -739                        #  Add to xtream class
    -740                        self.groups.append(new_group)
    -741                    else:
    -742                        # Save what did not pass schema validation
    -743                        print(cat_obj)
    -744
    -745                # Sort Categories
    -746                self.groups.sort(key=lambda x: x.name)
    -747            else:
    -748                print(f" - Could not load {loading_stream_type} Groups")
    -749                break
    -750
    -751            ## Get Streams
    -752
    -753            # Try loading local file
    -754            dt = 0
    -755            start = timer()
    -756            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    -757            # If file empty or does not exists, download it from remote
    -758            if all_streams is None:
    -759                # Load all Streams and save file locally
    -760                all_streams = self._load_streams_from_provider(loading_stream_type)
    -761                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
    -762            dt = timer() - start
    -763
    -764            # If we got the STREAMS data, show the statistics and load Streams
    -765            if all_streams is not None:
    -766                print(
    -767                    f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams " \
    -768                    f"in {dt:.3f} seconds"
    -769                    )
    -770                ## Add Streams to dictionaries
    -771
    -772                skipped_adult_content = 0
    -773                skipped_no_name_content = 0
    -774
    -775                number_of_streams = len(all_streams)
    -776                current_stream_number = 0
    -777                # Calculate 1% of total number of streams
    -778                # This is used to slow down the progress bar
    -779                one_percent_number_of_streams = number_of_streams/100
    -780                start = timer()
    -781                for stream_channel in all_streams:
    -782                    skip_stream = False
    -783                    current_stream_number += 1
    +            
    741    def load_iptv(self) -> bool:
    +742        """Load XTream IPTV
    +743
    +744        - Add all Live TV to XTream.channels
    +745        - Add all VOD to XTream.movies
    +746        - Add all Series to XTream.series
    +747          Series contains Seasons and Episodes. Those are not automatically
    +748          retrieved from the server to reduce the loading time.
    +749        - Add all groups to XTream.groups
    +750          Groups are for all three channel types, Live TV, VOD, and Series
    +751
    +752        Returns:
    +753            bool: True if successfull, False if error
    +754        """
    +755        # If pyxtream has not authenticated the connection, return empty
    +756        if self.state["authenticated"] is False:
    +757            print("Warning, cannot load steams since authorization failed")
    +758            return False
    +759
    +760        # If pyxtream has already loaded the data, skip and return success
    +761        if self.state["loaded"] is True:
    +762            print("Warning, data has already been loaded.")
    +763            return True
    +764
    +765        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    +766            ## Get GROUPS
    +767
    +768            # Try loading local file
    +769            dt = 0
    +770            start = timer()
    +771            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    +772            # If file empty or does not exists, download it from remote
    +773            if all_cat is None:
    +774                # Load all Groups and save file locally
    +775                all_cat = self._load_categories_from_provider(loading_stream_type)
    +776                if all_cat is not None:
    +777                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
    +778            dt = timer() - start
    +779
    +780            # If we got the GROUPS data, show the statistics and load GROUPS
    +781            if all_cat is not None:
    +782                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    +783                ## Add GROUPS to dictionaries
     784
    -785                    # Show download progress every 1% of total number of streams
    -786                    if current_stream_number < one_percent_number_of_streams:
    -787                        progress(
    -788                            current_stream_number,
    -789                            number_of_streams,
    -790                            f"Processing {loading_stream_type} Streams"
    -791                            )
    -792                        one_percent_number_of_streams *= 2
    -793
    -794                    # Validate JSON scheme
    -795                    if self.validate_json:
    -796                        if loading_stream_type == self.series_type:
    -797                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    -798                                print(stream_channel)
    -799                        elif loading_stream_type == self.live_type:
    -800                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    -801                                print(stream_channel)
    -802                        else:
    -803                            # vod_type
    -804                            if not schemaValidator(stream_channel, SchemaType.VOD):
    -805                                print(stream_channel)
    -806
    -807                    # Skip if the name of the stream is empty
    -808                    if stream_channel["name"] == "":
    -809                        skip_stream = True
    -810                        skipped_no_name_content = skipped_no_name_content + 1
    -811                        self._save_to_file_skipped_streams(stream_channel)
    -812
    -813                    # Skip if the user chose to hide adult streams
    -814                    if self.hide_adult_content and loading_stream_type == self.live_type:
    -815                        if "is_adult" in stream_channel:
    -816                            if stream_channel["is_adult"] == "1":
    -817                                skip_stream = True
    -818                                skipped_adult_content = skipped_adult_content + 1
    -819                                self._save_to_file_skipped_streams(stream_channel)
    -820
    -821                    if not skip_stream:
    -822                        # Some channels have no group,
    -823                        # so let's add them to the catch all group
    -824                        if stream_channel["category_id"] is None:
    -825                            stream_channel["category_id"] = "9999"
    -826                        elif stream_channel["category_id"] != "1":
    -827                            pass
    -828
    -829                        # Find the first occurence of the group that the
    -830                        # Channel or Stream is pointing to
    -831                        the_group = next(
    -832                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    -833                            None
    -834                        )
    -835
    -836                        # Set group title
    -837                        if the_group is not None:
    -838                            group_title = the_group.name
    -839                        else:
    -840                            if loading_stream_type == self.live_type:
    -841                                group_title = self.live_catch_all_group.name
    -842                                the_group = self.live_catch_all_group
    -843                            elif loading_stream_type == self.vod_type:
    -844                                group_title = self.vod_catch_all_group.name
    -845                                the_group = self.vod_catch_all_group
    -846                            elif loading_stream_type == self.series_type:
    -847                                group_title = self.series_catch_all_group.name
    -848                                the_group = self.series_catch_all_group
    -849
    -850
    +785                # Add the catch-all-errors group
    +786                if loading_stream_type == self.live_type:
    +787                    self.groups.append(self.live_catch_all_group)
    +788                elif loading_stream_type == self.vod_type:
    +789                    self.groups.append(self.vod_catch_all_group)
    +790                elif loading_stream_type == self.series_type:
    +791                    self.groups.append(self.series_catch_all_group)
    +792
    +793                for cat_obj in all_cat:
    +794                    if schemaValidator(cat_obj, SchemaType.GROUP):
    +795                        # Create Group (Category)
    +796                        new_group = Group(cat_obj, loading_stream_type)
    +797                        #  Add to xtream class
    +798                        self.groups.append(new_group)
    +799                    else:
    +800                        # Save what did not pass schema validation
    +801                        print(cat_obj)
    +802
    +803                # Sort Categories
    +804                self.groups.sort(key=lambda x: x.name)
    +805            else:
    +806                print(f" - Could not load {loading_stream_type} Groups")
    +807                break
    +808
    +809            ## Get Streams
    +810
    +811            # Try loading local file
    +812            dt = 0
    +813            start = timer()
    +814            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    +815            # If file empty or does not exists, download it from remote
    +816            if all_streams is None:
    +817                # Load all Streams and save file locally
    +818                all_streams = self._load_streams_from_provider(loading_stream_type)
    +819                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
    +820            dt = timer() - start
    +821
    +822            # If we got the STREAMS data, show the statistics and load Streams
    +823            if all_streams is not None:
    +824                print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    +825                ## Add Streams to dictionaries
    +826
    +827                skipped_adult_content = 0
    +828                skipped_no_name_content = 0
    +829
    +830                number_of_streams = len(all_streams)
    +831                current_stream_number = 0
    +832                # Calculate 1% of total number of streams
    +833                # This is used to slow down the progress bar
    +834                one_percent_number_of_streams = number_of_streams/100
    +835                start = timer()
    +836                for stream_channel in all_streams:
    +837                    skip_stream = False
    +838                    current_stream_number += 1
    +839
    +840                    # Show download progress every 1% of total number of streams
    +841                    if current_stream_number < one_percent_number_of_streams:
    +842                        progress(
    +843                            current_stream_number,
    +844                            number_of_streams,
    +845                            f"Processing {loading_stream_type} Streams"
    +846                            )
    +847                        one_percent_number_of_streams *= 2
    +848
    +849                    # Validate JSON scheme
    +850                    if self.validate_json:
     851                        if loading_stream_type == self.series_type:
    -852                            # Load all Series
    -853                            new_series = Serie(self, stream_channel)
    -854                            # To get all the Episodes for every Season of each
    -855                            # Series is very time consuming, we will only
    -856                            # populate the Series once the user click on the
    -857                            # Series, the Seasons and Episodes will be loaded
    -858                            # using x.getSeriesInfoByID() function
    -859
    -860                        else:
    -861                            new_channel = Channel(
    -862                                self,
    -863                                group_title,
    -864                                stream_channel
    -865                            )
    -866
    -867                        if new_channel.group_id == "9999":
    -868                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    -869
    -870                        # Save the new channel to the local list of channels
    -871                        if loading_stream_type == self.live_type:
    -872                            self.channels.append(new_channel)
    -873                        elif loading_stream_type == self.vod_type:
    -874                            self.movies.append(new_channel)
    -875                            if new_channel.age_days_from_added < 31:
    -876                                self.movies_30days.append(new_channel)
    -877                            if new_channel.age_days_from_added < 7:
    -878                                self.movies_7days.append(new_channel)
    -879                        else:
    -880                            self.series.append(new_series)
    -881
    -882                        # Add stream to the specific Group
    -883                        if the_group is not None:
    -884                            if loading_stream_type != self.series_type:
    -885                                the_group.channels.append(new_channel)
    -886                            else:
    -887                                the_group.series.append(new_series)
    -888                        else:
    -889                            print(f" - Group not found `{stream_channel['name']}`")
    -890                print("\n")
    -891                # Print information of which streams have been skipped
    -892                if self.hide_adult_content:
    -893                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    -894                if skipped_no_name_content > 0:
    -895                    print(f" - Skipped {skipped_no_name_content} unprintable {loading_stream_type} streams")
    -896            else:
    -897                print(f" - Could not load {loading_stream_type} Streams")
    -898
    -899            self.state["loaded"] = True
    +852                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    +853                                print(stream_channel)
    +854                        elif loading_stream_type == self.live_type:
    +855                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    +856                                print(stream_channel)
    +857                        else:
    +858                            # vod_type
    +859                            if not schemaValidator(stream_channel, SchemaType.VOD):
    +860                                print(stream_channel)
    +861
    +862                    # Skip if the name of the stream is empty
    +863                    if stream_channel["name"] == "":
    +864                        skip_stream = True
    +865                        skipped_no_name_content = skipped_no_name_content + 1
    +866                        self._save_to_file_skipped_streams(stream_channel)
    +867
    +868                    # Skip if the user chose to hide adult streams
    +869                    if self.hide_adult_content and loading_stream_type == self.live_type:
    +870                        if "is_adult" in stream_channel:
    +871                            if stream_channel["is_adult"] == "1":
    +872                                skip_stream = True
    +873                                skipped_adult_content = skipped_adult_content + 1
    +874                                self._save_to_file_skipped_streams(stream_channel)
    +875
    +876                    if not skip_stream:
    +877                        # Some channels have no group,
    +878                        # so let's add them to the catch all group
    +879                        if stream_channel["category_id"] == "":
    +880                            stream_channel["category_id"] = "9999"
    +881                        elif stream_channel["category_id"] != "1":
    +882                            pass
    +883
    +884                        # Find the first occurence of the group that the
    +885                        # Channel or Stream is pointing to
    +886                        the_group = next(
    +887                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    +888                            None
    +889                        )
    +890
    +891                        # Set group title
    +892                        if the_group is not None:
    +893                            group_title = the_group.name
    +894                        else:
    +895                            if loading_stream_type == self.live_type:
    +896                                group_title = self.live_catch_all_group.name
    +897                                the_group = self.live_catch_all_group
    +898                            elif loading_stream_type == self.vod_type:
    +899                                group_title = self.vod_catch_all_group.name
    +900                                the_group = self.vod_catch_all_group
    +901                            elif loading_stream_type == self.series_type:
    +902                                group_title = self.series_catch_all_group.name
    +903                                the_group = self.series_catch_all_group
    +904
    +905
    +906                        if loading_stream_type == self.series_type:
    +907                            # Load all Series
    +908                            new_series = Serie(self, stream_channel)
    +909                            # To get all the Episodes for every Season of each
    +910                            # Series is very time consuming, we will only
    +911                            # populate the Series once the user click on the
    +912                            # Series, the Seasons and Episodes will be loaded
    +913                            # using x.getSeriesInfoByID() function
    +914
    +915                        else:
    +916                            new_channel = Channel(
    +917                                self,
    +918                                group_title,
    +919                                stream_channel
    +920                            )
    +921
    +922                        if new_channel.group_id == "9999":
    +923                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    +924
    +925                        # Save the new channel to the local list of channels
    +926                        if loading_stream_type == self.live_type:
    +927                            self.channels.append(new_channel)
    +928                        elif loading_stream_type == self.vod_type:
    +929                            self.movies.append(new_channel)
    +930                            if new_channel.age_days_from_added < 31:
    +931                                self.movies_30days.append(new_channel)
    +932                            if new_channel.age_days_from_added < 7:
    +933                                self.movies_7days.append(new_channel)
    +934                        else:
    +935                            self.series.append(new_series)
    +936
    +937                        # Add stream to the specific Group
    +938                        if the_group is not None:
    +939                            if loading_stream_type != self.series_type:
    +940                                the_group.channels.append(new_channel)
    +941                            else:
    +942                                the_group.series.append(new_series)
    +943                        else:
    +944                            print(f" - Group not found `{stream_channel['name']}`")
    +945                print("\n")
    +946                # Print information of which streams have been skipped
    +947                if self.hide_adult_content:
    +948                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    +949                if skipped_no_name_content > 0:
    +950                    print(f" - Skipped {skipped_no_name_content} "
    +951                          "unprintable {loading_stream_type} streams")
    +952            else:
    +953                print(f" - Could not load {loading_stream_type} Streams")
    +954
    +955            self.state["loaded"] = True
     
    @@ -4491,30 +4690,30 @@

    -
    916    def get_series_info_by_id(self, get_series: dict):
    -917        """Get Seasons and Episodes for a Series
    -918
    -919        Args:
    -920            get_series (dict): Series dictionary
    -921        """
    -922
    -923        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    -924
    -925        if series_seasons["seasons"] is None:
    -926            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
    -927
    -928        for series_info in series_seasons["seasons"]:
    -929            season_name = series_info["name"]
    -930            season_key = series_info['season_number']
    -931            season = Season(season_name)
    -932            get_series.seasons[season_name] = season
    -933            if "episodes" in series_seasons.keys():
    -934                for series_season in series_seasons["episodes"].keys():
    -935                    for episode_info in series_seasons["episodes"][str(series_season)]:
    -936                        new_episode_channel = Episode(
    -937                            self, series_info, "Testing", episode_info
    -938                        )
    -939                        season.episodes[episode_info["title"]] = new_episode_channel
    +            
    972    def get_series_info_by_id(self, get_series: dict):
    +973        """Get Seasons and Episodes for a Series
    +974
    +975        Args:
    +976            get_series (dict): Series dictionary
    +977        """
    +978
    +979        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    +980
    +981        if series_seasons["seasons"] is None:
    +982            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
    +983
    +984        for series_info in series_seasons["seasons"]:
    +985            season_name = series_info["name"]
    +986            season_key = series_info['season_number']
    +987            season = Season(season_name)
    +988            get_series.seasons[season_name] = season
    +989            if "episodes" in series_seasons.keys():
    +990                for series_season in series_seasons["episodes"].keys():
    +991                    for episode_info in series_seasons["episodes"][str(series_season)]:
    +992                        new_episode_channel = Episode(
    +993                            self, series_info, "Testing", episode_info
    +994                        )
    +995                        season.episodes[episode_info["title"]] = new_episode_channel
     
    @@ -4537,8 +4736,8 @@

    -
    1064    def vodInfoByID(self, vod_id):
    -1065        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
    +            
    1145    def vodInfoByID(self, vod_id):
    +1146        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
     
    @@ -4556,8 +4755,8 @@

    -
    1069    def liveEpgByStream(self, stream_id):
    -1070        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
    +            
    1150    def liveEpgByStream(self, stream_id):
    +1151        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
     
    @@ -4575,8 +4774,8 @@

    -
    1072    def liveEpgByStreamAndLimit(self, stream_id, limit):
    -1073        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
    +            
    1153    def liveEpgByStreamAndLimit(self, stream_id, limit):
    +1154        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
     
    @@ -4594,8 +4793,8 @@

    -
    1077    def allLiveEpgByStream(self, stream_id):
    -1078        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
    +            
    1158    def allLiveEpgByStream(self, stream_id):
    +1159        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
     
    @@ -4613,8 +4812,8 @@

    -
    1081    def allEpg(self):
    -1082        return self._get_request(self.get_all_epg_URL())
    +            
    1162    def allEpg(self):
    +1163        return self._get_request(self.get_all_epg_URL())
     
    @@ -4632,8 +4831,8 @@

    -
    1085    def get_live_categories_URL(self) -> str:
    -1086        return f"{self.base_url}&action=get_live_categories"
    +            
    1166    def get_live_categories_URL(self) -> str:
    +1167        return f"{self.base_url}&action=get_live_categories"
     
    @@ -4651,8 +4850,8 @@

    -
    1088    def get_live_streams_URL(self) -> str:
    -1089        return f"{self.base_url}&action=get_live_streams"
    +            
    1169    def get_live_streams_URL(self) -> str:
    +1170        return f"{self.base_url}&action=get_live_streams"
     
    @@ -4670,8 +4869,8 @@

    -
    1091    def get_live_streams_URL_by_category(self, category_id) -> str:
    -1092        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
    +            
    1172    def get_live_streams_URL_by_category(self, category_id) -> str:
    +1173        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
     
    @@ -4689,8 +4888,8 @@

    -
    1094    def get_vod_cat_URL(self) -> str:
    -1095        return f"{self.base_url}&action=get_vod_categories"
    +            
    1175    def get_vod_cat_URL(self) -> str:
    +1176        return f"{self.base_url}&action=get_vod_categories"
     
    @@ -4708,8 +4907,8 @@

    -
    1097    def get_vod_streams_URL(self) -> str:
    -1098        return f"{self.base_url}&action=get_vod_streams"
    +            
    1178    def get_vod_streams_URL(self) -> str:
    +1179        return f"{self.base_url}&action=get_vod_streams"
     
    @@ -4727,8 +4926,8 @@

    -
    1100    def get_vod_streams_URL_by_category(self, category_id) -> str:
    -1101        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
    +            
    1181    def get_vod_streams_URL_by_category(self, category_id) -> str:
    +1182        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
     
    @@ -4746,8 +4945,8 @@

    -
    1103    def get_series_cat_URL(self) -> str:
    -1104        return f"{self.base_url}&action=get_series_categories"
    +            
    1184    def get_series_cat_URL(self) -> str:
    +1185        return f"{self.base_url}&action=get_series_categories"
     
    @@ -4765,8 +4964,8 @@

    -
    1106    def get_series_URL(self) -> str:
    -1107        return f"{self.base_url}&action=get_series"
    +            
    1187    def get_series_URL(self) -> str:
    +1188        return f"{self.base_url}&action=get_series"
     
    @@ -4784,8 +4983,8 @@

    -
    1109    def get_series_URL_by_category(self, category_id) -> str:
    -1110        return f"{self.base_url}&action=get_series&category_id={category_id}"
    +            
    1190    def get_series_URL_by_category(self, category_id) -> str:
    +1191        return f"{self.base_url}&action=get_series&category_id={category_id}"
     
    @@ -4803,8 +5002,8 @@

    -
    1112    def get_series_info_URL_by_ID(self, series_id) -> str:
    -1113        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
    +            
    1193    def get_series_info_URL_by_ID(self, series_id) -> str:
    +1194        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
     
    @@ -4822,8 +5021,8 @@

    -
    1115    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    -1116        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
    +            
    1196    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    +1197        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
     
    @@ -4841,8 +5040,8 @@

    -
    1118    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    -1119        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
    +            
    1199    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    +1200        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
     
    @@ -4860,8 +5059,8 @@

    -
    1121    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    -1122        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
    +            
    1202    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    +1203        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
     
    @@ -4879,8 +5078,8 @@

    -
    1124    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    -1125        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
    +            
    1205    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    +1206        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
     
    @@ -4898,8 +5097,8 @@

    -
    1127    def get_all_epg_URL(self) -> str:
    -1128        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
    +            
    1208    def get_all_epg_URL(self) -> str:
    +1209        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
     
    diff --git a/docs/pyxtream/rest_api.html b/docs/pyxtream/rest_api.html index f279eb1..85172cc 100644 --- a/docs/pyxtream/rest_api.html +++ b/docs/pyxtream/rest_api.html @@ -3,14 +3,14 @@ - + pyxtream.rest_api API documentation - +
    -
    16class EndpointAction(object):
    -17
    -18    def __init__(self, action, function_name):
    -19        self.function_name = function_name
    -20        self.action = action
    -21
    -22    def __call__(self, **args):
    -23
    -24        if args != {}:
    -25
    -26            #Stream Search
    -27            if self.function_name == "stream_search":
    -28                regex_term = r"^.*{}.*$".format(args['term'])
    -29                answer = self.action(regex_term,  return_type = 'JSON')
    -30
    -31            # Download stream
    -32            elif self.function_name == "download_stream":
    -33                answer = self.action(int(args['stream_id']))
    -34
    -35            else:
    -36                print(args)
    -37                answer = "Hello"
    -38
    -39            self.response = FlaskResponse(answer, status=200, headers={})
    -40            self.response.headers["Content-Type"] = "text/json; charset=utf-8"
    -41        else:
    -42            answer = self.action
    -43            self.response = FlaskResponse(answer, status=200, headers={})
    -44            self.response.headers["Content-Type"] = "text/html; charset=utf-8"
    -45
    -46        return self.response
    +            
    13class EndpointAction(object):
    +14
    +15    def __init__(self, action, function_name):
    +16        self.function_name = function_name
    +17        self.action = action
    +18
    +19    def __call__(self, **args):
    +20
    +21        if args != {}:
    +22
    +23            #Stream Search
    +24            if self.function_name == "stream_search":
    +25                regex_term = r"^.*{}.*$".format(args['term'])
    +26                answer = self.action(regex_term,  return_type = 'JSON')
    +27
    +28            # Download stream
    +29            elif self.function_name == "download_stream":
    +30                answer = self.action(int(args['stream_id']))
    +31
    +32            else:
    +33                print(args)
    +34                answer = "Hello"
    +35
    +36            self.response = FlaskResponse(answer, status=200, headers={})
    +37            self.response.headers["Content-Type"] = "text/json; charset=utf-8"
    +38        else:
    +39            answer = self.action
    +40            self.response = FlaskResponse(answer, status=200, headers={})
    +41            self.response.headers["Content-Type"] = "text/html; charset=utf-8"
    +42
    +43        return self.response
     
    @@ -254,9 +251,9 @@

    -
    18    def __init__(self, action, function_name):
    -19        self.function_name = function_name
    -20        self.action = action
    +            
    15    def __init__(self, action, function_name):
    +16        self.function_name = function_name
    +17        self.action = action
     
    @@ -297,49 +294,49 @@

    -
    48class FlaskWrap(Thread):
    -49
    -50    home_template = """
    -51<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    -52    """
    +            
    45class FlaskWrap(Thread):
    +46
    +47    home_template = """
    +48<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    +49    """
    +50
    +51    host: str = ""
    +52    port: int = 0
     53
    -54    host: str = ""
    -55    port: int = 0
    -56
    -57    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +54    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +55
    +56        log = logging.getLogger('werkzeug')
    +57        log.setLevel(logging.ERROR)
     58
    -59        log = logging.getLogger('werkzeug')
    -60        log.setLevel(logging.ERROR)
    -61
    -62        self.host = host
    -63        self.port = port
    -64        self.debug = debug
    -65
    -66        self.app = Flask(name)
    -67        self.xt = xtream
    -68        Thread.__init__(self)
    -69
    -70        # Configure Thread
    -71        self.name ="pyxtream REST API"
    -72        self.daemon = True
    -73
    -74        # Load HTML Home Template if any
    -75        if html_template_folder is not None:
    -76            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -77            if path.isfile(self.home_template_file_name):
    -78                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -79                    self.home_template = home_html.read()
    -80
    -81        # Add all endpoints
    -82        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -83        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -84        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +59        self.host = host
    +60        self.port = port
    +61        self.debug = debug
    +62
    +63        self.app = Flask(name)
    +64        self.xt = xtream
    +65        Thread.__init__(self)
    +66
    +67        # Configure Thread
    +68        self.name ="pyxtream REST API"
    +69        self.daemon = True
    +70
    +71        # Load HTML Home Template if any
    +72        if html_template_folder is not None:
    +73            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +74            if path.isfile(self.home_template_file_name):
    +75                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +76                    self.home_template = home_html.read()
    +77
    +78        # Add all endpoints
    +79        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    +80        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    +81        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +82
    +83    def run(self):
    +84        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
     85
    -86    def run(self):
    -87        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    -88
    -89    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -90        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +86    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +87        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    @@ -361,34 +358,34 @@

    -
    57    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +            
    54    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +55
    +56        log = logging.getLogger('werkzeug')
    +57        log.setLevel(logging.ERROR)
     58
    -59        log = logging.getLogger('werkzeug')
    -60        log.setLevel(logging.ERROR)
    -61
    -62        self.host = host
    -63        self.port = port
    -64        self.debug = debug
    -65
    -66        self.app = Flask(name)
    -67        self.xt = xtream
    -68        Thread.__init__(self)
    -69
    -70        # Configure Thread
    -71        self.name ="pyxtream REST API"
    -72        self.daemon = True
    -73
    -74        # Load HTML Home Template if any
    -75        if html_template_folder is not None:
    -76            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -77            if path.isfile(self.home_template_file_name):
    -78                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -79                    self.home_template = home_html.read()
    -80
    -81        # Add all endpoints
    -82        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -83        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -84        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +59        self.host = host
    +60        self.port = port
    +61        self.debug = debug
    +62
    +63        self.app = Flask(name)
    +64        self.xt = xtream
    +65        Thread.__init__(self)
    +66
    +67        # Configure Thread
    +68        self.name ="pyxtream REST API"
    +69        self.daemon = True
    +70
    +71        # Load HTML Home Template if any
    +72        if html_template_folder is not None:
    +73            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +74            if path.isfile(self.home_template_file_name):
    +75                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +76                    self.home_template = home_html.read()
    +77
    +78        # Add all endpoints
    +79        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    +80        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    +81        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
     
    @@ -403,7 +400,7 @@

    name is the thread name. By default, a unique name is constructed of the form "Thread-N" where N is a small decimal number.

    -

    args is the argument tuple for the target invocation. Defaults to ().

    +

    args is a list or tuple of arguments for the target invocation. Defaults to ().

    kwargs is a dictionary of keyword arguments for the target invocation. Defaults to {}.

    @@ -485,13 +482,27 @@

    -
    + +
    name - + +
    - +
    1181    @property
    +1182    def name(self):
    +1183        """A string used for identification purposes only.
    +1184
    +1185        It has no semantics. Multiple threads may be given the same name. The
    +1186        initial name is set by the constructor.
    +1187
    +1188        """
    +1189        assert self._initialized, "Thread.__init__() not called"
    +1190        return self._name
    +
    + +

    A string used for identification purposes only.

    It has no semantics. Multiple threads may be given the same name. The @@ -501,13 +512,31 @@

    -
    + +
    daemon - + +
    - +
    1235    @property
    +1236    def daemon(self):
    +1237        """A boolean value indicating whether this thread is a daemon thread.
    +1238
    +1239        This must be set before start() is called, otherwise RuntimeError is
    +1240        raised. Its initial value is inherited from the creating thread; the
    +1241        main thread is not a daemon thread and therefore all threads created in
    +1242        the main thread default to daemon = False.
    +1243
    +1244        The entire Python program exits when only daemon threads are left.
    +1245
    +1246        """
    +1247        assert self._initialized, "Thread.__init__() not called"
    +1248        return self._daemonic
    +
    + +

    A boolean value indicating whether this thread is a daemon thread.

    This must be set before start() is called, otherwise RuntimeError is @@ -531,8 +560,8 @@

    -
    86    def run(self):
    -87        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +            
    83    def run(self):
    +84        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
     
    @@ -557,8 +586,8 @@

    -
    89    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -90        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +            
    86    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +87        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    diff --git a/docs/pyxtream/schemaValidator.html b/docs/pyxtream/schemaValidator.html index 8853e04..a0af665 100644 --- a/docs/pyxtream/schemaValidator.html +++ b/docs/pyxtream/schemaValidator.html @@ -3,14 +3,14 @@ - + pyxtream.schemaValidator API documentation - +
    -

    An enumeration.

    -
    - +
    diff --git a/docs/pyxtream/version.html b/docs/pyxtream/version.html index e4e3f8b..1cf8a26 100644 --- a/docs/pyxtream/version.html +++ b/docs/pyxtream/version.html @@ -3,14 +3,14 @@ - + pyxtream.version API documentation - +
    -
    -
    Inherited Members
    -
    -
    threading.Thread
    -
    start
    -
    join
    -
    ident
    -
    is_alive
    -
    isDaemon
    -
    setDaemon
    -
    getName
    -
    setName
    -
    native_id
    - -
    -
    diff --git a/docs/pyxtream/schemaValidator.html b/docs/pyxtream/schemaValidator.html index a0af665..8dc3e2a 100644 --- a/docs/pyxtream/schemaValidator.html +++ b/docs/pyxtream/schemaValidator.html @@ -3,14 +3,14 @@ - + pyxtream.schemaValidator API documentation - +
    -
    -
    Inherited Members
    -
    -
    enum.Enum
    -
    name
    -
    value
    - -
    -
    diff --git a/docs/pyxtream/version.html b/docs/pyxtream/version.html index 1cf8a26..e224733 100644 --- a/docs/pyxtream/version.html +++ b/docs/pyxtream/version.html @@ -3,14 +3,14 @@ - + pyxtream.version API documentation - +
    + +
    @@ -88,14 +118,40 @@
    + \ No newline at end of file diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py index cffcb07..94df6d6 100755 --- a/pyxtream/pyxtream.py +++ b/pyxtream/pyxtream.py @@ -240,6 +240,9 @@ class Serie: raw = "" def __init__(self, xtream: object, series_info): + + series_info["added"] = series_info["last_modified"] + # Raw JSON Series self.raw = series_info self.xtream = xtream @@ -268,6 +271,10 @@ def __init__(self, xtream: object, series_info): if "genre" in series_info.keys(): self.genre = series_info["genre"] + self.url = f"{xtream.server}/series/" \ + f"{xtream.authorization['username']}/" \ + f"{xtream.authorization['password']}/{self.series_id}/" + def export_json(self): jsondata = {} @@ -303,7 +310,7 @@ class XTream: series_type = "Series" auth_data = {} - authorization = {} + authorization = {'username': '', 'password': ''} groups = [] channels = [] @@ -333,6 +340,9 @@ class XTream: validate_json: bool = True + # Used by REST API to get download progress + download_progress = 0 + def __init__( self, provider_name: str, @@ -344,6 +354,7 @@ def __init__( cache_path: str = "", reload_time_sec: int = 60*60*8, validate_json: bool = False, + enable_flask: bool = False, debug_flask: bool = True ): """Initialize Xtream Class @@ -359,8 +370,9 @@ def __init__( Defaults to empty string. reload_time_sec (int, optional): Number of seconds before automatic reloading (-1 to turn it OFF) - debug_flask (bool, optional): Enable the debug mode in Flask validate_json (bool, optional): Check Xtream API provided JSON for validity + enable_flask (bool, optional): Enable Flask + debug_flask (bool, optional): Enable the debug mode in Flask Returns: XTream Class Instance @@ -413,16 +425,23 @@ def __init__( print("Reload timer is OFF") if self.state['authenticated']: - if USE_FLASK: + if USE_FLASK and enable_flask: + print("Starting Web Interface") self.flaskapp = FlaskWrap( 'pyxtream', self, self.html_template_folder, debug=debug_flask ) self.flaskapp.start() + else: + print("Web interface not running") + + def get_last_7days(self): + return json.dumps(self.movies_7days, default= lambda x:x.export_json()) def search_stream(self, keyword: str, ignore_case: bool = True, return_type: str = "LIST", - stream_type: list = ("series", "movies", "channels")) -> list: + stream_type: list = ("series", "movies", "channels"), + added_after: datetime = None) -> list: """Search for streams Args: @@ -430,6 +449,7 @@ def search_stream(self, keyword: str, ignore_case (bool, optional): True to ignore case during search. Defaults to "True". return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". stream_type (list, optional): Search within specific stream type. + added_after (datetime, optional): Search for items that have been added after a certain date. Returns: list: List with all the results, it could be empty. @@ -450,8 +470,13 @@ def search_stream(self, keyword: str, collection = stream_collections[stream_type_name] print(f"Checking {len(collection)} {stream_type_name}") for stream in collection: - if re.match(regex, stream.name) is not None: - search_result.append(stream.export_json()) + if stream.name and re.match(regex, stream.name) is not None: + if added_after is None: + # Add all matches + search_result.append(stream.export_json()) + else: + # Only add if it is more recent + pass else: print(f"`{stream_type_name}` not found in collection") @@ -473,6 +498,10 @@ def download_video(self, stream_id: int) -> str: """ url = "" filename = "" + for series_stream in self.series: + if series_stream.series_id == stream_id: + url = f"{series_stream.url}/{series_stream.episodes["1"].id}.{series_stream.episodes["1"].container_extension}" + for stream in self.movies: if stream.id == stream_id: url = stream.url @@ -530,6 +559,7 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: # Set downloaded size downloaded_bytes = 0 + self.download_progress = 0 # Set stream blocks block_bytes = int(4*mb_size) # 4 MB @@ -544,6 +574,7 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: for data in response.iter_content(block_bytes,decode_unicode=False): downloaded_bytes += block_bytes progress(downloaded_bytes,total_content_size,"Downloading") + self.download_progress = downloaded_bytes file.write(data) ret_code = True @@ -557,7 +588,10 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: print(f"URL has a file with unexpected content-type {content_type}") else: print(f"HTTP error {response.status_code} while retrieving from {url}") + except requests.exceptions.ReadTimeout: + print("Read Timeout, try again") except Exception as e: + print("Unknown error") print(e) return ret_code @@ -626,7 +660,7 @@ def authenticate(self): # Request authentication, wait 4 seconds maximum r = requests.get(url, timeout=(4), headers=self.connection_headers) i = 31 - except requests.exceptions.ConnectionError: + except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout): time.sleep(1) print(f"{i} ", end='',flush=True) i += 1 @@ -866,7 +900,7 @@ def load_iptv(self) -> bool: if not skip_stream: # Some channels have no group, # so let's add them to the catch all group - if stream_channel["category_id"] == "": + if not stream_channel["category_id"]: stream_channel["category_id"] = "9999" elif stream_channel["category_id"] != "1": pass @@ -943,6 +977,7 @@ def load_iptv(self) -> bool: print(f" - Could not load {loading_stream_type} Streams") self.state["loaded"] = True + return True def _save_to_file_skipped_streams(self, stream_channel: Channel): @@ -1130,16 +1165,20 @@ def _load_streams_by_category_from_provider(self, stream_type: str, category_id) return self._get_request(url) # GET SERIES Info - def _load_series_info_by_id_from_provider(self, series_id: str): + def _load_series_info_by_id_from_provider(self, series_id: str, return_type: str = "DICT"): """Gets informations about a Serie Args: series_id (str): Serie ID as described in Group + return_type (str, optional): Output format, 'DICT' or 'JSON'. Defaults to "DICT". Returns: [type]: JSON if successfull, otherwise None """ - return self._get_request(self.get_series_info_URL_by_ID(series_id)) + data = self._get_request(self.get_series_info_URL_by_ID(series_id)) + if return_type == "JSON": + return json.dumps(data, ensure_ascii=False) + return data # The seasons array, might be filled or might be completely empty. # If it is not empty, it will contain the cover, overview and the air date diff --git a/pyxtream/rest_api.py b/pyxtream/rest_api.py index bc7ef02..0901fbf 100644 --- a/pyxtream/rest_api.py +++ b/pyxtream/rest_api.py @@ -1,46 +1,49 @@ # Import Flask to control IPTV via REST API from threading import Thread - -from flask import Flask -from flask import Response as FlaskResponse -from flask import request as FlaskRequest import logging from os import path - +from flask import Flask +from flask import Response as FlaskResponse class EndpointAction(object): + response: FlaskResponse + def __init__(self, action, function_name): self.function_name = function_name self.action = action def __call__(self, **args): - - if args != {}: - - #Stream Search - if self.function_name == "stream_search": - regex_term = r"^.*{}.*$".format(args['term']) - answer = self.action(regex_term, return_type = 'JSON') - - # Download stream - elif self.function_name == "download_stream": - answer = self.action(int(args['stream_id'])) - - else: - print(args) - answer = "Hello" - - self.response = FlaskResponse(answer, status=200, headers={}) - self.response.headers["Content-Type"] = "text/json; charset=utf-8" - else: - answer = self.action - self.response = FlaskResponse(answer, status=200, headers={}) - self.response.headers["Content-Type"] = "text/html; charset=utf-8" - + content_types = { + 'html': "text/html; charset=utf-8", + 'json': "text/json; charset=utf-8" + } + + handlers = { + # Add handlers here + "stream_search_generic": lambda: self._handle_search(args['term']), + "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')), + "download_stream": lambda: self.action(int(args['stream_id'])), + "download_stream_progress": lambda: self.action, + "get_last_7days": lambda: self.action(), + "home": lambda: self.action, + "get_series": lambda: self.action(int(args['series_id']), "JSON") + } + + answer = handlers[self.function_name]() + content_type = content_types['json'] if self.function_name not in ('home', 'download_stream_progress') else content_types['html'] + + self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type}) return self.response + def _handle_search(self, term, stream_type=None): + regex_term = r"^.*{}.*$".format(term) + if stream_type: + stream_type = [stream_type] if stream_type else ("series", "movies", "channels") + return self.action(regex_term, return_type='JSON', stream_type=stream_type) + return self.action(regex_term, return_type='JSON') + class FlaskWrap(Thread): home_template = """ @@ -50,7 +53,9 @@ class FlaskWrap(Thread): host: str = "" port: int = 0 - def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True): + def __init__(self, name, xtream: object, html_template_folder: str = None, + host: str = "0.0.0.0", port: int = 5000, debug: bool = True + ): log = logging.getLogger('werkzeug') log.setLevel(logging.ERROR) @@ -75,9 +80,31 @@ def __init__(self, name, xtream: object, html_template_folder: str = None, host: self.home_template = home_html.read() # Add all endpoints - self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""]) - self.add_endpoint(endpoint='/stream_search/', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"]) - self.add_endpoint(endpoint='/download_stream//', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"]) + self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,"home"]) + self.add_endpoint(endpoint='/stream_search/', + endpoint_name='stream_search_generic', + handler=[self.xt.search_stream, 'stream_search_generic'] + ) + self.add_endpoint(endpoint='/stream_search//', + endpoint_name='stream_search_with_type', + handler=[self.xt.search_stream, 'stream_search_with_type'] + ) + self.add_endpoint(endpoint='/download_stream//', + endpoint_name='download_stream', + handler=[self.xt.download_video,"download_stream"] + ) + self.add_endpoint(endpoint='/download_stream_progress//', + endpoint_name='download_stream_progress', + handler=[self.xt.download_progress,"download_stream_progress"] + ) + self.add_endpoint(endpoint='/get_last_7days', + endpoint_name='get_last_7days', + handler=[self.xt.get_last_7days,"get_last_7days"] + ) + self.add_endpoint(endpoint='/get_series/', + endpoint_name='get_series', + handler=[self.xt._load_series_info_by_id_from_provider,"get_series"] + ) def run(self): self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port) diff --git a/run_tests.sh b/run_tests.sh new file mode 100755 index 0000000..ce16506 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1 @@ +poetry run pytest --cov=pyxtream pyxtream \ No newline at end of file diff --git a/test/test_pyxtream.py b/test/test_pyxtream.py new file mode 100644 index 0000000..8d19a2b --- /dev/null +++ b/test/test_pyxtream.py @@ -0,0 +1,175 @@ +# test_pyxtream.py +import os +from datetime import datetime, timedelta +from unittest.mock import Mock, patch + +import pytest + +import sys +sys.path.insert(0, '../pyxtream') +from pyxtream import Channel, Episode, Group, Serie, XTream + +# Mock data for provider connection +mock_provider_name = "Test Provider" +mock_provider_username = "test_user" # Must be the same as in the MOCK_AUTH_DATA +mock_provider_password = "test_pass" # Must be the same as in the MOCK_AUTH_DATA +mock_provider_url = "http://test.server.com" # Must be the same as in the MOCK_AUTH_DATA + + +# Mock data for testing +MOCK_AUTH_DATA = { + "user_info": { + "username": mock_provider_username, + "password": mock_provider_password, + "exp_date": str(int((datetime.now() + timedelta(days=30)).timestamp())) + }, + "server_info": { + "url": "test.server.com", + "https_port": "443" + } +} + +MOCK_CATEGORIES = [ + {"category_id": 1, "category_name": "Live TV"}, + {"category_id": 2, "category_name": "Movies"} +] + +MOCK_STREAMS = [ + {"stream_id": 1, "name": "Channel 1", "stream_type": "live", "category_id": 1, + "stream_icon": f"{mock_provider_url}/icon1.png", "added": "1638316800"}, + {"stream_id": 2, "name": "Movie 1", "stream_type": "movie", "category_id": 2, + "stream_icon": f"{mock_provider_url}/icon2.png", "added": "1638316800"} +] + +MOCK_SERIES_INFO = { + "seasons": [ + {"season_number": 1, "name": "Season 1", "cover": f"{mock_provider_url}/cover1.jpg"} + ], + "episodes": { + "1": [ + {"id": 1, "title": "Episode 1", "container_extension": "mp4", "info": {}} + ] + } +} + +# Fixture for environment setup +@pytest.fixture(autouse=True) +def setup_environment(monkeypatch): + """Setup environment before each test.""" + # Mock the cache directory + monkeypatch.setattr("pyxtream.XTream.cache_path", "/tmp/pyxtream_cache") + # Ensure the cache directory exists + os.makedirs("/tmp/pyxtream_cache", exist_ok=True) + + +@pytest.fixture(scope="module") +def mock_xtream(): + with patch('requests.get') as mock_get: + mock_get.return_value.ok = True + mock_get.return_value.json.return_value = MOCK_AUTH_DATA + USE_FLASK=False + xtream = XTream( + provider_name=mock_provider_name, + provider_username=mock_provider_username, + provider_password=mock_provider_password, + provider_url=mock_provider_url + ) + return xtream + +def test_authentication(mock_xtream): + assert mock_xtream.state["authenticated"] is True + assert mock_xtream.authorization["username"] == mock_provider_username + assert mock_xtream.authorization["password"] == mock_provider_password + +def test_channel_initialization(mock_xtream): + stream_info = { + "stream_id": "123", + "name": "Test Channel", + "stream_icon": f"{mock_provider_url}/icon.png", + "stream_type": "live", + "category_id": "1", + "added": "1638316800", + "container_extension": "ts" + } + channel = Channel(mock_xtream, "Test Group", stream_info) + assert channel.id == "123" + assert channel.name == "Test Channel" + assert channel.logo == f"{mock_provider_url}/icon.png" + assert channel.group_title == "Test Group" + assert channel.url.startswith( + f"{mock_provider_url}/live/{mock_provider_username}/{mock_provider_password}/123.ts" + ) + +def test_group_initialization(): + group_info = {"category_id": 1, "category_name": "Live TV"} + group = Group(group_info, "Live") + assert group.group_id == 1 + assert group.name == "Live TV" + assert group.group_type == 0 # TV_GROUP + +def test_serie_initialization(mock_xtream): + series_info = { + "series_id": 1, + "name": "Test Series", + "cover": f"{mock_provider_url}/cover.jpg", + "last_modified": "1638316800", + "plot": "Test plot", + "youtube_trailer": "http://youtube.com/trailer", + "genre": "Action" + } + serie = Serie(mock_xtream, series_info) + assert serie.series_id == 1 + assert serie.name == "Test Series" + assert serie.logo == f"{mock_provider_url}/cover.jpg" + assert serie.url.startswith( + f"{mock_provider_url}/series/{mock_provider_username}/{mock_provider_password}/1/" + ) + assert serie.plot == "Test plot" + assert serie.youtube_trailer == "http://youtube.com/trailer" + assert serie.genre == "Action" + assert isinstance(serie.seasons, dict) + assert isinstance(serie.episodes, dict) + +def test_episode_initialization(mock_xtream): + series_info = {"cover": f"{mock_provider_url}/cover.jpg"} + episode_info = { + "id": 1, + "title": "Episode 1", + "container_extension": "mp4", + "info": {}, + "episode_num": 1 + } + episode = Episode(mock_xtream, series_info, "Test Group", episode_info) + assert episode.id == 1 + assert episode.title == "Episode 1" + +def test_load_categories(mock_xtream): + with patch.object(mock_xtream, '_get_request', return_value=MOCK_CATEGORIES) as mock_get: + # Test live categories + categories = mock_xtream._load_categories_from_provider(mock_xtream.live_type) + assert len(categories) == 2 + assert categories[0]["category_name"] == "Live TV" + +def test_load_streams(mock_xtream): + with patch.object(mock_xtream, '_get_request', return_value=MOCK_STREAMS) as mock_get: + + # Test live streams + streams = mock_xtream._load_streams_from_provider(mock_xtream.live_type) + assert len(streams) == 2 + assert streams[0]["name"] == "Channel 1" + +def test_validate_url(mock_xtream): + assert mock_xtream._validate_url("http://valid.url") is True + assert mock_xtream._validate_url("invalid.url") is False + +def test_slugify(mock_xtream): + assert mock_xtream._slugify("Test String!") == "test string!" + assert mock_xtream._slugify("123ABC") == "123abc" + +def test_get_logo_local_path(mock_xtream): + logo_url = f"{mock_provider_url}/logo.png" + expected_path = os.path.join( + mock_xtream.cache_path, + "test provider-logo.png" + ) + assert mock_xtream._get_logo_local_path(logo_url) == expected_path From 8aac7d4dc466333041d7278d4389d9fd978fb72c Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Mon, 17 Feb 2025 13:44:15 -0600 Subject: [PATCH 18/41] Fixed button --- pyxtream/html/index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pyxtream/html/index.html b/pyxtream/html/index.html index 87dd1a5..22af5b2 100644 --- a/pyxtream/html/index.html +++ b/pyxtream/html/index.html @@ -144,10 +144,6 @@
    id="open_series_btn" data-bs-toggle="tooltip" title=""> - From 6881655868da1e324711dced13be590db58a6987 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Mon, 17 Feb 2025 13:56:32 -0600 Subject: [PATCH 19/41] Fixed Functional Test --- functional_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/functional_test.py b/functional_test.py index 70004d7..3513b8a 100755 --- a/functional_test.py +++ b/functional_test.py @@ -48,7 +48,8 @@ def str2list(input_string: str) -> list: PROVIDER_PASSWORD, PROVIDER_URL, reload_time_sec=60*60*8, - debug_flask=True + debug_flask=True, + enable_flask=True ) sleep(0.5) From 14ec4f4d8bba7ca484f11cba6ec48a769e3c19f6 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Mon, 17 Feb 2025 14:17:23 -0600 Subject: [PATCH 20/41] Updated 0.7.3 change log --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c40ed04..e3c4174 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ xTream.movies[{},{},...] | Date | Version | Description | | ----------- | -----| ----------- | -| 2025-02-17 | 0.7.3 | - Added Initial PyTest| +| 2025-02-17 | 0.7.3 | - Added Initial PyTest and Coverage
    - Added timestamp field "added" to Series to match channels "added" field
    - Added string field "url" to Series to quickly get the series download address
    - Added new API "get_last_7days()" shows the last added streams in the last 7 days in JSON format
    - Changed internal function _load_series_info_by_id_from_provider to allow returned value to change to JSON
    - Changed search_stream function to only search in specific collections
    - Refactored "rest_api.py" to make it easier to extend in the future
    - Added new rest API
    - Changed to Poetry environment
    - Changed Functional Test to test loading series information
    - Changed sample index.html to test more features| | 2024-09-02 | 0.7.2 | - Added missing request package to setup.py
    - Refactored the search stream function and now, it can search for a specific stream type
    - Refactored the download stream function
    - Refactored the _get_request function and removed the call to the sleep function
    - Added functional test to get series json output from a series_id
    - Added functional test to get EPG for a specific stream ID
    - Added xtream account expiration date printed on the console during authentication
    - Improved results with the Flask HTML page and differentiating between movies and series
    - Improved code readability| | 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
    - Fixed provider name in functional_test
    - Improved print out of connection attempts
    - Added method to read latest changes in functional_test | 2023-11-08 | 0.7.0 | - Added Schema Validator
    - Added Channel Age
    - Added list of movies added in the last 30 and 7 days
    - Updated code based on PyLint
    - Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| From a62102c62c107a7cd4c57cbb47b1f1601ffda680 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Mon, 17 Feb 2025 14:28:35 -0600 Subject: [PATCH 21/41] Updated docs --- PYPI.md | 2 +- docs/pyxtream.html | 8 +- docs/pyxtream/progress.html | 6 +- docs/pyxtream/pyxtream.html | 306 +++++++++++------------ docs/pyxtream/rest_api.html | 374 +++++++++++++++-------------- docs/pyxtream/schemaValidator.html | 12 +- 6 files changed, 357 insertions(+), 351 deletions(-) diff --git a/PYPI.md b/PYPI.md index 852001d..e8fe20c 100644 --- a/PYPI.md +++ b/PYPI.md @@ -2,7 +2,7 @@ ```shell poetry lock -poetry install --sync +poetry sync poetry debug resolve ``` diff --git a/docs/pyxtream.html b/docs/pyxtream.html index 6421399..0b1204f 100644 --- a/docs/pyxtream.html +++ b/docs/pyxtream.html @@ -50,15 +50,15 @@

    -
    1from .progress import progress
    -2from .pyxtream import XTream
    +                        
    1from .progress import progress
    +2from .pyxtream import XTream
     3
     4try:
    -5    from .rest_api import FlaskWrap
    +5    from .rest_api import FlaskWrap
     6    USE_FLASK = True
     7except ImportError:
     8    USE_FLASK = False
    -9from .version import __author__, __author_email__, __version__
    +9from .version import __author__, __author_email__, __version__
     
    diff --git a/docs/pyxtream/progress.html b/docs/pyxtream/progress.html index 8038999..3daa1eb 100644 --- a/docs/pyxtream/progress.html +++ b/docs/pyxtream/progress.html @@ -73,10 +73,10 @@

    17# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 18# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 19# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -20import sys +20import sys 21 22 -23def progress(count, total, status=''): +23def progress(count, total, status=''): 24 bar_len = 60 25 filled_len = int(round(bar_len * count / float(total))) 26 @@ -100,7 +100,7 @@

    -
    24def progress(count, total, status=''):
    +            
    24def progress(count, total, status=''):
     25    bar_len = 60
     26    filled_len = int(round(bar_len * count / float(total)))
     27
    diff --git a/docs/pyxtream/pyxtream.html b/docs/pyxtream/pyxtream.html
    index aa82502..f2f376e 100644
    --- a/docs/pyxtream/pyxtream.html
    +++ b/docs/pyxtream/pyxtream.html
    @@ -454,31 +454,31 @@ 

    15> _Note_: It does not support M3U 16""" 17 - 18import json + 18import json 19# used for URL validation - 20import re - 21import time - 22from os import makedirs - 23from os import path as osp - 24from os import remove + 20import re + 21import time + 22from os import makedirs + 23from os import path as osp + 24from os import remove 25# Timing xtream json downloads - 26from timeit import default_timer as timer - 27from typing import List, Tuple, Optional - 28from datetime import datetime, timedelta - 29import requests + 26from timeit import default_timer as timer + 27from typing import List, Tuple, Optional + 28from datetime import datetime, timedelta + 29import requests 30 - 31from pyxtream.schemaValidator import SchemaType, schemaValidator + 31from pyxtream.schemaValidator import SchemaType, schemaValidator 32 33try: - 34 from pyxtream.rest_api import FlaskWrap + 34 from pyxtream.rest_api import FlaskWrap 35 USE_FLASK = True 36except ImportError: 37 USE_FLASK = False 38 - 39from pyxtream.progress import progress + 39from pyxtream.progress import progress 40 41 - 42class Channel: + 42class Channel: 43 # Required by Hypnotix 44 info = "" 45 id = "" @@ -501,7 +501,7 @@

    62 # This contains the raw JSON data 63 raw = "" 64 - 65 def __init__(self, xtream: object, group_title, stream_info): + 65 def __init__(self, xtream: object, group_title, stream_info): 66 self.date_now = datetime.now() 67 68 stream_type = stream_info["stream_type"] @@ -556,7 +556,7 @@

    117 if not xtream._validate_url(self.url): 118 print(f"{self.name} - Bad URL? `{self.url}`") 119 - 120 def export_json(self): + 120 def export_json(self): 121 jsondata = {} 122 123 jsondata["url"] = self.url @@ -566,7 +566,7 @@

    127 return jsondata 128 129 - 130class Group: + 130class Group: 131 # Required by Hypnotix 132 name = "" 133 group_type = "" @@ -577,7 +577,7 @@

    138 # This contains the raw JSON data 139 raw = "" 140 - 141 def convert_region_shortname_to_fullname(self, shortname): + 141 def convert_region_shortname_to_fullname(self, shortname): 142 143 if shortname == "AR": 144 return "Arab" @@ -592,7 +592,7 @@

    153 154 return "" 155 - 156 def __init__(self, group_info: dict, stream_type: str): + 156 def __init__(self, group_info: dict, stream_type: str): 157 # Raw JSON Group 158 self.raw = group_info 159 @@ -623,7 +623,7 @@

    184 self.group_id = int(group_info["category_id"]) 185 186 - 187class Episode: + 187class Episode: 188 # Required by Hypnotix 189 title = "" 190 name = "" @@ -634,7 +634,7 @@

    195 # This contains the raw JSON data 196 raw = "" 197 - 198 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: + 198 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: 199 # Raw JSON Episode 200 self.raw = episode_info 201 @@ -658,7 +658,7 @@

    219 print(f"{self.name} - Bad URL? `{self.url}`") 220 221 - 222class Serie: + 222class Serie: 223 # Required by Hypnotix 224 name = "" 225 logo = "" @@ -673,7 +673,7 @@

    234 # This contains the raw JSON data 235 raw = "" 236 - 237 def __init__(self, xtream: object, series_info): + 237 def __init__(self, xtream: object, series_info): 238 # Raw JSON Series 239 self.raw = series_info 240 self.xtream = xtream @@ -702,7 +702,7 @@

    263 if "genre" in series_info.keys(): 264 self.genre = series_info["genre"] 265 - 266 def export_json(self): + 266 def export_json(self): 267 jsondata = {} 268 269 jsondata.update(self.raw) @@ -710,15 +710,15 @@

    271 272 return jsondata 273 - 274class Season: + 274class Season: 275 # Required by Hypnotix 276 name = "" 277 - 278 def __init__(self, name): + 278 def __init__(self, name): 279 self.name = name 280 self.episodes = {} 281 - 282class XTream: + 282class XTream: 283 284 name = "" 285 server = "" @@ -759,7 +759,7 @@

    320 # JSON dictionary from the provider 321 threshold_time_sec = -1 322 - 323 def __init__( + 323 def __init__( 324 self, 325 provider_name: str, 326 provider_username: str, @@ -843,7 +843,7 @@

    404 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) 405 self.flaskapp.start() 406 - 407 def search_stream(self, keyword: str, + 407 def search_stream(self, keyword: str, 408 ignore_case: bool = True, 409 return_type: str = "LIST", 410 stream_type: list = ("series", "movies", "channels")) -> list: @@ -908,7 +908,7 @@

    469 470 return search_result 471 - 472 def download_video(self, stream_id: int) -> str: + 472 def download_video(self, stream_id: int) -> str: 473 """Download Video from Stream ID 474 475 Args: @@ -933,7 +933,7 @@

    494 495 return filename 496 - 497 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: + 497 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: 498 """Download a stream 499 500 Args: @@ -1003,7 +1003,7 @@

    564 565 return ret_code 566 - 567 def _slugify(self, string: str) -> str: + 567 def _slugify(self, string: str) -> str: 568 """Normalize string 569 570 Normalizes string, converts to lowercase, removes non-alpha characters, @@ -1017,7 +1017,7 @@

    578 """ 579 return "".join(x.lower() for x in string if x.isprintable()) 580 - 581 def _validate_url(self, url: str) -> bool: + 581 def _validate_url(self, url: str) -> bool: 582 regex = re.compile( 583 r"^(?:http|ftp)s?://" # http:// or https:// 584 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... @@ -1030,7 +1030,7 @@

    591 592 return re.match(regex, url) is not None 593 - 594 def _get_logo_local_path(self, logo_url: str) -> str: + 594 def _get_logo_local_path(self, logo_url: str) -> str: 595 """Convert the Logo URL to a local Logo Path 596 597 Args: @@ -1050,7 +1050,7 @@

    611 ) 612 return local_logo_path 613 - 614 def authenticate(self): + 614 def authenticate(self): 615 """Login to provider""" 616 # If we have not yet successfully authenticated, attempt authentication 617 if self.state["authenticated"] is False: @@ -1101,7 +1101,7 @@

    662 else: 663 print(f"\n{self.name}: Provider refused the connection") 664 - 665 def _load_from_file(self, filename) -> dict: + 665 def _load_from_file(self, filename) -> dict: 666 """Try to load the dictionary from file 667 668 Args: @@ -1136,7 +1136,7 @@

    697 698 return None 699 - 700 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 700 def _save_to_file(self, data_list: dict, filename: str) -> bool: 701 """Save a dictionary to file 702 703 This function will overwrite the file if already exists @@ -1176,7 +1176,7 @@

    737 # else: 738 # return False 739 - 740 def load_iptv(self) -> bool: + 740 def load_iptv(self) -> bool: 741 """Load XTream IPTV 742 743 - Add all Live TV to XTream.channels @@ -1392,7 +1392,7 @@

    953 954 self.state["loaded"] = True 955 - 956 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 956 def _save_to_file_skipped_streams(self, stream_channel: Channel): 957 958 # Build the full path 959 full_filename = osp.join(self.cache_path, "skipped_streams.json") @@ -1407,7 +1407,7 @@

    968 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") 969 return False 970 - 971 def get_series_info_by_id(self, get_series: dict): + 971 def get_series_info_by_id(self, get_series: dict): 972 """Get Seasons and Episodes for a Series 973 974 Args: @@ -1432,7 +1432,7 @@

    993 ) 994 season.episodes[episode_info["title"]] = new_episode_channel 995 - 996 def _handle_request_exception(self, exception: requests.exceptions.RequestException): + 996 def _handle_request_exception(self, exception: requests.exceptions.RequestException): 997 """Handle different types of request exceptions.""" 998 if isinstance(exception, requests.exceptions.ConnectionError): 999 print(" - Connection Error: Possible network problem \ @@ -1446,7 +1446,7 @@

    1007 else: 1008 print(f" - An unexpected error occurred: {exception}") 1009 -1010 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: +1010 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: 1011 """Generic GET Request with Error handling 1012 1013 Args: @@ -1494,7 +1494,7 @@

    1055 # return None 1056 1057 # GET Stream Categories -1058 def _load_categories_from_provider(self, stream_type: str): +1058 def _load_categories_from_provider(self, stream_type: str): 1059 """Get from provider all category for specific stream type from provider 1060 1061 Args: @@ -1516,7 +1516,7 @@

    1077 return self._get_request(url) 1078 1079 # GET Streams -1080 def _load_streams_from_provider(self, stream_type: str): +1080 def _load_streams_from_provider(self, stream_type: str): 1081 """Get from provider all streams for specific stream type 1082 1083 Args: @@ -1538,7 +1538,7 @@

    1099 return self._get_request(url) 1100 1101 # GET Streams by Category -1102 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1102 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): 1103 """Get from provider all streams for specific stream type with category/group ID 1104 1105 Args: @@ -1562,7 +1562,7 @@

    1123 return self._get_request(url) 1124 1125 # GET SERIES Info -1126 def _load_series_info_by_id_from_provider(self, series_id: str): +1126 def _load_series_info_by_id_from_provider(self, series_id: str): 1127 """Gets informations about a Serie 1128 1129 Args: @@ -1580,70 +1580,70 @@

    1141 # from the episodes array. 1142 1143 # GET VOD Info -1144 def vodInfoByID(self, vod_id): +1144 def vodInfoByID(self, vod_id): 1145 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) 1146 1147 # GET short_epg for LIVE Streams (same as stalker portal, 1148 # prints the next X EPG that will play soon) -1149 def liveEpgByStream(self, stream_id): +1149 def liveEpgByStream(self, stream_id): 1150 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) 1151 -1152 def liveEpgByStreamAndLimit(self, stream_id, limit): +1152 def liveEpgByStreamAndLimit(self, stream_id, limit): 1153 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) 1154 1155 # GET ALL EPG for LIVE Streams (same as stalker portal, 1156 # but it will print all epg listings regardless of the day) -1157 def allLiveEpgByStream(self, stream_id): +1157 def allLiveEpgByStream(self, stream_id): 1158 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) 1159 1160 # Full EPG List for all Streams -1161 def allEpg(self): +1161 def allEpg(self): 1162 return self._get_request(self.get_all_epg_URL()) 1163 1164 ## URL-builder methods -1165 def get_live_categories_URL(self) -> str: +1165 def get_live_categories_URL(self) -> str: 1166 return f"{self.base_url}&action=get_live_categories" 1167 -1168 def get_live_streams_URL(self) -> str: +1168 def get_live_streams_URL(self) -> str: 1169 return f"{self.base_url}&action=get_live_streams" 1170 -1171 def get_live_streams_URL_by_category(self, category_id) -> str: +1171 def get_live_streams_URL_by_category(self, category_id) -> str: 1172 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" 1173 -1174 def get_vod_cat_URL(self) -> str: +1174 def get_vod_cat_URL(self) -> str: 1175 return f"{self.base_url}&action=get_vod_categories" 1176 -1177 def get_vod_streams_URL(self) -> str: +1177 def get_vod_streams_URL(self) -> str: 1178 return f"{self.base_url}&action=get_vod_streams" 1179 -1180 def get_vod_streams_URL_by_category(self, category_id) -> str: +1180 def get_vod_streams_URL_by_category(self, category_id) -> str: 1181 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" 1182 -1183 def get_series_cat_URL(self) -> str: +1183 def get_series_cat_URL(self) -> str: 1184 return f"{self.base_url}&action=get_series_categories" 1185 -1186 def get_series_URL(self) -> str: +1186 def get_series_URL(self) -> str: 1187 return f"{self.base_url}&action=get_series" 1188 -1189 def get_series_URL_by_category(self, category_id) -> str: +1189 def get_series_URL_by_category(self, category_id) -> str: 1190 return f"{self.base_url}&action=get_series&category_id={category_id}" 1191 -1192 def get_series_info_URL_by_ID(self, series_id) -> str: +1192 def get_series_info_URL_by_ID(self, series_id) -> str: 1193 return f"{self.base_url}&action=get_series_info&series_id={series_id}" 1194 -1195 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1195 def get_VOD_info_URL_by_ID(self, vod_id) -> str: 1196 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" 1197 -1198 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1198 def get_live_epg_URL_by_stream(self, stream_id) -> str: 1199 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" 1200 -1201 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1201 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: 1202 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" 1203 -1204 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1204 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: 1205 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" 1206 -1207 def get_all_epg_URL(self) -> str: +1207 def get_all_epg_URL(self) -> str: 1208 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"

    @@ -1660,7 +1660,7 @@

    -
     43class Channel:
    +            
     43class Channel:
      44    # Required by Hypnotix
      45    info = ""
      46    id = ""
    @@ -1683,7 +1683,7 @@ 

    63 # This contains the raw JSON data 64 raw = "" 65 - 66 def __init__(self, xtream: object, group_title, stream_info): + 66 def __init__(self, xtream: object, group_title, stream_info): 67 self.date_now = datetime.now() 68 69 stream_type = stream_info["stream_type"] @@ -1738,7 +1738,7 @@

    118 if not xtream._validate_url(self.url): 119 print(f"{self.name} - Bad URL? `{self.url}`") 120 -121 def export_json(self): +121 def export_json(self): 122 jsondata = {} 123 124 jsondata["url"] = self.url @@ -1761,7 +1761,7 @@

    -
     66    def __init__(self, xtream: object, group_title, stream_info):
    +            
     66    def __init__(self, xtream: object, group_title, stream_info):
      67        self.date_now = datetime.now()
      68
      69        stream_type = stream_info["stream_type"]
    @@ -2023,7 +2023,7 @@ 

    -
    121    def export_json(self):
    +            
    121    def export_json(self):
     122        jsondata = {}
     123
     124        jsondata["url"] = self.url
    @@ -2049,7 +2049,7 @@ 

    -
    131class Group:
    +            
    131class Group:
     132    # Required by Hypnotix
     133    name = ""
     134    group_type = ""
    @@ -2060,7 +2060,7 @@ 

    139 # This contains the raw JSON data 140 raw = "" 141 -142 def convert_region_shortname_to_fullname(self, shortname): +142 def convert_region_shortname_to_fullname(self, shortname): 143 144 if shortname == "AR": 145 return "Arab" @@ -2075,7 +2075,7 @@

    154 155 return "" 156 -157 def __init__(self, group_info: dict, stream_type: str): +157 def __init__(self, group_info: dict, stream_type: str): 158 # Raw JSON Group 159 self.raw = group_info 160 @@ -2119,7 +2119,7 @@

    -
    157    def __init__(self, group_info: dict, stream_type: str):
    +            
    157    def __init__(self, group_info: dict, stream_type: str):
     158        # Raw JSON Group
     159        self.raw = group_info
     160
    @@ -2213,7 +2213,7 @@ 

    -
    142    def convert_region_shortname_to_fullname(self, shortname):
    +            
    142    def convert_region_shortname_to_fullname(self, shortname):
     143
     144        if shortname == "AR":
     145            return "Arab"
    @@ -2289,7 +2289,7 @@ 

    -
    188class Episode:
    +            
    188class Episode:
     189    # Required by Hypnotix
     190    title = ""
     191    name = ""
    @@ -2300,7 +2300,7 @@ 

    196 # This contains the raw JSON data 197 raw = "" 198 -199 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: +199 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: 200 # Raw JSON Episode 201 self.raw = episode_info 202 @@ -2337,7 +2337,7 @@

    -
    199    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
    +            
    199    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
     200        # Raw JSON Episode
     201        self.raw = episode_info
     202
    @@ -2513,7 +2513,7 @@ 

    -
    223class Serie:
    +            
    223class Serie:
     224    # Required by Hypnotix
     225    name = ""
     226    logo = ""
    @@ -2528,7 +2528,7 @@ 

    235 # This contains the raw JSON data 236 raw = "" 237 -238 def __init__(self, xtream: object, series_info): +238 def __init__(self, xtream: object, series_info): 239 # Raw JSON Series 240 self.raw = series_info 241 self.xtream = xtream @@ -2557,7 +2557,7 @@

    264 if "genre" in series_info.keys(): 265 self.genre = series_info["genre"] 266 -267 def export_json(self): +267 def export_json(self): 268 jsondata = {} 269 270 jsondata.update(self.raw) @@ -2579,7 +2579,7 @@

    -
    238    def __init__(self, xtream: object, series_info):
    +            
    238    def __init__(self, xtream: object, series_info):
     239        # Raw JSON Series
     240        self.raw = series_info
     241        self.xtream = xtream
    @@ -2753,7 +2753,7 @@ 

    -
    267    def export_json(self):
    +            
    267    def export_json(self):
     268        jsondata = {}
     269
     270        jsondata.update(self.raw)
    @@ -2778,11 +2778,11 @@ 

    -
    275class Season:
    +            
    275class Season:
     276    # Required by Hypnotix
     277    name = ""
     278
    -279    def __init__(self, name):
    +279    def __init__(self, name):
     280        self.name = name
     281        self.episodes = {}
     
    @@ -2800,7 +2800,7 @@

    -
    279    def __init__(self, name):
    +            
    279    def __init__(self, name):
     280        self.name = name
     281        self.episodes = {}
     
    @@ -2844,7 +2844,7 @@

    -
     283class XTream:
    +            
     283class XTream:
      284
      285    name = ""
      286    server = ""
    @@ -2885,7 +2885,7 @@ 

    321 # JSON dictionary from the provider 322 threshold_time_sec = -1 323 - 324 def __init__( + 324 def __init__( 325 self, 326 provider_name: str, 327 provider_username: str, @@ -2969,7 +2969,7 @@

    405 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) 406 self.flaskapp.start() 407 - 408 def search_stream(self, keyword: str, + 408 def search_stream(self, keyword: str, 409 ignore_case: bool = True, 410 return_type: str = "LIST", 411 stream_type: list = ("series", "movies", "channels")) -> list: @@ -3034,7 +3034,7 @@

    470 471 return search_result 472 - 473 def download_video(self, stream_id: int) -> str: + 473 def download_video(self, stream_id: int) -> str: 474 """Download Video from Stream ID 475 476 Args: @@ -3059,7 +3059,7 @@

    495 496 return filename 497 - 498 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: + 498 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: 499 """Download a stream 500 501 Args: @@ -3129,7 +3129,7 @@

    565 566 return ret_code 567 - 568 def _slugify(self, string: str) -> str: + 568 def _slugify(self, string: str) -> str: 569 """Normalize string 570 571 Normalizes string, converts to lowercase, removes non-alpha characters, @@ -3143,7 +3143,7 @@

    579 """ 580 return "".join(x.lower() for x in string if x.isprintable()) 581 - 582 def _validate_url(self, url: str) -> bool: + 582 def _validate_url(self, url: str) -> bool: 583 regex = re.compile( 584 r"^(?:http|ftp)s?://" # http:// or https:// 585 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... @@ -3156,7 +3156,7 @@

    592 593 return re.match(regex, url) is not None 594 - 595 def _get_logo_local_path(self, logo_url: str) -> str: + 595 def _get_logo_local_path(self, logo_url: str) -> str: 596 """Convert the Logo URL to a local Logo Path 597 598 Args: @@ -3176,7 +3176,7 @@

    612 ) 613 return local_logo_path 614 - 615 def authenticate(self): + 615 def authenticate(self): 616 """Login to provider""" 617 # If we have not yet successfully authenticated, attempt authentication 618 if self.state["authenticated"] is False: @@ -3227,7 +3227,7 @@

    663 else: 664 print(f"\n{self.name}: Provider refused the connection") 665 - 666 def _load_from_file(self, filename) -> dict: + 666 def _load_from_file(self, filename) -> dict: 667 """Try to load the dictionary from file 668 669 Args: @@ -3262,7 +3262,7 @@

    698 699 return None 700 - 701 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 701 def _save_to_file(self, data_list: dict, filename: str) -> bool: 702 """Save a dictionary to file 703 704 This function will overwrite the file if already exists @@ -3302,7 +3302,7 @@

    738 # else: 739 # return False 740 - 741 def load_iptv(self) -> bool: + 741 def load_iptv(self) -> bool: 742 """Load XTream IPTV 743 744 - Add all Live TV to XTream.channels @@ -3518,7 +3518,7 @@

    954 955 self.state["loaded"] = True 956 - 957 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 957 def _save_to_file_skipped_streams(self, stream_channel: Channel): 958 959 # Build the full path 960 full_filename = osp.join(self.cache_path, "skipped_streams.json") @@ -3533,7 +3533,7 @@

    969 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") 970 return False 971 - 972 def get_series_info_by_id(self, get_series: dict): + 972 def get_series_info_by_id(self, get_series: dict): 973 """Get Seasons and Episodes for a Series 974 975 Args: @@ -3558,7 +3558,7 @@

    994 ) 995 season.episodes[episode_info["title"]] = new_episode_channel 996 - 997 def _handle_request_exception(self, exception: requests.exceptions.RequestException): + 997 def _handle_request_exception(self, exception: requests.exceptions.RequestException): 998 """Handle different types of request exceptions.""" 999 if isinstance(exception, requests.exceptions.ConnectionError): 1000 print(" - Connection Error: Possible network problem \ @@ -3572,7 +3572,7 @@

    1008 else: 1009 print(f" - An unexpected error occurred: {exception}") 1010 -1011 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: +1011 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: 1012 """Generic GET Request with Error handling 1013 1014 Args: @@ -3620,7 +3620,7 @@

    1056 # return None 1057 1058 # GET Stream Categories -1059 def _load_categories_from_provider(self, stream_type: str): +1059 def _load_categories_from_provider(self, stream_type: str): 1060 """Get from provider all category for specific stream type from provider 1061 1062 Args: @@ -3642,7 +3642,7 @@

    1078 return self._get_request(url) 1079 1080 # GET Streams -1081 def _load_streams_from_provider(self, stream_type: str): +1081 def _load_streams_from_provider(self, stream_type: str): 1082 """Get from provider all streams for specific stream type 1083 1084 Args: @@ -3664,7 +3664,7 @@

    1100 return self._get_request(url) 1101 1102 # GET Streams by Category -1103 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1103 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): 1104 """Get from provider all streams for specific stream type with category/group ID 1105 1106 Args: @@ -3688,7 +3688,7 @@

    1124 return self._get_request(url) 1125 1126 # GET SERIES Info -1127 def _load_series_info_by_id_from_provider(self, series_id: str): +1127 def _load_series_info_by_id_from_provider(self, series_id: str): 1128 """Gets informations about a Serie 1129 1130 Args: @@ -3706,70 +3706,70 @@

    1142 # from the episodes array. 1143 1144 # GET VOD Info -1145 def vodInfoByID(self, vod_id): +1145 def vodInfoByID(self, vod_id): 1146 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) 1147 1148 # GET short_epg for LIVE Streams (same as stalker portal, 1149 # prints the next X EPG that will play soon) -1150 def liveEpgByStream(self, stream_id): +1150 def liveEpgByStream(self, stream_id): 1151 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) 1152 -1153 def liveEpgByStreamAndLimit(self, stream_id, limit): +1153 def liveEpgByStreamAndLimit(self, stream_id, limit): 1154 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) 1155 1156 # GET ALL EPG for LIVE Streams (same as stalker portal, 1157 # but it will print all epg listings regardless of the day) -1158 def allLiveEpgByStream(self, stream_id): +1158 def allLiveEpgByStream(self, stream_id): 1159 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) 1160 1161 # Full EPG List for all Streams -1162 def allEpg(self): +1162 def allEpg(self): 1163 return self._get_request(self.get_all_epg_URL()) 1164 1165 ## URL-builder methods -1166 def get_live_categories_URL(self) -> str: +1166 def get_live_categories_URL(self) -> str: 1167 return f"{self.base_url}&action=get_live_categories" 1168 -1169 def get_live_streams_URL(self) -> str: +1169 def get_live_streams_URL(self) -> str: 1170 return f"{self.base_url}&action=get_live_streams" 1171 -1172 def get_live_streams_URL_by_category(self, category_id) -> str: +1172 def get_live_streams_URL_by_category(self, category_id) -> str: 1173 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" 1174 -1175 def get_vod_cat_URL(self) -> str: +1175 def get_vod_cat_URL(self) -> str: 1176 return f"{self.base_url}&action=get_vod_categories" 1177 -1178 def get_vod_streams_URL(self) -> str: +1178 def get_vod_streams_URL(self) -> str: 1179 return f"{self.base_url}&action=get_vod_streams" 1180 -1181 def get_vod_streams_URL_by_category(self, category_id) -> str: +1181 def get_vod_streams_URL_by_category(self, category_id) -> str: 1182 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" 1183 -1184 def get_series_cat_URL(self) -> str: +1184 def get_series_cat_URL(self) -> str: 1185 return f"{self.base_url}&action=get_series_categories" 1186 -1187 def get_series_URL(self) -> str: +1187 def get_series_URL(self) -> str: 1188 return f"{self.base_url}&action=get_series" 1189 -1190 def get_series_URL_by_category(self, category_id) -> str: +1190 def get_series_URL_by_category(self, category_id) -> str: 1191 return f"{self.base_url}&action=get_series&category_id={category_id}" 1192 -1193 def get_series_info_URL_by_ID(self, series_id) -> str: +1193 def get_series_info_URL_by_ID(self, series_id) -> str: 1194 return f"{self.base_url}&action=get_series_info&series_id={series_id}" 1195 -1196 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1196 def get_VOD_info_URL_by_ID(self, vod_id) -> str: 1197 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" 1198 -1199 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1199 def get_live_epg_URL_by_stream(self, stream_id) -> str: 1200 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" 1201 -1202 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1202 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: 1203 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" 1204 -1205 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1205 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: 1206 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" 1207 -1208 def get_all_epg_URL(self) -> str: +1208 def get_all_epg_URL(self) -> str: 1209 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"

    @@ -3786,7 +3786,7 @@

    -
    324    def __init__(
    +            
    324    def __init__(
     325        self,
     326        provider_name: str,
     327        provider_username: str,
    @@ -4233,7 +4233,7 @@ 

    -
    408    def search_stream(self, keyword: str,
    +            
    408    def search_stream(self, keyword: str,
     409                      ignore_case: bool = True,
     410                      return_type: str = "LIST",
     411                      stream_type: list = ("series", "movies", "channels")) -> list:
    @@ -4325,7 +4325,7 @@ 

    -
    473    def download_video(self, stream_id: int) -> str:
    +            
    473    def download_video(self, stream_id: int) -> str:
     474        """Download Video from Stream ID
     475
     476        Args:
    @@ -4374,7 +4374,7 @@ 

    -
    615    def authenticate(self):
    +            
    615    def authenticate(self):
     616        """Login to provider"""
     617        # If we have not yet successfully authenticated, attempt authentication
     618        if self.state["authenticated"] is False:
    @@ -4443,7 +4443,7 @@ 

    -
    741    def load_iptv(self) -> bool:
    +            
    741    def load_iptv(self) -> bool:
     742        """Load XTream IPTV
     743
     744        - Add all Live TV to XTream.channels
    @@ -4690,7 +4690,7 @@ 

    -
    972    def get_series_info_by_id(self, get_series: dict):
    +            
    972    def get_series_info_by_id(self, get_series: dict):
     973        """Get Seasons and Episodes for a Series
     974
     975        Args:
    @@ -4736,7 +4736,7 @@ 

    -
    1145    def vodInfoByID(self, vod_id):
    +            
    1145    def vodInfoByID(self, vod_id):
     1146        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
     
    @@ -4755,7 +4755,7 @@

    -
    1150    def liveEpgByStream(self, stream_id):
    +            
    1150    def liveEpgByStream(self, stream_id):
     1151        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
     
    @@ -4774,7 +4774,7 @@

    -
    1153    def liveEpgByStreamAndLimit(self, stream_id, limit):
    +            
    1153    def liveEpgByStreamAndLimit(self, stream_id, limit):
     1154        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
     
    @@ -4793,7 +4793,7 @@

    -
    1158    def allLiveEpgByStream(self, stream_id):
    +            
    1158    def allLiveEpgByStream(self, stream_id):
     1159        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
     
    @@ -4812,7 +4812,7 @@

    -
    1162    def allEpg(self):
    +            
    1162    def allEpg(self):
     1163        return self._get_request(self.get_all_epg_URL())
     
    @@ -4831,7 +4831,7 @@

    -
    1166    def get_live_categories_URL(self) -> str:
    +            
    1166    def get_live_categories_URL(self) -> str:
     1167        return f"{self.base_url}&action=get_live_categories"
     
    @@ -4850,7 +4850,7 @@

    -
    1169    def get_live_streams_URL(self) -> str:
    +            
    1169    def get_live_streams_URL(self) -> str:
     1170        return f"{self.base_url}&action=get_live_streams"
     
    @@ -4869,7 +4869,7 @@

    -
    1172    def get_live_streams_URL_by_category(self, category_id) -> str:
    +            
    1172    def get_live_streams_URL_by_category(self, category_id) -> str:
     1173        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
     
    @@ -4888,7 +4888,7 @@

    -
    1175    def get_vod_cat_URL(self) -> str:
    +            
    1175    def get_vod_cat_URL(self) -> str:
     1176        return f"{self.base_url}&action=get_vod_categories"
     
    @@ -4907,7 +4907,7 @@

    -
    1178    def get_vod_streams_URL(self) -> str:
    +            
    1178    def get_vod_streams_URL(self) -> str:
     1179        return f"{self.base_url}&action=get_vod_streams"
     
    @@ -4926,7 +4926,7 @@

    -
    1181    def get_vod_streams_URL_by_category(self, category_id) -> str:
    +            
    1181    def get_vod_streams_URL_by_category(self, category_id) -> str:
     1182        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
     
    @@ -4945,7 +4945,7 @@

    -
    1184    def get_series_cat_URL(self) -> str:
    +            
    1184    def get_series_cat_URL(self) -> str:
     1185        return f"{self.base_url}&action=get_series_categories"
     
    @@ -4964,7 +4964,7 @@

    -
    1187    def get_series_URL(self) -> str:
    +            
    1187    def get_series_URL(self) -> str:
     1188        return f"{self.base_url}&action=get_series"
     
    @@ -4983,7 +4983,7 @@

    -
    1190    def get_series_URL_by_category(self, category_id) -> str:
    +            
    1190    def get_series_URL_by_category(self, category_id) -> str:
     1191        return f"{self.base_url}&action=get_series&category_id={category_id}"
     
    @@ -5002,7 +5002,7 @@

    -
    1193    def get_series_info_URL_by_ID(self, series_id) -> str:
    +            
    1193    def get_series_info_URL_by_ID(self, series_id) -> str:
     1194        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
     
    @@ -5021,7 +5021,7 @@

    -
    1196    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    +            
    1196    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
     1197        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
     
    @@ -5040,7 +5040,7 @@

    -
    1199    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    +            
    1199    def get_live_epg_URL_by_stream(self, stream_id) -> str:
     1200        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
     
    @@ -5059,7 +5059,7 @@

    -
    1202    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    +            
    1202    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
     1203        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
     
    @@ -5078,7 +5078,7 @@

    -
    1205    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    +            
    1205    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
     1206        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
     
    @@ -5097,7 +5097,7 @@

    -
    1208    def get_all_epg_URL(self) -> str:
    +            
    1208    def get_all_epg_URL(self) -> str:
     1209        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
     
    diff --git a/docs/pyxtream/rest_api.html b/docs/pyxtream/rest_api.html index bd528ce..a181699 100644 --- a/docs/pyxtream/rest_api.html +++ b/docs/pyxtream/rest_api.html @@ -106,90 +106,93 @@

     1# Import Flask to control IPTV via REST API
    - 2from threading import Thread
    + 2from threading import Thread
      3
    - 4from flask import Flask
    - 5from flask import Response as FlaskResponse
    - 6from flask import request as FlaskRequest
    - 7import logging
    - 8from os import path
    + 4from flask import Flask
    + 5from flask import Response as FlaskResponse
    + 6from flask import request as FlaskRequest
    + 7import logging
    + 8from os import path
      9
     10
    -11class EndpointAction(object):
    +11class EndpointAction(object):
     12
    -13    def __init__(self, action, function_name):
    +13    def __init__(self, action, function_name):
     14        self.function_name = function_name
     15        self.action = action
     16
    -17    def __call__(self, **args):
    -18
    -19        if args != {}:
    -20
    -21            #Stream Search
    -22            if self.function_name == "stream_search":
    -23                regex_term = r"^.*{}.*$".format(args['term'])
    -24                answer = self.action(regex_term,  return_type = 'JSON')
    -25
    -26            # Download stream
    -27            elif self.function_name == "download_stream":
    -28                answer = self.action(int(args['stream_id']))
    -29
    -30            else:
    -31                print(args)
    -32                answer = "Hello"
    -33
    -34            self.response = FlaskResponse(answer, status=200, headers={})
    -35            self.response.headers["Content-Type"] = "text/json; charset=utf-8"
    -36        else:
    -37            answer = self.action
    -38            self.response = FlaskResponse(answer, status=200, headers={})
    -39            self.response.headers["Content-Type"] = "text/html; charset=utf-8"
    -40
    -41        return self.response
    -42
    -43class FlaskWrap(Thread):
    -44
    -45    home_template = """
    -46<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    -47    """
    -48
    -49    host: str = ""
    -50    port: int = 0
    +17    def __call__(self, **args):
    +18        content_types = {
    +19            'html': "text/html; charset=utf-8",
    +20            'json': "text/json; charset=utf-8"
    +21        }
    +22
    +23        handlers = {
    +24            "stream_search_generic": lambda: self._handle_search(args['term']),
    +25            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    +26            "download_stream": lambda: self.action(int(args['stream_id'])),
    +27            "download_stream_progress": lambda: self.action,
    +28            "get_last_7days": lambda: self.action(),
    +29            "home": lambda: self.action
    +30        }
    +31
    +32        answer = handlers[self.function_name]()
    +33        content_type = content_types['json'] if self.function_name != "home" and self.function_name != "download_stream_progress" else content_types['html']
    +34        
    +35        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    +36
    +37        return self.response
    +38
    +39    def _handle_search(self, term, stream_type=None):
    +40        regex_term = r"^.*{}.*$".format(term)
    +41        if stream_type:
    +42            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    +43            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    +44        return self.action(regex_term, return_type='JSON')
    +45
    +46class FlaskWrap(Thread):
    +47
    +48    home_template = """
    +49<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    +50    """
     51
    -52    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    -53
    -54        log = logging.getLogger('werkzeug')
    -55        log.setLevel(logging.ERROR)
    +52    host: str = ""
    +53    port: int = 0
    +54
    +55    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
     56
    -57        self.host = host
    -58        self.port = port
    -59        self.debug = debug
    -60
    -61        self.app = Flask(name)
    -62        self.xt = xtream
    -63        Thread.__init__(self)
    -64
    -65        # Configure Thread
    -66        self.name ="pyxtream REST API"
    -67        self.daemon = True
    -68
    -69        # Load HTML Home Template if any
    -70        if html_template_folder is not None:
    -71            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -72            if path.isfile(self.home_template_file_name):
    -73                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -74                    self.home_template = home_html.read()
    -75
    -76        # Add all endpoints
    -77        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -78        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -79        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    -80
    -81    def run(self):
    -82        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +57        log = logging.getLogger('werkzeug')
    +58        log.setLevel(logging.ERROR)
    +59
    +60        self.host = host
    +61        self.port = port
    +62        self.debug = debug
    +63
    +64        self.app = Flask(name)
    +65        self.xt = xtream
    +66        Thread.__init__(self)
    +67
    +68        # Configure Thread
    +69        self.name ="pyxtream REST API"
    +70        self.daemon = True
    +71
    +72        # Load HTML Home Template if any
    +73        if html_template_folder is not None:
    +74            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +75            if path.isfile(self.home_template_file_name):
    +76                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +77                    self.home_template = home_html.read()
    +78
    +79        # Add all endpoints
    +80        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    +81        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    +82        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
     83
    -84    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -85        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +84    def run(self):
    +85        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +86
    +87    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +88        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    @@ -205,37 +208,40 @@

    -
    13class EndpointAction(object):
    -14
    -15    def __init__(self, action, function_name):
    -16        self.function_name = function_name
    -17        self.action = action
    -18
    -19    def __call__(self, **args):
    -20
    -21        if args != {}:
    -22
    -23            #Stream Search
    -24            if self.function_name == "stream_search":
    -25                regex_term = r"^.*{}.*$".format(args['term'])
    -26                answer = self.action(regex_term,  return_type = 'JSON')
    -27
    -28            # Download stream
    -29            elif self.function_name == "download_stream":
    -30                answer = self.action(int(args['stream_id']))
    -31
    -32            else:
    -33                print(args)
    -34                answer = "Hello"
    -35
    -36            self.response = FlaskResponse(answer, status=200, headers={})
    -37            self.response.headers["Content-Type"] = "text/json; charset=utf-8"
    -38        else:
    -39            answer = self.action
    -40            self.response = FlaskResponse(answer, status=200, headers={})
    -41            self.response.headers["Content-Type"] = "text/html; charset=utf-8"
    -42
    -43        return self.response
    +            
    12class EndpointAction(object):
    +13
    +14    def __init__(self, action, function_name):
    +15        self.function_name = function_name
    +16        self.action = action
    +17
    +18    def __call__(self, **args):
    +19        content_types = {
    +20            'html': "text/html; charset=utf-8",
    +21            'json': "text/json; charset=utf-8"
    +22        }
    +23
    +24        handlers = {
    +25            "stream_search_generic": lambda: self._handle_search(args['term']),
    +26            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    +27            "download_stream": lambda: self.action(int(args['stream_id'])),
    +28            "download_stream_progress": lambda: self.action,
    +29            "get_last_7days": lambda: self.action(),
    +30            "home": lambda: self.action
    +31        }
    +32
    +33        answer = handlers[self.function_name]()
    +34        content_type = content_types['json'] if self.function_name != "home" and self.function_name != "download_stream_progress" else content_types['html']
    +35        
    +36        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    +37
    +38        return self.response
    +39
    +40    def _handle_search(self, term, stream_type=None):
    +41        regex_term = r"^.*{}.*$".format(term)
    +42        if stream_type:
    +43            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    +44            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    +45        return self.action(regex_term, return_type='JSON')
     
    @@ -251,9 +257,9 @@

    -
    15    def __init__(self, action, function_name):
    -16        self.function_name = function_name
    -17        self.action = action
    +            
    14    def __init__(self, action, function_name):
    +15        self.function_name = function_name
    +16        self.action = action
     
    @@ -294,49 +300,49 @@

    -
    45class FlaskWrap(Thread):
    -46
    -47    home_template = """
    -48<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    -49    """
    -50
    -51    host: str = ""
    -52    port: int = 0
    -53
    -54    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +            
    47class FlaskWrap(Thread):
    +48
    +49    home_template = """
    +50<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    +51    """
    +52
    +53    host: str = ""
    +54    port: int = 0
     55
    -56        log = logging.getLogger('werkzeug')
    -57        log.setLevel(logging.ERROR)
    -58
    -59        self.host = host
    -60        self.port = port
    -61        self.debug = debug
    -62
    -63        self.app = Flask(name)
    -64        self.xt = xtream
    -65        Thread.__init__(self)
    -66
    -67        # Configure Thread
    -68        self.name ="pyxtream REST API"
    -69        self.daemon = True
    -70
    -71        # Load HTML Home Template if any
    -72        if html_template_folder is not None:
    -73            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -74            if path.isfile(self.home_template_file_name):
    -75                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -76                    self.home_template = home_html.read()
    -77
    -78        # Add all endpoints
    -79        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -80        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -81        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    -82
    -83    def run(self):
    -84        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    -85
    -86    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -87        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +56    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +57
    +58        log = logging.getLogger('werkzeug')
    +59        log.setLevel(logging.ERROR)
    +60
    +61        self.host = host
    +62        self.port = port
    +63        self.debug = debug
    +64
    +65        self.app = Flask(name)
    +66        self.xt = xtream
    +67        Thread.__init__(self)
    +68
    +69        # Configure Thread
    +70        self.name ="pyxtream REST API"
    +71        self.daemon = True
    +72
    +73        # Load HTML Home Template if any
    +74        if html_template_folder is not None:
    +75            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +76            if path.isfile(self.home_template_file_name):
    +77                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +78                    self.home_template = home_html.read()
    +79
    +80        # Add all endpoints
    +81        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    +82        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    +83        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +84
    +85    def run(self):
    +86        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +87
    +88    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +89        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    @@ -358,34 +364,34 @@

    -
    54    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    -55
    -56        log = logging.getLogger('werkzeug')
    -57        log.setLevel(logging.ERROR)
    -58
    -59        self.host = host
    -60        self.port = port
    -61        self.debug = debug
    -62
    -63        self.app = Flask(name)
    -64        self.xt = xtream
    -65        Thread.__init__(self)
    -66
    -67        # Configure Thread
    -68        self.name ="pyxtream REST API"
    -69        self.daemon = True
    -70
    -71        # Load HTML Home Template if any
    -72        if html_template_folder is not None:
    -73            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -74            if path.isfile(self.home_template_file_name):
    -75                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -76                    self.home_template = home_html.read()
    -77
    -78        # Add all endpoints
    -79        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -80        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -81        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +            
    56    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    +57
    +58        log = logging.getLogger('werkzeug')
    +59        log.setLevel(logging.ERROR)
    +60
    +61        self.host = host
    +62        self.port = port
    +63        self.debug = debug
    +64
    +65        self.app = Flask(name)
    +66        self.xt = xtream
    +67        Thread.__init__(self)
    +68
    +69        # Configure Thread
    +70        self.name ="pyxtream REST API"
    +71        self.daemon = True
    +72
    +73        # Load HTML Home Template if any
    +74        if html_template_folder is not None:
    +75            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +76            if path.isfile(self.home_template_file_name):
    +77                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +78                    self.home_template = home_html.read()
    +79
    +80        # Add all endpoints
    +81        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    +82        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    +83        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
     
    @@ -491,7 +497,7 @@

    1181    @property
    -1182    def name(self):
    +1182    def name(self):
     1183        """A string used for identification purposes only.
     1184
     1185        It has no semantics. Multiple threads may be given the same name. The
    @@ -521,7 +527,7 @@ 

    1235    @property
    -1236    def daemon(self):
    +1236    def daemon(self):
     1237        """A boolean value indicating whether this thread is a daemon thread.
     1238
     1239        This must be set before start() is called, otherwise RuntimeError is
    @@ -560,8 +566,8 @@ 

    -
    83    def run(self):
    -84        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +            
    85    def run(self):
    +86        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
     
    @@ -586,8 +592,8 @@

    -
    86    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -87        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +            
    88    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +89        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    diff --git a/docs/pyxtream/schemaValidator.html b/docs/pyxtream/schemaValidator.html index 8dc3e2a..44a7ba5 100644 --- a/docs/pyxtream/schemaValidator.html +++ b/docs/pyxtream/schemaValidator.html @@ -96,13 +96,13 @@

    -
      1from enum import Enum
    +                        
      1from enum import Enum
       2
    -  3from jsonschema import exceptions, validate
    +  3from jsonschema import exceptions, validate
       4
       5
       6# class syntax
    -  7class SchemaType(Enum):
    +  7class SchemaType(Enum):
       8    SERIES = 1
       9    SERIES_INFO = 2
      10    LIVE = 3
    @@ -371,7 +371,7 @@ 

    273 } 274} 275 -276def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool: +276def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool: 277 278 if (schemaType == SchemaType.SERIES): 279 json_schema = series_schema @@ -410,7 +410,7 @@

    -
     9class SchemaType(Enum):
    +            
     9class SchemaType(Enum):
     10    SERIES = 1
     11    SERIES_INFO = 2
     12    LIVE = 3
    @@ -583,7 +583,7 @@ 

    -
    278def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
    +            
    278def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
     279
     280    if (schemaType == SchemaType.SERIES):
     281        json_schema = series_schema
    
    From 4beb95f4295fa8282e37c1d95ca73b7801b333ed Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:04:43 -0600
    Subject: [PATCH 22/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 8 +-------
     1 file changed, 1 insertion(+), 7 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index e9d8d1c..5fa8c69 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -26,14 +26,8 @@ jobs:
         - name: Install dependencies
           run: |
             python -m pip install --upgrade pip
    -        pip install -r flake8 pytest pytest-cov pdoc
    +        pip install -r pytest pytest-cov pdoc
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    -    - name: Lint with flake8
    -      run: |
    -        # stop the build if there are Python syntax errors or undefined names
    -        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
    -        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
    -        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
         - name: Test with pytest
           run: |
             pytest --cov=pyxtream pyxtream
    
    From ccf5624bb2399de4d105dfad8bac21c35d84abed Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:08:29 -0600
    Subject: [PATCH 23/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 5fa8c69..fde9338 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -30,4 +30,4 @@ jobs:
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
         - name: Test with pytest
           run: |
    -        pytest --cov=pyxtream pyxtream
    +        python3 -m pytest --cov=pyxtream pyxtream
    
    From 57f85b0ca9d3c1634b0bfce1ffbd785f641f09fb Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:32:23 -0600
    Subject: [PATCH 24/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index fde9338..1fc49df 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -26,7 +26,7 @@ jobs:
         - name: Install dependencies
           run: |
             python -m pip install --upgrade pip
    -        pip install -r pytest pytest-cov pdoc
    +        pip install pytest pytest-cov pdoc
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
         - name: Test with pytest
           run: |
    
    From bdd5ce298abcd2ad5a7a65a03f1a5ae42ff11277 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:38:37 -0600
    Subject: [PATCH 25/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 1fc49df..0cb4c76 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -30,4 +30,5 @@ jobs:
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
         - name: Test with pytest
           run: |
    +        pwd
             python3 -m pytest --cov=pyxtream pyxtream
    
    From d514f8df83a4198999c6f1a395de3b972064c9cf Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:41:50 -0600
    Subject: [PATCH 26/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 3 +--
     1 file changed, 1 insertion(+), 2 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 0cb4c76..84e79ef 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -30,5 +30,4 @@ jobs:
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
         - name: Test with pytest
           run: |
    -        pwd
    -        python3 -m pytest --cov=pyxtream pyxtream
    +        python3 -m pytest --cov=pyxtream test/pyxtream
    
    From eb11650cfab8071a0de944fd0da7df838cc970b4 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 15:44:33 -0600
    Subject: [PATCH 27/41] Updated pytest workflow
    
    ---
     .github/workflows/run_pytest.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 84e79ef..561c0c4 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -30,4 +30,4 @@ jobs:
             if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
         - name: Test with pytest
           run: |
    -        python3 -m pytest --cov=pyxtream test/pyxtream
    +        python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    
    From 474477477fb7ec6f1eeadd91df14892c0833e463 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 19:10:38 -0600
    Subject: [PATCH 28/41] Fixed misspellings
    
    ---
     pyxtream/pyxtream.py | 20 ++++++++++----------
     1 file changed, 10 insertions(+), 10 deletions(-)
    
    diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py
    index 94df6d6..e4a5d3f 100755
    --- a/pyxtream/pyxtream.py
    +++ b/pyxtream/pyxtream.py
    @@ -491,7 +491,7 @@ def download_video(self, stream_id: int) -> str:
             """Download Video from Stream ID
     
             Args:
    -            stream_id (int): Stirng identifing the stream ID
    +            stream_id (int): String identifying the stream ID
     
             Returns:
                 str: Absolute Path Filename where the file was saved. Empty if could not download
    @@ -711,7 +711,7 @@ def _load_from_file(self, filename) -> dict:
     
                 my_data = None
     
    -            # Get the enlapsed seconds since last file update
    +            # Get the elapsed seconds since last file update
                 file_age_sec = time.time() - osp.getmtime(full_filename)
                 # If the file was updated less than the threshold time,
                 # it means that the file is still fresh, we can load it.
    @@ -739,7 +739,7 @@ def _save_to_file(self, data_list: dict, filename: str) -> bool:
                 filename (str): Name of the file
     
             Returns:
    -            bool: True if successfull, False if error
    +            bool: True if successful, False if error
             """
             if data_list is None:
                 return False
    @@ -765,7 +765,7 @@ def load_iptv(self) -> bool:
               Groups are for all three channel types, Live TV, VOD, and Series
     
             Returns:
    -            bool: True if successfull, False if error
    +            bool: True if successful, False if error
             """
             # If pyxtream has not authenticated the connection, return empty
             if self.state["authenticated"] is False:
    @@ -905,7 +905,7 @@ def load_iptv(self) -> bool:
                             elif stream_channel["category_id"] != "1":
                                 pass
     
    -                        # Find the first occurence of the group that the
    +                        # Find the first occurrence of the group that the
                             # Channel or Stream is pointing to
                             the_group = next(
                                 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    @@ -1104,7 +1104,7 @@ def _load_categories_from_provider(self, stream_type: str):
                 stream_type (str): Stream type can be Live, VOD, Series
     
             Returns:
    -            [type]: JSON if successfull, otherwise None
    +            [type]: JSON if successful, otherwise None
             """
             url = ""
             if stream_type == self.live_type:
    @@ -1126,7 +1126,7 @@ def _load_streams_from_provider(self, stream_type: str):
                 stream_type (str): Stream type can be Live, VOD, Series
     
             Returns:
    -            [type]: JSON if successfull, otherwise None
    +            [type]: JSON if successful, otherwise None
             """
             url = ""
             if stream_type == self.live_type:
    @@ -1149,7 +1149,7 @@ def _load_streams_by_category_from_provider(self, stream_type: str, category_id)
                 category_id ([type]): Category/Group ID.
     
             Returns:
    -            [type]: JSON if successfull, otherwise None
    +            [type]: JSON if successful, otherwise None
             """
             url = ""
     
    @@ -1166,14 +1166,14 @@ def _load_streams_by_category_from_provider(self, stream_type: str, category_id)
     
         # GET SERIES Info
         def _load_series_info_by_id_from_provider(self, series_id: str, return_type: str = "DICT"):
    -        """Gets informations about a Serie
    +        """Gets information about a Serie
     
             Args:
                 series_id (str): Serie ID as described in Group
                 return_type (str, optional): Output format, 'DICT' or 'JSON'. Defaults to "DICT".
     
             Returns:
    -            [type]: JSON if successfull, otherwise None
    +            [type]: JSON if successful, otherwise None
             """
             data = self._get_request(self.get_series_info_URL_by_ID(series_id))
             if return_type == "JSON":
    
    From 87cb8bc0536fc84dc8524ea8a5d1cd2d7a741c7c Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 19:11:02 -0600
    Subject: [PATCH 29/41] Updated python for testing
    
    ---
     .github/workflows/run_pytest.yml | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 561c0c4..af6bc63 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -19,10 +19,10 @@ jobs:
     
         steps:
         - uses: actions/checkout@v4
    -    - name: Set up Python 3.10
    +    - name: Set up Python 3.12
           uses: actions/setup-python@v3
           with:
    -        python-version: "3.10"
    +        python-version: "3.12"
         - name: Install dependencies
           run: |
             python -m pip install --upgrade pip
    
    From 27e688fb0184b0b4bb4e0229c2c38617c2019e01 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 19:14:23 -0600
    Subject: [PATCH 30/41] Fixing Workflow
    
    ---
     .github/workflows/run_pytest.yml |  2 +-
     test/requirements.txt            | 11 +++++++++++
     2 files changed, 12 insertions(+), 1 deletion(-)
     create mode 100644 test/requirements.txt
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index af6bc63..9f61e09 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -27,7 +27,7 @@ jobs:
           run: |
             python -m pip install --upgrade pip
             pip install pytest pytest-cov pdoc
    -        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    +        if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
         - name: Test with pytest
           run: |
             python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    diff --git a/test/requirements.txt b/test/requirements.txt
    new file mode 100644
    index 0000000..2e6fbf8
    --- /dev/null
    +++ b/test/requirements.txt
    @@ -0,0 +1,11 @@
    +attrs==25.1.0 ; python_version >= "3.9"
    +certifi==2025.1.31 ; python_version >= "3.9"
    +charset-normalizer==3.4.1 ; python_version >= "3.9"
    +idna==3.10 ; python_version >= "3.9"
    +jsonschema-specifications==2024.10.1 ; python_version >= "3.9"
    +jsonschema==4.23.0 ; python_version >= "3.9"
    +referencing==0.36.2 ; python_version >= "3.9"
    +requests==2.32.3 ; python_version >= "3.9"
    +rpds-py==0.22.3 ; python_version >= "3.9"
    +typing-extensions==4.12.2 ; python_version < "3.13" and python_version >= "3.9"
    +urllib3==2.3.0 ; python_version >= "3.9"
    
    From 287a7b7581329ec66693373f26532ae477109bb3 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 19:21:54 -0600
    Subject: [PATCH 31/41] Multiversions of Python
    
    ---
     .github/workflows/run_pytest.yml | 7 +++++--
     1 file changed, 5 insertions(+), 2 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 9f61e09..dca2c63 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -16,13 +16,16 @@ jobs:
       build:
     
         runs-on: ubuntu-latest
    +    strategy:
    +      matrix:
    +        python-version: ["3.9", "3.10", "3.11", "3.12"]
     
         steps:
         - uses: actions/checkout@v4
    -    - name: Set up Python 3.12
    +    - name: Set up Python ${{ matrix.python-version }}
           uses: actions/setup-python@v3
           with:
    -        python-version: "3.12"
    +        python-version: ${{ matrix.python-version }}
         - name: Install dependencies
           run: |
             python -m pip install --upgrade pip
    
    From 655960cf008cb558f888df87af2ec0f9cff518f9 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 20:10:40 -0600
    Subject: [PATCH 32/41] Add flake8 checker
    
    ---
     .github/workflows/run_pytest.yml | 5 ++++-
     1 file changed, 4 insertions(+), 1 deletion(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index dca2c63..ebb210f 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -29,8 +29,11 @@ jobs:
         - name: Install dependencies
           run: |
             python -m pip install --upgrade pip
    -        pip install pytest pytest-cov pdoc
    +        pip install flake8 pytest pytest-cov pdoc
             if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
    +    - name: Check Syntax with flake8
    +      run: |
    +        flake8 pyxtream
         - name: Test with pytest
           run: |
             python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    
    From 73b13aedd2069cdbe3fa0db49d0f52659f101b85 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 21:00:47 -0600
    Subject: [PATCH 33/41] Fixed flake8 errors
    
    ---
     .github/workflows/run_pytest.yml |  2 +-
     pyxtream/pyxtream.py             | 64 ++++++++++++++++----------------
     2 files changed, 33 insertions(+), 33 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index ebb210f..5b847e4 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -33,7 +33,7 @@ jobs:
             if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
         - name: Check Syntax with flake8
           run: |
    -        flake8 pyxtream
    +        flake8 --extend-ignore=E501 pyxtream
         - name: Test with pytest
           run: |
             python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py
    index e4a5d3f..f742871 100755
    --- a/pyxtream/pyxtream.py
    +++ b/pyxtream/pyxtream.py
    @@ -4,7 +4,7 @@
     
     Module handles downloading xtream data.
     
    -Part of this content comes from 
    +Part of this content comes from
     - https://github.com/chazlarson/py-xtream-codes/blob/master/xtream.py
     - https://github.com/linuxmint/hypnotix
     
    @@ -215,9 +215,9 @@ def __init__(self, xtream: object, series_info, group_title, episode_info) -> No
             self.logo = series_info["cover"]
             self.logo_path = xtream._get_logo_local_path(self.logo)
     
    -        self.url =  f"{xtream.server}/series/" \
    -                    f"{xtream.authorization['username']}/" \
    -                    f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
    +        self.url = f"{xtream.server}/series/" \
    +                   f"{xtream.authorization['username']}/" \
    +                   f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
     
             # Check that the constructed URL is valid
             if not xtream._validate_url(self.url):
    @@ -271,9 +271,9 @@ def __init__(self, xtream: object, series_info):
             if "genre" in series_info.keys():
                 self.genre = series_info["genre"]
     
    -        self.url =  f"{xtream.server}/series/" \
    -                    f"{xtream.authorization['username']}/" \
    -                    f"{xtream.authorization['password']}/{self.series_id}/"
    +        self.url = f"{xtream.server}/series/" \
    +                   f"{xtream.authorization['username']}/" \
    +                   f"{xtream.authorization['password']}/{self.series_id}/"
     
         def export_json(self):
             jsondata = {}
    @@ -283,6 +283,7 @@ def export_json(self):
     
             return jsondata
     
    +
     class Season:
         # Required by Hypnotix
         name = ""
    @@ -291,6 +292,7 @@ def __init__(self, name):
             self.name = name
             self.episodes = {}
     
    +
     class XTream:
     
         name = ""
    @@ -326,13 +328,13 @@ class XTream:
         hide_adult_content = False
     
         live_catch_all_group = Group(
    -        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, live_type
    +        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, live_type
         )
         vod_catch_all_group = Group(
    -        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, vod_type
    +        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, vod_type
         )
         series_catch_all_group = Group(
    -        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, series_type
    +        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, series_type
         )
         # If the cached JSON file is older than threshold_time_sec then load a new
         # JSON dictionary from the provider
    @@ -356,7 +358,7 @@ def __init__(
             validate_json: bool = False,
             enable_flask: bool = False,
             debug_flask: bool = True
    -        ):
    +            ):
             """Initialize Xtream Class
     
             Args:
    @@ -378,9 +380,9 @@ def __init__(
     
             - Note 1: If it fails to authorize with provided username and password,
                     auth_data will be an empty dictionary.
    -        - Note 2: The JSON validation option will take considerable amount of time and it should be 
    +        - Note 2: The JSON validation option will take considerable amount of time and it should be
                       used only as a debug tool. The Xtream API JSON from the provider passes through a
    -                  schema that represent the best available understanding of how the Xtream API 
    +                  schema that represent the best available understanding of how the Xtream API
                       works.
             """
             self.server = provider_url
    @@ -396,7 +398,7 @@ def __init__(
             self.app_fullpath = osp.dirname(osp.realpath(__file__))
     
             # prepare location of local html template
    -        self.html_template_folder = osp.join(self.app_fullpath,"html")
    +        self.html_template_folder = osp.join(self.app_fullpath, "html")
     
             # if the cache_path is specified, test that it is a directory
             if self.cache_path != "":
    @@ -415,7 +417,7 @@ def __init__(
             if headers is not None:
                 self.connection_headers = headers
             else:
    -            self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"}
    +            self.connection_headers = {'User-Agent': "Wget/1.20.3 (linux-gnu)"}
     
             self.authenticate()
     
    @@ -435,7 +437,7 @@ def __init__(
                     print("Web interface not running")
     
         def get_last_7days(self):
    -        return json.dumps(self.movies_7days, default= lambda x:x.export_json())
    +        return json.dumps(self.movies_7days, default=lambda x: x.export_json())
     
         def search_stream(self, keyword: str,
                           ignore_case: bool = True,
    @@ -506,11 +508,11 @@ def download_video(self, stream_id: int) -> str:
                 if stream.id == stream_id:
                     url = stream.url
                     fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    -                filename = osp.join(self.cache_path,fn)
    +                filename = osp.join(self.cache_path, fn)
     
             # If the url was correctly built and file does not exists, start downloading
             if url != "":
    -            if not self._download_video_impl(url,filename):
    +            if not self._download_video_impl(url, filename):
                     return "Error"
     
             return filename
    @@ -551,10 +553,10 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
                 # If there is an answer from the remote server
                 if response.status_code in (200, 206):
                     # Get content type Binary or Text
    -                content_type = response.headers.get('content-type',None)
    +                content_type = response.headers.get('content-type', None)
     
                     # Get total playlist byte size
    -                total_content_size = int(response.headers.get('content-length',None))
    +                total_content_size = int(response.headers.get('content-length', None))
                     total_content_size_mb = total_content_size/mb_size
     
                     # Set downloaded size
    @@ -571,9 +573,9 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
                         with open(fullpath_filename, mode) as file:
     
                             # Grab data by block_bytes
    -                        for data in response.iter_content(block_bytes,decode_unicode=False):
    +                        for data in response.iter_content(block_bytes, decode_unicode=False):
                                 downloaded_bytes += block_bytes
    -                            progress(downloaded_bytes,total_content_size,"Downloading")
    +                            progress(downloaded_bytes, total_content_size, "Downloading")
                                 self.download_progress = downloaded_bytes
                                 file.write(data)
     
    @@ -662,7 +664,7 @@ def authenticate(self):
                         i = 31
                     except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
                         time.sleep(1)
    -                    print(f"{i} ", end='',flush=True)
    +                    print(f"{i} ", end='', flush=True)
                         i += 1
     
                 if r is not None:
    @@ -787,7 +789,7 @@ def load_iptv(self) -> bool:
                 pass
     
             for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    -            ## Get GROUPS
    +            # Get GROUPS
     
                 # Try loading local file
                 dt = 0
    @@ -798,13 +800,13 @@ def load_iptv(self) -> bool:
                     # Load all Groups and save file locally
                     all_cat = self._load_categories_from_provider(loading_stream_type)
                     if all_cat is not None:
    -                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
    +                    self._save_to_file(all_cat, f"all_groups_{loading_stream_type}.json")
                 dt = timer() - start
     
                 # If we got the GROUPS data, show the statistics and load GROUPS
                 if all_cat is not None:
                     print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    -                ## Add GROUPS to dictionaries
    +                # Add GROUPS to dictionaries
     
                     # Add the catch-all-errors group
                     if loading_stream_type == self.live_type:
    @@ -830,7 +832,7 @@ def load_iptv(self) -> bool:
                     print(f" - Could not load {loading_stream_type} Groups")
                     break
     
    -            ## Get Streams
    +            # Get Streams
     
                 # Try loading local file
                 dt = 0
    @@ -840,13 +842,13 @@ def load_iptv(self) -> bool:
                 if all_streams is None:
                     # Load all Streams and save file locally
                     all_streams = self._load_streams_from_provider(loading_stream_type)
    -                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
    +                self._save_to_file(all_streams, f"all_stream_{loading_stream_type}.json")
                 dt = timer() - start
     
                 # If we got the STREAMS data, show the statistics and load Streams
                 if all_streams is not None:
                     print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    -                ## Add Streams to dictionaries
    +                # Add Streams to dictionaries
     
                     skipped_adult_content = 0
                     skipped_no_name_content = 0
    @@ -926,7 +928,6 @@ def load_iptv(self) -> bool:
                                     group_title = self.series_catch_all_group.name
                                     the_group = self.series_catch_all_group
     
    -
                             if loading_stream_type == self.series_type:
                                 # Load all Series
                                 new_series = Serie(self, stream_channel)
    @@ -1011,7 +1012,6 @@ def get_series_info_by_id(self, get_series: dict):
     
             for series_info in series_seasons["seasons"]:
                 season_name = series_info["name"]
    -            season_key = series_info['season_number']
                 season = Season(season_name)
                 get_series.seasons[season_name] = season
                 if "episodes" in series_seasons.keys():
    @@ -1207,7 +1207,7 @@ def allLiveEpgByStream(self, stream_id):
         def allEpg(self):
             return self._get_request(self.get_all_epg_URL())
     
    -    ## URL-builder methods
    +    # URL-builder methods
         def get_live_categories_URL(self) -> str:
             return f"{self.base_url}&action=get_live_categories"
     
    
    From 68d782e0aa491fee1cb51e372f712b06c7d24ffb Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 21:14:16 -0600
    Subject: [PATCH 34/41] Flake8 fixes
    
    ---
     .github/workflows/run_pytest.yml |   2 +-
     pyxtream/progress.py             |  20 ++--
     pyxtream/rest_api.py             |  30 ++---
     pyxtream/schemaValidator.py      | 181 ++++++++++++++++---------------
     4 files changed, 118 insertions(+), 115 deletions(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index 5b847e4..ef5ea58 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -33,7 +33,7 @@ jobs:
             if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
         - name: Check Syntax with flake8
           run: |
    -        flake8 --extend-ignore=E501 pyxtream
    +        flake8 --extend-ignore=E501 pyxtream/pyxtream.py rest_api.py schemaValidator.py version.py progress.py
         - name: Test with pytest
           run: |
             python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    diff --git a/pyxtream/progress.py b/pyxtream/progress.py
    index e837f58..6d93fa8 100644
    --- a/pyxtream/progress.py
    +++ b/pyxtream/progress.py
    @@ -1,21 +1,21 @@
     # The MIT License (MIT)
     # Copyright (c) 2016 Vladimir Ignatev
     #
    -# Permission is hereby granted, free of charge, to any person obtaining 
    -# a copy of this software and associated documentation files (the "Software"), 
    -# to deal in the Software without restriction, including without limitation 
    -# the rights to use, copy, modify, merge, publish, distribute, sublicense, 
    -# and/or sell copies of the Software, and to permit persons to whom the Software 
    +# Permission is hereby granted, free of charge, to any person obtaining
    +# a copy of this software and associated documentation files (the "Software"),
    +# to deal in the Software without restriction, including without limitation
    +# the rights to use, copy, modify, merge, publish, distribute, sublicense,
    +# and/or sell copies of the Software, and to permit persons to whom the Software
     # is furnished to do so, subject to the following conditions:
    -# 
    -# The above copyright notice and this permission notice shall be included 
    +#
    +# The above copyright notice and this permission notice shall be included
     # in all copies or substantial portions of the Software.
     #
    -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
     # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
     # FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
    -# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
    +# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
     # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     import sys
     
    diff --git a/pyxtream/rest_api.py b/pyxtream/rest_api.py
    index 0901fbf..5149ecd 100644
    --- a/pyxtream/rest_api.py
    +++ b/pyxtream/rest_api.py
    @@ -6,6 +6,7 @@
     from flask import Flask
     from flask import Response as FlaskResponse
     
    +
     class EndpointAction(object):
     
         response: FlaskResponse
    @@ -22,13 +23,13 @@ def __call__(self, **args):
     
             handlers = {
                 # Add handlers here
    -            "stream_search_generic":    lambda: self._handle_search(args['term']),
    -            "stream_search_with_type":  lambda: self._handle_search(args['term'], args.get('type')),
    -            "download_stream":          lambda: self.action(int(args['stream_id'])),
    +            "stream_search_generic": lambda: self._handle_search(args['term']),
    +            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    +            "download_stream": lambda: self.action(int(args['stream_id'])),
                 "download_stream_progress": lambda: self.action,
    -            "get_last_7days":           lambda: self.action(),
    -            "home":                     lambda: self.action,
    -            "get_series":               lambda: self.action(int(args['series_id']), "JSON")
    +            "get_last_7days": lambda: self.action(),
    +            "home": lambda: self.action,
    +            "get_series": lambda: self.action(int(args['series_id']), "JSON")
             }
     
             answer = handlers[self.function_name]()
    @@ -44,6 +45,7 @@ def _handle_search(self, term, stream_type=None):
                 return self.action(regex_term, return_type='JSON', stream_type=stream_type)
             return self.action(regex_term, return_type='JSON')
     
    +
     class FlaskWrap(Thread):
     
         home_template = """
    @@ -69,18 +71,18 @@ def __init__(self, name, xtream: object, html_template_folder: str = None,
             Thread.__init__(self)
     
             # Configure Thread
    -        self.name ="pyxtream REST API"
    +        self.name = "pyxtream REST API"
             self.daemon = True
     
             # Load HTML Home Template if any
             if html_template_folder is not None:
    -            self.home_template_file_name = path.join(html_template_folder,"index.html")
    +            self.home_template_file_name = path.join(html_template_folder, "index.html")
                 if path.isfile(self.home_template_file_name):
    -                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    +                with open(self.home_template_file_name, 'r', encoding="utf-8") as home_html:
                         self.home_template = home_html.read()
     
             # Add all endpoints
    -        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,"home"])
    +        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template, "home"])
             self.add_endpoint(endpoint='/stream_search/',
                               endpoint_name='stream_search_generic',
                               handler=[self.xt.search_stream, 'stream_search_generic']
    @@ -91,19 +93,19 @@ def __init__(self, name, xtream: object, html_template_folder: str = None,
                               )
             self.add_endpoint(endpoint='/download_stream//',
                               endpoint_name='download_stream',
    -                          handler=[self.xt.download_video,"download_stream"]
    +                          handler=[self.xt.download_video, "download_stream"]
                               )
             self.add_endpoint(endpoint='/download_stream_progress//',
                               endpoint_name='download_stream_progress',
    -                          handler=[self.xt.download_progress,"download_stream_progress"]
    +                          handler=[self.xt.download_progress, "download_stream_progress"]
                               )
             self.add_endpoint(endpoint='/get_last_7days',
                               endpoint_name='get_last_7days',
    -                          handler=[self.xt.get_last_7days,"get_last_7days"]
    +                          handler=[self.xt.get_last_7days, "get_last_7days"]
                               )
             self.add_endpoint(endpoint='/get_series/',
                               endpoint_name='get_series',
    -                          handler=[self.xt._load_series_info_by_id_from_provider,"get_series"]
    +                          handler=[self.xt._load_series_info_by_id_from_provider, "get_series"]
                               )
     
         def run(self):
    diff --git a/pyxtream/schemaValidator.py b/pyxtream/schemaValidator.py
    index dd3cfd6..1645729 100644
    --- a/pyxtream/schemaValidator.py
    +++ b/pyxtream/schemaValidator.py
    @@ -13,6 +13,7 @@ class SchemaType(Enum):
         CHANNEL = 5
         GROUP = 6
     
    +
     series_schema = {
         "$schema": "https://json-schema.org/draft/2020-12/schema",
         "$id": "https://example.com/product.schema.json",
    @@ -28,13 +29,13 @@ class SchemaType(Enum):
                             "type": "string",
                             "format": "date"
                         },
    -                    "episode_count": { "type": "integer" },
    -                    "id": { "type": "integer" },
    -                    "name": { "type": "string" },
    -                    "overview": { "type": "string" },
    -                    "season_number": { "type": "integer" },
    +                    "episode_count": {"type": "integer"},
    +                    "id": {"type": "integer"},
    +                    "name": {"type": "string"},
    +                    "overview": {"type": "string"},
    +                    "season_number": {"type": "integer"},
                         "cover": {
    -                        "type": "string", 
    +                        "type": "string",
                             "format": "uri",
                             "qt-uri-protocols": [
                                 "http",
    @@ -42,7 +43,7 @@ class SchemaType(Enum):
                             ]
                         },
                         "cover_big": {
    -                        "type": "string", 
    +                        "type": "string",
                             "format": "uri",
                             "qt-uri-protocols": [
                                 "http",
    @@ -58,7 +59,7 @@ class SchemaType(Enum):
             },
             "info": {
                 "properties": {
    -                "name": { "type": "string" },
    +                "name": {"type": "string"},
                     "cover": {
                         "type": "string",
                         "format": "uri",
    @@ -67,14 +68,14 @@ class SchemaType(Enum):
                             "https"
                         ]
                     },
    -                "plot": { "type": "string" },
    -                "cast": { "type": "string" },
    -                "director": { "type": "string" },
    -                "genre": { "type": "string" },
    -                "releaseDate": { "type": "string", "format": "date" },
    -                "last_modified": { "type": "string", "format": "integer" },
    -                "rating": { "type": "string", "format": "integer" },
    -                "rating_5based": { "type": "number" },
    +                "plot": {"type": "string"},
    +                "cast": {"type": "string"},
    +                "director": {"type": "string"},
    +                "genre": {"type": "string"},
    +                "releaseDate": {"type": "string", "format": "date"},
    +                "last_modified": {"type": "string", "format": "integer"},
    +                "rating": {"type": "string", "format": "integer"},
    +                "rating_5based": {"type": "number"},
                     "backdrop_path": {
                         "type": "array",
                         "items": {
    @@ -86,9 +87,9 @@ class SchemaType(Enum):
                             ]
                         }
                     },
    -                "youtube_trailed": { "type": "string" },
    -                "episode_run_time": { "type": "string", "format": "integer" },
    -                "category_id": { "type": "string", "format": "integer" }
    +                "youtube_trailed": {"type": "string"},
    +                "episode_run_time": {"type": "string", "format": "integer"},
    +                "category_id": {"type": "string", "format": "integer"}
                 },
                 "required": [
                     "name"
    @@ -101,20 +102,20 @@ class SchemaType(Enum):
                         "type": "array",
                         "items": {
                             "properties": {
    -                            "id": { "type": "string", "format": "integer" },
    -                            "episode_num": {"type": "integer" },
    -                            "title": { "type": "string" },
    -                            "container_extension": { "type": "string" },
    +                            "id": {"type": "string", "format": "integer"},
    +                            "episode_num": {"type": "integer"},
    +                            "title": {"type": "string"},
    +                            "container_extension": {"type": "string"},
                                 "info": {
                                     "type": "object",
                                     "items": {
    -                                    "plot": { "type": "string" }
    +                                    "plot": {"type": "string"}
                                     }
                                 },
    -                            "customer_sid": { "type": "string" },
    -                            "added": { "type": "string", "format": "integer" },
    -                            "season": { "type": "integer" },
    -                            "direct_source": { "type": "string" }
    +                            "customer_sid": {"type": "string"},
    +                            "added": {"type": "string", "format": "integer"},
    +                            "season": {"type": "integer"},
    +                            "direct_source": {"type": "string"}
                             }
                         }
                     },
    @@ -134,7 +135,7 @@ class SchemaType(Enum):
         "description": "xtream API Series Info Schema",
         "type": "object",
         "properties": {
    -        "name": { "type": "string" },
    +        "name": {"type": "string"},
             "cover": {
                 "type": "string",
                 "format": "uri",
    @@ -143,14 +144,14 @@ class SchemaType(Enum):
                     "https"
                 ]
             },
    -        "plot": { "type": "string" },
    -        "cast": { "type": "string" },
    -        "director": { "type": "string" },
    -        "genre": { "type": "string" },
    -        "releaseDate": { "type": "string", "format": "date" },
    -        "last_modified": { "type": "string", "format": "integer" },
    -        "rating": { "type": "string", "format": "integer" },
    -        "rating_5based": { "type": "number" },
    +        "plot": {"type": "string"},
    +        "cast": {"type": "string"},
    +        "director": {"type": "string"},
    +        "genre": {"type": "string"},
    +        "releaseDate": {"type": "string", "format": "date"},
    +        "last_modified": {"type": "string", "format": "integer"},
    +        "rating": {"type": "string", "format": "integer"},
    +        "rating_5based": {"type": "number"},
             "backdrop_path": {
                 "anyOf": [
                     {
    @@ -167,9 +168,9 @@ class SchemaType(Enum):
                     {"type": "string"}
                 ]
             },
    -        "youtube_trailed": { "type": "string" },
    -        "episode_run_time": { "type": "string", "format": "integer" },
    -        "category_id": { "type": "string", "format": "integer" }
    +        "youtube_trailed": {"type": "string"},
    +        "episode_run_time": {"type": "string", "format": "integer"},
    +        "category_id": {"type": "string", "format": "integer"}
         },
         "required": [
             "name",
    @@ -183,39 +184,39 @@ class SchemaType(Enum):
         "description": "xtream API Live Schema",
         "type": "object",
         "properties": {
    -        "num": { "type": "integer" },
    -        "name": { "type": "string" },
    -        "stream_type": { "type": "string" },
    -        "stream_id": { "type": "integer" },
    +        "num": {"type": "integer"},
    +        "name": {"type": "string"},
    +        "stream_type": {"type": "string"},
    +        "stream_id": {"type": "integer"},
             "stream_icon": {
                 "anyOf": [
                     {
    -                "type": "string",
    -                "format": "uri",
    -                "qt-uri-protocols": [
    -                    "http",
    -                    "https"
    -                ]
    +                    "type": "string",
    +                    "format": "uri",
    +                    "qt-uri-protocols": [
    +                        "http",
    +                        "https"
    +                    ]
                     },
    -                { "type": "null" }
    +                {"type": "null"}
                 ]
             },
             "epg_channel_id": {
    -            "anyOf": [ 
    -                { "type": "null" },
    -                { "type": "string" }
    +            "anyOf": [
    +                {"type": "null"},
    +                {"type": "string"}
                 ]
             },
    -        "added": { "type": "string", "format": "integer" },
    -        "is_adult": { "type": "string", "format":"number" },
    -        "category_id": { "type": "string" },
    -        "custom_sid": { "type": "string" },
    -        "tv_archive": { "type": "number" },
    -        "direct_source": { "type": "string" },
    -        "tv_archive_duration":{
    +        "added": {"type": "string", "format": "integer"},
    +        "is_adult": {"type": "string", "format": "number"},
    +        "category_id": {"type": "string"},
    +        "custom_sid": {"type": "string"},
    +        "tv_archive": {"type": "number"},
    +        "direct_source": {"type": "string"},
    +        "tv_archive_duration": {
                 "anyOf": [
    -                { "type": "number" },
    -                { "type": "string", "format": "integer" }
    +                {"type": "number"},
    +                {"type": "string", "format": "integer"}
                 ]
             }
         }
    @@ -227,37 +228,37 @@ class SchemaType(Enum):
         "description": "xtream API VOD Schema",
         "type": "object",
         "properties": {
    -        "num": { "type": "integer" },
    -        "name": { "type": "string" },
    -        "stream_type": { "type": "string" },
    -        "stream_id": { "type": "integer" },
    +        "num": {"type": "integer"},
    +        "name": {"type": "string"},
    +        "stream_type": {"type": "string"},
    +        "stream_id": {"type": "integer"},
             "stream_icon": {
                 "anyOf": [
                     {
    -                "type": "string",
    -                "format": "uri",
    -                "qt-uri-protocols": [
    -                    "http",
    -                    "https"
    -                ]
    +                    "type": "string",
    +                    "format": "uri",
    +                    "qt-uri-protocols": [
    +                        "http",
    +                        "https"
    +                    ]
                     },
    -                { "type": "null" }
    +                {"type": "null"}
                 ]
             },
    -        "rating": { 
    -            "anyOf": [ 
    -                { "type": "null" },
    -                { "type": "string", "format": "integer" },
    -                { "type": "number" }
    +        "rating": {
    +            "anyOf": [
    +                {"type": "null"},
    +                {"type": "string", "format": "integer"},
    +                {"type": "number"}
                 ]
             },
    -        "rating_5based": { "type": "number" },
    -        "added": { "type": "string", "format": "integer" },
    -        "is_adult": { "type": "string", "format":"number" },
    -        "category_id": { "type": "string" },
    -        "container_extension": { "type": "string" },
    -        "custom_sid": { "type": "string" },
    -        "direct_source": { "type": "string" }
    +        "rating_5based": {"type": "number"},
    +        "added": {"type": "string", "format": "integer"},
    +        "is_adult": {"type": "string", "format": "number"},
    +        "category_id": {"type": "string"},
    +        "container_extension": {"type": "string"},
    +        "custom_sid": {"type": "string"},
    +        "direct_source": {"type": "string"}
         }
     }
     channel_schema = {}
    @@ -268,12 +269,13 @@ class SchemaType(Enum):
         "description": "xtream API Group Schema",
         "type": "object",
         "properties": {
    -        "category_id": { "type": "string" },
    -        "category_name": { "type": "string" },
    -        "parent_id": { "type": "integer" }
    +        "category_id": {"type": "string"},
    +        "category_name": {"type": "string"},
    +        "parent_id": {"type": "integer"}
         }
     }
     
    +
     def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
     
         if (schemaType == SchemaType.SERIES):
    @@ -291,7 +293,6 @@ def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
         else:
             json_schema = "{}"
     
    -
         try:
             validate(instance=jsonData, schema=json_schema)
         except exceptions.ValidationError as err:
    
    From 2f9d7a0a4e37af463a29485a167db73bb0520661 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 21:51:55 -0600
    Subject: [PATCH 35/41] Fixed yaml
    
    ---
     .github/workflows/run_pytest.yml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/.github/workflows/run_pytest.yml b/.github/workflows/run_pytest.yml
    index ef5ea58..e826202 100644
    --- a/.github/workflows/run_pytest.yml
    +++ b/.github/workflows/run_pytest.yml
    @@ -33,7 +33,7 @@ jobs:
             if [ -f test/requirements.txt ]; then pip install -r test/requirements.txt; fi
         - name: Check Syntax with flake8
           run: |
    -        flake8 --extend-ignore=E501 pyxtream/pyxtream.py rest_api.py schemaValidator.py version.py progress.py
    +        flake8 --extend-ignore=E501 pyxtream/pyxtream.py pyxtream/rest_api.py pyxtream/schemaValidator.py pyxtream/version.py pyxtream/progress.py
         - name: Test with pytest
           run: |
             python3 -m pytest --cov=pyxtream test/test_pyxtream.py
    
    From 7b248dda2f28ccc164abe353d774e4446f647542 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Mon, 17 Feb 2025 22:08:27 -0600
    Subject: [PATCH 36/41] More flake8 fixes
    
    ---
     functional_test.py   | 15 ++++++++-------
     pyxtream/pyxtream.py |  4 +++-
     2 files changed, 11 insertions(+), 8 deletions(-)
    
    diff --git a/functional_test.py b/functional_test.py
    index 3513b8a..4242158 100755
    --- a/functional_test.py
    +++ b/functional_test.py
    @@ -17,6 +17,7 @@
         print("Please edit this file with the provider credentials")
         sys.exit()
     
    +
     def str2list(input_string: str) -> list:
     
         """Convert a string with comma delimited numbers into a python list of integers
    @@ -27,7 +28,7 @@ def str2list(input_string: str) -> list:
         Returns:
             list: list of integers containing the commands
         """
    -    # conver to the list
    +    # convert to list
         output_list = input_string.split(",")
     
         # convert each element as integers
    @@ -40,6 +41,7 @@ def str2list(input_string: str) -> list:
     
         return li
     
    +
     print(f"pyxtream version {__version__}")
     
     xt = XTream(
    @@ -88,7 +90,6 @@ def str2list(input_string: str) -> list:
             print(f"\t[{choice}]: ")
     
             if choice == 0:
    -            #xt.flaskapp.shutdown()
                 sys.exit(0)
     
             elif choice == 1:
    @@ -97,7 +98,7 @@ def str2list(input_string: str) -> list:
     
             elif choice == 2:
                 search_string = input("Search for REGEX (ex. '^Destiny.*$'): ")
    -            search_result_obj = xt.search_stream(search_string, stream_type=("movies","series"))
    +            search_result_obj = xt.search_stream(search_string, stream_type=("movies", "series"))
                 result_number = len(search_result_obj)
                 print(f"\tFound {result_number} results")
                 if result_number < 10:
    @@ -110,7 +111,7 @@ def str2list(input_string: str) -> list:
             elif choice == 3:
                 search_string = input("Search for text: ")
                 search_result_obj = xt.search_stream(
    -                rf"^.*{search_string}.*$", stream_type=("movies","series")
    +                rf"^.*{search_string}.*$", stream_type=("movies", "series")
                     )
                 result_number = len(search_result_obj)
                 print(f"\tFound {result_number} results")
    @@ -133,20 +134,20 @@ def str2list(input_string: str) -> list:
             elif choice == 5:
                 url = input("Enter URL to download: ")
                 filename = input("Enter Fullpath Filename: ")
    -            xt._download_video_impl(url,filename)
    +            xt._download_video_impl(url, filename)
     
             elif choice == 6:
                 NUM_MOVIES = len(xt.movies_30days)
                 print(f"Found {NUM_MOVIES} new movies in the past 30 days")
                 if NUM_MOVIES < 20:
    -                for i in range(0,NUM_MOVIES):
    +                for i in range(0, NUM_MOVIES):
                         print(xt.movies_30days[i].title)
     
             elif choice == 7:
                 NUM_MOVIES = len(xt.movies_7days)
                 print(f"Found {NUM_MOVIES} new movies in the past 7 days")
                 if NUM_MOVIES < 20:
    -                for i in range(0,NUM_MOVIES):
    +                for i in range(0, NUM_MOVIES):
                         print(xt.movies_7days[i].title)
     
             elif choice == 8:
    diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py
    index f742871..0db7642 100755
    --- a/pyxtream/pyxtream.py
    +++ b/pyxtream/pyxtream.py
    @@ -502,7 +502,9 @@ def download_video(self, stream_id: int) -> str:
             filename = ""
             for series_stream in self.series:
                 if series_stream.series_id == stream_id:
    -                url = f"{series_stream.url}/{series_stream.episodes["1"].id}.{series_stream.episodes["1"].container_extension}"
    +                episode_object: Episode = series_stream.episodes["1"]
    +                url = f"{series_stream.url}/{episode_object.id}."\
    +                      f"{episode_object.container_extension}"
     
             for stream in self.movies:
                 if stream.id == stream_id:
    
    From 1c3ff8f6f5de1559c386325b5b30d447b6827415 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Wed, 19 Feb 2025 21:48:22 -0600
    Subject: [PATCH 37/41] Added download progress
    
    ---
     pyxtream/pyxtream.py | 11 ++++++++---
     pyxtream/rest_api.py | 10 +++++-----
     2 files changed, 13 insertions(+), 8 deletions(-)
    
    diff --git a/pyxtream/pyxtream.py b/pyxtream/pyxtream.py
    index 0db7642..bbddf61 100755
    --- a/pyxtream/pyxtream.py
    +++ b/pyxtream/pyxtream.py
    @@ -343,7 +343,7 @@ class XTream:
         validate_json: bool = True
     
         # Used by REST API to get download progress
    -    download_progress = 0
    +    download_progress: dict = {'StreamId': 0, 'Total': 0, 'Progress': 0}
     
         def __init__(
             self,
    @@ -436,6 +436,10 @@ def __init__(
                 else:
                     print("Web interface not running")
     
    +    def get_download_progress(self, stream_id: int = None):
    +        # TODO: Add check for stream specific ID
    +        return json.dumps(self.download_progress)
    +
         def get_last_7days(self):
             return json.dumps(self.movies_7days, default=lambda x: x.export_json())
     
    @@ -563,7 +567,8 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
     
                     # Set downloaded size
                     downloaded_bytes = 0
    -                self.download_progress = 0
    +                self.download_progress['Total'] = total_content_size
    +                self.download_progress['Progress'] = 0
     
                     # Set stream blocks
                     block_bytes = int(4*mb_size)     # 4 MB
    @@ -578,7 +583,7 @@ def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
                             for data in response.iter_content(block_bytes, decode_unicode=False):
                                 downloaded_bytes += block_bytes
                                 progress(downloaded_bytes, total_content_size, "Downloading")
    -                            self.download_progress = downloaded_bytes
    +                            self.download_progress['Progress'] = downloaded_bytes
                                 file.write(data)
     
                         ret_code = True
    diff --git a/pyxtream/rest_api.py b/pyxtream/rest_api.py
    index 5149ecd..5086c2c 100644
    --- a/pyxtream/rest_api.py
    +++ b/pyxtream/rest_api.py
    @@ -26,14 +26,14 @@ def __call__(self, **args):
                 "stream_search_generic": lambda: self._handle_search(args['term']),
                 "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
                 "download_stream": lambda: self.action(int(args['stream_id'])),
    -            "download_stream_progress": lambda: self.action,
    +            "get_download_progress": lambda: self.action(int(args['stream_id'])),
                 "get_last_7days": lambda: self.action(),
                 "home": lambda: self.action,
                 "get_series": lambda: self.action(int(args['series_id']), "JSON")
             }
     
             answer = handlers[self.function_name]()
    -        content_type = content_types['json'] if self.function_name not in ('home', 'download_stream_progress') else content_types['html']
    +        content_type = content_types['json'] if self.function_name not in ('home') else content_types['html']
     
             self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
             return self.response
    @@ -95,9 +95,9 @@ def __init__(self, name, xtream: object, html_template_folder: str = None,
                               endpoint_name='download_stream',
                               handler=[self.xt.download_video, "download_stream"]
                               )
    -        self.add_endpoint(endpoint='/download_stream_progress//',
    -                          endpoint_name='download_stream_progress',
    -                          handler=[self.xt.download_progress, "download_stream_progress"]
    +        self.add_endpoint(endpoint='/get_download_progress//',
    +                          endpoint_name='get_download_progress',
    +                          handler=[self.xt.get_download_progress, "get_download_progress"]
                               )
             self.add_endpoint(endpoint='/get_last_7days',
                               endpoint_name='get_last_7days',
    
    From 05c25170d3ec35b9ad5f232aaa91947f09fe6534 Mon Sep 17 00:00:00 2001
    From: Claudio Olmi 
    Date: Wed, 19 Feb 2025 22:22:00 -0600
    Subject: [PATCH 38/41] Updated README.md
    
    ---
     README.md | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/README.md b/README.md
    index e3c4174..94537fe 100644
    --- a/README.md
    +++ b/README.md
    @@ -106,6 +106,7 @@ xTream.movies[{},{},...]
     - xTream.allEpg()
     
     # Versioning
    +Follows the Semantic Versioning from https://semver.org/
     - Increment the MAJOR version when you make incompatible API changes.
     - Increment the MINOR version when you add functionality in a backwards-compatible manner.
     - Increment the PATCH version when you make backwards-compatible bug fixes.
    @@ -114,7 +115,7 @@ xTream.movies[{},{},...]
     
     | Date | Version | Description |
     | ----------- | -----| ----------- |
    -| 2025-02-17 | 0.7.3 | - Added Initial PyTest and Coverage
    - Added timestamp field "added" to Series to match channels "added" field
    - Added string field "url" to Series to quickly get the series download address
    - Added new API "get_last_7days()" shows the last added streams in the last 7 days in JSON format
    - Changed internal function _load_series_info_by_id_from_provider to allow returned value to change to JSON
    - Changed search_stream function to only search in specific collections
    - Refactored "rest_api.py" to make it easier to extend in the future
    - Added new rest API
    - Changed to Poetry environment
    - Changed Functional Test to test loading series information
    - Changed sample index.html to test more features| +| 2025-02-17 | 0.7.3 | - Added Initial PyTest and Coverage
    - Added timestamp field "added" to Series to match channels "added" field
    - Added string field "url" to Series to quickly get the series download address
    - Added new API "get_last_7days()" returns the last added streams in the last 7 days in JSON format
    - Added new API "get_download_progress()" returns information on the current download stream in JSON format
    - Changed internal function _load_series_info_by_id_from_provider to allow returned value to change to JSON
    - Changed search_stream function to only search in specific collections
    - Refactored "rest_api.py" to make it easier to extend in the future
    - Added new rest API
    - Changed to Poetry environment
    - Changed Functional Test to test loading series information
    - Changed sample index.html to test more features| | 2024-09-02 | 0.7.2 | - Added missing request package to setup.py
    - Refactored the search stream function and now, it can search for a specific stream type
    - Refactored the download stream function
    - Refactored the _get_request function and removed the call to the sleep function
    - Added functional test to get series json output from a series_id
    - Added functional test to get EPG for a specific stream ID
    - Added xtream account expiration date printed on the console during authentication
    - Improved results with the Flask HTML page and differentiating between movies and series
    - Improved code readability| | 2024-05-21 | 0.7.1 | - Fixed missing jsonschema package
    - Fixed provider name in functional_test
    - Improved print out of connection attempts
    - Added method to read latest changes in functional_test | 2023-11-08 | 0.7.0 | - Added Schema Validator
    - Added Channel Age
    - Added list of movies added in the last 30 and 7 days
    - Updated code based on PyLint
    - Fixed Flask package to be optional [richard-de-vos](https://github.com/richard-de-vos)| From e511a87b379250f17be39025111a5d8998aaa989 Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Thu, 20 Feb 2025 21:48:26 -0600 Subject: [PATCH 39/41] Updated version --- pyproject.toml | 2 +- pyxtream/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index fba8f7e..5a7b31c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pyxtream" -version = "0.7.2" +version = "0.7.3" requires-python = ">=3.9" description = "xtream IPTV loader" authors = [{name = "Claudio Olmi", email = ""}] diff --git a/pyxtream/version.py b/pyxtream/version.py index 35131a4..0fea4f0 100644 --- a/pyxtream/version.py +++ b/pyxtream/version.py @@ -1,4 +1,4 @@ -__version__ = '0.7.2' +__version__ = '0.7.3' __author__ = 'Claudio Olmi' __author_email__ = 'superolmo2@gmail.com' From 43b40f731c13d3c74cd247d3dd16fce195a87abc Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Sat, 22 Feb 2025 23:11:12 -0600 Subject: [PATCH 40/41] Documentation --- docs/pyxtream.html | 2 +- docs/pyxtream/progress.html | 20 +- docs/pyxtream/pyxtream.html | 6143 +++++++++++++++------------- docs/pyxtream/rest_api.html | 491 ++- docs/pyxtream/schemaValidator.html | 616 +-- docs/pyxtream/version.html | 2 +- docs/search.js | 2 +- run_tests.sh | 2 +- setup.py | 39 - 9 files changed, 3806 insertions(+), 3511 deletions(-) delete mode 100644 setup.py diff --git a/docs/pyxtream.html b/docs/pyxtream.html index 0b1204f..b693496 100644 --- a/docs/pyxtream.html +++ b/docs/pyxtream.html @@ -51,7 +51,7 @@

    1from .progress import progress
    -2from .pyxtream import XTream
    +2from .pyxtream import XTream, Channel, Group, Serie, Episode
     3
     4try:
     5    from .rest_api import FlaskWrap
    diff --git a/docs/pyxtream/progress.html b/docs/pyxtream/progress.html
    index 3daa1eb..d7e09e6 100644
    --- a/docs/pyxtream/progress.html
    +++ b/docs/pyxtream/progress.html
    @@ -57,21 +57,21 @@ 

     1# The MIT License (MIT)
      2# Copyright (c) 2016 Vladimir Ignatev
      3#
    - 4# Permission is hereby granted, free of charge, to any person obtaining 
    - 5# a copy of this software and associated documentation files (the "Software"), 
    - 6# to deal in the Software without restriction, including without limitation 
    - 7# the rights to use, copy, modify, merge, publish, distribute, sublicense, 
    - 8# and/or sell copies of the Software, and to permit persons to whom the Software 
    + 4# Permission is hereby granted, free of charge, to any person obtaining
    + 5# a copy of this software and associated documentation files (the "Software"),
    + 6# to deal in the Software without restriction, including without limitation
    + 7# the rights to use, copy, modify, merge, publish, distribute, sublicense,
    + 8# and/or sell copies of the Software, and to permit persons to whom the Software
      9# is furnished to do so, subject to the following conditions:
    -10# 
    -11# The above copyright notice and this permission notice shall be included 
    +10#
    +11# The above copyright notice and this permission notice shall be included
     12# in all copies or substantial portions of the Software.
     13#
    -14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
    -15# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
    +14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
    +15# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
     16# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
     17# FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
    -18# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
    +18# OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
     19# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20import sys
     21
    diff --git a/docs/pyxtream/pyxtream.html b/docs/pyxtream/pyxtream.html
    index f2f376e..62682de 100644
    --- a/docs/pyxtream/pyxtream.html
    +++ b/docs/pyxtream/pyxtream.html
    @@ -210,6 +210,9 @@ 

    API Documentation

  • episodes
  • +
  • + url +
  • export_json
  • @@ -252,6 +255,18 @@

    API Documentation

  • password
  • +
  • + base_url +
  • +
  • + base_url_ssl +
  • +
  • + cache_path +
  • +
  • + account_expiration +
  • live_type
  • @@ -307,10 +322,10 @@

    API Documentation

    threshold_time_sec
  • - cache_path + validate_json
  • - validate_json + download_progress
  • app_fullpath @@ -318,6 +333,12 @@

    API Documentation

  • html_template_folder
  • +
  • + get_download_progress +
  • +
  • + get_last_7days +
  • search_stream
  • @@ -416,7 +437,7 @@

    Module handles downloading xtream data.

    -

    Part of this content comes from

    +

    Part of this content comes from

    • https://github.com/chazlarson/py-xtream-codes/blob/master/xtream.py
    • @@ -443,7 +464,7 @@

      4 5Module handles downloading xtream data. 6 - 7Part of this content comes from + 7Part of this content comes from 8- https://github.com/chazlarson/py-xtream-codes/blob/master/xtream.py 9- https://github.com/linuxmint/hypnotix 10 @@ -458,1193 +479,1246 @@

      19# used for URL validation 20import re 21import time - 22from os import makedirs - 23from os import path as osp - 24from os import remove - 25# Timing xtream json downloads - 26from timeit import default_timer as timer - 27from typing import List, Tuple, Optional - 28from datetime import datetime, timedelta - 29import requests - 30 - 31from pyxtream.schemaValidator import SchemaType, schemaValidator - 32 - 33try: - 34 from pyxtream.rest_api import FlaskWrap - 35 USE_FLASK = True - 36except ImportError: - 37 USE_FLASK = False - 38 - 39from pyxtream.progress import progress - 40 + 22import sys + 23from os import makedirs + 24from os import path as osp + 25 + 26# Timing xtream json downloads + 27from timeit import default_timer as timer + 28from typing import Tuple, Optional + 29from datetime import datetime, timedelta + 30import requests + 31 + 32from pyxtream.schemaValidator import SchemaType, schemaValidator + 33 + 34try: + 35 from pyxtream.rest_api import FlaskWrap + 36 USE_FLASK = True + 37except ImportError: + 38 USE_FLASK = False + 39 + 40from pyxtream.progress import progress 41 - 42class Channel: - 43 # Required by Hypnotix - 44 info = "" - 45 id = "" - 46 name = "" # What is the difference between the below name and title? - 47 logo = "" - 48 logo_path = "" - 49 group_title = "" - 50 title = "" - 51 url = "" - 52 - 53 # XTream - 54 stream_type: str = "" - 55 group_id: str = "" - 56 is_adult: int = 0 - 57 added: int = 0 - 58 epg_channel_id: str = "" - 59 age_days_from_added: int = 0 - 60 date_now: datetime - 61 - 62 # This contains the raw JSON data - 63 raw = "" - 64 - 65 def __init__(self, xtream: object, group_title, stream_info): - 66 self.date_now = datetime.now() - 67 - 68 stream_type = stream_info["stream_type"] - 69 # Adjust the odd "created_live" type - 70 if stream_type in ("created_live", "radio_streams"): - 71 stream_type = "live" - 72 - 73 if stream_type not in ("live", "movie"): - 74 print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`") - 75 else: - 76 # Raw JSON Channel - 77 self.raw = stream_info - 78 - 79 stream_name = stream_info["name"] - 80 - 81 # Required by Hypnotix - 82 self.id = stream_info["stream_id"] - 83 self.name = stream_name - 84 self.logo = stream_info["stream_icon"] - 85 self.logo_path = xtream._get_logo_local_path(self.logo) - 86 self.group_title = group_title - 87 self.title = stream_name - 88 - 89 # Check if category_id key is available - 90 if "category_id" in stream_info.keys(): - 91 self.group_id = int(stream_info["category_id"]) - 92 - 93 if stream_type == "live": - 94 stream_extension = "ts" + 42 + 43class Channel: + 44 # Required by Hypnotix + 45 info = "" + 46 id = "" + 47 name = "" # What is the difference between the below name and title? + 48 logo = "" + 49 logo_path = "" + 50 group_title = "" + 51 title = "" + 52 url = "" + 53 + 54 # XTream + 55 stream_type: str = "" + 56 group_id: str = "" + 57 is_adult: int = 0 + 58 added: int = 0 + 59 epg_channel_id: str = "" + 60 age_days_from_added: int = 0 + 61 date_now: datetime + 62 + 63 # This contains the raw JSON data + 64 raw = "" + 65 + 66 def __init__(self, xtream: object, group_title, stream_info): + 67 self.date_now = datetime.now() + 68 + 69 stream_type = stream_info["stream_type"] + 70 # Adjust the odd "created_live" type + 71 if stream_type in ("created_live", "radio_streams"): + 72 stream_type = "live" + 73 + 74 if stream_type not in ("live", "movie"): + 75 print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`") + 76 else: + 77 # Raw JSON Channel + 78 self.raw = stream_info + 79 + 80 stream_name = stream_info["name"] + 81 + 82 # Required by Hypnotix + 83 self.id = stream_info["stream_id"] + 84 self.name = stream_name + 85 self.logo = stream_info["stream_icon"] + 86 self.logo_path = xtream._get_logo_local_path(self.logo) + 87 self.group_title = group_title + 88 self.title = stream_name + 89 + 90 # Check if category_id key is available + 91 if "category_id" in stream_info.keys(): + 92 self.group_id = int(stream_info["category_id"]) + 93 + 94 stream_extension = "" 95 - 96 # Check if epg_channel_id key is available - 97 if "epg_channel_id" in stream_info.keys(): - 98 self.epg_channel_id = stream_info["epg_channel_id"] - 99 - 100 elif stream_type == "movie": - 101 stream_extension = stream_info["container_extension"] + 96 if stream_type == "live": + 97 stream_extension = "ts" + 98 + 99 # Check if epg_channel_id key is available + 100 if "epg_channel_id" in stream_info.keys(): + 101 self.epg_channel_id = stream_info["epg_channel_id"] 102 - 103 # Default to 0 - 104 self.is_adult = 0 - 105 # Check if is_adult key is available - 106 if "is_adult" in stream_info.keys(): - 107 self.is_adult = int(stream_info["is_adult"]) - 108 - 109 self.added = int(stream_info["added"]) - 110 self.age_days_from_added = abs(datetime.utcfromtimestamp(self.added) - self.date_now).days + 103 elif stream_type == "movie": + 104 stream_extension = stream_info["container_extension"] + 105 + 106 # Default to 0 + 107 self.is_adult = 0 + 108 # Check if is_adult key is available + 109 if "is_adult" in stream_info.keys(): + 110 self.is_adult = int(stream_info["is_adult"]) 111 - 112 # Required by Hypnotix - 113 self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \ - 114 f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}" - 115 - 116 # Check that the constructed URL is valid - 117 if not xtream._validate_url(self.url): - 118 print(f"{self.name} - Bad URL? `{self.url}`") - 119 - 120 def export_json(self): - 121 jsondata = {} - 122 - 123 jsondata["url"] = self.url - 124 jsondata.update(self.raw) - 125 jsondata["logo_path"] = self.logo_path - 126 - 127 return jsondata - 128 - 129 - 130class Group: - 131 # Required by Hypnotix - 132 name = "" - 133 group_type = "" + 112 self.added = int(stream_info["added"]) + 113 self.age_days_from_added = abs( + 114 datetime.utcfromtimestamp(self.added) - self.date_now + 115 ).days + 116 + 117 # Required by Hypnotix + 118 self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \ + 119 f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}" + 120 + 121 # Check that the constructed URL is valid + 122 if not xtream._validate_url(self.url): + 123 print(f"{self.name} - Bad URL? `{self.url}`") + 124 + 125 def export_json(self): + 126 jsondata = {} + 127 + 128 jsondata["url"] = self.url + 129 jsondata.update(self.raw) + 130 jsondata["logo_path"] = self.logo_path + 131 + 132 return jsondata + 133 134 - 135 # XTream - 136 group_id = "" - 137 - 138 # This contains the raw JSON data - 139 raw = "" - 140 - 141 def convert_region_shortname_to_fullname(self, shortname): + 135class Group: + 136 # Required by Hypnotix + 137 name = "" + 138 group_type = "" + 139 + 140 # XTream + 141 group_id = "" 142 - 143 if shortname == "AR": - 144 return "Arab" - 145 if shortname == "AM": - 146 return "America" - 147 if shortname == "AS": - 148 return "Asia" - 149 if shortname == "AF": - 150 return "Africa" - 151 if shortname == "EU": - 152 return "Europe" - 153 - 154 return "" - 155 - 156 def __init__(self, group_info: dict, stream_type: str): - 157 # Raw JSON Group - 158 self.raw = group_info - 159 - 160 self.channels = [] - 161 self.series = [] - 162 - 163 TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3) + 143 # This contains the raw JSON data + 144 raw = "" + 145 + 146 def convert_region_shortname_to_fullname(self, shortname): + 147 + 148 if shortname == "AR": + 149 return "Arab" + 150 if shortname == "AM": + 151 return "America" + 152 if shortname == "AS": + 153 return "Asia" + 154 if shortname == "AF": + 155 return "Africa" + 156 if shortname == "EU": + 157 return "Europe" + 158 + 159 return "" + 160 + 161 def __init__(self, group_info: dict, stream_type: str): + 162 # Raw JSON Group + 163 self.raw = group_info 164 - 165 if "VOD" == stream_type: - 166 self.group_type = MOVIES_GROUP - 167 elif "Series" == stream_type: - 168 self.group_type = SERIES_GROUP - 169 elif "Live" == stream_type: - 170 self.group_type = TV_GROUP - 171 else: - 172 print(f"Unrecognized stream type `{stream_type}` for `{group_info}`") - 173 - 174 self.name = group_info["category_name"] - 175 split_name = self.name.split('|') - 176 self.region_shortname = "" - 177 self.region_longname = "" - 178 if len(split_name) > 1: - 179 self.region_shortname = split_name[0].strip() - 180 self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname) - 181 - 182 # Check if category_id key is available - 183 if "category_id" in group_info.keys(): - 184 self.group_id = int(group_info["category_id"]) - 185 + 165 self.channels = [] + 166 self.series = [] + 167 + 168 TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3) + 169 + 170 if "VOD" == stream_type: + 171 self.group_type = MOVIES_GROUP + 172 elif "Series" == stream_type: + 173 self.group_type = SERIES_GROUP + 174 elif "Live" == stream_type: + 175 self.group_type = TV_GROUP + 176 else: + 177 print(f"Unrecognized stream type `{stream_type}` for `{group_info}`") + 178 + 179 self.name = group_info["category_name"] + 180 split_name = self.name.split('|') + 181 self.region_shortname = "" + 182 self.region_longname = "" + 183 if len(split_name) > 1: + 184 self.region_shortname = split_name[0].strip() + 185 self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname) 186 - 187class Episode: - 188 # Required by Hypnotix - 189 title = "" - 190 name = "" - 191 info = "" - 192 - 193 # XTream - 194 - 195 # This contains the raw JSON data - 196 raw = "" + 187 # Check if category_id key is available + 188 if "category_id" in group_info.keys(): + 189 self.group_id = int(group_info["category_id"]) + 190 + 191 + 192class Episode: + 193 # Required by Hypnotix + 194 title = "" + 195 name = "" + 196 info = "" 197 - 198 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: - 199 # Raw JSON Episode - 200 self.raw = episode_info - 201 - 202 self.title = episode_info["title"] - 203 self.name = self.title - 204 self.group_title = group_title - 205 self.id = episode_info["id"] - 206 self.container_extension = episode_info["container_extension"] - 207 self.episode_number = episode_info["episode_num"] - 208 self.av_info = episode_info["info"] - 209 - 210 self.logo = series_info["cover"] - 211 self.logo_path = xtream._get_logo_local_path(self.logo) - 212 - 213 self.url = f"{xtream.server}/series/" \ - 214 f"{xtream.authorization['username']}/" \ - 215 f"{xtream.authorization['password']}/{self.id}.{self.container_extension}" - 216 - 217 # Check that the constructed URL is valid - 218 if not xtream._validate_url(self.url): - 219 print(f"{self.name} - Bad URL? `{self.url}`") - 220 + 198 # XTream + 199 + 200 # This contains the raw JSON data + 201 raw = "" + 202 + 203 def __init__(self, xtream: object, series_info, group_title, episode_info) -> None: + 204 # Raw JSON Episode + 205 self.raw = episode_info + 206 + 207 self.title = episode_info["title"] + 208 self.name = self.title + 209 self.group_title = group_title + 210 self.id = episode_info["id"] + 211 self.container_extension = episode_info["container_extension"] + 212 self.episode_number = episode_info["episode_num"] + 213 self.av_info = episode_info["info"] + 214 + 215 self.logo = series_info["cover"] + 216 self.logo_path = xtream._get_logo_local_path(self.logo) + 217 + 218 self.url = f"{xtream.server}/series/" \ + 219 f"{xtream.authorization['username']}/" \ + 220 f"{xtream.authorization['password']}/{self.id}.{self.container_extension}" 221 - 222class Serie: - 223 # Required by Hypnotix - 224 name = "" - 225 logo = "" - 226 logo_path = "" - 227 - 228 # XTream - 229 series_id = "" - 230 plot = "" - 231 youtube_trailer = "" - 232 genre = "" - 233 - 234 # This contains the raw JSON data - 235 raw = "" - 236 - 237 def __init__(self, xtream: object, series_info): - 238 # Raw JSON Series - 239 self.raw = series_info - 240 self.xtream = xtream + 222 # Check that the constructed URL is valid + 223 if not xtream._validate_url(self.url): + 224 print(f"{self.name} - Bad URL? `{self.url}`") + 225 + 226 + 227class Serie: + 228 # Required by Hypnotix + 229 name = "" + 230 logo = "" + 231 logo_path = "" + 232 + 233 # XTream + 234 series_id = "" + 235 plot = "" + 236 youtube_trailer = "" + 237 genre = "" + 238 + 239 # This contains the raw JSON data + 240 raw = "" 241 - 242 # Required by Hypnotix - 243 self.name = series_info["name"] - 244 self.logo = series_info["cover"] - 245 self.logo_path = xtream._get_logo_local_path(self.logo) - 246 - 247 self.seasons = {} - 248 self.episodes = {} + 242 def __init__(self, xtream: object, series_info): + 243 + 244 series_info["added"] = series_info["last_modified"] + 245 + 246 # Raw JSON Series + 247 self.raw = series_info + 248 self.xtream = xtream 249 - 250 # Check if category_id key is available - 251 if "series_id" in series_info.keys(): - 252 self.series_id = int(series_info["series_id"]) - 253 - 254 # Check if plot key is available - 255 if "plot" in series_info.keys(): - 256 self.plot = series_info["plot"] + 250 # Required by Hypnotix + 251 self.name = series_info["name"] + 252 self.logo = series_info["cover"] + 253 self.logo_path = xtream._get_logo_local_path(self.logo) + 254 + 255 self.seasons = {} + 256 self.episodes = {} 257 - 258 # Check if youtube_trailer key is available - 259 if "youtube_trailer" in series_info.keys(): - 260 self.youtube_trailer = series_info["youtube_trailer"] + 258 # Check if category_id key is available + 259 if "series_id" in series_info.keys(): + 260 self.series_id = int(series_info["series_id"]) 261 - 262 # Check if genre key is available - 263 if "genre" in series_info.keys(): - 264 self.genre = series_info["genre"] + 262 # Check if plot key is available + 263 if "plot" in series_info.keys(): + 264 self.plot = series_info["plot"] 265 - 266 def export_json(self): - 267 jsondata = {} - 268 - 269 jsondata.update(self.raw) - 270 jsondata['logo_path'] = self.logo_path - 271 - 272 return jsondata + 266 # Check if youtube_trailer key is available + 267 if "youtube_trailer" in series_info.keys(): + 268 self.youtube_trailer = series_info["youtube_trailer"] + 269 + 270 # Check if genre key is available + 271 if "genre" in series_info.keys(): + 272 self.genre = series_info["genre"] 273 - 274class Season: - 275 # Required by Hypnotix - 276 name = "" + 274 self.url = f"{xtream.server}/series/" \ + 275 f"{xtream.authorization['username']}/" \ + 276 f"{xtream.authorization['password']}/{self.series_id}/" 277 - 278 def __init__(self, name): - 279 self.name = name - 280 self.episodes = {} - 281 - 282class XTream: + 278 def export_json(self): + 279 jsondata = {} + 280 + 281 jsondata.update(self.raw) + 282 jsondata['logo_path'] = self.logo_path 283 - 284 name = "" - 285 server = "" - 286 secure_server = "" - 287 username = "" - 288 password = "" - 289 - 290 live_type = "Live" - 291 vod_type = "VOD" - 292 series_type = "Series" - 293 - 294 auth_data = {} - 295 authorization = {} - 296 - 297 groups = [] - 298 channels = [] - 299 series = [] - 300 movies = [] - 301 movies_30days = [] - 302 movies_7days = [] - 303 - 304 connection_headers = {} + 284 return jsondata + 285 + 286 + 287class Season: + 288 # Required by Hypnotix + 289 name = "" + 290 + 291 def __init__(self, name): + 292 self.name = name + 293 self.episodes = {} + 294 + 295 + 296class XTream: + 297 + 298 name = "" + 299 server = "" + 300 secure_server = "" + 301 username = "" + 302 password = "" + 303 base_url = "" + 304 base_url_ssl = "" 305 - 306 state = {'authenticated': False, 'loaded': False} + 306 cache_path = "" 307 - 308 hide_adult_content = False + 308 account_expiration: timedelta 309 - 310 live_catch_all_group = Group( - 311 {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, live_type - 312 ) - 313 vod_catch_all_group = Group( - 314 {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, vod_type - 315 ) - 316 series_catch_all_group = Group( - 317 {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, series_type - 318 ) - 319 # If the cached JSON file is older than threshold_time_sec then load a new - 320 # JSON dictionary from the provider - 321 threshold_time_sec = -1 - 322 - 323 def __init__( - 324 self, - 325 provider_name: str, - 326 provider_username: str, - 327 provider_password: str, - 328 provider_url: str, - 329 headers: dict = None, - 330 hide_adult_content: bool = False, - 331 cache_path: str = "", - 332 reload_time_sec: int = 60*60*8, - 333 validate_json: bool = False, - 334 debug_flask: bool = True - 335 ): - 336 """Initialize Xtream Class - 337 - 338 Args: - 339 provider_name (str): Name of the IPTV provider - 340 provider_username (str): User name of the IPTV provider - 341 provider_password (str): Password of the IPTV provider - 342 provider_url (str): URL of the IPTV provider - 343 headers (dict): Requests Headers - 344 hide_adult_content(bool, optional): When `True` hide stream that are marked for adult - 345 cache_path (str, optional): Location where to save loaded files. - 346 Defaults to empty string. - 347 reload_time_sec (int, optional): Number of seconds before automatic reloading - 348 (-1 to turn it OFF) - 349 debug_flask (bool, optional): Enable the debug mode in Flask - 350 validate_json (bool, optional): Check Xtream API provided JSON for validity - 351 - 352 Returns: XTream Class Instance - 353 - 354 - Note 1: If it fails to authorize with provided username and password, - 355 auth_data will be an empty dictionary. - 356 - Note 2: The JSON validation option will take considerable amount of time and it should be - 357 used only as a debug tool. The Xtream API JSON from the provider passes through a - 358 schema that represent the best available understanding of how the Xtream API - 359 works. - 360 """ - 361 self.server = provider_url - 362 self.username = provider_username - 363 self.password = provider_password - 364 self.name = provider_name - 365 self.cache_path = cache_path - 366 self.hide_adult_content = hide_adult_content - 367 self.threshold_time_sec = reload_time_sec - 368 self.validate_json = validate_json - 369 - 370 # get the pyxtream local path - 371 self.app_fullpath = osp.dirname(osp.realpath(__file__)) - 372 - 373 # prepare location of local html template - 374 self.html_template_folder = osp.join(self.app_fullpath,"html") - 375 - 376 # if the cache_path is specified, test that it is a directory - 377 if self.cache_path != "": - 378 # If the cache_path is not a directory, clear it - 379 if not osp.isdir(self.cache_path): - 380 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") - 381 self.cache_path == "" - 382 - 383 # If the cache_path is still empty, use default - 384 if self.cache_path == "": - 385 self.cache_path = osp.expanduser("~/.xtream-cache/") - 386 if not osp.isdir(self.cache_path): - 387 makedirs(self.cache_path, exist_ok=True) - 388 print(f"pyxtream cache path located at {self.cache_path}") - 389 - 390 if headers is not None: - 391 self.connection_headers = headers - 392 else: - 393 self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"} - 394 - 395 self.authenticate() + 310 live_type = "Live" + 311 vod_type = "VOD" + 312 series_type = "Series" + 313 + 314 auth_data = {} + 315 authorization = {'username': '', 'password': ''} + 316 + 317 groups = [] + 318 channels = [] + 319 series = [] + 320 movies = [] + 321 movies_30days = [] + 322 movies_7days = [] + 323 + 324 connection_headers = {} + 325 + 326 state = {'authenticated': False, 'loaded': False} + 327 + 328 hide_adult_content = False + 329 + 330 live_catch_all_group = Group( + 331 {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, live_type + 332 ) + 333 vod_catch_all_group = Group( + 334 {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, vod_type + 335 ) + 336 series_catch_all_group = Group( + 337 {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, series_type + 338 ) + 339 # If the cached JSON file is older than threshold_time_sec then load a new + 340 # JSON dictionary from the provider + 341 threshold_time_sec = -1 + 342 + 343 validate_json: bool = True + 344 + 345 # Used by REST API to get download progress + 346 download_progress: dict = {'StreamId': 0, 'Total': 0, 'Progress': 0} + 347 + 348 def __init__( + 349 self, + 350 provider_name: str, + 351 provider_username: str, + 352 provider_password: str, + 353 provider_url: str, + 354 headers: dict = None, + 355 hide_adult_content: bool = False, + 356 cache_path: str = "", + 357 reload_time_sec: int = 60*60*8, + 358 validate_json: bool = False, + 359 enable_flask: bool = False, + 360 debug_flask: bool = True + 361 ): + 362 """Initialize Xtream Class + 363 + 364 Args: + 365 provider_name (str): Name of the IPTV provider + 366 provider_username (str): User name of the IPTV provider + 367 provider_password (str): Password of the IPTV provider + 368 provider_url (str): URL of the IPTV provider + 369 headers (dict): Requests Headers + 370 hide_adult_content(bool, optional): When `True` hide stream that are marked for adult + 371 cache_path (str, optional): Location where to save loaded files. + 372 Defaults to empty string. + 373 reload_time_sec (int, optional): Number of seconds before automatic reloading + 374 (-1 to turn it OFF) + 375 validate_json (bool, optional): Check Xtream API provided JSON for validity + 376 enable_flask (bool, optional): Enable Flask + 377 debug_flask (bool, optional): Enable the debug mode in Flask + 378 + 379 Returns: XTream Class Instance + 380 + 381 - Note 1: If it fails to authorize with provided username and password, + 382 auth_data will be an empty dictionary. + 383 - Note 2: The JSON validation option will take considerable amount of time and it should be + 384 used only as a debug tool. The Xtream API JSON from the provider passes through a + 385 schema that represent the best available understanding of how the Xtream API + 386 works. + 387 """ + 388 self.server = provider_url + 389 self.username = provider_username + 390 self.password = provider_password + 391 self.name = provider_name + 392 self.cache_path = cache_path + 393 self.hide_adult_content = hide_adult_content + 394 self.threshold_time_sec = reload_time_sec + 395 self.validate_json = validate_json 396 - 397 if self.threshold_time_sec > 0: - 398 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") - 399 else: - 400 print("Reload timer is OFF") - 401 - 402 if self.state['authenticated']: - 403 if USE_FLASK: - 404 self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask) - 405 self.flaskapp.start() - 406 - 407 def search_stream(self, keyword: str, - 408 ignore_case: bool = True, - 409 return_type: str = "LIST", - 410 stream_type: list = ("series", "movies", "channels")) -> list: - 411 """Search for streams - 412 - 413 Args: - 414 keyword (str): Keyword to search for. Supports REGEX - 415 ignore_case (bool, optional): True to ignore case during search. Defaults to "True". - 416 return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". - 417 stream_type (list, optional): Search within specific stream type. - 418 - 419 Returns: - 420 list: List with all the results, it could be empty. - 421 """ - 422 - 423 search_result = [] - 424 regex_flags = re.IGNORECASE if ignore_case else 0 - 425 regex = re.compile(keyword, regex_flags) - 426 # if ignore_case: - 427 # regex = re.compile(keyword, re.IGNORECASE) - 428 # else: - 429 # regex = re.compile(keyword) - 430 - 431 # if "movies" in stream_type: - 432 # print(f"Checking {len(self.movies)} movies") - 433 # for stream in self.movies: - 434 # if re.match(regex, stream.name) is not None: - 435 # search_result.append(stream.export_json()) - 436 - 437 # if "channels" in stream_type: - 438 # print(f"Checking {len(self.channels)} channels") - 439 # for stream in self.channels: - 440 # if re.match(regex, stream.name) is not None: - 441 # search_result.append(stream.export_json()) + 397 # get the pyxtream local path + 398 self.app_fullpath = osp.dirname(osp.realpath(__file__)) + 399 + 400 # prepare location of local html template + 401 self.html_template_folder = osp.join(self.app_fullpath, "html") + 402 + 403 # if the cache_path is specified, test that it is a directory + 404 if self.cache_path != "": + 405 # If the cache_path is not a directory, clear it + 406 if not osp.isdir(self.cache_path): + 407 print(" - Cache Path is not a directory, using default '~/.xtream-cache/'") + 408 self.cache_path = "" + 409 + 410 # If the cache_path is still empty, use default + 411 if self.cache_path == "": + 412 self.cache_path = osp.expanduser("~/.xtream-cache/") + 413 if not osp.isdir(self.cache_path): + 414 makedirs(self.cache_path, exist_ok=True) + 415 print(f"pyxtream cache path located at {self.cache_path}") + 416 + 417 if headers is not None: + 418 self.connection_headers = headers + 419 else: + 420 self.connection_headers = {'User-Agent': "Wget/1.20.3 (linux-gnu)"} + 421 + 422 self.authenticate() + 423 + 424 if self.threshold_time_sec > 0: + 425 print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds") + 426 else: + 427 print("Reload timer is OFF") + 428 + 429 if self.state['authenticated']: + 430 if USE_FLASK and enable_flask: + 431 print("Starting Web Interface") + 432 self.flaskapp = FlaskWrap( + 433 'pyxtream', self, self.html_template_folder, debug=debug_flask + 434 ) + 435 self.flaskapp.start() + 436 else: + 437 print("Web interface not running") + 438 + 439 def get_download_progress(self, stream_id: int = None): + 440 # TODO: Add check for stream specific ID + 441 return json.dumps(self.download_progress) 442 - 443 # if "series" in stream_type: - 444 # print(f"Checking {len(self.series)} series") - 445 # for stream in self.series: - 446 # if re.match(regex, stream.name) is not None: - 447 # search_result.append(stream.export_json()) - 448 - 449 stream_collections = { - 450 "movies": self.movies, - 451 "channels": self.channels, - 452 "series": self.series - 453 } - 454 - 455 for stream_type_name in stream_type: - 456 if stream_type_name in stream_collections: - 457 collection = stream_collections[stream_type_name] - 458 print(f"Checking {len(collection)} {stream_type_name}") - 459 for stream in collection: - 460 if re.match(regex, stream.name) is not None: - 461 search_result.append(stream.export_json()) - 462 else: - 463 print(f"`{stream_type_name}` not found in collection") - 464 - 465 if return_type == "JSON": - 466 # if search_result is not None: - 467 print(f"Found {len(search_result)} results `{keyword}`") - 468 return json.dumps(search_result, ensure_ascii=False) - 469 - 470 return search_result - 471 - 472 def download_video(self, stream_id: int) -> str: - 473 """Download Video from Stream ID - 474 - 475 Args: - 476 stream_id (int): Stirng identifing the stream ID - 477 - 478 Returns: - 479 str: Absolute Path Filename where the file was saved. Empty if could not download - 480 """ - 481 url = "" - 482 filename = "" - 483 for stream in self.movies: - 484 if stream.id == stream_id: - 485 url = stream.url - 486 fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}" - 487 filename = osp.join(self.cache_path,fn) + 443 def get_last_7days(self): + 444 return json.dumps(self.movies_7days, default=lambda x: x.export_json()) + 445 + 446 def search_stream(self, keyword: str, + 447 ignore_case: bool = True, + 448 return_type: str = "LIST", + 449 stream_type: list = ("series", "movies", "channels"), + 450 added_after: datetime = None) -> list: + 451 """Search for streams + 452 + 453 Args: + 454 keyword (str): Keyword to search for. Supports REGEX + 455 ignore_case (bool, optional): True to ignore case during search. Defaults to "True". + 456 return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". + 457 stream_type (list, optional): Search within specific stream type. + 458 added_after (datetime, optional): Search for items that have been added after a certain date. + 459 + 460 Returns: + 461 list: List with all the results, it could be empty. + 462 """ + 463 + 464 search_result = [] + 465 regex_flags = re.IGNORECASE if ignore_case else 0 + 466 regex = re.compile(keyword, regex_flags) + 467 + 468 stream_collections = { + 469 "movies": self.movies, + 470 "channels": self.channels, + 471 "series": self.series + 472 } + 473 + 474 for stream_type_name in stream_type: + 475 if stream_type_name in stream_collections: + 476 collection = stream_collections[stream_type_name] + 477 print(f"Checking {len(collection)} {stream_type_name}") + 478 for stream in collection: + 479 if stream.name and re.match(regex, stream.name) is not None: + 480 if added_after is None: + 481 # Add all matches + 482 search_result.append(stream.export_json()) + 483 else: + 484 # Only add if it is more recent + 485 pass + 486 else: + 487 print(f"`{stream_type_name}` not found in collection") 488 - 489 # If the url was correctly built and file does not exists, start downloading - 490 if url != "": - 491 #if not osp.isfile(filename): - 492 if not self._download_video_impl(url,filename): - 493 return "Error" - 494 - 495 return filename - 496 - 497 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: - 498 """Download a stream - 499 - 500 Args: - 501 url (str): Complete URL of the stream - 502 fullpath_filename (str): Complete File path where to save the stream - 503 - 504 Returns: - 505 bool: True if successful, False if error - 506 """ - 507 ret_code = False - 508 mb_size = 1024*1024 - 509 try: - 510 print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`") - 511 - 512 # Check if the file already exists - 513 if osp.exists(fullpath_filename): - 514 # If the file exists, resume the download from where it left off - 515 file_size = osp.getsize(fullpath_filename) - 516 self.connection_headers['Range'] = f'bytes={file_size}-' - 517 mode = 'ab' # Append to the existing file - 518 print(f"Resuming from {file_size:_} bytes") - 519 else: - 520 # If the file does not exist, start a new download - 521 mode = 'wb' # Write a new file - 522 - 523 # Make the request to download - 524 response = requests.get(url, timeout=(10), stream=True, allow_redirects=True, headers=self.connection_headers) - 525 # If there is an answer from the remote server - 526 if response.status_code == 200 or response.status_code == 206: - 527 # Get content type Binary or Text - 528 content_type = response.headers.get('content-type',None) - 529 - 530 # Get total playlist byte size - 531 total_content_size = int(response.headers.get('content-length',None)) - 532 total_content_size_mb = total_content_size/mb_size - 533 - 534 # Set downloaded size - 535 downloaded_bytes = 0 - 536 - 537 # Set stream blocks - 538 block_bytes = int(4*mb_size) # 4 MB - 539 - 540 print(f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})") - 541 if content_type.split('/')[0] != "text": - 542 with open(fullpath_filename, mode) as file: - 543 - 544 # Grab data by block_bytes - 545 for data in response.iter_content(block_bytes,decode_unicode=False): - 546 downloaded_bytes += block_bytes - 547 progress(downloaded_bytes,total_content_size,"Downloading") - 548 file.write(data) - 549 - 550 if downloaded_bytes == total_content_size: - 551 ret_code = True - 552 - 553 # Delete Range if it was added - 554 try: - 555 del self.connection_headers['Range'] - 556 except KeyError: - 557 pass - 558 else: - 559 print(f"URL has a file with unexpected content-type {content_type}") - 560 else: - 561 print(f"HTTP error {response.status_code} while retrieving from {url}") - 562 except Exception as e: - 563 print(e) - 564 - 565 return ret_code - 566 - 567 def _slugify(self, string: str) -> str: - 568 """Normalize string - 569 - 570 Normalizes string, converts to lowercase, removes non-alpha characters, - 571 and converts spaces to hyphens. + 489 if return_type == "JSON": + 490 # if search_result is not None: + 491 print(f"Found {len(search_result)} results `{keyword}`") + 492 return json.dumps(search_result, ensure_ascii=False) + 493 + 494 return search_result + 495 + 496 def download_video(self, stream_id: int) -> str: + 497 """Download Video from Stream ID + 498 + 499 Args: + 500 stream_id (int): String identifying the stream ID + 501 + 502 Returns: + 503 str: Absolute Path Filename where the file was saved. Empty if could not download + 504 """ + 505 url = "" + 506 filename = "" + 507 for series_stream in self.series: + 508 if series_stream.series_id == stream_id: + 509 episode_object: Episode = series_stream.episodes["1"] + 510 url = f"{series_stream.url}/{episode_object.id}."\ + 511 f"{episode_object.container_extension}" + 512 + 513 for stream in self.movies: + 514 if stream.id == stream_id: + 515 url = stream.url + 516 fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}" + 517 filename = osp.join(self.cache_path, fn) + 518 + 519 # If the url was correctly built and file does not exists, start downloading + 520 if url != "": + 521 if not self._download_video_impl(url, filename): + 522 return "Error" + 523 + 524 return filename + 525 + 526 def _download_video_impl(self, url: str, fullpath_filename: str) -> bool: + 527 """Download a stream + 528 + 529 Args: + 530 url (str): Complete URL of the stream + 531 fullpath_filename (str): Complete File path where to save the stream + 532 + 533 Returns: + 534 bool: True if successful, False if error + 535 """ + 536 ret_code = False + 537 mb_size = 1024*1024 + 538 try: + 539 print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`") + 540 + 541 # Check if the file already exists + 542 if osp.exists(fullpath_filename): + 543 # If the file exists, resume the download from where it left off + 544 file_size = osp.getsize(fullpath_filename) + 545 self.connection_headers['Range'] = f'bytes={file_size}-' + 546 mode = 'ab' # Append to the existing file + 547 print(f"Resuming from {file_size:_} bytes") + 548 else: + 549 # If the file does not exist, start a new download + 550 mode = 'wb' # Write a new file + 551 + 552 # Make the request to download + 553 response = requests.get( + 554 url, timeout=(10), + 555 stream=True, + 556 allow_redirects=True, + 557 headers=self.connection_headers + 558 ) + 559 # If there is an answer from the remote server + 560 if response.status_code in (200, 206): + 561 # Get content type Binary or Text + 562 content_type = response.headers.get('content-type', None) + 563 + 564 # Get total playlist byte size + 565 total_content_size = int(response.headers.get('content-length', None)) + 566 total_content_size_mb = total_content_size/mb_size + 567 + 568 # Set downloaded size + 569 downloaded_bytes = 0 + 570 self.download_progress['Total'] = total_content_size + 571 self.download_progress['Progress'] = 0 572 - 573 Args: - 574 string (str): String to be normalized + 573 # Set stream blocks + 574 block_bytes = int(4*mb_size) # 4 MB 575 - 576 Returns: - 577 str: Normalized String - 578 """ - 579 return "".join(x.lower() for x in string if x.isprintable()) - 580 - 581 def _validate_url(self, url: str) -> bool: - 582 regex = re.compile( - 583 r"^(?:http|ftp)s?://" # http:// or https:// - 584 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... - 585 r"localhost|" # localhost... - 586 r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip - 587 r"(?::\d+)?" # optional port - 588 r"(?:/?|[/?]\S+)$", - 589 re.IGNORECASE, - 590 ) - 591 - 592 return re.match(regex, url) is not None - 593 - 594 def _get_logo_local_path(self, logo_url: str) -> str: - 595 """Convert the Logo URL to a local Logo Path - 596 - 597 Args: - 598 logoURL (str): The Logo URL - 599 - 600 Returns: - 601 [type]: The logo path as a string or None - 602 """ - 603 local_logo_path = None - 604 if logo_url is not None: - 605 if not self._validate_url(logo_url): - 606 logo_url = None - 607 else: - 608 local_logo_path = osp.join( - 609 self.cache_path, - 610 f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}" - 611 ) - 612 return local_logo_path + 576 print( + 577 f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})" + 578 ) + 579 if content_type.split('/')[0] != "text": + 580 with open(fullpath_filename, mode) as file: + 581 + 582 # Grab data by block_bytes + 583 for data in response.iter_content(block_bytes, decode_unicode=False): + 584 downloaded_bytes += block_bytes + 585 progress(downloaded_bytes, total_content_size, "Downloading") + 586 self.download_progress['Progress'] = downloaded_bytes + 587 file.write(data) + 588 + 589 ret_code = True + 590 + 591 # Delete Range if it was added + 592 try: + 593 del self.connection_headers['Range'] + 594 except KeyError: + 595 pass + 596 else: + 597 print(f"URL has a file with unexpected content-type {content_type}") + 598 else: + 599 print(f"HTTP error {response.status_code} while retrieving from {url}") + 600 except requests.exceptions.ReadTimeout: + 601 print("Read Timeout, try again") + 602 except Exception as e: + 603 print("Unknown error") + 604 print(e) + 605 + 606 return ret_code + 607 + 608 def _slugify(self, string: str) -> str: + 609 """Normalize string + 610 + 611 Normalizes string, converts to lowercase, removes non-alpha characters, + 612 and converts spaces to hyphens. 613 - 614 def authenticate(self): - 615 """Login to provider""" - 616 # If we have not yet successfully authenticated, attempt authentication - 617 if self.state["authenticated"] is False: - 618 # Erase any previous data - 619 self.auth_data = {} - 620 # Loop through 30 seconds - 621 i = 0 - 622 r = None - 623 # Prepare the authentication url - 624 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 625 print("Attempting connection... ", end='') - 626 while i < 30: - 627 try: - 628 # Request authentication, wait 4 seconds maximum - 629 r = requests.get(url, timeout=(4), headers=self.connection_headers) - 630 i = 31 - 631 except requests.exceptions.ConnectionError: - 632 time.sleep(1) - 633 print(f"{i} ", end='',flush=True) - 634 i += 1 - 635 - 636 if r is not None: - 637 # If the answer is ok, process data and change state - 638 if r.ok: - 639 print("Connected") - 640 self.auth_data = r.json() - 641 self.authorization = { - 642 "username": self.auth_data["user_info"]["username"], - 643 "password": self.auth_data["user_info"]["password"] - 644 } - 645 # Account expiration date - 646 self.account_expiration = timedelta( - 647 seconds=( - 648 int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp() - 649 ) - 650 ) - 651 # Mark connection authorized - 652 self.state["authenticated"] = True - 653 # Construct the base url for all requests - 654 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" - 655 # If there is a secure server connection, construct the base url SSL for all requests - 656 if "https_port" in self.auth_data["server_info"]: - 657 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ - 658 f"/player_api.php?username={self.username}&password={self.password}" - 659 print(f"Account expires in {str(self.account_expiration)}") - 660 else: - 661 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") - 662 else: - 663 print(f"\n{self.name}: Provider refused the connection") - 664 - 665 def _load_from_file(self, filename) -> dict: - 666 """Try to load the dictionary from file - 667 - 668 Args: - 669 filename ([type]): File name containing the data - 670 - 671 Returns: - 672 dict: Dictionary if found and no errors, None if file does not exists - 673 """ - 674 # Build the full path - 675 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 614 Args: + 615 string (str): String to be normalized + 616 + 617 Returns: + 618 str: Normalized String + 619 """ + 620 return "".join(x.lower() for x in string if x.isprintable()) + 621 + 622 def _validate_url(self, url: str) -> bool: + 623 regex = re.compile( + 624 r"^(?:http|ftp)s?://" # http:// or https:// + 625 r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|" # domain... + 626 r"localhost|" # localhost... + 627 r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip + 628 r"(?::\d+)?" # optional port + 629 r"(?:/?|[/?]\S+)$", + 630 re.IGNORECASE, + 631 ) + 632 + 633 return re.match(regex, url) is not None + 634 + 635 def _get_logo_local_path(self, logo_url: str) -> str: + 636 """Convert the Logo URL to a local Logo Path + 637 + 638 Args: + 639 logoURL (str): The Logo URL + 640 + 641 Returns: + 642 [type]: The logo path as a string or None + 643 """ + 644 local_logo_path = None + 645 if logo_url is not None: + 646 if not self._validate_url(logo_url): + 647 logo_url = None + 648 else: + 649 local_logo_path = osp.join( + 650 self.cache_path, + 651 f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}" + 652 ) + 653 return local_logo_path + 654 + 655 def authenticate(self): + 656 """Login to provider""" + 657 # If we have not yet successfully authenticated, attempt authentication + 658 if self.state["authenticated"] is False: + 659 # Erase any previous data + 660 self.auth_data = {} + 661 # Loop through 30 seconds + 662 i = 0 + 663 r = None + 664 # Prepare the authentication url + 665 url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 666 print("Attempting connection... ", end='') + 667 while i < 30: + 668 try: + 669 # Request authentication, wait 4 seconds maximum + 670 r = requests.get(url, timeout=(4), headers=self.connection_headers) + 671 i = 31 + 672 except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout): + 673 time.sleep(1) + 674 print(f"{i} ", end='', flush=True) + 675 i += 1 676 - 677 # If the cached file exists, attempt to load it - 678 if osp.isfile(full_filename): - 679 - 680 my_data = None - 681 - 682 # Get the enlapsed seconds since last file update - 683 file_age_sec = time.time() - osp.getmtime(full_filename) - 684 # If the file was updated less than the threshold time, - 685 # it means that the file is still fresh, we can load it. - 686 # Otherwise skip and return None to force a re-download - 687 if self.threshold_time_sec > file_age_sec: - 688 # Load the JSON data - 689 try: - 690 with open(full_filename, mode="r", encoding="utf-8") as myfile: - 691 my_data = json.load(myfile) - 692 if len(my_data) == 0: - 693 my_data = None - 694 except Exception as e: - 695 print(f" - Could not load from file `{full_filename}`: e=`{e}`") - 696 return my_data - 697 - 698 return None - 699 - 700 def _save_to_file(self, data_list: dict, filename: str) -> bool: - 701 """Save a dictionary to file - 702 - 703 This function will overwrite the file if already exists - 704 - 705 Args: - 706 data_list (dict): Dictionary to save - 707 filename (str): Name of the file + 677 if r is not None: + 678 # If the answer is ok, process data and change state + 679 if r.ok: + 680 print("Connected") + 681 self.auth_data = r.json() + 682 self.authorization = { + 683 "username": self.auth_data["user_info"]["username"], + 684 "password": self.auth_data["user_info"]["password"] + 685 } + 686 # Account expiration date + 687 self.account_expiration = timedelta( + 688 seconds=( + 689 int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp() + 690 ) + 691 ) + 692 # Mark connection authorized + 693 self.state["authenticated"] = True + 694 # Construct the base url for all requests + 695 self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}" + 696 # If there is a secure server connection, construct the base url SSL for all requests + 697 if "https_port" in self.auth_data["server_info"]: + 698 self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \ + 699 f"/player_api.php?username={self.username}&password={self.password}" + 700 print(f"Account expires in {str(self.account_expiration)}") + 701 else: + 702 print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`") + 703 else: + 704 print(f"\n{self.name}: Provider refused the connection") + 705 + 706 def _load_from_file(self, filename) -> dict: + 707 """Try to load the dictionary from file 708 - 709 Returns: - 710 bool: True if successfull, False if error - 711 """ - 712 if data_list is None: - 713 return False - 714 - 715 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 716 try: - 717 with open(full_filename, mode="wt", encoding="utf-8") as file: - 718 json.dump(data_list, file, ensure_ascii=False) - 719 return True - 720 except Exception as e: - 721 print(f" - Could not save to file `{full_filename}`: e=`{e}`") - 722 return False - 723 # if data_list is not None: - 724 - 725 # #Build the full path - 726 # full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") - 727 # # If the path makes sense, save the file - 728 # json_data = json.dumps(data_list, ensure_ascii=False) - 729 # try: - 730 # with open(full_filename, mode="wt", encoding="utf-8") as myfile: - 731 # myfile.write(json_data) - 732 # except Exception as e: - 733 # print(f" - Could not save to file `{full_filename}`: e=`{e}`") - 734 # return False - 735 - 736 # return True - 737 # else: - 738 # return False - 739 - 740 def load_iptv(self) -> bool: - 741 """Load XTream IPTV - 742 - 743 - Add all Live TV to XTream.channels - 744 - Add all VOD to XTream.movies - 745 - Add all Series to XTream.series - 746 Series contains Seasons and Episodes. Those are not automatically - 747 retrieved from the server to reduce the loading time. - 748 - Add all groups to XTream.groups - 749 Groups are for all three channel types, Live TV, VOD, and Series - 750 - 751 Returns: - 752 bool: True if successfull, False if error - 753 """ - 754 # If pyxtream has not authenticated the connection, return empty - 755 if self.state["authenticated"] is False: - 756 print("Warning, cannot load steams since authorization failed") - 757 return False - 758 - 759 # If pyxtream has already loaded the data, skip and return success - 760 if self.state["loaded"] is True: - 761 print("Warning, data has already been loaded.") - 762 return True - 763 - 764 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): - 765 ## Get GROUPS - 766 - 767 # Try loading local file - 768 dt = 0 - 769 start = timer() - 770 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") - 771 # If file empty or does not exists, download it from remote - 772 if all_cat is None: - 773 # Load all Groups and save file locally - 774 all_cat = self._load_categories_from_provider(loading_stream_type) - 775 if all_cat is not None: - 776 self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json") - 777 dt = timer() - start - 778 - 779 # If we got the GROUPS data, show the statistics and load GROUPS - 780 if all_cat is not None: - 781 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") - 782 ## Add GROUPS to dictionaries + 709 Args: + 710 filename ([type]): File name containing the data + 711 + 712 Returns: + 713 dict: Dictionary if found and no errors, None if file does not exists + 714 """ + 715 # Build the full path + 716 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 717 + 718 # If the cached file exists, attempt to load it + 719 if osp.isfile(full_filename): + 720 + 721 my_data = None + 722 + 723 # Get the elapsed seconds since last file update + 724 file_age_sec = time.time() - osp.getmtime(full_filename) + 725 # If the file was updated less than the threshold time, + 726 # it means that the file is still fresh, we can load it. + 727 # Otherwise skip and return None to force a re-download + 728 if self.threshold_time_sec > file_age_sec: + 729 # Load the JSON data + 730 try: + 731 with open(full_filename, mode="r", encoding="utf-8") as myfile: + 732 my_data = json.load(myfile) + 733 if len(my_data) == 0: + 734 my_data = None + 735 except Exception as e: + 736 print(f" - Could not load from file `{full_filename}`: e=`{e}`") + 737 return my_data + 738 + 739 return None + 740 + 741 def _save_to_file(self, data_list: dict, filename: str) -> bool: + 742 """Save a dictionary to file + 743 + 744 This function will overwrite the file if already exists + 745 + 746 Args: + 747 data_list (dict): Dictionary to save + 748 filename (str): Name of the file + 749 + 750 Returns: + 751 bool: True if successful, False if error + 752 """ + 753 if data_list is None: + 754 return False + 755 + 756 full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}") + 757 try: + 758 with open(full_filename, mode="wt", encoding="utf-8") as file: + 759 json.dump(data_list, file, ensure_ascii=False) + 760 return True + 761 except Exception as e: + 762 print(f" - Could not save to file `{full_filename}`: e=`{e}`") + 763 return False + 764 + 765 def load_iptv(self) -> bool: + 766 """Load XTream IPTV + 767 + 768 - Add all Live TV to XTream.channels + 769 - Add all VOD to XTream.movies + 770 - Add all Series to XTream.series + 771 Series contains Seasons and Episodes. Those are not automatically + 772 retrieved from the server to reduce the loading time. + 773 - Add all groups to XTream.groups + 774 Groups are for all three channel types, Live TV, VOD, and Series + 775 + 776 Returns: + 777 bool: True if successful, False if error + 778 """ + 779 # If pyxtream has not authenticated the connection, return empty + 780 if self.state["authenticated"] is False: + 781 print("Warning, cannot load steams since authorization failed") + 782 return False 783 - 784 # Add the catch-all-errors group - 785 if loading_stream_type == self.live_type: - 786 self.groups.append(self.live_catch_all_group) - 787 elif loading_stream_type == self.vod_type: - 788 self.groups.append(self.vod_catch_all_group) - 789 elif loading_stream_type == self.series_type: - 790 self.groups.append(self.series_catch_all_group) - 791 - 792 for cat_obj in all_cat: - 793 if schemaValidator(cat_obj, SchemaType.GROUP): - 794 # Create Group (Category) - 795 new_group = Group(cat_obj, loading_stream_type) - 796 # Add to xtream class - 797 self.groups.append(new_group) - 798 else: - 799 # Save what did not pass schema validation - 800 print(cat_obj) - 801 - 802 # Sort Categories - 803 self.groups.sort(key=lambda x: x.name) - 804 else: - 805 print(f" - Could not load {loading_stream_type} Groups") - 806 break - 807 - 808 ## Get Streams - 809 - 810 # Try loading local file - 811 dt = 0 - 812 start = timer() - 813 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") - 814 # If file empty or does not exists, download it from remote - 815 if all_streams is None: - 816 # Load all Streams and save file locally - 817 all_streams = self._load_streams_from_provider(loading_stream_type) - 818 self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json") - 819 dt = timer() - start - 820 - 821 # If we got the STREAMS data, show the statistics and load Streams - 822 if all_streams is not None: - 823 print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds") - 824 ## Add Streams to dictionaries + 784 # If pyxtream has already loaded the data, skip and return success + 785 if self.state["loaded"] is True: + 786 print("Warning, data has already been loaded.") + 787 return True + 788 + 789 # Delete skipped channels from cache + 790 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 791 try: + 792 f = open(full_filename, mode="r+", encoding="utf-8") + 793 f.truncate(0) + 794 f.close() + 795 except FileNotFoundError: + 796 pass + 797 + 798 for loading_stream_type in (self.live_type, self.vod_type, self.series_type): + 799 # Get GROUPS + 800 + 801 # Try loading local file + 802 dt = 0 + 803 start = timer() + 804 all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json") + 805 # If file empty or does not exists, download it from remote + 806 if all_cat is None: + 807 # Load all Groups and save file locally + 808 all_cat = self._load_categories_from_provider(loading_stream_type) + 809 if all_cat is not None: + 810 self._save_to_file(all_cat, f"all_groups_{loading_stream_type}.json") + 811 dt = timer() - start + 812 + 813 # If we got the GROUPS data, show the statistics and load GROUPS + 814 if all_cat is not None: + 815 print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds") + 816 # Add GROUPS to dictionaries + 817 + 818 # Add the catch-all-errors group + 819 if loading_stream_type == self.live_type: + 820 self.groups.append(self.live_catch_all_group) + 821 elif loading_stream_type == self.vod_type: + 822 self.groups.append(self.vod_catch_all_group) + 823 elif loading_stream_type == self.series_type: + 824 self.groups.append(self.series_catch_all_group) 825 - 826 skipped_adult_content = 0 - 827 skipped_no_name_content = 0 - 828 - 829 number_of_streams = len(all_streams) - 830 current_stream_number = 0 - 831 # Calculate 1% of total number of streams - 832 # This is used to slow down the progress bar - 833 one_percent_number_of_streams = number_of_streams/100 - 834 start = timer() - 835 for stream_channel in all_streams: - 836 skip_stream = False - 837 current_stream_number += 1 - 838 - 839 # Show download progress every 1% of total number of streams - 840 if current_stream_number < one_percent_number_of_streams: - 841 progress( - 842 current_stream_number, - 843 number_of_streams, - 844 f"Processing {loading_stream_type} Streams" - 845 ) - 846 one_percent_number_of_streams *= 2 - 847 - 848 # Validate JSON scheme - 849 if self.validate_json: - 850 if loading_stream_type == self.series_type: - 851 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): - 852 print(stream_channel) - 853 elif loading_stream_type == self.live_type: - 854 if not schemaValidator(stream_channel, SchemaType.LIVE): - 855 print(stream_channel) - 856 else: - 857 # vod_type - 858 if not schemaValidator(stream_channel, SchemaType.VOD): - 859 print(stream_channel) - 860 - 861 # Skip if the name of the stream is empty - 862 if stream_channel["name"] == "": - 863 skip_stream = True - 864 skipped_no_name_content = skipped_no_name_content + 1 - 865 self._save_to_file_skipped_streams(stream_channel) - 866 - 867 # Skip if the user chose to hide adult streams - 868 if self.hide_adult_content and loading_stream_type == self.live_type: - 869 if "is_adult" in stream_channel: - 870 if stream_channel["is_adult"] == "1": - 871 skip_stream = True - 872 skipped_adult_content = skipped_adult_content + 1 - 873 self._save_to_file_skipped_streams(stream_channel) - 874 - 875 if not skip_stream: - 876 # Some channels have no group, - 877 # so let's add them to the catch all group - 878 if stream_channel["category_id"] == "": - 879 stream_channel["category_id"] = "9999" - 880 elif stream_channel["category_id"] != "1": - 881 pass - 882 - 883 # Find the first occurence of the group that the - 884 # Channel or Stream is pointing to - 885 the_group = next( - 886 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), - 887 None - 888 ) - 889 - 890 # Set group title - 891 if the_group is not None: - 892 group_title = the_group.name - 893 else: - 894 if loading_stream_type == self.live_type: - 895 group_title = self.live_catch_all_group.name - 896 the_group = self.live_catch_all_group - 897 elif loading_stream_type == self.vod_type: - 898 group_title = self.vod_catch_all_group.name - 899 the_group = self.vod_catch_all_group - 900 elif loading_stream_type == self.series_type: - 901 group_title = self.series_catch_all_group.name - 902 the_group = self.series_catch_all_group - 903 - 904 - 905 if loading_stream_type == self.series_type: - 906 # Load all Series - 907 new_series = Serie(self, stream_channel) - 908 # To get all the Episodes for every Season of each - 909 # Series is very time consuming, we will only - 910 # populate the Series once the user click on the - 911 # Series, the Seasons and Episodes will be loaded - 912 # using x.getSeriesInfoByID() function - 913 - 914 else: - 915 new_channel = Channel( - 916 self, - 917 group_title, - 918 stream_channel - 919 ) - 920 - 921 if new_channel.group_id == "9999": - 922 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") + 826 for cat_obj in all_cat: + 827 if schemaValidator(cat_obj, SchemaType.GROUP): + 828 # Create Group (Category) + 829 new_group = Group(cat_obj, loading_stream_type) + 830 # Add to xtream class + 831 self.groups.append(new_group) + 832 else: + 833 # Save what did not pass schema validation + 834 print(cat_obj) + 835 + 836 # Sort Categories + 837 self.groups.sort(key=lambda x: x.name) + 838 else: + 839 print(f" - Could not load {loading_stream_type} Groups") + 840 break + 841 + 842 # Get Streams + 843 + 844 # Try loading local file + 845 dt = 0 + 846 start = timer() + 847 all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json") + 848 # If file empty or does not exists, download it from remote + 849 if all_streams is None: + 850 # Load all Streams and save file locally + 851 all_streams = self._load_streams_from_provider(loading_stream_type) + 852 self._save_to_file(all_streams, f"all_stream_{loading_stream_type}.json") + 853 dt = timer() - start + 854 + 855 # If we got the STREAMS data, show the statistics and load Streams + 856 if all_streams is not None: + 857 print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds") + 858 # Add Streams to dictionaries + 859 + 860 skipped_adult_content = 0 + 861 skipped_no_name_content = 0 + 862 + 863 number_of_streams = len(all_streams) + 864 current_stream_number = 0 + 865 # Calculate 1% of total number of streams + 866 # This is used to slow down the progress bar + 867 one_percent_number_of_streams = number_of_streams/100 + 868 start = timer() + 869 for stream_channel in all_streams: + 870 skip_stream = False + 871 current_stream_number += 1 + 872 + 873 # Show download progress every 1% of total number of streams + 874 if current_stream_number < one_percent_number_of_streams: + 875 progress( + 876 current_stream_number, + 877 number_of_streams, + 878 f"Processing {loading_stream_type} Streams" + 879 ) + 880 one_percent_number_of_streams *= 2 + 881 + 882 # Validate JSON scheme + 883 if self.validate_json: + 884 if loading_stream_type == self.series_type: + 885 if not schemaValidator(stream_channel, SchemaType.SERIES_INFO): + 886 print(stream_channel) + 887 elif loading_stream_type == self.live_type: + 888 if not schemaValidator(stream_channel, SchemaType.LIVE): + 889 print(stream_channel) + 890 else: + 891 # vod_type + 892 if not schemaValidator(stream_channel, SchemaType.VOD): + 893 print(stream_channel) + 894 + 895 # Skip if the name of the stream is empty + 896 if stream_channel["name"] == "": + 897 skip_stream = True + 898 skipped_no_name_content = skipped_no_name_content + 1 + 899 self._save_to_file_skipped_streams(stream_channel) + 900 + 901 # Skip if the user chose to hide adult streams + 902 if self.hide_adult_content and loading_stream_type == self.live_type: + 903 if "is_adult" in stream_channel: + 904 if stream_channel["is_adult"] == "1": + 905 skip_stream = True + 906 skipped_adult_content = skipped_adult_content + 1 + 907 self._save_to_file_skipped_streams(stream_channel) + 908 + 909 if not skip_stream: + 910 # Some channels have no group, + 911 # so let's add them to the catch all group + 912 if not stream_channel["category_id"]: + 913 stream_channel["category_id"] = "9999" + 914 elif stream_channel["category_id"] != "1": + 915 pass + 916 + 917 # Find the first occurrence of the group that the + 918 # Channel or Stream is pointing to + 919 the_group = next( + 920 (x for x in self.groups if x.group_id == int(stream_channel["category_id"])), + 921 None + 922 ) 923 - 924 # Save the new channel to the local list of channels - 925 if loading_stream_type == self.live_type: - 926 self.channels.append(new_channel) - 927 elif loading_stream_type == self.vod_type: - 928 self.movies.append(new_channel) - 929 if new_channel.age_days_from_added < 31: - 930 self.movies_30days.append(new_channel) - 931 if new_channel.age_days_from_added < 7: - 932 self.movies_7days.append(new_channel) - 933 else: - 934 self.series.append(new_series) - 935 - 936 # Add stream to the specific Group - 937 if the_group is not None: - 938 if loading_stream_type != self.series_type: - 939 the_group.channels.append(new_channel) - 940 else: - 941 the_group.series.append(new_series) - 942 else: - 943 print(f" - Group not found `{stream_channel['name']}`") - 944 print("\n") - 945 # Print information of which streams have been skipped - 946 if self.hide_adult_content: - 947 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") - 948 if skipped_no_name_content > 0: - 949 print(f" - Skipped {skipped_no_name_content} " - 950 "unprintable {loading_stream_type} streams") - 951 else: - 952 print(f" - Could not load {loading_stream_type} Streams") + 924 # Set group title + 925 if the_group is not None: + 926 group_title = the_group.name + 927 else: + 928 if loading_stream_type == self.live_type: + 929 group_title = self.live_catch_all_group.name + 930 the_group = self.live_catch_all_group + 931 elif loading_stream_type == self.vod_type: + 932 group_title = self.vod_catch_all_group.name + 933 the_group = self.vod_catch_all_group + 934 elif loading_stream_type == self.series_type: + 935 group_title = self.series_catch_all_group.name + 936 the_group = self.series_catch_all_group + 937 + 938 if loading_stream_type == self.series_type: + 939 # Load all Series + 940 new_series = Serie(self, stream_channel) + 941 # To get all the Episodes for every Season of each + 942 # Series is very time consuming, we will only + 943 # populate the Series once the user click on the + 944 # Series, the Seasons and Episodes will be loaded + 945 # using x.getSeriesInfoByID() function + 946 + 947 else: + 948 new_channel = Channel( + 949 self, + 950 group_title, + 951 stream_channel + 952 ) 953 - 954 self.state["loaded"] = True - 955 - 956 def _save_to_file_skipped_streams(self, stream_channel: Channel): - 957 - 958 # Build the full path - 959 full_filename = osp.join(self.cache_path, "skipped_streams.json") - 960 - 961 # If the path makes sense, save the file - 962 json_data = json.dumps(stream_channel, ensure_ascii=False) - 963 try: - 964 with open(full_filename, mode="a", encoding="utf-8") as myfile: - 965 myfile.writelines(json_data) - 966 return True - 967 except Exception as e: - 968 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") - 969 return False - 970 - 971 def get_series_info_by_id(self, get_series: dict): - 972 """Get Seasons and Episodes for a Series - 973 - 974 Args: - 975 get_series (dict): Series dictionary - 976 """ - 977 - 978 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) - 979 - 980 if series_seasons["seasons"] is None: - 981 series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}] - 982 - 983 for series_info in series_seasons["seasons"]: - 984 season_name = series_info["name"] - 985 season_key = series_info['season_number'] - 986 season = Season(season_name) - 987 get_series.seasons[season_name] = season - 988 if "episodes" in series_seasons.keys(): - 989 for series_season in series_seasons["episodes"].keys(): - 990 for episode_info in series_seasons["episodes"][str(series_season)]: - 991 new_episode_channel = Episode( - 992 self, series_info, "Testing", episode_info - 993 ) - 994 season.episodes[episode_info["title"]] = new_episode_channel - 995 - 996 def _handle_request_exception(self, exception: requests.exceptions.RequestException): - 997 """Handle different types of request exceptions.""" - 998 if isinstance(exception, requests.exceptions.ConnectionError): - 999 print(" - Connection Error: Possible network problem \ -1000 (e.g. DNS failure, refused connection, etc)") -1001 elif isinstance(exception, requests.exceptions.HTTPError): -1002 print(" - HTTP Error") -1003 elif isinstance(exception, requests.exceptions.TooManyRedirects): -1004 print(" - TooManyRedirects") -1005 elif isinstance(exception, requests.exceptions.ReadTimeout): -1006 print(" - Timeout while loading data") -1007 else: -1008 print(f" - An unexpected error occurred: {exception}") -1009 -1010 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: -1011 """Generic GET Request with Error handling + 954 if new_channel.group_id == "9999": + 955 print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}") + 956 + 957 # Save the new channel to the local list of channels + 958 if loading_stream_type == self.live_type: + 959 self.channels.append(new_channel) + 960 elif loading_stream_type == self.vod_type: + 961 self.movies.append(new_channel) + 962 if new_channel.age_days_from_added < 31: + 963 self.movies_30days.append(new_channel) + 964 if new_channel.age_days_from_added < 7: + 965 self.movies_7days.append(new_channel) + 966 else: + 967 self.series.append(new_series) + 968 + 969 # Add stream to the specific Group + 970 if the_group is not None: + 971 if loading_stream_type != self.series_type: + 972 the_group.channels.append(new_channel) + 973 else: + 974 the_group.series.append(new_series) + 975 else: + 976 print(f" - Group not found `{stream_channel['name']}`") + 977 print("\n") + 978 # Print information of which streams have been skipped + 979 if self.hide_adult_content: + 980 print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams") + 981 if skipped_no_name_content > 0: + 982 print(f" - Skipped {skipped_no_name_content} " + 983 "unprintable {loading_stream_type} streams") + 984 else: + 985 print(f" - Could not load {loading_stream_type} Streams") + 986 + 987 self.state["loaded"] = True + 988 return True + 989 + 990 def _save_to_file_skipped_streams(self, stream_channel: Channel): + 991 + 992 # Build the full path + 993 full_filename = osp.join(self.cache_path, "skipped_streams.json") + 994 + 995 # If the path makes sense, save the file + 996 json_data = json.dumps(stream_channel, ensure_ascii=False) + 997 try: + 998 with open(full_filename, mode="a", encoding="utf-8") as myfile: + 999 myfile.writelines(json_data) +1000 myfile.write('\n') +1001 return True +1002 except Exception as e: +1003 print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`") +1004 return False +1005 +1006 def get_series_info_by_id(self, get_series: dict): +1007 """Get Seasons and Episodes for a Series +1008 +1009 Args: +1010 get_series (dict): Series dictionary +1011 """ 1012 -1013 Args: -1014 URL (str): The URL where to GET content -1015 timeout (Tuple[int, int], optional): Connection and Downloading Timeout. -1016 Defaults to (2,15). -1017 -1018 Returns: -1019 Optional[dict]: JSON dictionary of the loaded data, or None -1020 """ -1021 for attempt in range(10): -1022 time.sleep(1) -1023 try: -1024 response = requests.get(url, timeout=timeout, headers=self.connection_headers) -1025 response.raise_for_status() # Raise an HTTPError for bad responses (4xx and 5xx) -1026 return response.json() -1027 except requests.exceptions.RequestException as e: -1028 self._handle_request_exception(e) -1029 -1030 return None -1031 # i = 0 -1032 # while i < 10: -1033 # time.sleep(1) -1034 # try: -1035 # r = requests.get(url, timeout=timeout, headers=self.connection_headers) -1036 # i = 20 -1037 # if r.status_code == 200: -1038 # return r.json() -1039 # except requests.exceptions.ConnectionError: -1040 # print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)") -1041 # i += 1 -1042 -1043 # except requests.exceptions.HTTPError: -1044 # print(" - HTTP Error") -1045 # i += 1 -1046 -1047 # except requests.exceptions.TooManyRedirects: -1048 # print(" - TooManyRedirects") -1049 # i += 1 -1050 -1051 # except requests.exceptions.ReadTimeout: -1052 # print(" - Timeout while loading data") -1053 # i += 1 -1054 -1055 # return None -1056 -1057 # GET Stream Categories -1058 def _load_categories_from_provider(self, stream_type: str): -1059 """Get from provider all category for specific stream type from provider -1060 -1061 Args: -1062 stream_type (str): Stream type can be Live, VOD, Series -1063 -1064 Returns: -1065 [type]: JSON if successfull, otherwise None -1066 """ -1067 url = "" -1068 if stream_type == self.live_type: -1069 url = self.get_live_categories_URL() -1070 elif stream_type == self.vod_type: -1071 url = self.get_vod_cat_URL() -1072 elif stream_type == self.series_type: -1073 url = self.get_series_cat_URL() -1074 else: -1075 url = "" -1076 -1077 return self._get_request(url) -1078 -1079 # GET Streams -1080 def _load_streams_from_provider(self, stream_type: str): -1081 """Get from provider all streams for specific stream type +1013 series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id) +1014 +1015 if series_seasons["seasons"] is None: +1016 series_seasons["seasons"] = [ +1017 {"name": "Season 1", "cover": series_seasons["info"]["cover"]} +1018 ] +1019 +1020 for series_info in series_seasons["seasons"]: +1021 season_name = series_info["name"] +1022 season = Season(season_name) +1023 get_series.seasons[season_name] = season +1024 if "episodes" in series_seasons.keys(): +1025 for series_season in series_seasons["episodes"].keys(): +1026 for episode_info in series_seasons["episodes"][str(series_season)]: +1027 new_episode_channel = Episode( +1028 self, series_info, "Testing", episode_info +1029 ) +1030 season.episodes[episode_info["title"]] = new_episode_channel +1031 +1032 def _handle_request_exception(self, exception: requests.exceptions.RequestException): +1033 """Handle different types of request exceptions.""" +1034 if isinstance(exception, requests.exceptions.ConnectionError): +1035 print(" - Connection Error: Possible network problem \ +1036 (e.g. DNS failure, refused connection, etc)") +1037 elif isinstance(exception, requests.exceptions.HTTPError): +1038 print(" - HTTP Error") +1039 elif isinstance(exception, requests.exceptions.TooManyRedirects): +1040 print(" - TooManyRedirects") +1041 elif isinstance(exception, requests.exceptions.ReadTimeout): +1042 print(" - Timeout while loading data") +1043 else: +1044 print(f" - An unexpected error occurred: {exception}") +1045 +1046 def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]: +1047 """Generic GET Request with Error handling +1048 +1049 Args: +1050 URL (str): The URL where to GET content +1051 timeout (Tuple[int, int], optional): Connection and Downloading Timeout. +1052 Defaults to (2,15). +1053 +1054 Returns: +1055 Optional[dict]: JSON dictionary of the loaded data, or None +1056 """ +1057 +1058 kb_size = 1024 +1059 all_data = [] +1060 down_stats = {"bytes": 0, "kbytes": 0, "mbytes": 0, "start": 0.0, "delta_sec": 0.0} +1061 +1062 for attempt in range(10): +1063 try: +1064 response = requests.get( +1065 url, +1066 stream=True, +1067 timeout=timeout, +1068 headers=self.connection_headers +1069 ) +1070 response.raise_for_status() # Raise an HTTPError for bad responses (4xx and 5xx) +1071 break +1072 except requests.exceptions.RequestException as e: +1073 self._handle_request_exception(e) +1074 return None +1075 +1076 # If there is an answer from the remote server +1077 if response.status_code in (200, 206): +1078 down_stats["start"] = time.perf_counter() +1079 +1080 # Set downloaded size +1081 down_stats["bytes"] = 0 1082 -1083 Args: -1084 stream_type (str): Stream type can be Live, VOD, Series +1083 # Set stream blocks +1084 block_bytes = int(1*kb_size*kb_size) # 4 MB 1085 -1086 Returns: -1087 [type]: JSON if successfull, otherwise None -1088 """ -1089 url = "" -1090 if stream_type == self.live_type: -1091 url = self.get_live_streams_URL() -1092 elif stream_type == self.vod_type: -1093 url = self.get_vod_streams_URL() -1094 elif stream_type == self.series_type: -1095 url = self.get_series_URL() -1096 else: -1097 url = "" -1098 -1099 return self._get_request(url) -1100 -1101 # GET Streams by Category -1102 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): -1103 """Get from provider all streams for specific stream type with category/group ID -1104 -1105 Args: -1106 stream_type (str): Stream type can be Live, VOD, Series -1107 category_id ([type]): Category/Group ID. -1108 -1109 Returns: -1110 [type]: JSON if successfull, otherwise None -1111 """ -1112 url = "" -1113 -1114 if stream_type == self.live_type: -1115 url = self.get_live_streams_URL_by_category(category_id) -1116 elif stream_type == self.vod_type: -1117 url = self.get_vod_streams_URL_by_category(category_id) -1118 elif stream_type == self.series_type: -1119 url = self.get_series_URL_by_category(category_id) -1120 else: -1121 url = "" -1122 -1123 return self._get_request(url) -1124 -1125 # GET SERIES Info -1126 def _load_series_info_by_id_from_provider(self, series_id: str): -1127 """Gets informations about a Serie -1128 -1129 Args: -1130 series_id (str): Serie ID as described in Group +1086 # Grab data by block_bytes +1087 for data in response.iter_content(block_bytes, decode_unicode=False): +1088 down_stats["bytes"] += len(data) +1089 down_stats["kbytes"] = down_stats["bytes"]/kb_size +1090 down_stats["mbytes"] = down_stats["bytes"]/kb_size/kb_size +1091 down_stats["delta_sec"] = time.perf_counter() - down_stats["start"] +1092 download_speed_average = down_stats["kbytes"]//down_stats["delta_sec"] +1093 sys.stdout.write( +1094 f'\rDownloading {down_stats["kbytes"]:.1f} MB at {download_speed_average:.0f} kB/s' +1095 ) +1096 sys.stdout.flush() +1097 all_data.append(data) +1098 print(" - Done") +1099 full_content = b''.join(all_data) +1100 return json.loads(full_content) +1101 +1102 print(f"HTTP error {response.status_code} while retrieving from {url}") +1103 +1104 return None +1105 +1106 # GET Stream Categories +1107 def _load_categories_from_provider(self, stream_type: str): +1108 """Get from provider all category for specific stream type from provider +1109 +1110 Args: +1111 stream_type (str): Stream type can be Live, VOD, Series +1112 +1113 Returns: +1114 [type]: JSON if successful, otherwise None +1115 """ +1116 url = "" +1117 if stream_type == self.live_type: +1118 url = self.get_live_categories_URL() +1119 elif stream_type == self.vod_type: +1120 url = self.get_vod_cat_URL() +1121 elif stream_type == self.series_type: +1122 url = self.get_series_cat_URL() +1123 else: +1124 url = "" +1125 +1126 return self._get_request(url) +1127 +1128 # GET Streams +1129 def _load_streams_from_provider(self, stream_type: str): +1130 """Get from provider all streams for specific stream type 1131 -1132 Returns: -1133 [type]: JSON if successfull, otherwise None -1134 """ -1135 return self._get_request(self.get_series_info_URL_by_ID(series_id)) -1136 -1137 # The seasons array, might be filled or might be completely empty. -1138 # If it is not empty, it will contain the cover, overview and the air date -1139 # of the selected season. -1140 # In your APP if you want to display the series, you have to take that -1141 # from the episodes array. -1142 -1143 # GET VOD Info -1144 def vodInfoByID(self, vod_id): -1145 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) -1146 -1147 # GET short_epg for LIVE Streams (same as stalker portal, -1148 # prints the next X EPG that will play soon) -1149 def liveEpgByStream(self, stream_id): -1150 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) -1151 -1152 def liveEpgByStreamAndLimit(self, stream_id, limit): -1153 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) -1154 -1155 # GET ALL EPG for LIVE Streams (same as stalker portal, -1156 # but it will print all epg listings regardless of the day) -1157 def allLiveEpgByStream(self, stream_id): -1158 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) -1159 -1160 # Full EPG List for all Streams -1161 def allEpg(self): -1162 return self._get_request(self.get_all_epg_URL()) -1163 -1164 ## URL-builder methods -1165 def get_live_categories_URL(self) -> str: -1166 return f"{self.base_url}&action=get_live_categories" -1167 -1168 def get_live_streams_URL(self) -> str: -1169 return f"{self.base_url}&action=get_live_streams" -1170 -1171 def get_live_streams_URL_by_category(self, category_id) -> str: -1172 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" +1132 Args: +1133 stream_type (str): Stream type can be Live, VOD, Series +1134 +1135 Returns: +1136 [type]: JSON if successful, otherwise None +1137 """ +1138 url = "" +1139 if stream_type == self.live_type: +1140 url = self.get_live_streams_URL() +1141 elif stream_type == self.vod_type: +1142 url = self.get_vod_streams_URL() +1143 elif stream_type == self.series_type: +1144 url = self.get_series_URL() +1145 else: +1146 url = "" +1147 +1148 return self._get_request(url) +1149 +1150 # GET Streams by Category +1151 def _load_streams_by_category_from_provider(self, stream_type: str, category_id): +1152 """Get from provider all streams for specific stream type with category/group ID +1153 +1154 Args: +1155 stream_type (str): Stream type can be Live, VOD, Series +1156 category_id ([type]): Category/Group ID. +1157 +1158 Returns: +1159 [type]: JSON if successful, otherwise None +1160 """ +1161 url = "" +1162 +1163 if stream_type == self.live_type: +1164 url = self.get_live_streams_URL_by_category(category_id) +1165 elif stream_type == self.vod_type: +1166 url = self.get_vod_streams_URL_by_category(category_id) +1167 elif stream_type == self.series_type: +1168 url = self.get_series_URL_by_category(category_id) +1169 else: +1170 url = "" +1171 +1172 return self._get_request(url) 1173 -1174 def get_vod_cat_URL(self) -> str: -1175 return f"{self.base_url}&action=get_vod_categories" -1176 -1177 def get_vod_streams_URL(self) -> str: -1178 return f"{self.base_url}&action=get_vod_streams" -1179 -1180 def get_vod_streams_URL_by_category(self, category_id) -> str: -1181 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" -1182 -1183 def get_series_cat_URL(self) -> str: -1184 return f"{self.base_url}&action=get_series_categories" -1185 -1186 def get_series_URL(self) -> str: -1187 return f"{self.base_url}&action=get_series" -1188 -1189 def get_series_URL_by_category(self, category_id) -> str: -1190 return f"{self.base_url}&action=get_series&category_id={category_id}" -1191 -1192 def get_series_info_URL_by_ID(self, series_id) -> str: -1193 return f"{self.base_url}&action=get_series_info&series_id={series_id}" -1194 -1195 def get_VOD_info_URL_by_ID(self, vod_id) -> str: -1196 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" -1197 -1198 def get_live_epg_URL_by_stream(self, stream_id) -> str: -1199 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" -1200 -1201 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: -1202 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" -1203 -1204 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: -1205 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" -1206 -1207 def get_all_epg_URL(self) -> str: -1208 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}" +1174 # GET SERIES Info +1175 def _load_series_info_by_id_from_provider(self, series_id: str, return_type: str = "DICT"): +1176 """Gets information about a Serie +1177 +1178 Args: +1179 series_id (str): Serie ID as described in Group +1180 return_type (str, optional): Output format, 'DICT' or 'JSON'. Defaults to "DICT". +1181 +1182 Returns: +1183 [type]: JSON if successful, otherwise None +1184 """ +1185 data = self._get_request(self.get_series_info_URL_by_ID(series_id)) +1186 if return_type == "JSON": +1187 return json.dumps(data, ensure_ascii=False) +1188 return data +1189 +1190 # The seasons array, might be filled or might be completely empty. +1191 # If it is not empty, it will contain the cover, overview and the air date +1192 # of the selected season. +1193 # In your APP if you want to display the series, you have to take that +1194 # from the episodes array. +1195 +1196 # GET VOD Info +1197 def vodInfoByID(self, vod_id): +1198 return self._get_request(self.get_VOD_info_URL_by_ID(vod_id)) +1199 +1200 # GET short_epg for LIVE Streams (same as stalker portal, +1201 # prints the next X EPG that will play soon) +1202 def liveEpgByStream(self, stream_id): +1203 return self._get_request(self.get_live_epg_URL_by_stream(stream_id)) +1204 +1205 def liveEpgByStreamAndLimit(self, stream_id, limit): +1206 return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit)) +1207 +1208 # GET ALL EPG for LIVE Streams (same as stalker portal, +1209 # but it will print all epg listings regardless of the day) +1210 def allLiveEpgByStream(self, stream_id): +1211 return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id)) +1212 +1213 # Full EPG List for all Streams +1214 def allEpg(self): +1215 return self._get_request(self.get_all_epg_URL()) +1216 +1217 # URL-builder methods +1218 def get_live_categories_URL(self) -> str: +1219 return f"{self.base_url}&action=get_live_categories" +1220 +1221 def get_live_streams_URL(self) -> str: +1222 return f"{self.base_url}&action=get_live_streams" +1223 +1224 def get_live_streams_URL_by_category(self, category_id) -> str: +1225 return f"{self.base_url}&action=get_live_streams&category_id={category_id}" +1226 +1227 def get_vod_cat_URL(self) -> str: +1228 return f"{self.base_url}&action=get_vod_categories" +1229 +1230 def get_vod_streams_URL(self) -> str: +1231 return f"{self.base_url}&action=get_vod_streams" +1232 +1233 def get_vod_streams_URL_by_category(self, category_id) -> str: +1234 return f"{self.base_url}&action=get_vod_streams&category_id={category_id}" +1235 +1236 def get_series_cat_URL(self) -> str: +1237 return f"{self.base_url}&action=get_series_categories" +1238 +1239 def get_series_URL(self) -> str: +1240 return f"{self.base_url}&action=get_series" +1241 +1242 def get_series_URL_by_category(self, category_id) -> str: +1243 return f"{self.base_url}&action=get_series&category_id={category_id}" +1244 +1245 def get_series_info_URL_by_ID(self, series_id) -> str: +1246 return f"{self.base_url}&action=get_series_info&series_id={series_id}" +1247 +1248 def get_VOD_info_URL_by_ID(self, vod_id) -> str: +1249 return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}" +1250 +1251 def get_live_epg_URL_by_stream(self, stream_id) -> str: +1252 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}" +1253 +1254 def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str: +1255 return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}" +1256 +1257 def get_all_live_epg_URL_by_stream(self, stream_id) -> str: +1258 return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}" +1259 +1260 def get_all_epg_URL(self) -> str: +1261 return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"

    @@ -1660,92 +1734,96 @@

    -
     43class Channel:
    - 44    # Required by Hypnotix
    - 45    info = ""
    - 46    id = ""
    - 47    name = ""  # What is the difference between the below name and title?
    - 48    logo = ""
    - 49    logo_path = ""
    - 50    group_title = ""
    - 51    title = ""
    - 52    url = ""
    - 53
    - 54    # XTream
    - 55    stream_type: str = ""
    - 56    group_id: str = ""
    - 57    is_adult: int = 0
    - 58    added: int = 0
    - 59    epg_channel_id: str = ""
    - 60    age_days_from_added: int = 0
    - 61    date_now: datetime
    - 62
    - 63    # This contains the raw JSON data
    - 64    raw = ""
    - 65
    - 66    def __init__(self, xtream: object, group_title, stream_info):
    - 67        self.date_now = datetime.now()
    - 68
    - 69        stream_type = stream_info["stream_type"]
    - 70        # Adjust the odd "created_live" type
    - 71        if stream_type in ("created_live", "radio_streams"):
    - 72            stream_type = "live"
    - 73
    - 74        if stream_type not in ("live", "movie"):
    - 75            print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`")
    - 76        else:
    - 77            # Raw JSON Channel
    - 78            self.raw = stream_info
    - 79
    - 80            stream_name = stream_info["name"]
    - 81
    - 82            # Required by Hypnotix
    - 83            self.id = stream_info["stream_id"]
    - 84            self.name = stream_name
    - 85            self.logo = stream_info["stream_icon"]
    - 86            self.logo_path = xtream._get_logo_local_path(self.logo)
    - 87            self.group_title = group_title
    - 88            self.title = stream_name
    - 89
    - 90            # Check if category_id key is available
    - 91            if "category_id" in stream_info.keys():
    - 92                self.group_id = int(stream_info["category_id"])
    - 93
    - 94            if stream_type == "live":
    - 95                stream_extension = "ts"
    +            
     44class Channel:
    + 45    # Required by Hypnotix
    + 46    info = ""
    + 47    id = ""
    + 48    name = ""  # What is the difference between the below name and title?
    + 49    logo = ""
    + 50    logo_path = ""
    + 51    group_title = ""
    + 52    title = ""
    + 53    url = ""
    + 54
    + 55    # XTream
    + 56    stream_type: str = ""
    + 57    group_id: str = ""
    + 58    is_adult: int = 0
    + 59    added: int = 0
    + 60    epg_channel_id: str = ""
    + 61    age_days_from_added: int = 0
    + 62    date_now: datetime
    + 63
    + 64    # This contains the raw JSON data
    + 65    raw = ""
    + 66
    + 67    def __init__(self, xtream: object, group_title, stream_info):
    + 68        self.date_now = datetime.now()
    + 69
    + 70        stream_type = stream_info["stream_type"]
    + 71        # Adjust the odd "created_live" type
    + 72        if stream_type in ("created_live", "radio_streams"):
    + 73            stream_type = "live"
    + 74
    + 75        if stream_type not in ("live", "movie"):
    + 76            print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`")
    + 77        else:
    + 78            # Raw JSON Channel
    + 79            self.raw = stream_info
    + 80
    + 81            stream_name = stream_info["name"]
    + 82
    + 83            # Required by Hypnotix
    + 84            self.id = stream_info["stream_id"]
    + 85            self.name = stream_name
    + 86            self.logo = stream_info["stream_icon"]
    + 87            self.logo_path = xtream._get_logo_local_path(self.logo)
    + 88            self.group_title = group_title
    + 89            self.title = stream_name
    + 90
    + 91            # Check if category_id key is available
    + 92            if "category_id" in stream_info.keys():
    + 93                self.group_id = int(stream_info["category_id"])
    + 94
    + 95            stream_extension = ""
      96
    - 97                # Check if epg_channel_id key is available
    - 98                if "epg_channel_id" in stream_info.keys():
    - 99                    self.epg_channel_id = stream_info["epg_channel_id"]
    -100
    -101            elif stream_type == "movie":
    -102                stream_extension = stream_info["container_extension"]
    + 97            if stream_type == "live":
    + 98                stream_extension = "ts"
    + 99
    +100                # Check if epg_channel_id key is available
    +101                if "epg_channel_id" in stream_info.keys():
    +102                    self.epg_channel_id = stream_info["epg_channel_id"]
     103
    -104            # Default to 0
    -105            self.is_adult = 0
    -106            # Check if is_adult key is available
    -107            if "is_adult" in stream_info.keys():
    -108                self.is_adult = int(stream_info["is_adult"])
    -109
    -110            self.added = int(stream_info["added"])
    -111            self.age_days_from_added = abs(datetime.utcfromtimestamp(self.added) - self.date_now).days
    +104            elif stream_type == "movie":
    +105                stream_extension = stream_info["container_extension"]
    +106
    +107            # Default to 0
    +108            self.is_adult = 0
    +109            # Check if is_adult key is available
    +110            if "is_adult" in stream_info.keys():
    +111                self.is_adult = int(stream_info["is_adult"])
     112
    -113            # Required by Hypnotix
    -114            self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \
    -115                       f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}"
    -116
    -117            # Check that the constructed URL is valid
    -118            if not xtream._validate_url(self.url):
    -119                print(f"{self.name} - Bad URL? `{self.url}`")
    -120
    -121    def export_json(self):
    -122        jsondata = {}
    -123
    -124        jsondata["url"] = self.url
    -125        jsondata.update(self.raw)
    -126        jsondata["logo_path"] = self.logo_path
    -127
    -128        return jsondata
    +113            self.added = int(stream_info["added"])
    +114            self.age_days_from_added = abs(
    +115                datetime.utcfromtimestamp(self.added) - self.date_now
    +116                ).days
    +117
    +118            # Required by Hypnotix
    +119            self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \
    +120                       f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}"
    +121
    +122            # Check that the constructed URL is valid
    +123            if not xtream._validate_url(self.url):
    +124                print(f"{self.name} - Bad URL? `{self.url}`")
    +125
    +126    def export_json(self):
    +127        jsondata = {}
    +128
    +129        jsondata["url"] = self.url
    +130        jsondata.update(self.raw)
    +131        jsondata["logo_path"] = self.logo_path
    +132
    +133        return jsondata
     
    @@ -1761,60 +1839,64 @@

    -
     66    def __init__(self, xtream: object, group_title, stream_info):
    - 67        self.date_now = datetime.now()
    - 68
    - 69        stream_type = stream_info["stream_type"]
    - 70        # Adjust the odd "created_live" type
    - 71        if stream_type in ("created_live", "radio_streams"):
    - 72            stream_type = "live"
    - 73
    - 74        if stream_type not in ("live", "movie"):
    - 75            print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`")
    - 76        else:
    - 77            # Raw JSON Channel
    - 78            self.raw = stream_info
    - 79
    - 80            stream_name = stream_info["name"]
    - 81
    - 82            # Required by Hypnotix
    - 83            self.id = stream_info["stream_id"]
    - 84            self.name = stream_name
    - 85            self.logo = stream_info["stream_icon"]
    - 86            self.logo_path = xtream._get_logo_local_path(self.logo)
    - 87            self.group_title = group_title
    - 88            self.title = stream_name
    - 89
    - 90            # Check if category_id key is available
    - 91            if "category_id" in stream_info.keys():
    - 92                self.group_id = int(stream_info["category_id"])
    - 93
    - 94            if stream_type == "live":
    - 95                stream_extension = "ts"
    +            
     67    def __init__(self, xtream: object, group_title, stream_info):
    + 68        self.date_now = datetime.now()
    + 69
    + 70        stream_type = stream_info["stream_type"]
    + 71        # Adjust the odd "created_live" type
    + 72        if stream_type in ("created_live", "radio_streams"):
    + 73            stream_type = "live"
    + 74
    + 75        if stream_type not in ("live", "movie"):
    + 76            print(f"Error the channel has unknown stream type `{stream_type}`\n`{stream_info}`")
    + 77        else:
    + 78            # Raw JSON Channel
    + 79            self.raw = stream_info
    + 80
    + 81            stream_name = stream_info["name"]
    + 82
    + 83            # Required by Hypnotix
    + 84            self.id = stream_info["stream_id"]
    + 85            self.name = stream_name
    + 86            self.logo = stream_info["stream_icon"]
    + 87            self.logo_path = xtream._get_logo_local_path(self.logo)
    + 88            self.group_title = group_title
    + 89            self.title = stream_name
    + 90
    + 91            # Check if category_id key is available
    + 92            if "category_id" in stream_info.keys():
    + 93                self.group_id = int(stream_info["category_id"])
    + 94
    + 95            stream_extension = ""
      96
    - 97                # Check if epg_channel_id key is available
    - 98                if "epg_channel_id" in stream_info.keys():
    - 99                    self.epg_channel_id = stream_info["epg_channel_id"]
    -100
    -101            elif stream_type == "movie":
    -102                stream_extension = stream_info["container_extension"]
    + 97            if stream_type == "live":
    + 98                stream_extension = "ts"
    + 99
    +100                # Check if epg_channel_id key is available
    +101                if "epg_channel_id" in stream_info.keys():
    +102                    self.epg_channel_id = stream_info["epg_channel_id"]
     103
    -104            # Default to 0
    -105            self.is_adult = 0
    -106            # Check if is_adult key is available
    -107            if "is_adult" in stream_info.keys():
    -108                self.is_adult = int(stream_info["is_adult"])
    -109
    -110            self.added = int(stream_info["added"])
    -111            self.age_days_from_added = abs(datetime.utcfromtimestamp(self.added) - self.date_now).days
    +104            elif stream_type == "movie":
    +105                stream_extension = stream_info["container_extension"]
    +106
    +107            # Default to 0
    +108            self.is_adult = 0
    +109            # Check if is_adult key is available
    +110            if "is_adult" in stream_info.keys():
    +111                self.is_adult = int(stream_info["is_adult"])
     112
    -113            # Required by Hypnotix
    -114            self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \
    -115                       f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}"
    -116
    -117            # Check that the constructed URL is valid
    -118            if not xtream._validate_url(self.url):
    -119                print(f"{self.name} - Bad URL? `{self.url}`")
    +113            self.added = int(stream_info["added"])
    +114            self.age_days_from_added = abs(
    +115                datetime.utcfromtimestamp(self.added) - self.date_now
    +116                ).days
    +117
    +118            # Required by Hypnotix
    +119            self.url = f"{xtream.server}/{stream_type}/{xtream.authorization['username']}/" \
    +120                       f"{xtream.authorization['password']}/{stream_info['stream_id']}.{stream_extension}"
    +121
    +122            # Check that the constructed URL is valid
    +123            if not xtream._validate_url(self.url):
    +124                print(f"{self.name} - Bad URL? `{self.url}`")
     
    @@ -2023,14 +2105,14 @@

    -
    121    def export_json(self):
    -122        jsondata = {}
    -123
    -124        jsondata["url"] = self.url
    -125        jsondata.update(self.raw)
    -126        jsondata["logo_path"] = self.logo_path
    -127
    -128        return jsondata
    +            
    126    def export_json(self):
    +127        jsondata = {}
    +128
    +129        jsondata["url"] = self.url
    +130        jsondata.update(self.raw)
    +131        jsondata["logo_path"] = self.logo_path
    +132
    +133        return jsondata
     
    @@ -2049,61 +2131,61 @@

    -
    131class Group:
    -132    # Required by Hypnotix
    -133    name = ""
    -134    group_type = ""
    -135
    -136    # XTream
    -137    group_id = ""
    -138
    -139    # This contains the raw JSON data
    -140    raw = ""
    -141
    -142    def convert_region_shortname_to_fullname(self, shortname):
    +            
    136class Group:
    +137    # Required by Hypnotix
    +138    name = ""
    +139    group_type = ""
    +140
    +141    # XTream
    +142    group_id = ""
     143
    -144        if shortname == "AR":
    -145            return "Arab"
    -146        if shortname == "AM":
    -147            return "America"
    -148        if shortname == "AS":
    -149            return "Asia"
    -150        if shortname == "AF":
    -151            return "Africa"
    -152        if shortname == "EU":
    -153            return "Europe"
    -154
    -155        return ""
    -156
    -157    def __init__(self, group_info: dict, stream_type: str):
    -158        # Raw JSON Group
    -159        self.raw = group_info
    -160
    -161        self.channels = []
    -162        self.series = []
    -163
    -164        TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3)
    +144    # This contains the raw JSON data
    +145    raw = ""
    +146
    +147    def convert_region_shortname_to_fullname(self, shortname):
    +148
    +149        if shortname == "AR":
    +150            return "Arab"
    +151        if shortname == "AM":
    +152            return "America"
    +153        if shortname == "AS":
    +154            return "Asia"
    +155        if shortname == "AF":
    +156            return "Africa"
    +157        if shortname == "EU":
    +158            return "Europe"
    +159
    +160        return ""
    +161
    +162    def __init__(self, group_info: dict, stream_type: str):
    +163        # Raw JSON Group
    +164        self.raw = group_info
     165
    -166        if "VOD" == stream_type:
    -167            self.group_type = MOVIES_GROUP
    -168        elif "Series" == stream_type:
    -169            self.group_type = SERIES_GROUP
    -170        elif "Live" == stream_type:
    -171            self.group_type = TV_GROUP
    -172        else:
    -173            print(f"Unrecognized stream type `{stream_type}` for `{group_info}`")
    -174
    -175        self.name = group_info["category_name"]
    -176        split_name = self.name.split('|')
    -177        self.region_shortname = ""
    -178        self.region_longname = ""
    -179        if len(split_name) > 1:
    -180            self.region_shortname = split_name[0].strip()
    -181            self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname)
    -182
    -183        # Check if category_id key is available
    -184        if "category_id" in group_info.keys():
    -185            self.group_id = int(group_info["category_id"])
    +166        self.channels = []
    +167        self.series = []
    +168
    +169        TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3)
    +170
    +171        if "VOD" == stream_type:
    +172            self.group_type = MOVIES_GROUP
    +173        elif "Series" == stream_type:
    +174            self.group_type = SERIES_GROUP
    +175        elif "Live" == stream_type:
    +176            self.group_type = TV_GROUP
    +177        else:
    +178            print(f"Unrecognized stream type `{stream_type}` for `{group_info}`")
    +179
    +180        self.name = group_info["category_name"]
    +181        split_name = self.name.split('|')
    +182        self.region_shortname = ""
    +183        self.region_longname = ""
    +184        if len(split_name) > 1:
    +185            self.region_shortname = split_name[0].strip()
    +186            self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname)
    +187
    +188        # Check if category_id key is available
    +189        if "category_id" in group_info.keys():
    +190            self.group_id = int(group_info["category_id"])
     
    @@ -2119,35 +2201,35 @@

    -
    157    def __init__(self, group_info: dict, stream_type: str):
    -158        # Raw JSON Group
    -159        self.raw = group_info
    -160
    -161        self.channels = []
    -162        self.series = []
    -163
    -164        TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3)
    +            
    162    def __init__(self, group_info: dict, stream_type: str):
    +163        # Raw JSON Group
    +164        self.raw = group_info
     165
    -166        if "VOD" == stream_type:
    -167            self.group_type = MOVIES_GROUP
    -168        elif "Series" == stream_type:
    -169            self.group_type = SERIES_GROUP
    -170        elif "Live" == stream_type:
    -171            self.group_type = TV_GROUP
    -172        else:
    -173            print(f"Unrecognized stream type `{stream_type}` for `{group_info}`")
    -174
    -175        self.name = group_info["category_name"]
    -176        split_name = self.name.split('|')
    -177        self.region_shortname = ""
    -178        self.region_longname = ""
    -179        if len(split_name) > 1:
    -180            self.region_shortname = split_name[0].strip()
    -181            self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname)
    -182
    -183        # Check if category_id key is available
    -184        if "category_id" in group_info.keys():
    -185            self.group_id = int(group_info["category_id"])
    +166        self.channels = []
    +167        self.series = []
    +168
    +169        TV_GROUP, MOVIES_GROUP, SERIES_GROUP = range(3)
    +170
    +171        if "VOD" == stream_type:
    +172            self.group_type = MOVIES_GROUP
    +173        elif "Series" == stream_type:
    +174            self.group_type = SERIES_GROUP
    +175        elif "Live" == stream_type:
    +176            self.group_type = TV_GROUP
    +177        else:
    +178            print(f"Unrecognized stream type `{stream_type}` for `{group_info}`")
    +179
    +180        self.name = group_info["category_name"]
    +181        split_name = self.name.split('|')
    +182        self.region_shortname = ""
    +183        self.region_longname = ""
    +184        if len(split_name) > 1:
    +185            self.region_shortname = split_name[0].strip()
    +186            self.region_longname = self.convert_region_shortname_to_fullname(self.region_shortname)
    +187
    +188        # Check if category_id key is available
    +189        if "category_id" in group_info.keys():
    +190            self.group_id = int(group_info["category_id"])
     
    @@ -2213,20 +2295,20 @@

    -
    142    def convert_region_shortname_to_fullname(self, shortname):
    -143
    -144        if shortname == "AR":
    -145            return "Arab"
    -146        if shortname == "AM":
    -147            return "America"
    -148        if shortname == "AS":
    -149            return "Asia"
    -150        if shortname == "AF":
    -151            return "Africa"
    -152        if shortname == "EU":
    -153            return "Europe"
    -154
    -155        return ""
    +            
    147    def convert_region_shortname_to_fullname(self, shortname):
    +148
    +149        if shortname == "AR":
    +150            return "Arab"
    +151        if shortname == "AM":
    +152            return "America"
    +153        if shortname == "AS":
    +154            return "Asia"
    +155        if shortname == "AF":
    +156            return "Africa"
    +157        if shortname == "EU":
    +158            return "Europe"
    +159
    +160        return ""
     
    @@ -2289,39 +2371,39 @@

    -
    188class Episode:
    -189    # Required by Hypnotix
    -190    title = ""
    -191    name = ""
    -192    info = ""
    -193
    -194    # XTream
    -195
    -196    # This contains the raw JSON data
    -197    raw = ""
    +            
    193class Episode:
    +194    # Required by Hypnotix
    +195    title = ""
    +196    name = ""
    +197    info = ""
     198
    -199    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
    -200        # Raw JSON Episode
    -201        self.raw = episode_info
    -202
    -203        self.title = episode_info["title"]
    -204        self.name = self.title
    -205        self.group_title = group_title
    -206        self.id = episode_info["id"]
    -207        self.container_extension = episode_info["container_extension"]
    -208        self.episode_number = episode_info["episode_num"]
    -209        self.av_info = episode_info["info"]
    -210
    -211        self.logo = series_info["cover"]
    -212        self.logo_path = xtream._get_logo_local_path(self.logo)
    -213
    -214        self.url =  f"{xtream.server}/series/" \
    -215                    f"{xtream.authorization['username']}/" \
    -216                    f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
    -217
    -218        # Check that the constructed URL is valid
    -219        if not xtream._validate_url(self.url):
    -220            print(f"{self.name} - Bad URL? `{self.url}`")
    +199    # XTream
    +200
    +201    # This contains the raw JSON data
    +202    raw = ""
    +203
    +204    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
    +205        # Raw JSON Episode
    +206        self.raw = episode_info
    +207
    +208        self.title = episode_info["title"]
    +209        self.name = self.title
    +210        self.group_title = group_title
    +211        self.id = episode_info["id"]
    +212        self.container_extension = episode_info["container_extension"]
    +213        self.episode_number = episode_info["episode_num"]
    +214        self.av_info = episode_info["info"]
    +215
    +216        self.logo = series_info["cover"]
    +217        self.logo_path = xtream._get_logo_local_path(self.logo)
    +218
    +219        self.url = f"{xtream.server}/series/" \
    +220                   f"{xtream.authorization['username']}/" \
    +221                   f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
    +222
    +223        # Check that the constructed URL is valid
    +224        if not xtream._validate_url(self.url):
    +225            print(f"{self.name} - Bad URL? `{self.url}`")
     
    @@ -2337,28 +2419,28 @@

    -
    199    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
    -200        # Raw JSON Episode
    -201        self.raw = episode_info
    -202
    -203        self.title = episode_info["title"]
    -204        self.name = self.title
    -205        self.group_title = group_title
    -206        self.id = episode_info["id"]
    -207        self.container_extension = episode_info["container_extension"]
    -208        self.episode_number = episode_info["episode_num"]
    -209        self.av_info = episode_info["info"]
    -210
    -211        self.logo = series_info["cover"]
    -212        self.logo_path = xtream._get_logo_local_path(self.logo)
    -213
    -214        self.url =  f"{xtream.server}/series/" \
    -215                    f"{xtream.authorization['username']}/" \
    -216                    f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
    -217
    -218        # Check that the constructed URL is valid
    -219        if not xtream._validate_url(self.url):
    -220            print(f"{self.name} - Bad URL? `{self.url}`")
    +            
    204    def __init__(self, xtream: object, series_info, group_title, episode_info) -> None:
    +205        # Raw JSON Episode
    +206        self.raw = episode_info
    +207
    +208        self.title = episode_info["title"]
    +209        self.name = self.title
    +210        self.group_title = group_title
    +211        self.id = episode_info["id"]
    +212        self.container_extension = episode_info["container_extension"]
    +213        self.episode_number = episode_info["episode_num"]
    +214        self.av_info = episode_info["info"]
    +215
    +216        self.logo = series_info["cover"]
    +217        self.logo_path = xtream._get_logo_local_path(self.logo)
    +218
    +219        self.url = f"{xtream.server}/series/" \
    +220                   f"{xtream.authorization['username']}/" \
    +221                   f"{xtream.authorization['password']}/{self.id}.{self.container_extension}"
    +222
    +223        # Check that the constructed URL is valid
    +224        if not xtream._validate_url(self.url):
    +225            print(f"{self.name} - Bad URL? `{self.url}`")
     
    @@ -2513,57 +2595,64 @@

    -
    223class Serie:
    -224    # Required by Hypnotix
    -225    name = ""
    -226    logo = ""
    -227    logo_path = ""
    -228
    -229    # XTream
    -230    series_id = ""
    -231    plot = ""
    -232    youtube_trailer = ""
    -233    genre = ""
    -234
    -235    # This contains the raw JSON data
    -236    raw = ""
    -237
    -238    def __init__(self, xtream: object, series_info):
    -239        # Raw JSON Series
    -240        self.raw = series_info
    -241        self.xtream = xtream
    +            
    228class Serie:
    +229    # Required by Hypnotix
    +230    name = ""
    +231    logo = ""
    +232    logo_path = ""
    +233
    +234    # XTream
    +235    series_id = ""
    +236    plot = ""
    +237    youtube_trailer = ""
    +238    genre = ""
    +239
    +240    # This contains the raw JSON data
    +241    raw = ""
     242
    -243        # Required by Hypnotix
    -244        self.name = series_info["name"]
    -245        self.logo = series_info["cover"]
    -246        self.logo_path = xtream._get_logo_local_path(self.logo)
    -247
    -248        self.seasons = {}
    -249        self.episodes = {}
    +243    def __init__(self, xtream: object, series_info):
    +244
    +245        series_info["added"] = series_info["last_modified"]
    +246
    +247        # Raw JSON Series
    +248        self.raw = series_info
    +249        self.xtream = xtream
     250
    -251        # Check if category_id key is available
    -252        if "series_id" in series_info.keys():
    -253            self.series_id = int(series_info["series_id"])
    -254
    -255        # Check if plot key is available
    -256        if "plot" in series_info.keys():
    -257            self.plot = series_info["plot"]
    +251        # Required by Hypnotix
    +252        self.name = series_info["name"]
    +253        self.logo = series_info["cover"]
    +254        self.logo_path = xtream._get_logo_local_path(self.logo)
    +255
    +256        self.seasons = {}
    +257        self.episodes = {}
     258
    -259        # Check if youtube_trailer key is available
    -260        if "youtube_trailer" in series_info.keys():
    -261            self.youtube_trailer = series_info["youtube_trailer"]
    +259        # Check if category_id key is available
    +260        if "series_id" in series_info.keys():
    +261            self.series_id = int(series_info["series_id"])
     262
    -263        # Check if genre key is available
    -264        if "genre" in series_info.keys():
    -265            self.genre = series_info["genre"]
    +263        # Check if plot key is available
    +264        if "plot" in series_info.keys():
    +265            self.plot = series_info["plot"]
     266
    -267    def export_json(self):
    -268        jsondata = {}
    -269
    -270        jsondata.update(self.raw)
    -271        jsondata['logo_path'] = self.logo_path
    -272
    -273        return jsondata
    +267        # Check if youtube_trailer key is available
    +268        if "youtube_trailer" in series_info.keys():
    +269            self.youtube_trailer = series_info["youtube_trailer"]
    +270
    +271        # Check if genre key is available
    +272        if "genre" in series_info.keys():
    +273            self.genre = series_info["genre"]
    +274
    +275        self.url = f"{xtream.server}/series/" \
    +276                   f"{xtream.authorization['username']}/" \
    +277                   f"{xtream.authorization['password']}/{self.series_id}/"
    +278
    +279    def export_json(self):
    +280        jsondata = {}
    +281
    +282        jsondata.update(self.raw)
    +283        jsondata['logo_path'] = self.logo_path
    +284
    +285        return jsondata
     
    @@ -2579,34 +2668,41 @@

    -
    238    def __init__(self, xtream: object, series_info):
    -239        # Raw JSON Series
    -240        self.raw = series_info
    -241        self.xtream = xtream
    -242
    -243        # Required by Hypnotix
    -244        self.name = series_info["name"]
    -245        self.logo = series_info["cover"]
    -246        self.logo_path = xtream._get_logo_local_path(self.logo)
    -247
    -248        self.seasons = {}
    -249        self.episodes = {}
    +            
    243    def __init__(self, xtream: object, series_info):
    +244
    +245        series_info["added"] = series_info["last_modified"]
    +246
    +247        # Raw JSON Series
    +248        self.raw = series_info
    +249        self.xtream = xtream
     250
    -251        # Check if category_id key is available
    -252        if "series_id" in series_info.keys():
    -253            self.series_id = int(series_info["series_id"])
    -254
    -255        # Check if plot key is available
    -256        if "plot" in series_info.keys():
    -257            self.plot = series_info["plot"]
    +251        # Required by Hypnotix
    +252        self.name = series_info["name"]
    +253        self.logo = series_info["cover"]
    +254        self.logo_path = xtream._get_logo_local_path(self.logo)
    +255
    +256        self.seasons = {}
    +257        self.episodes = {}
     258
    -259        # Check if youtube_trailer key is available
    -260        if "youtube_trailer" in series_info.keys():
    -261            self.youtube_trailer = series_info["youtube_trailer"]
    +259        # Check if category_id key is available
    +260        if "series_id" in series_info.keys():
    +261            self.series_id = int(series_info["series_id"])
     262
    -263        # Check if genre key is available
    -264        if "genre" in series_info.keys():
    -265            self.genre = series_info["genre"]
    +263        # Check if plot key is available
    +264        if "plot" in series_info.keys():
    +265            self.plot = series_info["plot"]
    +266
    +267        # Check if youtube_trailer key is available
    +268        if "youtube_trailer" in series_info.keys():
    +269            self.youtube_trailer = series_info["youtube_trailer"]
    +270
    +271        # Check if genre key is available
    +272        if "genre" in series_info.keys():
    +273            self.genre = series_info["genre"]
    +274
    +275        self.url = f"{xtream.server}/series/" \
    +276                   f"{xtream.authorization['username']}/" \
    +277                   f"{xtream.authorization['password']}/{self.series_id}/"
     
    @@ -2741,6 +2837,17 @@

    +

    +
    +
    + url + + +
    + + + +
    @@ -2753,13 +2860,13 @@

    -
    267    def export_json(self):
    -268        jsondata = {}
    -269
    -270        jsondata.update(self.raw)
    -271        jsondata['logo_path'] = self.logo_path
    -272
    -273        return jsondata
    +            
    279    def export_json(self):
    +280        jsondata = {}
    +281
    +282        jsondata.update(self.raw)
    +283        jsondata['logo_path'] = self.logo_path
    +284
    +285        return jsondata
     
    @@ -2778,13 +2885,13 @@

    -
    275class Season:
    -276    # Required by Hypnotix
    -277    name = ""
    -278
    -279    def __init__(self, name):
    -280        self.name = name
    -281        self.episodes = {}
    +            
    288class Season:
    +289    # Required by Hypnotix
    +290    name = ""
    +291
    +292    def __init__(self, name):
    +293        self.name = name
    +294        self.episodes = {}
     
    @@ -2800,9 +2907,9 @@

    -
    279    def __init__(self, name):
    -280        self.name = name
    -281        self.episodes = {}
    +            
    292    def __init__(self, name):
    +293        self.name = name
    +294        self.episodes = {}
     
    @@ -2844,933 +2951,972 @@

    -
     283class XTream:
    - 284
    - 285    name = ""
    - 286    server = ""
    - 287    secure_server = ""
    - 288    username = ""
    - 289    password = ""
    - 290
    - 291    live_type = "Live"
    - 292    vod_type = "VOD"
    - 293    series_type = "Series"
    - 294
    - 295    auth_data = {}
    - 296    authorization = {}
    - 297
    - 298    groups = []
    - 299    channels = []
    - 300    series = []
    - 301    movies = []
    - 302    movies_30days = []
    - 303    movies_7days = []
    - 304
    - 305    connection_headers = {}
    +            
     297class XTream:
    + 298
    + 299    name = ""
    + 300    server = ""
    + 301    secure_server = ""
    + 302    username = ""
    + 303    password = ""
    + 304    base_url = ""
    + 305    base_url_ssl = ""
      306
    - 307    state = {'authenticated': False, 'loaded': False}
    + 307    cache_path = ""
      308
    - 309    hide_adult_content = False
    + 309    account_expiration: timedelta
      310
    - 311    live_catch_all_group = Group(
    - 312        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, live_type
    - 313    )
    - 314    vod_catch_all_group = Group(
    - 315        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, vod_type
    - 316    )
    - 317    series_catch_all_group = Group(
    - 318        {"category_id": "9999", "category_name":"xEverythingElse", "parent_id":0}, series_type
    - 319    )
    - 320    # If the cached JSON file is older than threshold_time_sec then load a new
    - 321    # JSON dictionary from the provider
    - 322    threshold_time_sec = -1
    - 323
    - 324    def __init__(
    - 325        self,
    - 326        provider_name: str,
    - 327        provider_username: str,
    - 328        provider_password: str,
    - 329        provider_url: str,
    - 330        headers: dict = None,
    - 331        hide_adult_content: bool = False,
    - 332        cache_path: str = "",
    - 333        reload_time_sec: int = 60*60*8,
    - 334        validate_json: bool = False,
    - 335        debug_flask: bool = True
    - 336        ):
    - 337        """Initialize Xtream Class
    - 338
    - 339        Args:
    - 340            provider_name     (str):            Name of the IPTV provider
    - 341            provider_username (str):            User name of the IPTV provider
    - 342            provider_password (str):            Password of the IPTV provider
    - 343            provider_url      (str):            URL of the IPTV provider
    - 344            headers           (dict):           Requests Headers
    - 345            hide_adult_content(bool, optional): When `True` hide stream that are marked for adult
    - 346            cache_path        (str, optional):  Location where to save loaded files.
    - 347                                                Defaults to empty string.
    - 348            reload_time_sec   (int, optional):  Number of seconds before automatic reloading
    - 349                                                (-1 to turn it OFF)
    - 350            debug_flask       (bool, optional): Enable the debug mode in Flask
    - 351            validate_json     (bool, optional): Check Xtream API provided JSON for validity
    - 352
    - 353        Returns: XTream Class Instance
    - 354
    - 355        - Note 1: If it fails to authorize with provided username and password,
    - 356                auth_data will be an empty dictionary.
    - 357        - Note 2: The JSON validation option will take considerable amount of time and it should be 
    - 358                  used only as a debug tool. The Xtream API JSON from the provider passes through a
    - 359                  schema that represent the best available understanding of how the Xtream API 
    - 360                  works.
    - 361        """
    - 362        self.server = provider_url
    - 363        self.username = provider_username
    - 364        self.password = provider_password
    - 365        self.name = provider_name
    - 366        self.cache_path = cache_path
    - 367        self.hide_adult_content = hide_adult_content
    - 368        self.threshold_time_sec = reload_time_sec
    - 369        self.validate_json = validate_json
    - 370
    - 371        # get the pyxtream local path
    - 372        self.app_fullpath = osp.dirname(osp.realpath(__file__))
    - 373
    - 374        # prepare location of local html template
    - 375        self.html_template_folder = osp.join(self.app_fullpath,"html")
    - 376
    - 377        # if the cache_path is specified, test that it is a directory
    - 378        if self.cache_path != "":
    - 379            # If the cache_path is not a directory, clear it
    - 380            if not osp.isdir(self.cache_path):
    - 381                print(" - Cache Path is not a directory, using default '~/.xtream-cache/'")
    - 382                self.cache_path == ""
    - 383
    - 384        # If the cache_path is still empty, use default
    - 385        if self.cache_path == "":
    - 386            self.cache_path = osp.expanduser("~/.xtream-cache/")
    - 387            if not osp.isdir(self.cache_path):
    - 388                makedirs(self.cache_path, exist_ok=True)
    - 389            print(f"pyxtream cache path located at {self.cache_path}")
    - 390
    - 391        if headers is not None:
    - 392            self.connection_headers = headers
    - 393        else:
    - 394            self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"}
    - 395
    - 396        self.authenticate()
    + 311    live_type = "Live"
    + 312    vod_type = "VOD"
    + 313    series_type = "Series"
    + 314
    + 315    auth_data = {}
    + 316    authorization = {'username': '', 'password': ''}
    + 317
    + 318    groups = []
    + 319    channels = []
    + 320    series = []
    + 321    movies = []
    + 322    movies_30days = []
    + 323    movies_7days = []
    + 324
    + 325    connection_headers = {}
    + 326
    + 327    state = {'authenticated': False, 'loaded': False}
    + 328
    + 329    hide_adult_content = False
    + 330
    + 331    live_catch_all_group = Group(
    + 332        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, live_type
    + 333    )
    + 334    vod_catch_all_group = Group(
    + 335        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, vod_type
    + 336    )
    + 337    series_catch_all_group = Group(
    + 338        {"category_id": "9999", "category_name": "xEverythingElse", "parent_id": 0}, series_type
    + 339    )
    + 340    # If the cached JSON file is older than threshold_time_sec then load a new
    + 341    # JSON dictionary from the provider
    + 342    threshold_time_sec = -1
    + 343
    + 344    validate_json: bool = True
    + 345
    + 346    # Used by REST API to get download progress
    + 347    download_progress: dict = {'StreamId': 0, 'Total': 0, 'Progress': 0}
    + 348
    + 349    def __init__(
    + 350        self,
    + 351        provider_name: str,
    + 352        provider_username: str,
    + 353        provider_password: str,
    + 354        provider_url: str,
    + 355        headers: dict = None,
    + 356        hide_adult_content: bool = False,
    + 357        cache_path: str = "",
    + 358        reload_time_sec: int = 60*60*8,
    + 359        validate_json: bool = False,
    + 360        enable_flask: bool = False,
    + 361        debug_flask: bool = True
    + 362            ):
    + 363        """Initialize Xtream Class
    + 364
    + 365        Args:
    + 366            provider_name     (str):            Name of the IPTV provider
    + 367            provider_username (str):            User name of the IPTV provider
    + 368            provider_password (str):            Password of the IPTV provider
    + 369            provider_url      (str):            URL of the IPTV provider
    + 370            headers           (dict):           Requests Headers
    + 371            hide_adult_content(bool, optional): When `True` hide stream that are marked for adult
    + 372            cache_path        (str, optional):  Location where to save loaded files.
    + 373                                                Defaults to empty string.
    + 374            reload_time_sec   (int, optional):  Number of seconds before automatic reloading
    + 375                                                (-1 to turn it OFF)
    + 376            validate_json     (bool, optional): Check Xtream API provided JSON for validity
    + 377            enable_flask      (bool, optional): Enable Flask
    + 378            debug_flask       (bool, optional): Enable the debug mode in Flask
    + 379
    + 380        Returns: XTream Class Instance
    + 381
    + 382        - Note 1: If it fails to authorize with provided username and password,
    + 383                auth_data will be an empty dictionary.
    + 384        - Note 2: The JSON validation option will take considerable amount of time and it should be
    + 385                  used only as a debug tool. The Xtream API JSON from the provider passes through a
    + 386                  schema that represent the best available understanding of how the Xtream API
    + 387                  works.
    + 388        """
    + 389        self.server = provider_url
    + 390        self.username = provider_username
    + 391        self.password = provider_password
    + 392        self.name = provider_name
    + 393        self.cache_path = cache_path
    + 394        self.hide_adult_content = hide_adult_content
    + 395        self.threshold_time_sec = reload_time_sec
    + 396        self.validate_json = validate_json
      397
    - 398        if self.threshold_time_sec > 0:
    - 399            print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds")
    - 400        else:
    - 401            print("Reload timer is OFF")
    - 402
    - 403        if self.state['authenticated']:
    - 404            if USE_FLASK:
    - 405                self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask)
    - 406                self.flaskapp.start()
    - 407
    - 408    def search_stream(self, keyword: str,
    - 409                      ignore_case: bool = True,
    - 410                      return_type: str = "LIST",
    - 411                      stream_type: list = ("series", "movies", "channels")) -> list:
    - 412        """Search for streams
    - 413
    - 414        Args:
    - 415            keyword (str): Keyword to search for. Supports REGEX
    - 416            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    - 417            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    - 418            stream_type (list, optional): Search within specific stream type.
    - 419
    - 420        Returns:
    - 421            list: List with all the results, it could be empty.
    - 422        """
    - 423
    - 424        search_result = []
    - 425        regex_flags = re.IGNORECASE if ignore_case else 0
    - 426        regex = re.compile(keyword, regex_flags)
    - 427        # if ignore_case:
    - 428        #     regex = re.compile(keyword, re.IGNORECASE)
    - 429        # else:
    - 430        #     regex = re.compile(keyword)
    - 431
    - 432        # if "movies" in stream_type:
    - 433        #     print(f"Checking {len(self.movies)} movies")
    - 434        #     for stream in self.movies:
    - 435        #         if re.match(regex, stream.name) is not None:
    - 436        #             search_result.append(stream.export_json())
    - 437
    - 438        # if "channels" in stream_type:
    - 439        #     print(f"Checking {len(self.channels)} channels")
    - 440        #     for stream in self.channels:
    - 441        #         if re.match(regex, stream.name) is not None:
    - 442        #             search_result.append(stream.export_json())
    + 398        # get the pyxtream local path
    + 399        self.app_fullpath = osp.dirname(osp.realpath(__file__))
    + 400
    + 401        # prepare location of local html template
    + 402        self.html_template_folder = osp.join(self.app_fullpath, "html")
    + 403
    + 404        # if the cache_path is specified, test that it is a directory
    + 405        if self.cache_path != "":
    + 406            # If the cache_path is not a directory, clear it
    + 407            if not osp.isdir(self.cache_path):
    + 408                print(" - Cache Path is not a directory, using default '~/.xtream-cache/'")
    + 409                self.cache_path = ""
    + 410
    + 411        # If the cache_path is still empty, use default
    + 412        if self.cache_path == "":
    + 413            self.cache_path = osp.expanduser("~/.xtream-cache/")
    + 414            if not osp.isdir(self.cache_path):
    + 415                makedirs(self.cache_path, exist_ok=True)
    + 416            print(f"pyxtream cache path located at {self.cache_path}")
    + 417
    + 418        if headers is not None:
    + 419            self.connection_headers = headers
    + 420        else:
    + 421            self.connection_headers = {'User-Agent': "Wget/1.20.3 (linux-gnu)"}
    + 422
    + 423        self.authenticate()
    + 424
    + 425        if self.threshold_time_sec > 0:
    + 426            print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds")
    + 427        else:
    + 428            print("Reload timer is OFF")
    + 429
    + 430        if self.state['authenticated']:
    + 431            if USE_FLASK and enable_flask:
    + 432                print("Starting Web Interface")
    + 433                self.flaskapp = FlaskWrap(
    + 434                    'pyxtream', self, self.html_template_folder, debug=debug_flask
    + 435                    )
    + 436                self.flaskapp.start()
    + 437            else:
    + 438                print("Web interface not running")
    + 439
    + 440    def get_download_progress(self, stream_id: int = None):
    + 441        # TODO: Add check for stream specific ID
    + 442        return json.dumps(self.download_progress)
      443
    - 444        # if "series" in stream_type:
    - 445        #     print(f"Checking {len(self.series)} series")
    - 446        #     for stream in self.series:
    - 447        #         if re.match(regex, stream.name) is not None:
    - 448        #             search_result.append(stream.export_json())
    - 449
    - 450        stream_collections = {
    - 451            "movies": self.movies,
    - 452            "channels": self.channels,
    - 453            "series": self.series
    - 454        }
    - 455
    - 456        for stream_type_name in stream_type:
    - 457            if stream_type_name in stream_collections:
    - 458                collection = stream_collections[stream_type_name]
    - 459                print(f"Checking {len(collection)} {stream_type_name}")
    - 460                for stream in collection:
    - 461                    if re.match(regex, stream.name) is not None:
    - 462                        search_result.append(stream.export_json())
    - 463            else:
    - 464                print(f"`{stream_type_name}` not found in collection")
    - 465
    - 466        if return_type == "JSON":
    - 467            # if search_result is not None:
    - 468            print(f"Found {len(search_result)} results `{keyword}`")
    - 469            return json.dumps(search_result, ensure_ascii=False)
    - 470
    - 471        return search_result
    - 472
    - 473    def download_video(self, stream_id: int) -> str:
    - 474        """Download Video from Stream ID
    - 475
    - 476        Args:
    - 477            stream_id (int): Stirng identifing the stream ID
    - 478
    - 479        Returns:
    - 480            str: Absolute Path Filename where the file was saved. Empty if could not download
    - 481        """
    - 482        url = ""
    - 483        filename = ""
    - 484        for stream in self.movies:
    - 485            if stream.id == stream_id:
    - 486                url = stream.url
    - 487                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    - 488                filename = osp.join(self.cache_path,fn)
    + 444    def get_last_7days(self):
    + 445        return json.dumps(self.movies_7days, default=lambda x: x.export_json())
    + 446
    + 447    def search_stream(self, keyword: str,
    + 448                      ignore_case: bool = True,
    + 449                      return_type: str = "LIST",
    + 450                      stream_type: list = ("series", "movies", "channels"),
    + 451                      added_after: datetime = None) -> list:
    + 452        """Search for streams
    + 453
    + 454        Args:
    + 455            keyword (str): Keyword to search for. Supports REGEX
    + 456            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    + 457            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    + 458            stream_type (list, optional): Search within specific stream type.
    + 459            added_after (datetime, optional): Search for items that have been added after a certain date.
    + 460
    + 461        Returns:
    + 462            list: List with all the results, it could be empty.
    + 463        """
    + 464
    + 465        search_result = []
    + 466        regex_flags = re.IGNORECASE if ignore_case else 0
    + 467        regex = re.compile(keyword, regex_flags)
    + 468
    + 469        stream_collections = {
    + 470            "movies": self.movies,
    + 471            "channels": self.channels,
    + 472            "series": self.series
    + 473        }
    + 474
    + 475        for stream_type_name in stream_type:
    + 476            if stream_type_name in stream_collections:
    + 477                collection = stream_collections[stream_type_name]
    + 478                print(f"Checking {len(collection)} {stream_type_name}")
    + 479                for stream in collection:
    + 480                    if stream.name and re.match(regex, stream.name) is not None:
    + 481                        if added_after is None:
    + 482                            # Add all matches
    + 483                            search_result.append(stream.export_json())
    + 484                        else:
    + 485                            # Only add if it is more recent
    + 486                            pass
    + 487            else:
    + 488                print(f"`{stream_type_name}` not found in collection")
      489
    - 490        # If the url was correctly built and file does not exists, start downloading
    - 491        if url != "":
    - 492            #if not osp.isfile(filename):
    - 493            if not self._download_video_impl(url,filename):
    - 494                return "Error"
    - 495
    - 496        return filename
    - 497
    - 498    def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
    - 499        """Download a stream
    - 500
    - 501        Args:
    - 502            url (str): Complete URL of the stream
    - 503            fullpath_filename (str): Complete File path where to save the stream
    - 504
    - 505        Returns:
    - 506            bool: True if successful, False if error
    - 507        """
    - 508        ret_code = False
    - 509        mb_size = 1024*1024
    - 510        try:
    - 511            print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`")
    - 512
    - 513            # Check if the file already exists
    - 514            if osp.exists(fullpath_filename):
    - 515                # If the file exists, resume the download from where it left off
    - 516                file_size = osp.getsize(fullpath_filename)
    - 517                self.connection_headers['Range'] = f'bytes={file_size}-'
    - 518                mode = 'ab'  # Append to the existing file
    - 519                print(f"Resuming from {file_size:_} bytes")
    - 520            else:
    - 521                # If the file does not exist, start a new download
    - 522                mode = 'wb'  # Write a new file
    - 523
    - 524            # Make the request to download
    - 525            response = requests.get(url, timeout=(10), stream=True, allow_redirects=True, headers=self.connection_headers)
    - 526            # If there is an answer from the remote server
    - 527            if response.status_code == 200 or response.status_code == 206:
    - 528                # Get content type Binary or Text
    - 529                content_type = response.headers.get('content-type',None)
    - 530
    - 531                # Get total playlist byte size
    - 532                total_content_size = int(response.headers.get('content-length',None))
    - 533                total_content_size_mb = total_content_size/mb_size
    - 534
    - 535                # Set downloaded size
    - 536                downloaded_bytes = 0
    - 537
    - 538                # Set stream blocks
    - 539                block_bytes = int(4*mb_size)     # 4 MB
    - 540
    - 541                print(f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})")
    - 542                if content_type.split('/')[0] != "text":
    - 543                    with open(fullpath_filename, mode) as file:
    - 544
    - 545                        # Grab data by block_bytes
    - 546                        for data in response.iter_content(block_bytes,decode_unicode=False):
    - 547                            downloaded_bytes += block_bytes
    - 548                            progress(downloaded_bytes,total_content_size,"Downloading")
    - 549                            file.write(data)
    - 550
    - 551                    if downloaded_bytes == total_content_size:
    - 552                        ret_code = True
    - 553
    - 554                    # Delete Range if it was added
    - 555                    try:
    - 556                        del self.connection_headers['Range']
    - 557                    except KeyError:
    - 558                        pass
    - 559                else:
    - 560                    print(f"URL has a file with unexpected content-type {content_type}")
    - 561            else:
    - 562                print(f"HTTP error {response.status_code} while retrieving from {url}")
    - 563        except Exception as e:
    - 564            print(e)
    - 565
    - 566        return ret_code
    - 567
    - 568    def _slugify(self, string: str) -> str:
    - 569        """Normalize string
    - 570
    - 571        Normalizes string, converts to lowercase, removes non-alpha characters,
    - 572        and converts spaces to hyphens.
    + 490        if return_type == "JSON":
    + 491            # if search_result is not None:
    + 492            print(f"Found {len(search_result)} results `{keyword}`")
    + 493            return json.dumps(search_result, ensure_ascii=False)
    + 494
    + 495        return search_result
    + 496
    + 497    def download_video(self, stream_id: int) -> str:
    + 498        """Download Video from Stream ID
    + 499
    + 500        Args:
    + 501            stream_id (int): String identifying the stream ID
    + 502
    + 503        Returns:
    + 504            str: Absolute Path Filename where the file was saved. Empty if could not download
    + 505        """
    + 506        url = ""
    + 507        filename = ""
    + 508        for series_stream in self.series:
    + 509            if series_stream.series_id == stream_id:
    + 510                episode_object: Episode = series_stream.episodes["1"]
    + 511                url = f"{series_stream.url}/{episode_object.id}."\
    + 512                      f"{episode_object.container_extension}"
    + 513
    + 514        for stream in self.movies:
    + 515            if stream.id == stream_id:
    + 516                url = stream.url
    + 517                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    + 518                filename = osp.join(self.cache_path, fn)
    + 519
    + 520        # If the url was correctly built and file does not exists, start downloading
    + 521        if url != "":
    + 522            if not self._download_video_impl(url, filename):
    + 523                return "Error"
    + 524
    + 525        return filename
    + 526
    + 527    def _download_video_impl(self, url: str, fullpath_filename: str) -> bool:
    + 528        """Download a stream
    + 529
    + 530        Args:
    + 531            url (str): Complete URL of the stream
    + 532            fullpath_filename (str): Complete File path where to save the stream
    + 533
    + 534        Returns:
    + 535            bool: True if successful, False if error
    + 536        """
    + 537        ret_code = False
    + 538        mb_size = 1024*1024
    + 539        try:
    + 540            print(f"Downloading from URL `{url}` and saving at `{fullpath_filename}`")
    + 541
    + 542            # Check if the file already exists
    + 543            if osp.exists(fullpath_filename):
    + 544                # If the file exists, resume the download from where it left off
    + 545                file_size = osp.getsize(fullpath_filename)
    + 546                self.connection_headers['Range'] = f'bytes={file_size}-'
    + 547                mode = 'ab'  # Append to the existing file
    + 548                print(f"Resuming from {file_size:_} bytes")
    + 549            else:
    + 550                # If the file does not exist, start a new download
    + 551                mode = 'wb'  # Write a new file
    + 552
    + 553            # Make the request to download
    + 554            response = requests.get(
    + 555                url, timeout=(10),
    + 556                stream=True,
    + 557                allow_redirects=True,
    + 558                headers=self.connection_headers
    + 559                )
    + 560            # If there is an answer from the remote server
    + 561            if response.status_code in (200, 206):
    + 562                # Get content type Binary or Text
    + 563                content_type = response.headers.get('content-type', None)
    + 564
    + 565                # Get total playlist byte size
    + 566                total_content_size = int(response.headers.get('content-length', None))
    + 567                total_content_size_mb = total_content_size/mb_size
    + 568
    + 569                # Set downloaded size
    + 570                downloaded_bytes = 0
    + 571                self.download_progress['Total'] = total_content_size
    + 572                self.download_progress['Progress'] = 0
      573
    - 574        Args:
    - 575            string (str): String to be normalized
    + 574                # Set stream blocks
    + 575                block_bytes = int(4*mb_size)     # 4 MB
      576
    - 577        Returns:
    - 578            str: Normalized String
    - 579        """
    - 580        return "".join(x.lower() for x in string if x.isprintable())
    - 581
    - 582    def _validate_url(self, url: str) -> bool:
    - 583        regex = re.compile(
    - 584            r"^(?:http|ftp)s?://"  # http:// or https://
    - 585            r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"  # domain...
    - 586            r"localhost|"  # localhost...
    - 587            r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"  # ...or ip
    - 588            r"(?::\d+)?"  # optional port
    - 589            r"(?:/?|[/?]\S+)$",
    - 590            re.IGNORECASE,
    - 591        )
    - 592
    - 593        return re.match(regex, url) is not None
    - 594
    - 595    def _get_logo_local_path(self, logo_url: str) -> str:
    - 596        """Convert the Logo URL to a local Logo Path
    - 597
    - 598        Args:
    - 599            logoURL (str): The Logo URL
    - 600
    - 601        Returns:
    - 602            [type]: The logo path as a string or None
    - 603        """
    - 604        local_logo_path = None
    - 605        if logo_url is not None:
    - 606            if not self._validate_url(logo_url):
    - 607                logo_url = None
    - 608            else:
    - 609                local_logo_path = osp.join(
    - 610                    self.cache_path,
    - 611                    f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}"
    - 612                )
    - 613        return local_logo_path
    + 577                print(
    + 578                    f"Ready to download {total_content_size_mb:.1f} MB file ({total_content_size})"
    + 579                    )
    + 580                if content_type.split('/')[0] != "text":
    + 581                    with open(fullpath_filename, mode) as file:
    + 582
    + 583                        # Grab data by block_bytes
    + 584                        for data in response.iter_content(block_bytes, decode_unicode=False):
    + 585                            downloaded_bytes += block_bytes
    + 586                            progress(downloaded_bytes, total_content_size, "Downloading")
    + 587                            self.download_progress['Progress'] = downloaded_bytes
    + 588                            file.write(data)
    + 589
    + 590                    ret_code = True
    + 591
    + 592                    # Delete Range if it was added
    + 593                    try:
    + 594                        del self.connection_headers['Range']
    + 595                    except KeyError:
    + 596                        pass
    + 597                else:
    + 598                    print(f"URL has a file with unexpected content-type {content_type}")
    + 599            else:
    + 600                print(f"HTTP error {response.status_code} while retrieving from {url}")
    + 601        except requests.exceptions.ReadTimeout:
    + 602            print("Read Timeout, try again")
    + 603        except Exception as e:
    + 604            print("Unknown error")
    + 605            print(e)
    + 606
    + 607        return ret_code
    + 608
    + 609    def _slugify(self, string: str) -> str:
    + 610        """Normalize string
    + 611
    + 612        Normalizes string, converts to lowercase, removes non-alpha characters,
    + 613        and converts spaces to hyphens.
      614
    - 615    def authenticate(self):
    - 616        """Login to provider"""
    - 617        # If we have not yet successfully authenticated, attempt authentication
    - 618        if self.state["authenticated"] is False:
    - 619            # Erase any previous data
    - 620            self.auth_data = {}
    - 621            # Loop through 30 seconds
    - 622            i = 0
    - 623            r = None
    - 624            # Prepare the authentication url
    - 625            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    - 626            print("Attempting connection... ", end='')
    - 627            while i < 30:
    - 628                try:
    - 629                    # Request authentication, wait 4 seconds maximum
    - 630                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    - 631                    i = 31
    - 632                except requests.exceptions.ConnectionError:
    - 633                    time.sleep(1)
    - 634                    print(f"{i} ", end='',flush=True)
    - 635                    i += 1
    - 636
    - 637            if r is not None:
    - 638                # If the answer is ok, process data and change state
    - 639                if r.ok:
    - 640                    print("Connected")
    - 641                    self.auth_data = r.json()
    - 642                    self.authorization = {
    - 643                        "username": self.auth_data["user_info"]["username"],
    - 644                        "password": self.auth_data["user_info"]["password"]
    - 645                    }
    - 646                    # Account expiration date
    - 647                    self.account_expiration = timedelta(
    - 648                        seconds=(
    - 649                            int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp()
    - 650                        )
    - 651                    )
    - 652                    # Mark connection authorized
    - 653                    self.state["authenticated"] = True
    - 654                    # Construct the base url for all requests
    - 655                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    - 656                    # If there is a secure server connection, construct the base url SSL for all requests
    - 657                    if "https_port" in self.auth_data["server_info"]:
    - 658                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    - 659                                            f"/player_api.php?username={self.username}&password={self.password}"
    - 660                    print(f"Account expires in {str(self.account_expiration)}")
    - 661                else:
    - 662                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    - 663            else:
    - 664                print(f"\n{self.name}: Provider refused the connection")
    - 665
    - 666    def _load_from_file(self, filename) -> dict:
    - 667        """Try to load the dictionary from file
    - 668
    - 669        Args:
    - 670            filename ([type]): File name containing the data
    - 671
    - 672        Returns:
    - 673            dict: Dictionary if found and no errors, None if file does not exists
    - 674        """
    - 675        # Build the full path
    - 676        full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}")
    + 615        Args:
    + 616            string (str): String to be normalized
    + 617
    + 618        Returns:
    + 619            str: Normalized String
    + 620        """
    + 621        return "".join(x.lower() for x in string if x.isprintable())
    + 622
    + 623    def _validate_url(self, url: str) -> bool:
    + 624        regex = re.compile(
    + 625            r"^(?:http|ftp)s?://"  # http:// or https://
    + 626            r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"  # domain...
    + 627            r"localhost|"  # localhost...
    + 628            r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"  # ...or ip
    + 629            r"(?::\d+)?"  # optional port
    + 630            r"(?:/?|[/?]\S+)$",
    + 631            re.IGNORECASE,
    + 632        )
    + 633
    + 634        return re.match(regex, url) is not None
    + 635
    + 636    def _get_logo_local_path(self, logo_url: str) -> str:
    + 637        """Convert the Logo URL to a local Logo Path
    + 638
    + 639        Args:
    + 640            logoURL (str): The Logo URL
    + 641
    + 642        Returns:
    + 643            [type]: The logo path as a string or None
    + 644        """
    + 645        local_logo_path = None
    + 646        if logo_url is not None:
    + 647            if not self._validate_url(logo_url):
    + 648                logo_url = None
    + 649            else:
    + 650                local_logo_path = osp.join(
    + 651                    self.cache_path,
    + 652                    f"{self._slugify(self.name)}-{self._slugify(osp.split(logo_url)[-1])}"
    + 653                )
    + 654        return local_logo_path
    + 655
    + 656    def authenticate(self):
    + 657        """Login to provider"""
    + 658        # If we have not yet successfully authenticated, attempt authentication
    + 659        if self.state["authenticated"] is False:
    + 660            # Erase any previous data
    + 661            self.auth_data = {}
    + 662            # Loop through 30 seconds
    + 663            i = 0
    + 664            r = None
    + 665            # Prepare the authentication url
    + 666            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    + 667            print("Attempting connection... ", end='')
    + 668            while i < 30:
    + 669                try:
    + 670                    # Request authentication, wait 4 seconds maximum
    + 671                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    + 672                    i = 31
    + 673                except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
    + 674                    time.sleep(1)
    + 675                    print(f"{i} ", end='', flush=True)
    + 676                    i += 1
      677
    - 678        # If the cached file exists, attempt to load it
    - 679        if osp.isfile(full_filename):
    - 680
    - 681            my_data = None
    - 682
    - 683            # Get the enlapsed seconds since last file update
    - 684            file_age_sec = time.time() - osp.getmtime(full_filename)
    - 685            # If the file was updated less than the threshold time,
    - 686            # it means that the file is still fresh, we can load it.
    - 687            # Otherwise skip and return None to force a re-download
    - 688            if self.threshold_time_sec > file_age_sec:
    - 689                # Load the JSON data
    - 690                try:
    - 691                    with open(full_filename, mode="r", encoding="utf-8") as myfile:
    - 692                        my_data = json.load(myfile)
    - 693                        if len(my_data) == 0:
    - 694                            my_data = None
    - 695                except Exception as e:
    - 696                    print(f" - Could not load from file `{full_filename}`: e=`{e}`")
    - 697            return my_data
    - 698
    - 699        return None
    - 700
    - 701    def _save_to_file(self, data_list: dict, filename: str) -> bool:
    - 702        """Save a dictionary to file
    - 703
    - 704        This function will overwrite the file if already exists
    - 705
    - 706        Args:
    - 707            data_list (dict): Dictionary to save
    - 708            filename (str): Name of the file
    + 678            if r is not None:
    + 679                # If the answer is ok, process data and change state
    + 680                if r.ok:
    + 681                    print("Connected")
    + 682                    self.auth_data = r.json()
    + 683                    self.authorization = {
    + 684                        "username": self.auth_data["user_info"]["username"],
    + 685                        "password": self.auth_data["user_info"]["password"]
    + 686                    }
    + 687                    # Account expiration date
    + 688                    self.account_expiration = timedelta(
    + 689                        seconds=(
    + 690                            int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp()
    + 691                        )
    + 692                    )
    + 693                    # Mark connection authorized
    + 694                    self.state["authenticated"] = True
    + 695                    # Construct the base url for all requests
    + 696                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    + 697                    # If there is a secure server connection, construct the base url SSL for all requests
    + 698                    if "https_port" in self.auth_data["server_info"]:
    + 699                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    + 700                                            f"/player_api.php?username={self.username}&password={self.password}"
    + 701                    print(f"Account expires in {str(self.account_expiration)}")
    + 702                else:
    + 703                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    + 704            else:
    + 705                print(f"\n{self.name}: Provider refused the connection")
    + 706
    + 707    def _load_from_file(self, filename) -> dict:
    + 708        """Try to load the dictionary from file
      709
    - 710        Returns:
    - 711            bool: True if successfull, False if error
    - 712        """
    - 713        if data_list is None:
    - 714            return False
    - 715
    - 716        full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}")
    - 717        try:
    - 718            with open(full_filename, mode="wt", encoding="utf-8") as file:
    - 719                json.dump(data_list, file, ensure_ascii=False)
    - 720            return True
    - 721        except Exception as e:
    - 722            print(f" - Could not save to file `{full_filename}`: e=`{e}`")
    - 723            return False
    - 724        # if data_list is not None:
    - 725
    - 726        #     #Build the full path
    - 727        #     full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}")
    - 728        #     # If the path makes sense, save the file
    - 729        #     json_data = json.dumps(data_list, ensure_ascii=False)
    - 730        #     try:
    - 731        #         with open(full_filename, mode="wt", encoding="utf-8") as myfile:
    - 732        #             myfile.write(json_data)
    - 733        #     except Exception as e:
    - 734        #         print(f" - Could not save to file `{full_filename}`: e=`{e}`")
    - 735        #         return False
    - 736
    - 737        #     return True
    - 738        # else:
    - 739        #     return False
    - 740
    - 741    def load_iptv(self) -> bool:
    - 742        """Load XTream IPTV
    - 743
    - 744        - Add all Live TV to XTream.channels
    - 745        - Add all VOD to XTream.movies
    - 746        - Add all Series to XTream.series
    - 747          Series contains Seasons and Episodes. Those are not automatically
    - 748          retrieved from the server to reduce the loading time.
    - 749        - Add all groups to XTream.groups
    - 750          Groups are for all three channel types, Live TV, VOD, and Series
    - 751
    - 752        Returns:
    - 753            bool: True if successfull, False if error
    - 754        """
    - 755        # If pyxtream has not authenticated the connection, return empty
    - 756        if self.state["authenticated"] is False:
    - 757            print("Warning, cannot load steams since authorization failed")
    - 758            return False
    - 759
    - 760        # If pyxtream has already loaded the data, skip and return success
    - 761        if self.state["loaded"] is True:
    - 762            print("Warning, data has already been loaded.")
    - 763            return True
    - 764
    - 765        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    - 766            ## Get GROUPS
    - 767
    - 768            # Try loading local file
    - 769            dt = 0
    - 770            start = timer()
    - 771            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    - 772            # If file empty or does not exists, download it from remote
    - 773            if all_cat is None:
    - 774                # Load all Groups and save file locally
    - 775                all_cat = self._load_categories_from_provider(loading_stream_type)
    - 776                if all_cat is not None:
    - 777                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
    - 778            dt = timer() - start
    - 779
    - 780            # If we got the GROUPS data, show the statistics and load GROUPS
    - 781            if all_cat is not None:
    - 782                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    - 783                ## Add GROUPS to dictionaries
    + 710        Args:
    + 711            filename ([type]): File name containing the data
    + 712
    + 713        Returns:
    + 714            dict: Dictionary if found and no errors, None if file does not exists
    + 715        """
    + 716        # Build the full path
    + 717        full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}")
    + 718
    + 719        # If the cached file exists, attempt to load it
    + 720        if osp.isfile(full_filename):
    + 721
    + 722            my_data = None
    + 723
    + 724            # Get the elapsed seconds since last file update
    + 725            file_age_sec = time.time() - osp.getmtime(full_filename)
    + 726            # If the file was updated less than the threshold time,
    + 727            # it means that the file is still fresh, we can load it.
    + 728            # Otherwise skip and return None to force a re-download
    + 729            if self.threshold_time_sec > file_age_sec:
    + 730                # Load the JSON data
    + 731                try:
    + 732                    with open(full_filename, mode="r", encoding="utf-8") as myfile:
    + 733                        my_data = json.load(myfile)
    + 734                        if len(my_data) == 0:
    + 735                            my_data = None
    + 736                except Exception as e:
    + 737                    print(f" - Could not load from file `{full_filename}`: e=`{e}`")
    + 738            return my_data
    + 739
    + 740        return None
    + 741
    + 742    def _save_to_file(self, data_list: dict, filename: str) -> bool:
    + 743        """Save a dictionary to file
    + 744
    + 745        This function will overwrite the file if already exists
    + 746
    + 747        Args:
    + 748            data_list (dict): Dictionary to save
    + 749            filename (str): Name of the file
    + 750
    + 751        Returns:
    + 752            bool: True if successful, False if error
    + 753        """
    + 754        if data_list is None:
    + 755            return False
    + 756
    + 757        full_filename = osp.join(self.cache_path, f"{self._slugify(self.name)}-{filename}")
    + 758        try:
    + 759            with open(full_filename, mode="wt", encoding="utf-8") as file:
    + 760                json.dump(data_list, file, ensure_ascii=False)
    + 761            return True
    + 762        except Exception as e:
    + 763            print(f" - Could not save to file `{full_filename}`: e=`{e}`")
    + 764            return False
    + 765
    + 766    def load_iptv(self) -> bool:
    + 767        """Load XTream IPTV
    + 768
    + 769        - Add all Live TV to XTream.channels
    + 770        - Add all VOD to XTream.movies
    + 771        - Add all Series to XTream.series
    + 772          Series contains Seasons and Episodes. Those are not automatically
    + 773          retrieved from the server to reduce the loading time.
    + 774        - Add all groups to XTream.groups
    + 775          Groups are for all three channel types, Live TV, VOD, and Series
    + 776
    + 777        Returns:
    + 778            bool: True if successful, False if error
    + 779        """
    + 780        # If pyxtream has not authenticated the connection, return empty
    + 781        if self.state["authenticated"] is False:
    + 782            print("Warning, cannot load steams since authorization failed")
    + 783            return False
      784
    - 785                # Add the catch-all-errors group
    - 786                if loading_stream_type == self.live_type:
    - 787                    self.groups.append(self.live_catch_all_group)
    - 788                elif loading_stream_type == self.vod_type:
    - 789                    self.groups.append(self.vod_catch_all_group)
    - 790                elif loading_stream_type == self.series_type:
    - 791                    self.groups.append(self.series_catch_all_group)
    - 792
    - 793                for cat_obj in all_cat:
    - 794                    if schemaValidator(cat_obj, SchemaType.GROUP):
    - 795                        # Create Group (Category)
    - 796                        new_group = Group(cat_obj, loading_stream_type)
    - 797                        #  Add to xtream class
    - 798                        self.groups.append(new_group)
    - 799                    else:
    - 800                        # Save what did not pass schema validation
    - 801                        print(cat_obj)
    - 802
    - 803                # Sort Categories
    - 804                self.groups.sort(key=lambda x: x.name)
    - 805            else:
    - 806                print(f" - Could not load {loading_stream_type} Groups")
    - 807                break
    - 808
    - 809            ## Get Streams
    - 810
    - 811            # Try loading local file
    - 812            dt = 0
    - 813            start = timer()
    - 814            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    - 815            # If file empty or does not exists, download it from remote
    - 816            if all_streams is None:
    - 817                # Load all Streams and save file locally
    - 818                all_streams = self._load_streams_from_provider(loading_stream_type)
    - 819                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
    - 820            dt = timer() - start
    - 821
    - 822            # If we got the STREAMS data, show the statistics and load Streams
    - 823            if all_streams is not None:
    - 824                print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    - 825                ## Add Streams to dictionaries
    + 785        # If pyxtream has already loaded the data, skip and return success
    + 786        if self.state["loaded"] is True:
    + 787            print("Warning, data has already been loaded.")
    + 788            return True
    + 789
    + 790        # Delete skipped channels from cache
    + 791        full_filename = osp.join(self.cache_path, "skipped_streams.json")
    + 792        try:
    + 793            f = open(full_filename, mode="r+", encoding="utf-8")
    + 794            f.truncate(0)
    + 795            f.close()
    + 796        except FileNotFoundError:
    + 797            pass
    + 798
    + 799        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    + 800            # Get GROUPS
    + 801
    + 802            # Try loading local file
    + 803            dt = 0
    + 804            start = timer()
    + 805            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    + 806            # If file empty or does not exists, download it from remote
    + 807            if all_cat is None:
    + 808                # Load all Groups and save file locally
    + 809                all_cat = self._load_categories_from_provider(loading_stream_type)
    + 810                if all_cat is not None:
    + 811                    self._save_to_file(all_cat, f"all_groups_{loading_stream_type}.json")
    + 812            dt = timer() - start
    + 813
    + 814            # If we got the GROUPS data, show the statistics and load GROUPS
    + 815            if all_cat is not None:
    + 816                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    + 817                # Add GROUPS to dictionaries
    + 818
    + 819                # Add the catch-all-errors group
    + 820                if loading_stream_type == self.live_type:
    + 821                    self.groups.append(self.live_catch_all_group)
    + 822                elif loading_stream_type == self.vod_type:
    + 823                    self.groups.append(self.vod_catch_all_group)
    + 824                elif loading_stream_type == self.series_type:
    + 825                    self.groups.append(self.series_catch_all_group)
      826
    - 827                skipped_adult_content = 0
    - 828                skipped_no_name_content = 0
    - 829
    - 830                number_of_streams = len(all_streams)
    - 831                current_stream_number = 0
    - 832                # Calculate 1% of total number of streams
    - 833                # This is used to slow down the progress bar
    - 834                one_percent_number_of_streams = number_of_streams/100
    - 835                start = timer()
    - 836                for stream_channel in all_streams:
    - 837                    skip_stream = False
    - 838                    current_stream_number += 1
    - 839
    - 840                    # Show download progress every 1% of total number of streams
    - 841                    if current_stream_number < one_percent_number_of_streams:
    - 842                        progress(
    - 843                            current_stream_number,
    - 844                            number_of_streams,
    - 845                            f"Processing {loading_stream_type} Streams"
    - 846                            )
    - 847                        one_percent_number_of_streams *= 2
    - 848
    - 849                    # Validate JSON scheme
    - 850                    if self.validate_json:
    - 851                        if loading_stream_type == self.series_type:
    - 852                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    - 853                                print(stream_channel)
    - 854                        elif loading_stream_type == self.live_type:
    - 855                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    - 856                                print(stream_channel)
    - 857                        else:
    - 858                            # vod_type
    - 859                            if not schemaValidator(stream_channel, SchemaType.VOD):
    - 860                                print(stream_channel)
    - 861
    - 862                    # Skip if the name of the stream is empty
    - 863                    if stream_channel["name"] == "":
    - 864                        skip_stream = True
    - 865                        skipped_no_name_content = skipped_no_name_content + 1
    - 866                        self._save_to_file_skipped_streams(stream_channel)
    - 867
    - 868                    # Skip if the user chose to hide adult streams
    - 869                    if self.hide_adult_content and loading_stream_type == self.live_type:
    - 870                        if "is_adult" in stream_channel:
    - 871                            if stream_channel["is_adult"] == "1":
    - 872                                skip_stream = True
    - 873                                skipped_adult_content = skipped_adult_content + 1
    - 874                                self._save_to_file_skipped_streams(stream_channel)
    - 875
    - 876                    if not skip_stream:
    - 877                        # Some channels have no group,
    - 878                        # so let's add them to the catch all group
    - 879                        if stream_channel["category_id"] == "":
    - 880                            stream_channel["category_id"] = "9999"
    - 881                        elif stream_channel["category_id"] != "1":
    - 882                            pass
    - 883
    - 884                        # Find the first occurence of the group that the
    - 885                        # Channel or Stream is pointing to
    - 886                        the_group = next(
    - 887                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    - 888                            None
    - 889                        )
    - 890
    - 891                        # Set group title
    - 892                        if the_group is not None:
    - 893                            group_title = the_group.name
    - 894                        else:
    - 895                            if loading_stream_type == self.live_type:
    - 896                                group_title = self.live_catch_all_group.name
    - 897                                the_group = self.live_catch_all_group
    - 898                            elif loading_stream_type == self.vod_type:
    - 899                                group_title = self.vod_catch_all_group.name
    - 900                                the_group = self.vod_catch_all_group
    - 901                            elif loading_stream_type == self.series_type:
    - 902                                group_title = self.series_catch_all_group.name
    - 903                                the_group = self.series_catch_all_group
    - 904
    - 905
    - 906                        if loading_stream_type == self.series_type:
    - 907                            # Load all Series
    - 908                            new_series = Serie(self, stream_channel)
    - 909                            # To get all the Episodes for every Season of each
    - 910                            # Series is very time consuming, we will only
    - 911                            # populate the Series once the user click on the
    - 912                            # Series, the Seasons and Episodes will be loaded
    - 913                            # using x.getSeriesInfoByID() function
    - 914
    - 915                        else:
    - 916                            new_channel = Channel(
    - 917                                self,
    - 918                                group_title,
    - 919                                stream_channel
    - 920                            )
    - 921
    - 922                        if new_channel.group_id == "9999":
    - 923                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    + 827                for cat_obj in all_cat:
    + 828                    if schemaValidator(cat_obj, SchemaType.GROUP):
    + 829                        # Create Group (Category)
    + 830                        new_group = Group(cat_obj, loading_stream_type)
    + 831                        #  Add to xtream class
    + 832                        self.groups.append(new_group)
    + 833                    else:
    + 834                        # Save what did not pass schema validation
    + 835                        print(cat_obj)
    + 836
    + 837                # Sort Categories
    + 838                self.groups.sort(key=lambda x: x.name)
    + 839            else:
    + 840                print(f" - Could not load {loading_stream_type} Groups")
    + 841                break
    + 842
    + 843            # Get Streams
    + 844
    + 845            # Try loading local file
    + 846            dt = 0
    + 847            start = timer()
    + 848            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    + 849            # If file empty or does not exists, download it from remote
    + 850            if all_streams is None:
    + 851                # Load all Streams and save file locally
    + 852                all_streams = self._load_streams_from_provider(loading_stream_type)
    + 853                self._save_to_file(all_streams, f"all_stream_{loading_stream_type}.json")
    + 854            dt = timer() - start
    + 855
    + 856            # If we got the STREAMS data, show the statistics and load Streams
    + 857            if all_streams is not None:
    + 858                print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    + 859                # Add Streams to dictionaries
    + 860
    + 861                skipped_adult_content = 0
    + 862                skipped_no_name_content = 0
    + 863
    + 864                number_of_streams = len(all_streams)
    + 865                current_stream_number = 0
    + 866                # Calculate 1% of total number of streams
    + 867                # This is used to slow down the progress bar
    + 868                one_percent_number_of_streams = number_of_streams/100
    + 869                start = timer()
    + 870                for stream_channel in all_streams:
    + 871                    skip_stream = False
    + 872                    current_stream_number += 1
    + 873
    + 874                    # Show download progress every 1% of total number of streams
    + 875                    if current_stream_number < one_percent_number_of_streams:
    + 876                        progress(
    + 877                            current_stream_number,
    + 878                            number_of_streams,
    + 879                            f"Processing {loading_stream_type} Streams"
    + 880                            )
    + 881                        one_percent_number_of_streams *= 2
    + 882
    + 883                    # Validate JSON scheme
    + 884                    if self.validate_json:
    + 885                        if loading_stream_type == self.series_type:
    + 886                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    + 887                                print(stream_channel)
    + 888                        elif loading_stream_type == self.live_type:
    + 889                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    + 890                                print(stream_channel)
    + 891                        else:
    + 892                            # vod_type
    + 893                            if not schemaValidator(stream_channel, SchemaType.VOD):
    + 894                                print(stream_channel)
    + 895
    + 896                    # Skip if the name of the stream is empty
    + 897                    if stream_channel["name"] == "":
    + 898                        skip_stream = True
    + 899                        skipped_no_name_content = skipped_no_name_content + 1
    + 900                        self._save_to_file_skipped_streams(stream_channel)
    + 901
    + 902                    # Skip if the user chose to hide adult streams
    + 903                    if self.hide_adult_content and loading_stream_type == self.live_type:
    + 904                        if "is_adult" in stream_channel:
    + 905                            if stream_channel["is_adult"] == "1":
    + 906                                skip_stream = True
    + 907                                skipped_adult_content = skipped_adult_content + 1
    + 908                                self._save_to_file_skipped_streams(stream_channel)
    + 909
    + 910                    if not skip_stream:
    + 911                        # Some channels have no group,
    + 912                        # so let's add them to the catch all group
    + 913                        if not stream_channel["category_id"]:
    + 914                            stream_channel["category_id"] = "9999"
    + 915                        elif stream_channel["category_id"] != "1":
    + 916                            pass
    + 917
    + 918                        # Find the first occurrence of the group that the
    + 919                        # Channel or Stream is pointing to
    + 920                        the_group = next(
    + 921                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    + 922                            None
    + 923                        )
      924
    - 925                        # Save the new channel to the local list of channels
    - 926                        if loading_stream_type == self.live_type:
    - 927                            self.channels.append(new_channel)
    - 928                        elif loading_stream_type == self.vod_type:
    - 929                            self.movies.append(new_channel)
    - 930                            if new_channel.age_days_from_added < 31:
    - 931                                self.movies_30days.append(new_channel)
    - 932                            if new_channel.age_days_from_added < 7:
    - 933                                self.movies_7days.append(new_channel)
    - 934                        else:
    - 935                            self.series.append(new_series)
    - 936
    - 937                        # Add stream to the specific Group
    - 938                        if the_group is not None:
    - 939                            if loading_stream_type != self.series_type:
    - 940                                the_group.channels.append(new_channel)
    - 941                            else:
    - 942                                the_group.series.append(new_series)
    - 943                        else:
    - 944                            print(f" - Group not found `{stream_channel['name']}`")
    - 945                print("\n")
    - 946                # Print information of which streams have been skipped
    - 947                if self.hide_adult_content:
    - 948                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    - 949                if skipped_no_name_content > 0:
    - 950                    print(f" - Skipped {skipped_no_name_content} "
    - 951                          "unprintable {loading_stream_type} streams")
    - 952            else:
    - 953                print(f" - Could not load {loading_stream_type} Streams")
    + 925                        # Set group title
    + 926                        if the_group is not None:
    + 927                            group_title = the_group.name
    + 928                        else:
    + 929                            if loading_stream_type == self.live_type:
    + 930                                group_title = self.live_catch_all_group.name
    + 931                                the_group = self.live_catch_all_group
    + 932                            elif loading_stream_type == self.vod_type:
    + 933                                group_title = self.vod_catch_all_group.name
    + 934                                the_group = self.vod_catch_all_group
    + 935                            elif loading_stream_type == self.series_type:
    + 936                                group_title = self.series_catch_all_group.name
    + 937                                the_group = self.series_catch_all_group
    + 938
    + 939                        if loading_stream_type == self.series_type:
    + 940                            # Load all Series
    + 941                            new_series = Serie(self, stream_channel)
    + 942                            # To get all the Episodes for every Season of each
    + 943                            # Series is very time consuming, we will only
    + 944                            # populate the Series once the user click on the
    + 945                            # Series, the Seasons and Episodes will be loaded
    + 946                            # using x.getSeriesInfoByID() function
    + 947
    + 948                        else:
    + 949                            new_channel = Channel(
    + 950                                self,
    + 951                                group_title,
    + 952                                stream_channel
    + 953                            )
      954
    - 955            self.state["loaded"] = True
    - 956
    - 957    def _save_to_file_skipped_streams(self, stream_channel: Channel):
    - 958
    - 959        # Build the full path
    - 960        full_filename = osp.join(self.cache_path, "skipped_streams.json")
    - 961
    - 962        # If the path makes sense, save the file
    - 963        json_data = json.dumps(stream_channel, ensure_ascii=False)
    - 964        try:
    - 965            with open(full_filename, mode="a", encoding="utf-8") as myfile:
    - 966                myfile.writelines(json_data)
    - 967            return True
    - 968        except Exception as e:
    - 969            print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`")
    - 970        return False
    - 971
    - 972    def get_series_info_by_id(self, get_series: dict):
    - 973        """Get Seasons and Episodes for a Series
    - 974
    - 975        Args:
    - 976            get_series (dict): Series dictionary
    - 977        """
    - 978
    - 979        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    - 980
    - 981        if series_seasons["seasons"] is None:
    - 982            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
    - 983
    - 984        for series_info in series_seasons["seasons"]:
    - 985            season_name = series_info["name"]
    - 986            season_key = series_info['season_number']
    - 987            season = Season(season_name)
    - 988            get_series.seasons[season_name] = season
    - 989            if "episodes" in series_seasons.keys():
    - 990                for series_season in series_seasons["episodes"].keys():
    - 991                    for episode_info in series_seasons["episodes"][str(series_season)]:
    - 992                        new_episode_channel = Episode(
    - 993                            self, series_info, "Testing", episode_info
    - 994                        )
    - 995                        season.episodes[episode_info["title"]] = new_episode_channel
    - 996
    - 997    def _handle_request_exception(self, exception: requests.exceptions.RequestException):
    - 998        """Handle different types of request exceptions."""
    - 999        if isinstance(exception, requests.exceptions.ConnectionError):
    -1000            print(" - Connection Error: Possible network problem \
    -1001                  (e.g. DNS failure, refused connection, etc)")
    -1002        elif isinstance(exception, requests.exceptions.HTTPError):
    -1003            print(" - HTTP Error")
    -1004        elif isinstance(exception, requests.exceptions.TooManyRedirects):
    -1005            print(" - TooManyRedirects")
    -1006        elif isinstance(exception, requests.exceptions.ReadTimeout):
    -1007            print(" - Timeout while loading data")
    -1008        else:
    -1009            print(f" - An unexpected error occurred: {exception}")
    -1010
    -1011    def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]:
    -1012        """Generic GET Request with Error handling
    + 955                        if new_channel.group_id == "9999":
    + 956                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    + 957
    + 958                        # Save the new channel to the local list of channels
    + 959                        if loading_stream_type == self.live_type:
    + 960                            self.channels.append(new_channel)
    + 961                        elif loading_stream_type == self.vod_type:
    + 962                            self.movies.append(new_channel)
    + 963                            if new_channel.age_days_from_added < 31:
    + 964                                self.movies_30days.append(new_channel)
    + 965                            if new_channel.age_days_from_added < 7:
    + 966                                self.movies_7days.append(new_channel)
    + 967                        else:
    + 968                            self.series.append(new_series)
    + 969
    + 970                        # Add stream to the specific Group
    + 971                        if the_group is not None:
    + 972                            if loading_stream_type != self.series_type:
    + 973                                the_group.channels.append(new_channel)
    + 974                            else:
    + 975                                the_group.series.append(new_series)
    + 976                        else:
    + 977                            print(f" - Group not found `{stream_channel['name']}`")
    + 978                print("\n")
    + 979                # Print information of which streams have been skipped
    + 980                if self.hide_adult_content:
    + 981                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    + 982                if skipped_no_name_content > 0:
    + 983                    print(f" - Skipped {skipped_no_name_content} "
    + 984                          "unprintable {loading_stream_type} streams")
    + 985            else:
    + 986                print(f" - Could not load {loading_stream_type} Streams")
    + 987
    + 988            self.state["loaded"] = True
    + 989        return True
    + 990
    + 991    def _save_to_file_skipped_streams(self, stream_channel: Channel):
    + 992
    + 993        # Build the full path
    + 994        full_filename = osp.join(self.cache_path, "skipped_streams.json")
    + 995
    + 996        # If the path makes sense, save the file
    + 997        json_data = json.dumps(stream_channel, ensure_ascii=False)
    + 998        try:
    + 999            with open(full_filename, mode="a", encoding="utf-8") as myfile:
    +1000                myfile.writelines(json_data)
    +1001                myfile.write('\n')
    +1002            return True
    +1003        except Exception as e:
    +1004            print(f" - Could not save to skipped stream file `{full_filename}`: e=`{e}`")
    +1005        return False
    +1006
    +1007    def get_series_info_by_id(self, get_series: dict):
    +1008        """Get Seasons and Episodes for a Series
    +1009
    +1010        Args:
    +1011            get_series (dict): Series dictionary
    +1012        """
     1013
    -1014        Args:
    -1015            URL (str): The URL where to GET content
    -1016            timeout (Tuple[int, int], optional): Connection and Downloading Timeout.
    -1017                                                 Defaults to (2,15).
    -1018
    -1019        Returns:
    -1020            Optional[dict]: JSON dictionary of the loaded data, or None
    -1021        """
    -1022        for attempt in range(10):
    -1023            time.sleep(1)
    -1024            try:
    -1025                response = requests.get(url, timeout=timeout, headers=self.connection_headers)
    -1026                response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
    -1027                return response.json()
    -1028            except requests.exceptions.RequestException as e:
    -1029                self._handle_request_exception(e)
    -1030
    -1031        return None
    -1032        # i = 0
    -1033        # while i < 10:
    -1034        #     time.sleep(1)
    -1035        #     try:
    -1036        #         r = requests.get(url, timeout=timeout, headers=self.connection_headers)
    -1037        #         i = 20
    -1038        #         if r.status_code == 200:
    -1039        #             return r.json()
    -1040        #     except requests.exceptions.ConnectionError:
    -1041        #         print(" - Connection Error: Possible network problem (e.g. DNS failure, refused connection, etc)")
    -1042        #         i += 1
    -1043
    -1044        #     except requests.exceptions.HTTPError:
    -1045        #         print(" - HTTP Error")
    -1046        #         i += 1
    -1047
    -1048        #     except requests.exceptions.TooManyRedirects:
    -1049        #         print(" - TooManyRedirects")
    -1050        #         i += 1
    -1051
    -1052        #     except requests.exceptions.ReadTimeout:
    -1053        #         print(" - Timeout while loading data")
    -1054        #         i += 1
    -1055
    -1056        # return None
    -1057
    -1058    # GET Stream Categories
    -1059    def _load_categories_from_provider(self, stream_type: str):
    -1060        """Get from provider all category for specific stream type from provider
    -1061
    -1062        Args:
    -1063            stream_type (str): Stream type can be Live, VOD, Series
    -1064
    -1065        Returns:
    -1066            [type]: JSON if successfull, otherwise None
    -1067        """
    -1068        url = ""
    -1069        if stream_type == self.live_type:
    -1070            url = self.get_live_categories_URL()
    -1071        elif stream_type == self.vod_type:
    -1072            url = self.get_vod_cat_URL()
    -1073        elif stream_type == self.series_type:
    -1074            url = self.get_series_cat_URL()
    -1075        else:
    -1076            url = ""
    -1077
    -1078        return self._get_request(url)
    -1079
    -1080    # GET Streams
    -1081    def _load_streams_from_provider(self, stream_type: str):
    -1082        """Get from provider all streams for specific stream type
    +1014        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    +1015
    +1016        if series_seasons["seasons"] is None:
    +1017            series_seasons["seasons"] = [
    +1018                {"name": "Season 1", "cover": series_seasons["info"]["cover"]}
    +1019                ]
    +1020
    +1021        for series_info in series_seasons["seasons"]:
    +1022            season_name = series_info["name"]
    +1023            season = Season(season_name)
    +1024            get_series.seasons[season_name] = season
    +1025            if "episodes" in series_seasons.keys():
    +1026                for series_season in series_seasons["episodes"].keys():
    +1027                    for episode_info in series_seasons["episodes"][str(series_season)]:
    +1028                        new_episode_channel = Episode(
    +1029                            self, series_info, "Testing", episode_info
    +1030                        )
    +1031                        season.episodes[episode_info["title"]] = new_episode_channel
    +1032
    +1033    def _handle_request_exception(self, exception: requests.exceptions.RequestException):
    +1034        """Handle different types of request exceptions."""
    +1035        if isinstance(exception, requests.exceptions.ConnectionError):
    +1036            print(" - Connection Error: Possible network problem \
    +1037                  (e.g. DNS failure, refused connection, etc)")
    +1038        elif isinstance(exception, requests.exceptions.HTTPError):
    +1039            print(" - HTTP Error")
    +1040        elif isinstance(exception, requests.exceptions.TooManyRedirects):
    +1041            print(" - TooManyRedirects")
    +1042        elif isinstance(exception, requests.exceptions.ReadTimeout):
    +1043            print(" - Timeout while loading data")
    +1044        else:
    +1045            print(f" - An unexpected error occurred: {exception}")
    +1046
    +1047    def _get_request(self, url: str, timeout: Tuple[int, int] = (2, 15)) -> Optional[dict]:
    +1048        """Generic GET Request with Error handling
    +1049
    +1050        Args:
    +1051            URL (str): The URL where to GET content
    +1052            timeout (Tuple[int, int], optional): Connection and Downloading Timeout.
    +1053                                                 Defaults to (2,15).
    +1054
    +1055        Returns:
    +1056            Optional[dict]: JSON dictionary of the loaded data, or None
    +1057        """
    +1058
    +1059        kb_size = 1024
    +1060        all_data = []
    +1061        down_stats = {"bytes": 0, "kbytes": 0, "mbytes": 0, "start": 0.0, "delta_sec": 0.0}
    +1062
    +1063        for attempt in range(10):
    +1064            try:
    +1065                response = requests.get(
    +1066                    url,
    +1067                    stream=True,
    +1068                    timeout=timeout,
    +1069                    headers=self.connection_headers
    +1070                    )
    +1071                response.raise_for_status()  # Raise an HTTPError for bad responses (4xx and 5xx)
    +1072                break
    +1073            except requests.exceptions.RequestException as e:
    +1074                self._handle_request_exception(e)
    +1075                return None
    +1076
    +1077        # If there is an answer from the remote server
    +1078        if response.status_code in (200, 206):
    +1079            down_stats["start"] = time.perf_counter()
    +1080
    +1081            # Set downloaded size
    +1082            down_stats["bytes"] = 0
     1083
    -1084        Args:
    -1085            stream_type (str): Stream type can be Live, VOD, Series
    +1084            # Set stream blocks
    +1085            block_bytes = int(1*kb_size*kb_size)     # 4 MB
     1086
    -1087        Returns:
    -1088            [type]: JSON if successfull, otherwise None
    -1089        """
    -1090        url = ""
    -1091        if stream_type == self.live_type:
    -1092            url = self.get_live_streams_URL()
    -1093        elif stream_type == self.vod_type:
    -1094            url = self.get_vod_streams_URL()
    -1095        elif stream_type == self.series_type:
    -1096            url = self.get_series_URL()
    -1097        else:
    -1098            url = ""
    -1099
    -1100        return self._get_request(url)
    -1101
    -1102    # GET Streams by Category
    -1103    def _load_streams_by_category_from_provider(self, stream_type: str, category_id):
    -1104        """Get from provider all streams for specific stream type with category/group ID
    -1105
    -1106        Args:
    -1107            stream_type (str): Stream type can be Live, VOD, Series
    -1108            category_id ([type]): Category/Group ID.
    -1109
    -1110        Returns:
    -1111            [type]: JSON if successfull, otherwise None
    -1112        """
    -1113        url = ""
    -1114
    -1115        if stream_type == self.live_type:
    -1116            url = self.get_live_streams_URL_by_category(category_id)
    -1117        elif stream_type == self.vod_type:
    -1118            url = self.get_vod_streams_URL_by_category(category_id)
    -1119        elif stream_type == self.series_type:
    -1120            url = self.get_series_URL_by_category(category_id)
    -1121        else:
    -1122            url = ""
    -1123
    -1124        return self._get_request(url)
    -1125
    -1126    # GET SERIES Info
    -1127    def _load_series_info_by_id_from_provider(self, series_id: str):
    -1128        """Gets informations about a Serie
    -1129
    -1130        Args:
    -1131            series_id (str): Serie ID as described in Group
    +1087            # Grab data by block_bytes
    +1088            for data in response.iter_content(block_bytes, decode_unicode=False):
    +1089                down_stats["bytes"] += len(data)
    +1090                down_stats["kbytes"] = down_stats["bytes"]/kb_size
    +1091                down_stats["mbytes"] = down_stats["bytes"]/kb_size/kb_size
    +1092                down_stats["delta_sec"] = time.perf_counter() - down_stats["start"]
    +1093                download_speed_average = down_stats["kbytes"]//down_stats["delta_sec"]
    +1094                sys.stdout.write(
    +1095                    f'\rDownloading {down_stats["kbytes"]:.1f} MB at {download_speed_average:.0f} kB/s'
    +1096                    )
    +1097                sys.stdout.flush()
    +1098                all_data.append(data)
    +1099            print(" - Done")
    +1100            full_content = b''.join(all_data)
    +1101            return json.loads(full_content)
    +1102
    +1103        print(f"HTTP error {response.status_code} while retrieving from {url}")
    +1104
    +1105        return None
    +1106
    +1107    # GET Stream Categories
    +1108    def _load_categories_from_provider(self, stream_type: str):
    +1109        """Get from provider all category for specific stream type from provider
    +1110
    +1111        Args:
    +1112            stream_type (str): Stream type can be Live, VOD, Series
    +1113
    +1114        Returns:
    +1115            [type]: JSON if successful, otherwise None
    +1116        """
    +1117        url = ""
    +1118        if stream_type == self.live_type:
    +1119            url = self.get_live_categories_URL()
    +1120        elif stream_type == self.vod_type:
    +1121            url = self.get_vod_cat_URL()
    +1122        elif stream_type == self.series_type:
    +1123            url = self.get_series_cat_URL()
    +1124        else:
    +1125            url = ""
    +1126
    +1127        return self._get_request(url)
    +1128
    +1129    # GET Streams
    +1130    def _load_streams_from_provider(self, stream_type: str):
    +1131        """Get from provider all streams for specific stream type
     1132
    -1133        Returns:
    -1134            [type]: JSON if successfull, otherwise None
    -1135        """
    -1136        return self._get_request(self.get_series_info_URL_by_ID(series_id))
    -1137
    -1138    # The seasons array, might be filled or might be completely empty.
    -1139    # If it is not empty, it will contain the cover, overview and the air date
    -1140    # of the selected season.
    -1141    # In your APP if you want to display the series, you have to take that
    -1142    # from the episodes array.
    -1143
    -1144    # GET VOD Info
    -1145    def vodInfoByID(self, vod_id):
    -1146        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
    -1147
    -1148    # GET short_epg for LIVE Streams (same as stalker portal,
    -1149    # prints the next X EPG that will play soon)
    -1150    def liveEpgByStream(self, stream_id):
    -1151        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
    -1152
    -1153    def liveEpgByStreamAndLimit(self, stream_id, limit):
    -1154        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
    -1155
    -1156    #  GET ALL EPG for LIVE Streams (same as stalker portal,
    -1157    # but it will print all epg listings regardless of the day)
    -1158    def allLiveEpgByStream(self, stream_id):
    -1159        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
    -1160
    -1161    # Full EPG List for all Streams
    -1162    def allEpg(self):
    -1163        return self._get_request(self.get_all_epg_URL())
    -1164
    -1165    ## URL-builder methods
    -1166    def get_live_categories_URL(self) -> str:
    -1167        return f"{self.base_url}&action=get_live_categories"
    -1168
    -1169    def get_live_streams_URL(self) -> str:
    -1170        return f"{self.base_url}&action=get_live_streams"
    -1171
    -1172    def get_live_streams_URL_by_category(self, category_id) -> str:
    -1173        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
    +1133        Args:
    +1134            stream_type (str): Stream type can be Live, VOD, Series
    +1135
    +1136        Returns:
    +1137            [type]: JSON if successful, otherwise None
    +1138        """
    +1139        url = ""
    +1140        if stream_type == self.live_type:
    +1141            url = self.get_live_streams_URL()
    +1142        elif stream_type == self.vod_type:
    +1143            url = self.get_vod_streams_URL()
    +1144        elif stream_type == self.series_type:
    +1145            url = self.get_series_URL()
    +1146        else:
    +1147            url = ""
    +1148
    +1149        return self._get_request(url)
    +1150
    +1151    # GET Streams by Category
    +1152    def _load_streams_by_category_from_provider(self, stream_type: str, category_id):
    +1153        """Get from provider all streams for specific stream type with category/group ID
    +1154
    +1155        Args:
    +1156            stream_type (str): Stream type can be Live, VOD, Series
    +1157            category_id ([type]): Category/Group ID.
    +1158
    +1159        Returns:
    +1160            [type]: JSON if successful, otherwise None
    +1161        """
    +1162        url = ""
    +1163
    +1164        if stream_type == self.live_type:
    +1165            url = self.get_live_streams_URL_by_category(category_id)
    +1166        elif stream_type == self.vod_type:
    +1167            url = self.get_vod_streams_URL_by_category(category_id)
    +1168        elif stream_type == self.series_type:
    +1169            url = self.get_series_URL_by_category(category_id)
    +1170        else:
    +1171            url = ""
    +1172
    +1173        return self._get_request(url)
     1174
    -1175    def get_vod_cat_URL(self) -> str:
    -1176        return f"{self.base_url}&action=get_vod_categories"
    -1177
    -1178    def get_vod_streams_URL(self) -> str:
    -1179        return f"{self.base_url}&action=get_vod_streams"
    -1180
    -1181    def get_vod_streams_URL_by_category(self, category_id) -> str:
    -1182        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
    -1183
    -1184    def get_series_cat_URL(self) -> str:
    -1185        return f"{self.base_url}&action=get_series_categories"
    -1186
    -1187    def get_series_URL(self) -> str:
    -1188        return f"{self.base_url}&action=get_series"
    -1189
    -1190    def get_series_URL_by_category(self, category_id) -> str:
    -1191        return f"{self.base_url}&action=get_series&category_id={category_id}"
    -1192
    -1193    def get_series_info_URL_by_ID(self, series_id) -> str:
    -1194        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
    -1195
    -1196    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    -1197        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
    -1198
    -1199    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    -1200        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
    -1201
    -1202    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    -1203        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
    -1204
    -1205    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    -1206        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
    -1207
    -1208    def get_all_epg_URL(self) -> str:
    -1209        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
    +1175    # GET SERIES Info
    +1176    def _load_series_info_by_id_from_provider(self, series_id: str, return_type: str = "DICT"):
    +1177        """Gets information about a Serie
    +1178
    +1179        Args:
    +1180            series_id (str): Serie ID as described in Group
    +1181            return_type (str, optional): Output format, 'DICT' or 'JSON'. Defaults to "DICT".
    +1182
    +1183        Returns:
    +1184            [type]: JSON if successful, otherwise None
    +1185        """
    +1186        data = self._get_request(self.get_series_info_URL_by_ID(series_id))
    +1187        if return_type == "JSON":
    +1188            return json.dumps(data, ensure_ascii=False)
    +1189        return data
    +1190
    +1191    # The seasons array, might be filled or might be completely empty.
    +1192    # If it is not empty, it will contain the cover, overview and the air date
    +1193    # of the selected season.
    +1194    # In your APP if you want to display the series, you have to take that
    +1195    # from the episodes array.
    +1196
    +1197    # GET VOD Info
    +1198    def vodInfoByID(self, vod_id):
    +1199        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
    +1200
    +1201    # GET short_epg for LIVE Streams (same as stalker portal,
    +1202    # prints the next X EPG that will play soon)
    +1203    def liveEpgByStream(self, stream_id):
    +1204        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
    +1205
    +1206    def liveEpgByStreamAndLimit(self, stream_id, limit):
    +1207        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
    +1208
    +1209    #  GET ALL EPG for LIVE Streams (same as stalker portal,
    +1210    # but it will print all epg listings regardless of the day)
    +1211    def allLiveEpgByStream(self, stream_id):
    +1212        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
    +1213
    +1214    # Full EPG List for all Streams
    +1215    def allEpg(self):
    +1216        return self._get_request(self.get_all_epg_URL())
    +1217
    +1218    # URL-builder methods
    +1219    def get_live_categories_URL(self) -> str:
    +1220        return f"{self.base_url}&action=get_live_categories"
    +1221
    +1222    def get_live_streams_URL(self) -> str:
    +1223        return f"{self.base_url}&action=get_live_streams"
    +1224
    +1225    def get_live_streams_URL_by_category(self, category_id) -> str:
    +1226        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
    +1227
    +1228    def get_vod_cat_URL(self) -> str:
    +1229        return f"{self.base_url}&action=get_vod_categories"
    +1230
    +1231    def get_vod_streams_URL(self) -> str:
    +1232        return f"{self.base_url}&action=get_vod_streams"
    +1233
    +1234    def get_vod_streams_URL_by_category(self, category_id) -> str:
    +1235        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
    +1236
    +1237    def get_series_cat_URL(self) -> str:
    +1238        return f"{self.base_url}&action=get_series_categories"
    +1239
    +1240    def get_series_URL(self) -> str:
    +1241        return f"{self.base_url}&action=get_series"
    +1242
    +1243    def get_series_URL_by_category(self, category_id) -> str:
    +1244        return f"{self.base_url}&action=get_series&category_id={category_id}"
    +1245
    +1246    def get_series_info_URL_by_ID(self, series_id) -> str:
    +1247        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
    +1248
    +1249    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    +1250        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
    +1251
    +1252    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    +1253        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
    +1254
    +1255    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    +1256        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
    +1257
    +1258    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    +1259        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
    +1260
    +1261    def get_all_epg_URL(self) -> str:
    +1262        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
     
    @@ -3780,95 +3926,102 @@

    - XTream( provider_name: str, provider_username: str, provider_password: str, provider_url: str, headers: dict = None, hide_adult_content: bool = False, cache_path: str = '', reload_time_sec: int = 28800, validate_json: bool = False, debug_flask: bool = True) + XTream( provider_name: str, provider_username: str, provider_password: str, provider_url: str, headers: dict = None, hide_adult_content: bool = False, cache_path: str = '', reload_time_sec: int = 28800, validate_json: bool = False, enable_flask: bool = False, debug_flask: bool = True)
    -
    324    def __init__(
    -325        self,
    -326        provider_name: str,
    -327        provider_username: str,
    -328        provider_password: str,
    -329        provider_url: str,
    -330        headers: dict = None,
    -331        hide_adult_content: bool = False,
    -332        cache_path: str = "",
    -333        reload_time_sec: int = 60*60*8,
    -334        validate_json: bool = False,
    -335        debug_flask: bool = True
    -336        ):
    -337        """Initialize Xtream Class
    -338
    -339        Args:
    -340            provider_name     (str):            Name of the IPTV provider
    -341            provider_username (str):            User name of the IPTV provider
    -342            provider_password (str):            Password of the IPTV provider
    -343            provider_url      (str):            URL of the IPTV provider
    -344            headers           (dict):           Requests Headers
    -345            hide_adult_content(bool, optional): When `True` hide stream that are marked for adult
    -346            cache_path        (str, optional):  Location where to save loaded files.
    -347                                                Defaults to empty string.
    -348            reload_time_sec   (int, optional):  Number of seconds before automatic reloading
    -349                                                (-1 to turn it OFF)
    -350            debug_flask       (bool, optional): Enable the debug mode in Flask
    -351            validate_json     (bool, optional): Check Xtream API provided JSON for validity
    -352
    -353        Returns: XTream Class Instance
    -354
    -355        - Note 1: If it fails to authorize with provided username and password,
    -356                auth_data will be an empty dictionary.
    -357        - Note 2: The JSON validation option will take considerable amount of time and it should be 
    -358                  used only as a debug tool. The Xtream API JSON from the provider passes through a
    -359                  schema that represent the best available understanding of how the Xtream API 
    -360                  works.
    -361        """
    -362        self.server = provider_url
    -363        self.username = provider_username
    -364        self.password = provider_password
    -365        self.name = provider_name
    -366        self.cache_path = cache_path
    -367        self.hide_adult_content = hide_adult_content
    -368        self.threshold_time_sec = reload_time_sec
    -369        self.validate_json = validate_json
    -370
    -371        # get the pyxtream local path
    -372        self.app_fullpath = osp.dirname(osp.realpath(__file__))
    -373
    -374        # prepare location of local html template
    -375        self.html_template_folder = osp.join(self.app_fullpath,"html")
    -376
    -377        # if the cache_path is specified, test that it is a directory
    -378        if self.cache_path != "":
    -379            # If the cache_path is not a directory, clear it
    -380            if not osp.isdir(self.cache_path):
    -381                print(" - Cache Path is not a directory, using default '~/.xtream-cache/'")
    -382                self.cache_path == ""
    -383
    -384        # If the cache_path is still empty, use default
    -385        if self.cache_path == "":
    -386            self.cache_path = osp.expanduser("~/.xtream-cache/")
    -387            if not osp.isdir(self.cache_path):
    -388                makedirs(self.cache_path, exist_ok=True)
    -389            print(f"pyxtream cache path located at {self.cache_path}")
    -390
    -391        if headers is not None:
    -392            self.connection_headers = headers
    -393        else:
    -394            self.connection_headers = {'User-Agent':"Wget/1.20.3 (linux-gnu)"}
    -395
    -396        self.authenticate()
    +            
    349    def __init__(
    +350        self,
    +351        provider_name: str,
    +352        provider_username: str,
    +353        provider_password: str,
    +354        provider_url: str,
    +355        headers: dict = None,
    +356        hide_adult_content: bool = False,
    +357        cache_path: str = "",
    +358        reload_time_sec: int = 60*60*8,
    +359        validate_json: bool = False,
    +360        enable_flask: bool = False,
    +361        debug_flask: bool = True
    +362            ):
    +363        """Initialize Xtream Class
    +364
    +365        Args:
    +366            provider_name     (str):            Name of the IPTV provider
    +367            provider_username (str):            User name of the IPTV provider
    +368            provider_password (str):            Password of the IPTV provider
    +369            provider_url      (str):            URL of the IPTV provider
    +370            headers           (dict):           Requests Headers
    +371            hide_adult_content(bool, optional): When `True` hide stream that are marked for adult
    +372            cache_path        (str, optional):  Location where to save loaded files.
    +373                                                Defaults to empty string.
    +374            reload_time_sec   (int, optional):  Number of seconds before automatic reloading
    +375                                                (-1 to turn it OFF)
    +376            validate_json     (bool, optional): Check Xtream API provided JSON for validity
    +377            enable_flask      (bool, optional): Enable Flask
    +378            debug_flask       (bool, optional): Enable the debug mode in Flask
    +379
    +380        Returns: XTream Class Instance
    +381
    +382        - Note 1: If it fails to authorize with provided username and password,
    +383                auth_data will be an empty dictionary.
    +384        - Note 2: The JSON validation option will take considerable amount of time and it should be
    +385                  used only as a debug tool. The Xtream API JSON from the provider passes through a
    +386                  schema that represent the best available understanding of how the Xtream API
    +387                  works.
    +388        """
    +389        self.server = provider_url
    +390        self.username = provider_username
    +391        self.password = provider_password
    +392        self.name = provider_name
    +393        self.cache_path = cache_path
    +394        self.hide_adult_content = hide_adult_content
    +395        self.threshold_time_sec = reload_time_sec
    +396        self.validate_json = validate_json
     397
    -398        if self.threshold_time_sec > 0:
    -399            print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds")
    -400        else:
    -401            print("Reload timer is OFF")
    -402
    -403        if self.state['authenticated']:
    -404            if USE_FLASK:
    -405                self.flaskapp = FlaskWrap('pyxtream', self, self.html_template_folder, debug=debug_flask)
    -406                self.flaskapp.start()
    +398        # get the pyxtream local path
    +399        self.app_fullpath = osp.dirname(osp.realpath(__file__))
    +400
    +401        # prepare location of local html template
    +402        self.html_template_folder = osp.join(self.app_fullpath, "html")
    +403
    +404        # if the cache_path is specified, test that it is a directory
    +405        if self.cache_path != "":
    +406            # If the cache_path is not a directory, clear it
    +407            if not osp.isdir(self.cache_path):
    +408                print(" - Cache Path is not a directory, using default '~/.xtream-cache/'")
    +409                self.cache_path = ""
    +410
    +411        # If the cache_path is still empty, use default
    +412        if self.cache_path == "":
    +413            self.cache_path = osp.expanduser("~/.xtream-cache/")
    +414            if not osp.isdir(self.cache_path):
    +415                makedirs(self.cache_path, exist_ok=True)
    +416            print(f"pyxtream cache path located at {self.cache_path}")
    +417
    +418        if headers is not None:
    +419            self.connection_headers = headers
    +420        else:
    +421            self.connection_headers = {'User-Agent': "Wget/1.20.3 (linux-gnu)"}
    +422
    +423        self.authenticate()
    +424
    +425        if self.threshold_time_sec > 0:
    +426            print(f"Reload timer is ON and set to {self.threshold_time_sec} seconds")
    +427        else:
    +428            print("Reload timer is OFF")
    +429
    +430        if self.state['authenticated']:
    +431            if USE_FLASK and enable_flask:
    +432                print("Starting Web Interface")
    +433                self.flaskapp = FlaskWrap(
    +434                    'pyxtream', self, self.html_template_folder, debug=debug_flask
    +435                    )
    +436                self.flaskapp.start()
    +437            else:
    +438                print("Web interface not running")
     
    @@ -3885,17 +4038,18 @@

    Defaults to empty string. reload_time_sec (int, optional): Number of seconds before automatic reloading (-1 to turn it OFF) - debug_flask (bool, optional): Enable the debug mode in Flask - validate_json (bool, optional): Check Xtream API provided JSON for validity

    + validate_json (bool, optional): Check Xtream API provided JSON for validity + enable_flask (bool, optional): Enable Flask + debug_flask (bool, optional): Enable the debug mode in Flask

    Returns: XTream Class Instance

    • Note 1: If it fails to authorize with provided username and password, auth_data will be an empty dictionary.
    • -
    • Note 2: The JSON validation option will take considerable amount of time and it should be +
    • Note 2: The JSON validation option will take considerable amount of time and it should be used only as a debug tool. The Xtream API JSON from the provider passes through a -schema that represent the best available understanding of how the Xtream API +schema that represent the best available understanding of how the Xtream API works.

    @@ -3961,6 +4115,53 @@

    +

    +
    +
    + base_url = +'' + + +
    + + + + +
    +
    +
    + base_url_ssl = +'' + + +
    + + + + +
    +
    +
    + cache_path = +'' + + +
    + + + + +
    +
    +
    + account_expiration: datetime.timedelta + + +
    + + + +
    @@ -4013,7 +4214,7 @@

    authorization = -{} +{'username': '', 'password': ''}
    @@ -4178,24 +4379,26 @@

    -
    +
    - cache_path + validate_json: bool = +True
    - +
    -
    +
    - validate_json + download_progress: dict = +{'StreamId': 0, 'Total': 0, 'Progress': 0}
    - + @@ -4221,82 +4424,106 @@

    +

    +
    + +
    + + def + get_download_progress(self, stream_id: int = None): + + + +
    + +
    440    def get_download_progress(self, stream_id: int = None):
    +441        # TODO: Add check for stream specific ID
    +442        return json.dumps(self.download_progress)
    +
    + + + + +
    +
    + +
    + + def + get_last_7days(self): + + + +
    + +
    444    def get_last_7days(self):
    +445        return json.dumps(self.movies_7days, default=lambda x: x.export_json())
    +
    + + + +
    def - search_stream( self, keyword: str, ignore_case: bool = True, return_type: str = 'LIST', stream_type: list = ('series', 'movies', 'channels')) -> list: + search_stream( self, keyword: str, ignore_case: bool = True, return_type: str = 'LIST', stream_type: list = ('series', 'movies', 'channels'), added_after: datetime.datetime = None) -> list:
    -
    408    def search_stream(self, keyword: str,
    -409                      ignore_case: bool = True,
    -410                      return_type: str = "LIST",
    -411                      stream_type: list = ("series", "movies", "channels")) -> list:
    -412        """Search for streams
    -413
    -414        Args:
    -415            keyword (str): Keyword to search for. Supports REGEX
    -416            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    -417            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    -418            stream_type (list, optional): Search within specific stream type.
    -419
    -420        Returns:
    -421            list: List with all the results, it could be empty.
    -422        """
    -423
    -424        search_result = []
    -425        regex_flags = re.IGNORECASE if ignore_case else 0
    -426        regex = re.compile(keyword, regex_flags)
    -427        # if ignore_case:
    -428        #     regex = re.compile(keyword, re.IGNORECASE)
    -429        # else:
    -430        #     regex = re.compile(keyword)
    -431
    -432        # if "movies" in stream_type:
    -433        #     print(f"Checking {len(self.movies)} movies")
    -434        #     for stream in self.movies:
    -435        #         if re.match(regex, stream.name) is not None:
    -436        #             search_result.append(stream.export_json())
    -437
    -438        # if "channels" in stream_type:
    -439        #     print(f"Checking {len(self.channels)} channels")
    -440        #     for stream in self.channels:
    -441        #         if re.match(regex, stream.name) is not None:
    -442        #             search_result.append(stream.export_json())
    -443
    -444        # if "series" in stream_type:
    -445        #     print(f"Checking {len(self.series)} series")
    -446        #     for stream in self.series:
    -447        #         if re.match(regex, stream.name) is not None:
    -448        #             search_result.append(stream.export_json())
    -449
    -450        stream_collections = {
    -451            "movies": self.movies,
    -452            "channels": self.channels,
    -453            "series": self.series
    -454        }
    -455
    -456        for stream_type_name in stream_type:
    -457            if stream_type_name in stream_collections:
    -458                collection = stream_collections[stream_type_name]
    -459                print(f"Checking {len(collection)} {stream_type_name}")
    -460                for stream in collection:
    -461                    if re.match(regex, stream.name) is not None:
    -462                        search_result.append(stream.export_json())
    -463            else:
    -464                print(f"`{stream_type_name}` not found in collection")
    -465
    -466        if return_type == "JSON":
    -467            # if search_result is not None:
    -468            print(f"Found {len(search_result)} results `{keyword}`")
    -469            return json.dumps(search_result, ensure_ascii=False)
    -470
    -471        return search_result
    +            
    447    def search_stream(self, keyword: str,
    +448                      ignore_case: bool = True,
    +449                      return_type: str = "LIST",
    +450                      stream_type: list = ("series", "movies", "channels"),
    +451                      added_after: datetime = None) -> list:
    +452        """Search for streams
    +453
    +454        Args:
    +455            keyword (str): Keyword to search for. Supports REGEX
    +456            ignore_case (bool, optional): True to ignore case during search. Defaults to "True".
    +457            return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST".
    +458            stream_type (list, optional): Search within specific stream type.
    +459            added_after (datetime, optional): Search for items that have been added after a certain date.
    +460
    +461        Returns:
    +462            list: List with all the results, it could be empty.
    +463        """
    +464
    +465        search_result = []
    +466        regex_flags = re.IGNORECASE if ignore_case else 0
    +467        regex = re.compile(keyword, regex_flags)
    +468
    +469        stream_collections = {
    +470            "movies": self.movies,
    +471            "channels": self.channels,
    +472            "series": self.series
    +473        }
    +474
    +475        for stream_type_name in stream_type:
    +476            if stream_type_name in stream_collections:
    +477                collection = stream_collections[stream_type_name]
    +478                print(f"Checking {len(collection)} {stream_type_name}")
    +479                for stream in collection:
    +480                    if stream.name and re.match(regex, stream.name) is not None:
    +481                        if added_after is None:
    +482                            # Add all matches
    +483                            search_result.append(stream.export_json())
    +484                        else:
    +485                            # Only add if it is more recent
    +486                            pass
    +487            else:
    +488                print(f"`{stream_type_name}` not found in collection")
    +489
    +490        if return_type == "JSON":
    +491            # if search_result is not None:
    +492            print(f"Found {len(search_result)} results `{keyword}`")
    +493            return json.dumps(search_result, ensure_ascii=False)
    +494
    +495        return search_result
     
    @@ -4306,7 +4533,8 @@

    keyword (str): Keyword to search for. Supports REGEX ignore_case (bool, optional): True to ignore case during search. Defaults to "True". return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to "LIST". - stream_type (list, optional): Search within specific stream type.

    + stream_type (list, optional): Search within specific stream type. + added_after (datetime, optional): Search for items that have been added after a certain date.

    Returns: list: List with all the results, it could be empty.

    @@ -4325,37 +4553,42 @@

    -
    473    def download_video(self, stream_id: int) -> str:
    -474        """Download Video from Stream ID
    -475
    -476        Args:
    -477            stream_id (int): Stirng identifing the stream ID
    -478
    -479        Returns:
    -480            str: Absolute Path Filename where the file was saved. Empty if could not download
    -481        """
    -482        url = ""
    -483        filename = ""
    -484        for stream in self.movies:
    -485            if stream.id == stream_id:
    -486                url = stream.url
    -487                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    -488                filename = osp.join(self.cache_path,fn)
    -489
    -490        # If the url was correctly built and file does not exists, start downloading
    -491        if url != "":
    -492            #if not osp.isfile(filename):
    -493            if not self._download_video_impl(url,filename):
    -494                return "Error"
    -495
    -496        return filename
    +            
    497    def download_video(self, stream_id: int) -> str:
    +498        """Download Video from Stream ID
    +499
    +500        Args:
    +501            stream_id (int): String identifying the stream ID
    +502
    +503        Returns:
    +504            str: Absolute Path Filename where the file was saved. Empty if could not download
    +505        """
    +506        url = ""
    +507        filename = ""
    +508        for series_stream in self.series:
    +509            if series_stream.series_id == stream_id:
    +510                episode_object: Episode = series_stream.episodes["1"]
    +511                url = f"{series_stream.url}/{episode_object.id}."\
    +512                      f"{episode_object.container_extension}"
    +513
    +514        for stream in self.movies:
    +515            if stream.id == stream_id:
    +516                url = stream.url
    +517                fn = f"{self._slugify(stream.name)}.{stream.raw['container_extension']}"
    +518                filename = osp.join(self.cache_path, fn)
    +519
    +520        # If the url was correctly built and file does not exists, start downloading
    +521        if url != "":
    +522            if not self._download_video_impl(url, filename):
    +523                return "Error"
    +524
    +525        return filename
     

    Download Video from Stream ID

    Args: - stream_id (int): Stirng identifing the stream ID

    + stream_id (int): String identifying the stream ID

    Returns: str: Absolute Path Filename where the file was saved. Empty if could not download

    @@ -4374,56 +4607,56 @@

    -
    615    def authenticate(self):
    -616        """Login to provider"""
    -617        # If we have not yet successfully authenticated, attempt authentication
    -618        if self.state["authenticated"] is False:
    -619            # Erase any previous data
    -620            self.auth_data = {}
    -621            # Loop through 30 seconds
    -622            i = 0
    -623            r = None
    -624            # Prepare the authentication url
    -625            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    -626            print("Attempting connection... ", end='')
    -627            while i < 30:
    -628                try:
    -629                    # Request authentication, wait 4 seconds maximum
    -630                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    -631                    i = 31
    -632                except requests.exceptions.ConnectionError:
    -633                    time.sleep(1)
    -634                    print(f"{i} ", end='',flush=True)
    -635                    i += 1
    -636
    -637            if r is not None:
    -638                # If the answer is ok, process data and change state
    -639                if r.ok:
    -640                    print("Connected")
    -641                    self.auth_data = r.json()
    -642                    self.authorization = {
    -643                        "username": self.auth_data["user_info"]["username"],
    -644                        "password": self.auth_data["user_info"]["password"]
    -645                    }
    -646                    # Account expiration date
    -647                    self.account_expiration = timedelta(
    -648                        seconds=(
    -649                            int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp()
    -650                        )
    -651                    )
    -652                    # Mark connection authorized
    -653                    self.state["authenticated"] = True
    -654                    # Construct the base url for all requests
    -655                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    -656                    # If there is a secure server connection, construct the base url SSL for all requests
    -657                    if "https_port" in self.auth_data["server_info"]:
    -658                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    -659                                            f"/player_api.php?username={self.username}&password={self.password}"
    -660                    print(f"Account expires in {str(self.account_expiration)}")
    -661                else:
    -662                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    -663            else:
    -664                print(f"\n{self.name}: Provider refused the connection")
    +            
    656    def authenticate(self):
    +657        """Login to provider"""
    +658        # If we have not yet successfully authenticated, attempt authentication
    +659        if self.state["authenticated"] is False:
    +660            # Erase any previous data
    +661            self.auth_data = {}
    +662            # Loop through 30 seconds
    +663            i = 0
    +664            r = None
    +665            # Prepare the authentication url
    +666            url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    +667            print("Attempting connection... ", end='')
    +668            while i < 30:
    +669                try:
    +670                    # Request authentication, wait 4 seconds maximum
    +671                    r = requests.get(url, timeout=(4), headers=self.connection_headers)
    +672                    i = 31
    +673                except (requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout):
    +674                    time.sleep(1)
    +675                    print(f"{i} ", end='', flush=True)
    +676                    i += 1
    +677
    +678            if r is not None:
    +679                # If the answer is ok, process data and change state
    +680                if r.ok:
    +681                    print("Connected")
    +682                    self.auth_data = r.json()
    +683                    self.authorization = {
    +684                        "username": self.auth_data["user_info"]["username"],
    +685                        "password": self.auth_data["user_info"]["password"]
    +686                    }
    +687                    # Account expiration date
    +688                    self.account_expiration = timedelta(
    +689                        seconds=(
    +690                            int(self.auth_data["user_info"]["exp_date"])-datetime.now().timestamp()
    +691                        )
    +692                    )
    +693                    # Mark connection authorized
    +694                    self.state["authenticated"] = True
    +695                    # Construct the base url for all requests
    +696                    self.base_url = f"{self.server}/player_api.php?username={self.username}&password={self.password}"
    +697                    # If there is a secure server connection, construct the base url SSL for all requests
    +698                    if "https_port" in self.auth_data["server_info"]:
    +699                        self.base_url_ssl = f"https://{self.auth_data['server_info']['url']}:{self.auth_data['server_info']['https_port']}" \
    +700                                            f"/player_api.php?username={self.username}&password={self.password}"
    +701                    print(f"Account expires in {str(self.account_expiration)}")
    +702                else:
    +703                    print(f"Provider `{self.name}` could not be loaded. Reason: `{r.status_code} {r.reason}`")
    +704            else:
    +705                print(f"\n{self.name}: Provider refused the connection")
     
    @@ -4443,221 +4676,230 @@

    -
    741    def load_iptv(self) -> bool:
    -742        """Load XTream IPTV
    -743
    -744        - Add all Live TV to XTream.channels
    -745        - Add all VOD to XTream.movies
    -746        - Add all Series to XTream.series
    -747          Series contains Seasons and Episodes. Those are not automatically
    -748          retrieved from the server to reduce the loading time.
    -749        - Add all groups to XTream.groups
    -750          Groups are for all three channel types, Live TV, VOD, and Series
    -751
    -752        Returns:
    -753            bool: True if successfull, False if error
    -754        """
    -755        # If pyxtream has not authenticated the connection, return empty
    -756        if self.state["authenticated"] is False:
    -757            print("Warning, cannot load steams since authorization failed")
    -758            return False
    -759
    -760        # If pyxtream has already loaded the data, skip and return success
    -761        if self.state["loaded"] is True:
    -762            print("Warning, data has already been loaded.")
    -763            return True
    -764
    -765        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    -766            ## Get GROUPS
    -767
    -768            # Try loading local file
    -769            dt = 0
    -770            start = timer()
    -771            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    -772            # If file empty or does not exists, download it from remote
    -773            if all_cat is None:
    -774                # Load all Groups and save file locally
    -775                all_cat = self._load_categories_from_provider(loading_stream_type)
    -776                if all_cat is not None:
    -777                    self._save_to_file(all_cat,f"all_groups_{loading_stream_type}.json")
    -778            dt = timer() - start
    -779
    -780            # If we got the GROUPS data, show the statistics and load GROUPS
    -781            if all_cat is not None:
    -782                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    -783                ## Add GROUPS to dictionaries
    +            
    766    def load_iptv(self) -> bool:
    +767        """Load XTream IPTV
    +768
    +769        - Add all Live TV to XTream.channels
    +770        - Add all VOD to XTream.movies
    +771        - Add all Series to XTream.series
    +772          Series contains Seasons and Episodes. Those are not automatically
    +773          retrieved from the server to reduce the loading time.
    +774        - Add all groups to XTream.groups
    +775          Groups are for all three channel types, Live TV, VOD, and Series
    +776
    +777        Returns:
    +778            bool: True if successful, False if error
    +779        """
    +780        # If pyxtream has not authenticated the connection, return empty
    +781        if self.state["authenticated"] is False:
    +782            print("Warning, cannot load steams since authorization failed")
    +783            return False
     784
    -785                # Add the catch-all-errors group
    -786                if loading_stream_type == self.live_type:
    -787                    self.groups.append(self.live_catch_all_group)
    -788                elif loading_stream_type == self.vod_type:
    -789                    self.groups.append(self.vod_catch_all_group)
    -790                elif loading_stream_type == self.series_type:
    -791                    self.groups.append(self.series_catch_all_group)
    -792
    -793                for cat_obj in all_cat:
    -794                    if schemaValidator(cat_obj, SchemaType.GROUP):
    -795                        # Create Group (Category)
    -796                        new_group = Group(cat_obj, loading_stream_type)
    -797                        #  Add to xtream class
    -798                        self.groups.append(new_group)
    -799                    else:
    -800                        # Save what did not pass schema validation
    -801                        print(cat_obj)
    -802
    -803                # Sort Categories
    -804                self.groups.sort(key=lambda x: x.name)
    -805            else:
    -806                print(f" - Could not load {loading_stream_type} Groups")
    -807                break
    -808
    -809            ## Get Streams
    -810
    -811            # Try loading local file
    -812            dt = 0
    -813            start = timer()
    -814            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    -815            # If file empty or does not exists, download it from remote
    -816            if all_streams is None:
    -817                # Load all Streams and save file locally
    -818                all_streams = self._load_streams_from_provider(loading_stream_type)
    -819                self._save_to_file(all_streams,f"all_stream_{loading_stream_type}.json")
    -820            dt = timer() - start
    -821
    -822            # If we got the STREAMS data, show the statistics and load Streams
    -823            if all_streams is not None:
    -824                print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    -825                ## Add Streams to dictionaries
    +785        # If pyxtream has already loaded the data, skip and return success
    +786        if self.state["loaded"] is True:
    +787            print("Warning, data has already been loaded.")
    +788            return True
    +789
    +790        # Delete skipped channels from cache
    +791        full_filename = osp.join(self.cache_path, "skipped_streams.json")
    +792        try:
    +793            f = open(full_filename, mode="r+", encoding="utf-8")
    +794            f.truncate(0)
    +795            f.close()
    +796        except FileNotFoundError:
    +797            pass
    +798
    +799        for loading_stream_type in (self.live_type, self.vod_type, self.series_type):
    +800            # Get GROUPS
    +801
    +802            # Try loading local file
    +803            dt = 0
    +804            start = timer()
    +805            all_cat = self._load_from_file(f"all_groups_{loading_stream_type}.json")
    +806            # If file empty or does not exists, download it from remote
    +807            if all_cat is None:
    +808                # Load all Groups and save file locally
    +809                all_cat = self._load_categories_from_provider(loading_stream_type)
    +810                if all_cat is not None:
    +811                    self._save_to_file(all_cat, f"all_groups_{loading_stream_type}.json")
    +812            dt = timer() - start
    +813
    +814            # If we got the GROUPS data, show the statistics and load GROUPS
    +815            if all_cat is not None:
    +816                print(f"{self.name}: Loaded {len(all_cat)} {loading_stream_type} Groups in {dt:.3f} seconds")
    +817                # Add GROUPS to dictionaries
    +818
    +819                # Add the catch-all-errors group
    +820                if loading_stream_type == self.live_type:
    +821                    self.groups.append(self.live_catch_all_group)
    +822                elif loading_stream_type == self.vod_type:
    +823                    self.groups.append(self.vod_catch_all_group)
    +824                elif loading_stream_type == self.series_type:
    +825                    self.groups.append(self.series_catch_all_group)
     826
    -827                skipped_adult_content = 0
    -828                skipped_no_name_content = 0
    -829
    -830                number_of_streams = len(all_streams)
    -831                current_stream_number = 0
    -832                # Calculate 1% of total number of streams
    -833                # This is used to slow down the progress bar
    -834                one_percent_number_of_streams = number_of_streams/100
    -835                start = timer()
    -836                for stream_channel in all_streams:
    -837                    skip_stream = False
    -838                    current_stream_number += 1
    -839
    -840                    # Show download progress every 1% of total number of streams
    -841                    if current_stream_number < one_percent_number_of_streams:
    -842                        progress(
    -843                            current_stream_number,
    -844                            number_of_streams,
    -845                            f"Processing {loading_stream_type} Streams"
    -846                            )
    -847                        one_percent_number_of_streams *= 2
    -848
    -849                    # Validate JSON scheme
    -850                    if self.validate_json:
    -851                        if loading_stream_type == self.series_type:
    -852                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    -853                                print(stream_channel)
    -854                        elif loading_stream_type == self.live_type:
    -855                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    -856                                print(stream_channel)
    -857                        else:
    -858                            # vod_type
    -859                            if not schemaValidator(stream_channel, SchemaType.VOD):
    -860                                print(stream_channel)
    -861
    -862                    # Skip if the name of the stream is empty
    -863                    if stream_channel["name"] == "":
    -864                        skip_stream = True
    -865                        skipped_no_name_content = skipped_no_name_content + 1
    -866                        self._save_to_file_skipped_streams(stream_channel)
    -867
    -868                    # Skip if the user chose to hide adult streams
    -869                    if self.hide_adult_content and loading_stream_type == self.live_type:
    -870                        if "is_adult" in stream_channel:
    -871                            if stream_channel["is_adult"] == "1":
    -872                                skip_stream = True
    -873                                skipped_adult_content = skipped_adult_content + 1
    -874                                self._save_to_file_skipped_streams(stream_channel)
    -875
    -876                    if not skip_stream:
    -877                        # Some channels have no group,
    -878                        # so let's add them to the catch all group
    -879                        if stream_channel["category_id"] == "":
    -880                            stream_channel["category_id"] = "9999"
    -881                        elif stream_channel["category_id"] != "1":
    -882                            pass
    -883
    -884                        # Find the first occurence of the group that the
    -885                        # Channel or Stream is pointing to
    -886                        the_group = next(
    -887                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    -888                            None
    -889                        )
    -890
    -891                        # Set group title
    -892                        if the_group is not None:
    -893                            group_title = the_group.name
    -894                        else:
    -895                            if loading_stream_type == self.live_type:
    -896                                group_title = self.live_catch_all_group.name
    -897                                the_group = self.live_catch_all_group
    -898                            elif loading_stream_type == self.vod_type:
    -899                                group_title = self.vod_catch_all_group.name
    -900                                the_group = self.vod_catch_all_group
    -901                            elif loading_stream_type == self.series_type:
    -902                                group_title = self.series_catch_all_group.name
    -903                                the_group = self.series_catch_all_group
    -904
    -905
    -906                        if loading_stream_type == self.series_type:
    -907                            # Load all Series
    -908                            new_series = Serie(self, stream_channel)
    -909                            # To get all the Episodes for every Season of each
    -910                            # Series is very time consuming, we will only
    -911                            # populate the Series once the user click on the
    -912                            # Series, the Seasons and Episodes will be loaded
    -913                            # using x.getSeriesInfoByID() function
    -914
    -915                        else:
    -916                            new_channel = Channel(
    -917                                self,
    -918                                group_title,
    -919                                stream_channel
    -920                            )
    -921
    -922                        if new_channel.group_id == "9999":
    -923                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    +827                for cat_obj in all_cat:
    +828                    if schemaValidator(cat_obj, SchemaType.GROUP):
    +829                        # Create Group (Category)
    +830                        new_group = Group(cat_obj, loading_stream_type)
    +831                        #  Add to xtream class
    +832                        self.groups.append(new_group)
    +833                    else:
    +834                        # Save what did not pass schema validation
    +835                        print(cat_obj)
    +836
    +837                # Sort Categories
    +838                self.groups.sort(key=lambda x: x.name)
    +839            else:
    +840                print(f" - Could not load {loading_stream_type} Groups")
    +841                break
    +842
    +843            # Get Streams
    +844
    +845            # Try loading local file
    +846            dt = 0
    +847            start = timer()
    +848            all_streams = self._load_from_file(f"all_stream_{loading_stream_type}.json")
    +849            # If file empty or does not exists, download it from remote
    +850            if all_streams is None:
    +851                # Load all Streams and save file locally
    +852                all_streams = self._load_streams_from_provider(loading_stream_type)
    +853                self._save_to_file(all_streams, f"all_stream_{loading_stream_type}.json")
    +854            dt = timer() - start
    +855
    +856            # If we got the STREAMS data, show the statistics and load Streams
    +857            if all_streams is not None:
    +858                print(f"{self.name}: Loaded {len(all_streams)} {loading_stream_type} Streams in {dt:.3f} seconds")
    +859                # Add Streams to dictionaries
    +860
    +861                skipped_adult_content = 0
    +862                skipped_no_name_content = 0
    +863
    +864                number_of_streams = len(all_streams)
    +865                current_stream_number = 0
    +866                # Calculate 1% of total number of streams
    +867                # This is used to slow down the progress bar
    +868                one_percent_number_of_streams = number_of_streams/100
    +869                start = timer()
    +870                for stream_channel in all_streams:
    +871                    skip_stream = False
    +872                    current_stream_number += 1
    +873
    +874                    # Show download progress every 1% of total number of streams
    +875                    if current_stream_number < one_percent_number_of_streams:
    +876                        progress(
    +877                            current_stream_number,
    +878                            number_of_streams,
    +879                            f"Processing {loading_stream_type} Streams"
    +880                            )
    +881                        one_percent_number_of_streams *= 2
    +882
    +883                    # Validate JSON scheme
    +884                    if self.validate_json:
    +885                        if loading_stream_type == self.series_type:
    +886                            if not schemaValidator(stream_channel, SchemaType.SERIES_INFO):
    +887                                print(stream_channel)
    +888                        elif loading_stream_type == self.live_type:
    +889                            if not schemaValidator(stream_channel, SchemaType.LIVE):
    +890                                print(stream_channel)
    +891                        else:
    +892                            # vod_type
    +893                            if not schemaValidator(stream_channel, SchemaType.VOD):
    +894                                print(stream_channel)
    +895
    +896                    # Skip if the name of the stream is empty
    +897                    if stream_channel["name"] == "":
    +898                        skip_stream = True
    +899                        skipped_no_name_content = skipped_no_name_content + 1
    +900                        self._save_to_file_skipped_streams(stream_channel)
    +901
    +902                    # Skip if the user chose to hide adult streams
    +903                    if self.hide_adult_content and loading_stream_type == self.live_type:
    +904                        if "is_adult" in stream_channel:
    +905                            if stream_channel["is_adult"] == "1":
    +906                                skip_stream = True
    +907                                skipped_adult_content = skipped_adult_content + 1
    +908                                self._save_to_file_skipped_streams(stream_channel)
    +909
    +910                    if not skip_stream:
    +911                        # Some channels have no group,
    +912                        # so let's add them to the catch all group
    +913                        if not stream_channel["category_id"]:
    +914                            stream_channel["category_id"] = "9999"
    +915                        elif stream_channel["category_id"] != "1":
    +916                            pass
    +917
    +918                        # Find the first occurrence of the group that the
    +919                        # Channel or Stream is pointing to
    +920                        the_group = next(
    +921                            (x for x in self.groups if x.group_id == int(stream_channel["category_id"])),
    +922                            None
    +923                        )
     924
    -925                        # Save the new channel to the local list of channels
    -926                        if loading_stream_type == self.live_type:
    -927                            self.channels.append(new_channel)
    -928                        elif loading_stream_type == self.vod_type:
    -929                            self.movies.append(new_channel)
    -930                            if new_channel.age_days_from_added < 31:
    -931                                self.movies_30days.append(new_channel)
    -932                            if new_channel.age_days_from_added < 7:
    -933                                self.movies_7days.append(new_channel)
    -934                        else:
    -935                            self.series.append(new_series)
    -936
    -937                        # Add stream to the specific Group
    -938                        if the_group is not None:
    -939                            if loading_stream_type != self.series_type:
    -940                                the_group.channels.append(new_channel)
    -941                            else:
    -942                                the_group.series.append(new_series)
    -943                        else:
    -944                            print(f" - Group not found `{stream_channel['name']}`")
    -945                print("\n")
    -946                # Print information of which streams have been skipped
    -947                if self.hide_adult_content:
    -948                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    -949                if skipped_no_name_content > 0:
    -950                    print(f" - Skipped {skipped_no_name_content} "
    -951                          "unprintable {loading_stream_type} streams")
    -952            else:
    -953                print(f" - Could not load {loading_stream_type} Streams")
    +925                        # Set group title
    +926                        if the_group is not None:
    +927                            group_title = the_group.name
    +928                        else:
    +929                            if loading_stream_type == self.live_type:
    +930                                group_title = self.live_catch_all_group.name
    +931                                the_group = self.live_catch_all_group
    +932                            elif loading_stream_type == self.vod_type:
    +933                                group_title = self.vod_catch_all_group.name
    +934                                the_group = self.vod_catch_all_group
    +935                            elif loading_stream_type == self.series_type:
    +936                                group_title = self.series_catch_all_group.name
    +937                                the_group = self.series_catch_all_group
    +938
    +939                        if loading_stream_type == self.series_type:
    +940                            # Load all Series
    +941                            new_series = Serie(self, stream_channel)
    +942                            # To get all the Episodes for every Season of each
    +943                            # Series is very time consuming, we will only
    +944                            # populate the Series once the user click on the
    +945                            # Series, the Seasons and Episodes will be loaded
    +946                            # using x.getSeriesInfoByID() function
    +947
    +948                        else:
    +949                            new_channel = Channel(
    +950                                self,
    +951                                group_title,
    +952                                stream_channel
    +953                            )
     954
    -955            self.state["loaded"] = True
    +955                        if new_channel.group_id == "9999":
    +956                            print(f" - xEverythingElse Channel -> {new_channel.name} - {new_channel.stream_type}")
    +957
    +958                        # Save the new channel to the local list of channels
    +959                        if loading_stream_type == self.live_type:
    +960                            self.channels.append(new_channel)
    +961                        elif loading_stream_type == self.vod_type:
    +962                            self.movies.append(new_channel)
    +963                            if new_channel.age_days_from_added < 31:
    +964                                self.movies_30days.append(new_channel)
    +965                            if new_channel.age_days_from_added < 7:
    +966                                self.movies_7days.append(new_channel)
    +967                        else:
    +968                            self.series.append(new_series)
    +969
    +970                        # Add stream to the specific Group
    +971                        if the_group is not None:
    +972                            if loading_stream_type != self.series_type:
    +973                                the_group.channels.append(new_channel)
    +974                            else:
    +975                                the_group.series.append(new_series)
    +976                        else:
    +977                            print(f" - Group not found `{stream_channel['name']}`")
    +978                print("\n")
    +979                # Print information of which streams have been skipped
    +980                if self.hide_adult_content:
    +981                    print(f" - Skipped {skipped_adult_content} adult {loading_stream_type} streams")
    +982                if skipped_no_name_content > 0:
    +983                    print(f" - Skipped {skipped_no_name_content} "
    +984                          "unprintable {loading_stream_type} streams")
    +985            else:
    +986                print(f" - Could not load {loading_stream_type} Streams")
    +987
    +988            self.state["loaded"] = True
    +989        return True
     
    @@ -4674,7 +4916,7 @@

    Returns: - bool: True if successfull, False if error

    + bool: True if successful, False if error

    @@ -4690,30 +4932,31 @@

    -
    972    def get_series_info_by_id(self, get_series: dict):
    -973        """Get Seasons and Episodes for a Series
    -974
    -975        Args:
    -976            get_series (dict): Series dictionary
    -977        """
    -978
    -979        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    -980
    -981        if series_seasons["seasons"] is None:
    -982            series_seasons["seasons"] = [{"name": "Season 1", "cover": series_seasons["info"]["cover"]}]
    -983
    -984        for series_info in series_seasons["seasons"]:
    -985            season_name = series_info["name"]
    -986            season_key = series_info['season_number']
    -987            season = Season(season_name)
    -988            get_series.seasons[season_name] = season
    -989            if "episodes" in series_seasons.keys():
    -990                for series_season in series_seasons["episodes"].keys():
    -991                    for episode_info in series_seasons["episodes"][str(series_season)]:
    -992                        new_episode_channel = Episode(
    -993                            self, series_info, "Testing", episode_info
    -994                        )
    -995                        season.episodes[episode_info["title"]] = new_episode_channel
    +            
    1007    def get_series_info_by_id(self, get_series: dict):
    +1008        """Get Seasons and Episodes for a Series
    +1009
    +1010        Args:
    +1011            get_series (dict): Series dictionary
    +1012        """
    +1013
    +1014        series_seasons = self._load_series_info_by_id_from_provider(get_series.series_id)
    +1015
    +1016        if series_seasons["seasons"] is None:
    +1017            series_seasons["seasons"] = [
    +1018                {"name": "Season 1", "cover": series_seasons["info"]["cover"]}
    +1019                ]
    +1020
    +1021        for series_info in series_seasons["seasons"]:
    +1022            season_name = series_info["name"]
    +1023            season = Season(season_name)
    +1024            get_series.seasons[season_name] = season
    +1025            if "episodes" in series_seasons.keys():
    +1026                for series_season in series_seasons["episodes"].keys():
    +1027                    for episode_info in series_seasons["episodes"][str(series_season)]:
    +1028                        new_episode_channel = Episode(
    +1029                            self, series_info, "Testing", episode_info
    +1030                        )
    +1031                        season.episodes[episode_info["title"]] = new_episode_channel
     
    @@ -4736,8 +4979,8 @@

    -
    1145    def vodInfoByID(self, vod_id):
    -1146        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
    +            
    1198    def vodInfoByID(self, vod_id):
    +1199        return self._get_request(self.get_VOD_info_URL_by_ID(vod_id))
     
    @@ -4755,8 +4998,8 @@

    -
    1150    def liveEpgByStream(self, stream_id):
    -1151        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
    +            
    1203    def liveEpgByStream(self, stream_id):
    +1204        return self._get_request(self.get_live_epg_URL_by_stream(stream_id))
     
    @@ -4774,8 +5017,8 @@

    -
    1153    def liveEpgByStreamAndLimit(self, stream_id, limit):
    -1154        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
    +            
    1206    def liveEpgByStreamAndLimit(self, stream_id, limit):
    +1207        return self._get_request(self.get_live_epg_URL_by_stream_and_limit(stream_id, limit))
     
    @@ -4793,8 +5036,8 @@

    -
    1158    def allLiveEpgByStream(self, stream_id):
    -1159        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
    +            
    1211    def allLiveEpgByStream(self, stream_id):
    +1212        return self._get_request(self.get_all_live_epg_URL_by_stream(stream_id))
     
    @@ -4812,8 +5055,8 @@

    -
    1162    def allEpg(self):
    -1163        return self._get_request(self.get_all_epg_URL())
    +            
    1215    def allEpg(self):
    +1216        return self._get_request(self.get_all_epg_URL())
     
    @@ -4831,8 +5074,8 @@

    -
    1166    def get_live_categories_URL(self) -> str:
    -1167        return f"{self.base_url}&action=get_live_categories"
    +            
    1219    def get_live_categories_URL(self) -> str:
    +1220        return f"{self.base_url}&action=get_live_categories"
     
    @@ -4850,8 +5093,8 @@

    -
    1169    def get_live_streams_URL(self) -> str:
    -1170        return f"{self.base_url}&action=get_live_streams"
    +            
    1222    def get_live_streams_URL(self) -> str:
    +1223        return f"{self.base_url}&action=get_live_streams"
     
    @@ -4869,8 +5112,8 @@

    -
    1172    def get_live_streams_URL_by_category(self, category_id) -> str:
    -1173        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
    +            
    1225    def get_live_streams_URL_by_category(self, category_id) -> str:
    +1226        return f"{self.base_url}&action=get_live_streams&category_id={category_id}"
     
    @@ -4888,8 +5131,8 @@

    -
    1175    def get_vod_cat_URL(self) -> str:
    -1176        return f"{self.base_url}&action=get_vod_categories"
    +            
    1228    def get_vod_cat_URL(self) -> str:
    +1229        return f"{self.base_url}&action=get_vod_categories"
     
    @@ -4907,8 +5150,8 @@

    -
    1178    def get_vod_streams_URL(self) -> str:
    -1179        return f"{self.base_url}&action=get_vod_streams"
    +            
    1231    def get_vod_streams_URL(self) -> str:
    +1232        return f"{self.base_url}&action=get_vod_streams"
     
    @@ -4926,8 +5169,8 @@

    -
    1181    def get_vod_streams_URL_by_category(self, category_id) -> str:
    -1182        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
    +            
    1234    def get_vod_streams_URL_by_category(self, category_id) -> str:
    +1235        return f"{self.base_url}&action=get_vod_streams&category_id={category_id}"
     
    @@ -4945,8 +5188,8 @@

    -
    1184    def get_series_cat_URL(self) -> str:
    -1185        return f"{self.base_url}&action=get_series_categories"
    +            
    1237    def get_series_cat_URL(self) -> str:
    +1238        return f"{self.base_url}&action=get_series_categories"
     
    @@ -4964,8 +5207,8 @@

    -
    1187    def get_series_URL(self) -> str:
    -1188        return f"{self.base_url}&action=get_series"
    +            
    1240    def get_series_URL(self) -> str:
    +1241        return f"{self.base_url}&action=get_series"
     
    @@ -4983,8 +5226,8 @@

    -
    1190    def get_series_URL_by_category(self, category_id) -> str:
    -1191        return f"{self.base_url}&action=get_series&category_id={category_id}"
    +            
    1243    def get_series_URL_by_category(self, category_id) -> str:
    +1244        return f"{self.base_url}&action=get_series&category_id={category_id}"
     
    @@ -5002,8 +5245,8 @@

    -
    1193    def get_series_info_URL_by_ID(self, series_id) -> str:
    -1194        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
    +            
    1246    def get_series_info_URL_by_ID(self, series_id) -> str:
    +1247        return f"{self.base_url}&action=get_series_info&series_id={series_id}"
     
    @@ -5021,8 +5264,8 @@

    -
    1196    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    -1197        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
    +            
    1249    def get_VOD_info_URL_by_ID(self, vod_id) -> str:
    +1250        return f"{self.base_url}&action=get_vod_info&vod_id={vod_id}"
     
    @@ -5040,8 +5283,8 @@

    -
    1199    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    -1200        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
    +            
    1252    def get_live_epg_URL_by_stream(self, stream_id) -> str:
    +1253        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}"
     
    @@ -5059,8 +5302,8 @@

    -
    1202    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    -1203        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
    +            
    1255    def get_live_epg_URL_by_stream_and_limit(self, stream_id, limit) -> str:
    +1256        return f"{self.base_url}&action=get_short_epg&stream_id={stream_id}&limit={limit}"
     
    @@ -5078,8 +5321,8 @@

    -
    1205    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    -1206        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
    +            
    1258    def get_all_live_epg_URL_by_stream(self, stream_id) -> str:
    +1259        return f"{self.base_url}&action=get_simple_data_table&stream_id={stream_id}"
     
    @@ -5097,8 +5340,8 @@

    -
    1208    def get_all_epg_URL(self) -> str:
    -1209        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
    +            
    1261    def get_all_epg_URL(self) -> str:
    +1262        return f"{self.server}/xmltv.php?username={self.username}&password={self.password}"
     
    diff --git a/docs/pyxtream/rest_api.html b/docs/pyxtream/rest_api.html index a181699..0a10e49 100644 --- a/docs/pyxtream/rest_api.html +++ b/docs/pyxtream/rest_api.html @@ -36,6 +36,9 @@

    API Documentation

  • EndpointAction
  • +
  • + response +
  • function_name
  • @@ -105,94 +108,120 @@

    -
     1# Import Flask to control IPTV via REST API
    - 2from threading import Thread
    - 3
    - 4from flask import Flask
    - 5from flask import Response as FlaskResponse
    - 6from flask import request as FlaskRequest
    - 7import logging
    - 8from os import path
    - 9
    -10
    -11class EndpointAction(object):
    -12
    -13    def __init__(self, action, function_name):
    -14        self.function_name = function_name
    -15        self.action = action
    -16
    -17    def __call__(self, **args):
    -18        content_types = {
    -19            'html': "text/html; charset=utf-8",
    -20            'json': "text/json; charset=utf-8"
    -21        }
    -22
    -23        handlers = {
    -24            "stream_search_generic": lambda: self._handle_search(args['term']),
    -25            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    -26            "download_stream": lambda: self.action(int(args['stream_id'])),
    -27            "download_stream_progress": lambda: self.action,
    -28            "get_last_7days": lambda: self.action(),
    -29            "home": lambda: self.action
    -30        }
    -31
    -32        answer = handlers[self.function_name]()
    -33        content_type = content_types['json'] if self.function_name != "home" and self.function_name != "download_stream_progress" else content_types['html']
    -34        
    -35        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    -36
    -37        return self.response
    -38
    -39    def _handle_search(self, term, stream_type=None):
    -40        regex_term = r"^.*{}.*$".format(term)
    -41        if stream_type:
    -42            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    -43            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    -44        return self.action(regex_term, return_type='JSON')
    -45
    -46class FlaskWrap(Thread):
    -47
    -48    home_template = """
    -49<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    -50    """
    -51
    -52    host: str = ""
    -53    port: int = 0
    -54
    -55    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    -56
    -57        log = logging.getLogger('werkzeug')
    -58        log.setLevel(logging.ERROR)
    -59
    -60        self.host = host
    -61        self.port = port
    -62        self.debug = debug
    -63
    -64        self.app = Flask(name)
    -65        self.xt = xtream
    -66        Thread.__init__(self)
    -67
    -68        # Configure Thread
    -69        self.name ="pyxtream REST API"
    -70        self.daemon = True
    -71
    -72        # Load HTML Home Template if any
    -73        if html_template_folder is not None:
    -74            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -75            if path.isfile(self.home_template_file_name):
    -76                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -77                    self.home_template = home_html.read()
    -78
    -79        # Add all endpoints
    -80        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -81        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -82        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    -83
    -84    def run(self):
    -85        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    -86
    -87    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -88        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +                        
      1# Import Flask to control IPTV via REST API
    +  2from threading import Thread
    +  3import logging
    +  4from os import path
    +  5from flask import Flask
    +  6from flask import Response as FlaskResponse
    +  7
    +  8
    +  9class EndpointAction(object):
    + 10
    + 11    response: FlaskResponse
    + 12
    + 13    def __init__(self, action, function_name):
    + 14        self.function_name = function_name
    + 15        self.action = action
    + 16
    + 17    def __call__(self, **args):
    + 18        content_types = {
    + 19            'html': "text/html; charset=utf-8",
    + 20            'json': "text/json; charset=utf-8"
    + 21        }
    + 22
    + 23        handlers = {
    + 24            # Add handlers here
    + 25            "stream_search_generic": lambda: self._handle_search(args['term']),
    + 26            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    + 27            "download_stream": lambda: self.action(int(args['stream_id'])),
    + 28            "get_download_progress": lambda: self.action(int(args['stream_id'])),
    + 29            "get_last_7days": lambda: self.action(),
    + 30            "home": lambda: self.action,
    + 31            "get_series": lambda: self.action(int(args['series_id']), "JSON")
    + 32        }
    + 33
    + 34        answer = handlers[self.function_name]()
    + 35        content_type = content_types['json'] if self.function_name not in ('home') else content_types['html']
    + 36
    + 37        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    + 38        return self.response
    + 39
    + 40    def _handle_search(self, term, stream_type=None):
    + 41        regex_term = r"^.*{}.*$".format(term)
    + 42        if stream_type:
    + 43            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    + 44            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    + 45        return self.action(regex_term, return_type='JSON')
    + 46
    + 47
    + 48class FlaskWrap(Thread):
    + 49
    + 50    home_template = """
    + 51<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    + 52    """
    + 53
    + 54    host: str = ""
    + 55    port: int = 0
    + 56
    + 57    def __init__(self, name, xtream: object, html_template_folder: str = None,
    + 58                 host: str = "0.0.0.0", port: int = 5000, debug: bool = True
    + 59                 ):
    + 60
    + 61        log = logging.getLogger('werkzeug')
    + 62        log.setLevel(logging.ERROR)
    + 63
    + 64        self.host = host
    + 65        self.port = port
    + 66        self.debug = debug
    + 67
    + 68        self.app = Flask(name)
    + 69        self.xt = xtream
    + 70        Thread.__init__(self)
    + 71
    + 72        # Configure Thread
    + 73        self.name = "pyxtream REST API"
    + 74        self.daemon = True
    + 75
    + 76        # Load HTML Home Template if any
    + 77        if html_template_folder is not None:
    + 78            self.home_template_file_name = path.join(html_template_folder, "index.html")
    + 79            if path.isfile(self.home_template_file_name):
    + 80                with open(self.home_template_file_name, 'r', encoding="utf-8") as home_html:
    + 81                    self.home_template = home_html.read()
    + 82
    + 83        # Add all endpoints
    + 84        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template, "home"])
    + 85        self.add_endpoint(endpoint='/stream_search/<term>',
    + 86                          endpoint_name='stream_search_generic',
    + 87                          handler=[self.xt.search_stream, 'stream_search_generic']
    + 88                          )
    + 89        self.add_endpoint(endpoint='/stream_search/<term>/<type>',
    + 90                          endpoint_name='stream_search_with_type',
    + 91                          handler=[self.xt.search_stream, 'stream_search_with_type']
    + 92                          )
    + 93        self.add_endpoint(endpoint='/download_stream/<stream_id>/',
    + 94                          endpoint_name='download_stream',
    + 95                          handler=[self.xt.download_video, "download_stream"]
    + 96                          )
    + 97        self.add_endpoint(endpoint='/get_download_progress/<stream_id>/',
    + 98                          endpoint_name='get_download_progress',
    + 99                          handler=[self.xt.get_download_progress, "get_download_progress"]
    +100                          )
    +101        self.add_endpoint(endpoint='/get_last_7days',
    +102                          endpoint_name='get_last_7days',
    +103                          handler=[self.xt.get_last_7days, "get_last_7days"]
    +104                          )
    +105        self.add_endpoint(endpoint='/get_series/<series_id>',
    +106                          endpoint_name='get_series',
    +107                          handler=[self.xt._load_series_info_by_id_from_provider, "get_series"]
    +108                          )
    +109
    +110    def run(self):
    +111        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +112
    +113    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +114        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    @@ -208,40 +237,43 @@

    -
    12class EndpointAction(object):
    -13
    -14    def __init__(self, action, function_name):
    -15        self.function_name = function_name
    -16        self.action = action
    -17
    -18    def __call__(self, **args):
    -19        content_types = {
    -20            'html': "text/html; charset=utf-8",
    -21            'json': "text/json; charset=utf-8"
    -22        }
    -23
    -24        handlers = {
    -25            "stream_search_generic": lambda: self._handle_search(args['term']),
    -26            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    -27            "download_stream": lambda: self.action(int(args['stream_id'])),
    -28            "download_stream_progress": lambda: self.action,
    -29            "get_last_7days": lambda: self.action(),
    -30            "home": lambda: self.action
    -31        }
    -32
    -33        answer = handlers[self.function_name]()
    -34        content_type = content_types['json'] if self.function_name != "home" and self.function_name != "download_stream_progress" else content_types['html']
    -35        
    -36        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    -37
    -38        return self.response
    -39
    -40    def _handle_search(self, term, stream_type=None):
    -41        regex_term = r"^.*{}.*$".format(term)
    -42        if stream_type:
    -43            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    -44            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    -45        return self.action(regex_term, return_type='JSON')
    +            
    11class EndpointAction(object):
    +12
    +13    response: FlaskResponse
    +14
    +15    def __init__(self, action, function_name):
    +16        self.function_name = function_name
    +17        self.action = action
    +18
    +19    def __call__(self, **args):
    +20        content_types = {
    +21            'html': "text/html; charset=utf-8",
    +22            'json': "text/json; charset=utf-8"
    +23        }
    +24
    +25        handlers = {
    +26            # Add handlers here
    +27            "stream_search_generic": lambda: self._handle_search(args['term']),
    +28            "stream_search_with_type": lambda: self._handle_search(args['term'], args.get('type')),
    +29            "download_stream": lambda: self.action(int(args['stream_id'])),
    +30            "get_download_progress": lambda: self.action(int(args['stream_id'])),
    +31            "get_last_7days": lambda: self.action(),
    +32            "home": lambda: self.action,
    +33            "get_series": lambda: self.action(int(args['series_id']), "JSON")
    +34        }
    +35
    +36        answer = handlers[self.function_name]()
    +37        content_type = content_types['json'] if self.function_name not in ('home') else content_types['html']
    +38
    +39        self.response = FlaskResponse(answer, status=200, headers={"Content-Type": content_type})
    +40        return self.response
    +41
    +42    def _handle_search(self, term, stream_type=None):
    +43        regex_term = r"^.*{}.*$".format(term)
    +44        if stream_type:
    +45            stream_type = [stream_type] if stream_type else ("series", "movies", "channels")
    +46            return self.action(regex_term, return_type='JSON', stream_type=stream_type)
    +47        return self.action(regex_term, return_type='JSON')
     
    @@ -257,14 +289,25 @@

    -
    14    def __init__(self, action, function_name):
    -15        self.function_name = function_name
    -16        self.action = action
    +            
    15    def __init__(self, action, function_name):
    +16        self.function_name = function_name
    +17        self.action = action
     
    +
    +
    +
    + response: flask.wrappers.Response + + +
    + + + +
    @@ -300,49 +343,73 @@

    -
    47class FlaskWrap(Thread):
    -48
    -49    home_template = """
    -50<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    -51    """
    -52
    -53    host: str = ""
    -54    port: int = 0
    -55
    -56    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    -57
    -58        log = logging.getLogger('werkzeug')
    -59        log.setLevel(logging.ERROR)
    -60
    -61        self.host = host
    -62        self.port = port
    -63        self.debug = debug
    -64
    -65        self.app = Flask(name)
    -66        self.xt = xtream
    -67        Thread.__init__(self)
    -68
    -69        # Configure Thread
    -70        self.name ="pyxtream REST API"
    -71        self.daemon = True
    -72
    -73        # Load HTML Home Template if any
    -74        if html_template_folder is not None:
    -75            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -76            if path.isfile(self.home_template_file_name):
    -77                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -78                    self.home_template = home_html.read()
    -79
    -80        # Add all endpoints
    -81        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -82        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -83        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    -84
    -85    def run(self):
    -86        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    -87
    -88    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -89        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +            
     50class FlaskWrap(Thread):
    + 51
    + 52    home_template = """
    + 53<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>
    + 54    """
    + 55
    + 56    host: str = ""
    + 57    port: int = 0
    + 58
    + 59    def __init__(self, name, xtream: object, html_template_folder: str = None,
    + 60                 host: str = "0.0.0.0", port: int = 5000, debug: bool = True
    + 61                 ):
    + 62
    + 63        log = logging.getLogger('werkzeug')
    + 64        log.setLevel(logging.ERROR)
    + 65
    + 66        self.host = host
    + 67        self.port = port
    + 68        self.debug = debug
    + 69
    + 70        self.app = Flask(name)
    + 71        self.xt = xtream
    + 72        Thread.__init__(self)
    + 73
    + 74        # Configure Thread
    + 75        self.name = "pyxtream REST API"
    + 76        self.daemon = True
    + 77
    + 78        # Load HTML Home Template if any
    + 79        if html_template_folder is not None:
    + 80            self.home_template_file_name = path.join(html_template_folder, "index.html")
    + 81            if path.isfile(self.home_template_file_name):
    + 82                with open(self.home_template_file_name, 'r', encoding="utf-8") as home_html:
    + 83                    self.home_template = home_html.read()
    + 84
    + 85        # Add all endpoints
    + 86        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template, "home"])
    + 87        self.add_endpoint(endpoint='/stream_search/<term>',
    + 88                          endpoint_name='stream_search_generic',
    + 89                          handler=[self.xt.search_stream, 'stream_search_generic']
    + 90                          )
    + 91        self.add_endpoint(endpoint='/stream_search/<term>/<type>',
    + 92                          endpoint_name='stream_search_with_type',
    + 93                          handler=[self.xt.search_stream, 'stream_search_with_type']
    + 94                          )
    + 95        self.add_endpoint(endpoint='/download_stream/<stream_id>/',
    + 96                          endpoint_name='download_stream',
    + 97                          handler=[self.xt.download_video, "download_stream"]
    + 98                          )
    + 99        self.add_endpoint(endpoint='/get_download_progress/<stream_id>/',
    +100                          endpoint_name='get_download_progress',
    +101                          handler=[self.xt.get_download_progress, "get_download_progress"]
    +102                          )
    +103        self.add_endpoint(endpoint='/get_last_7days',
    +104                          endpoint_name='get_last_7days',
    +105                          handler=[self.xt.get_last_7days, "get_last_7days"]
    +106                          )
    +107        self.add_endpoint(endpoint='/get_series/<series_id>',
    +108                          endpoint_name='get_series',
    +109                          handler=[self.xt._load_series_info_by_id_from_provider, "get_series"]
    +110                          )
    +111
    +112    def run(self):
    +113        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +114
    +115    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +116        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    @@ -364,34 +431,58 @@

    -
    56    def __init__(self, name, xtream: object, html_template_folder: str = None, host: str = "0.0.0.0", port: int = 5000, debug: bool = True):
    -57
    -58        log = logging.getLogger('werkzeug')
    -59        log.setLevel(logging.ERROR)
    -60
    -61        self.host = host
    -62        self.port = port
    -63        self.debug = debug
    -64
    -65        self.app = Flask(name)
    -66        self.xt = xtream
    -67        Thread.__init__(self)
    -68
    -69        # Configure Thread
    -70        self.name ="pyxtream REST API"
    -71        self.daemon = True
    -72
    -73        # Load HTML Home Template if any
    -74        if html_template_folder is not None:
    -75            self.home_template_file_name = path.join(html_template_folder,"index.html")
    -76            if path.isfile(self.home_template_file_name):
    -77                with open(self.home_template_file_name,'r', encoding="utf-8") as home_html:
    -78                    self.home_template = home_html.read()
    -79
    -80        # Add all endpoints
    -81        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template,""])
    -82        self.add_endpoint(endpoint='/stream_search/<term>', endpoint_name='stream_search', handler=[self.xt.search_stream,"stream_search"])
    -83        self.add_endpoint(endpoint='/download_stream/<stream_id>/', endpoint_name='download_stream', handler=[self.xt.download_video,"download_stream"])
    +            
     59    def __init__(self, name, xtream: object, html_template_folder: str = None,
    + 60                 host: str = "0.0.0.0", port: int = 5000, debug: bool = True
    + 61                 ):
    + 62
    + 63        log = logging.getLogger('werkzeug')
    + 64        log.setLevel(logging.ERROR)
    + 65
    + 66        self.host = host
    + 67        self.port = port
    + 68        self.debug = debug
    + 69
    + 70        self.app = Flask(name)
    + 71        self.xt = xtream
    + 72        Thread.__init__(self)
    + 73
    + 74        # Configure Thread
    + 75        self.name = "pyxtream REST API"
    + 76        self.daemon = True
    + 77
    + 78        # Load HTML Home Template if any
    + 79        if html_template_folder is not None:
    + 80            self.home_template_file_name = path.join(html_template_folder, "index.html")
    + 81            if path.isfile(self.home_template_file_name):
    + 82                with open(self.home_template_file_name, 'r', encoding="utf-8") as home_html:
    + 83                    self.home_template = home_html.read()
    + 84
    + 85        # Add all endpoints
    + 86        self.add_endpoint(endpoint='/', endpoint_name='home', handler=[self.home_template, "home"])
    + 87        self.add_endpoint(endpoint='/stream_search/<term>',
    + 88                          endpoint_name='stream_search_generic',
    + 89                          handler=[self.xt.search_stream, 'stream_search_generic']
    + 90                          )
    + 91        self.add_endpoint(endpoint='/stream_search/<term>/<type>',
    + 92                          endpoint_name='stream_search_with_type',
    + 93                          handler=[self.xt.search_stream, 'stream_search_with_type']
    + 94                          )
    + 95        self.add_endpoint(endpoint='/download_stream/<stream_id>/',
    + 96                          endpoint_name='download_stream',
    + 97                          handler=[self.xt.download_video, "download_stream"]
    + 98                          )
    + 99        self.add_endpoint(endpoint='/get_download_progress/<stream_id>/',
    +100                          endpoint_name='get_download_progress',
    +101                          handler=[self.xt.get_download_progress, "get_download_progress"]
    +102                          )
    +103        self.add_endpoint(endpoint='/get_last_7days',
    +104                          endpoint_name='get_last_7days',
    +105                          handler=[self.xt.get_last_7days, "get_last_7days"]
    +106                          )
    +107        self.add_endpoint(endpoint='/get_series/<series_id>',
    +108                          endpoint_name='get_series',
    +109                          handler=[self.xt._load_series_info_by_id_from_provider, "get_series"]
    +110                          )
     
    @@ -566,8 +657,8 @@

    -
    85    def run(self):
    -86        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
    +            
    112    def run(self):
    +113        self.app.run(debug=self.debug, use_reloader=False, host=self.host, port=self.port)
     
    @@ -592,8 +683,8 @@

    -
    88    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    -89        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
    +            
    115    def add_endpoint(self, endpoint=None, endpoint_name=None, handler=None):
    +116        self.app.add_url_rule(endpoint, endpoint_name, EndpointAction(*handler))
     
    diff --git a/docs/pyxtream/schemaValidator.html b/docs/pyxtream/schemaValidator.html index 44a7ba5..b238558 100644 --- a/docs/pyxtream/schemaValidator.html +++ b/docs/pyxtream/schemaValidator.html @@ -110,291 +110,292 @@

    12 CHANNEL = 5 13 GROUP = 6 14 - 15series_schema = { - 16 "$schema": "https://json-schema.org/draft/2020-12/schema", - 17 "$id": "https://example.com/product.schema.json", - 18 "title": "Series", - 19 "description": "xtream API Series Schema", - 20 "type": "object", - 21 "properties": { - 22 "seasons": { - 23 "type": "array", - 24 "items": { - 25 "properties": { - 26 "air_date": { - 27 "type": "string", - 28 "format": "date" - 29 }, - 30 "episode_count": { "type": "integer" }, - 31 "id": { "type": "integer" }, - 32 "name": { "type": "string" }, - 33 "overview": { "type": "string" }, - 34 "season_number": { "type": "integer" }, - 35 "cover": { - 36 "type": "string", - 37 "format": "uri", - 38 "qt-uri-protocols": [ - 39 "http", - 40 "https" - 41 ] - 42 }, - 43 "cover_big": { - 44 "type": "string", - 45 "format": "uri", - 46 "qt-uri-protocols": [ - 47 "http", - 48 "https" - 49 ] - 50 }, - 51 }, - 52 "required": [ - 53 "id" - 54 ], - 55 "title": "Season" - 56 } - 57 }, - 58 "info": { - 59 "properties": { - 60 "name": { "type": "string" }, - 61 "cover": { - 62 "type": "string", - 63 "format": "uri", - 64 "qt-uri-protocols": [ - 65 "http", - 66 "https" - 67 ] - 68 }, - 69 "plot": { "type": "string" }, - 70 "cast": { "type": "string" }, - 71 "director": { "type": "string" }, - 72 "genre": { "type": "string" }, - 73 "releaseDate": { "type": "string", "format": "date" }, - 74 "last_modified": { "type": "string", "format": "integer" }, - 75 "rating": { "type": "string", "format": "integer" }, - 76 "rating_5based": { "type": "number" }, - 77 "backdrop_path": { - 78 "type": "array", - 79 "items": { - 80 "type": "string", - 81 "format": "uri", - 82 "qt-uri-protocols": [ - 83 "http", - 84 "https" - 85 ] - 86 } - 87 }, - 88 "youtube_trailed": { "type": "string" }, - 89 "episode_run_time": { "type": "string", "format": "integer" }, - 90 "category_id": { "type": "string", "format": "integer" } - 91 }, - 92 "required": [ - 93 "name" - 94 ], - 95 "title": "Info" - 96 }, - 97 "episodes": { - 98 "patternProperties": { - 99 r"^\d+$": { -100 "type": "array", -101 "items": { -102 "properties": { -103 "id": { "type": "string", "format": "integer" }, -104 "episode_num": {"type": "integer" }, -105 "title": { "type": "string" }, -106 "container_extension": { "type": "string" }, -107 "info": { -108 "type": "object", -109 "items": { -110 "plot": { "type": "string" } -111 } -112 }, -113 "customer_sid": { "type": "string" }, -114 "added": { "type": "string", "format": "integer" }, -115 "season": { "type": "integer" }, -116 "direct_source": { "type": "string" } -117 } -118 } -119 }, -120 } -121 } -122 }, -123 "required": [ -124 "info", -125 "seasons", -126 "episodes" -127 ] -128} -129series_info_schema = { -130 "$schema": "https://json-schema.org/draft/2020-12/schema", -131 "$id": "https://example.com/product.schema.json", -132 "title": "Series", -133 "description": "xtream API Series Info Schema", -134 "type": "object", -135 "properties": { -136 "name": { "type": "string" }, -137 "cover": { -138 "type": "string", -139 "format": "uri", -140 "qt-uri-protocols": [ -141 "http", -142 "https" -143 ] -144 }, -145 "plot": { "type": "string" }, -146 "cast": { "type": "string" }, -147 "director": { "type": "string" }, -148 "genre": { "type": "string" }, -149 "releaseDate": { "type": "string", "format": "date" }, -150 "last_modified": { "type": "string", "format": "integer" }, -151 "rating": { "type": "string", "format": "integer" }, -152 "rating_5based": { "type": "number" }, -153 "backdrop_path": { -154 "anyOf": [ -155 { -156 "type": "array", -157 "items": { -158 "type": "string", -159 "format": "uri", -160 "qt-uri-protocols": [ -161 "http", -162 "https" -163 ] -164 } -165 }, -166 {"type": "string"} -167 ] -168 }, -169 "youtube_trailed": { "type": "string" }, -170 "episode_run_time": { "type": "string", "format": "integer" }, -171 "category_id": { "type": "string", "format": "integer" } -172 }, -173 "required": [ -174 "name", -175 "category_id" -176 ] -177} -178live_schema = { -179 "$schema": "https://json-schema.org/draft/2020-12/schema", -180 "$id": "https://example.com/product.schema.json", -181 "title": "Live", -182 "description": "xtream API Live Schema", -183 "type": "object", -184 "properties": { -185 "num": { "type": "integer" }, -186 "name": { "type": "string" }, -187 "stream_type": { "type": "string" }, -188 "stream_id": { "type": "integer" }, -189 "stream_icon": { -190 "anyOf": [ -191 { -192 "type": "string", -193 "format": "uri", -194 "qt-uri-protocols": [ -195 "http", -196 "https" -197 ] -198 }, -199 { "type": "null" } -200 ] -201 }, -202 "epg_channel_id": { -203 "anyOf": [ -204 { "type": "null" }, -205 { "type": "string" } -206 ] -207 }, -208 "added": { "type": "string", "format": "integer" }, -209 "is_adult": { "type": "string", "format":"number" }, -210 "category_id": { "type": "string" }, -211 "custom_sid": { "type": "string" }, -212 "tv_archive": { "type": "number" }, -213 "direct_source": { "type": "string" }, -214 "tv_archive_duration":{ -215 "anyOf": [ -216 { "type": "number" }, -217 { "type": "string", "format": "integer" } -218 ] -219 } -220 } -221} -222vod_schema = { -223 "$schema": "https://json-schema.org/draft/2020-12/schema", -224 "$id": "https://example.com/product.schema.json", -225 "title": "VOD", -226 "description": "xtream API VOD Schema", -227 "type": "object", -228 "properties": { -229 "num": { "type": "integer" }, -230 "name": { "type": "string" }, -231 "stream_type": { "type": "string" }, -232 "stream_id": { "type": "integer" }, -233 "stream_icon": { -234 "anyOf": [ -235 { -236 "type": "string", -237 "format": "uri", -238 "qt-uri-protocols": [ -239 "http", -240 "https" -241 ] -242 }, -243 { "type": "null" } -244 ] -245 }, -246 "rating": { -247 "anyOf": [ -248 { "type": "null" }, -249 { "type": "string", "format": "integer" }, -250 { "type": "number" } -251 ] -252 }, -253 "rating_5based": { "type": "number" }, -254 "added": { "type": "string", "format": "integer" }, -255 "is_adult": { "type": "string", "format":"number" }, -256 "category_id": { "type": "string" }, -257 "container_extension": { "type": "string" }, -258 "custom_sid": { "type": "string" }, -259 "direct_source": { "type": "string" } -260 } -261} -262channel_schema = {} -263group_schema = { -264 "$schema": "https://json-schema.org/draft/2020-12/schema", -265 "$id": "https://example.com/product.schema.json", -266 "title": "Group", -267 "description": "xtream API Group Schema", -268 "type": "object", -269 "properties": { -270 "category_id": { "type": "string" }, -271 "category_name": { "type": "string" }, -272 "parent_id": { "type": "integer" } -273 } -274} -275 -276def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool: + 15 + 16series_schema = { + 17 "$schema": "https://json-schema.org/draft/2020-12/schema", + 18 "$id": "https://example.com/product.schema.json", + 19 "title": "Series", + 20 "description": "xtream API Series Schema", + 21 "type": "object", + 22 "properties": { + 23 "seasons": { + 24 "type": "array", + 25 "items": { + 26 "properties": { + 27 "air_date": { + 28 "type": "string", + 29 "format": "date" + 30 }, + 31 "episode_count": {"type": "integer"}, + 32 "id": {"type": "integer"}, + 33 "name": {"type": "string"}, + 34 "overview": {"type": "string"}, + 35 "season_number": {"type": "integer"}, + 36 "cover": { + 37 "type": "string", + 38 "format": "uri", + 39 "qt-uri-protocols": [ + 40 "http", + 41 "https" + 42 ] + 43 }, + 44 "cover_big": { + 45 "type": "string", + 46 "format": "uri", + 47 "qt-uri-protocols": [ + 48 "http", + 49 "https" + 50 ] + 51 }, + 52 }, + 53 "required": [ + 54 "id" + 55 ], + 56 "title": "Season" + 57 } + 58 }, + 59 "info": { + 60 "properties": { + 61 "name": {"type": "string"}, + 62 "cover": { + 63 "type": "string", + 64 "format": "uri", + 65 "qt-uri-protocols": [ + 66 "http", + 67 "https" + 68 ] + 69 }, + 70 "plot": {"type": "string"}, + 71 "cast": {"type": "string"}, + 72 "director": {"type": "string"}, + 73 "genre": {"type": "string"}, + 74 "releaseDate": {"type": "string", "format": "date"}, + 75 "last_modified": {"type": "string", "format": "integer"}, + 76 "rating": {"type": "string", "format": "integer"}, + 77 "rating_5based": {"type": "number"}, + 78 "backdrop_path": { + 79 "type": "array", + 80 "items": { + 81 "type": "string", + 82 "format": "uri", + 83 "qt-uri-protocols": [ + 84 "http", + 85 "https" + 86 ] + 87 } + 88 }, + 89 "youtube_trailed": {"type": "string"}, + 90 "episode_run_time": {"type": "string", "format": "integer"}, + 91 "category_id": {"type": "string", "format": "integer"} + 92 }, + 93 "required": [ + 94 "name" + 95 ], + 96 "title": "Info" + 97 }, + 98 "episodes": { + 99 "patternProperties": { +100 r"^\d+$": { +101 "type": "array", +102 "items": { +103 "properties": { +104 "id": {"type": "string", "format": "integer"}, +105 "episode_num": {"type": "integer"}, +106 "title": {"type": "string"}, +107 "container_extension": {"type": "string"}, +108 "info": { +109 "type": "object", +110 "items": { +111 "plot": {"type": "string"} +112 } +113 }, +114 "customer_sid": {"type": "string"}, +115 "added": {"type": "string", "format": "integer"}, +116 "season": {"type": "integer"}, +117 "direct_source": {"type": "string"} +118 } +119 } +120 }, +121 } +122 } +123 }, +124 "required": [ +125 "info", +126 "seasons", +127 "episodes" +128 ] +129} +130series_info_schema = { +131 "$schema": "https://json-schema.org/draft/2020-12/schema", +132 "$id": "https://example.com/product.schema.json", +133 "title": "Series", +134 "description": "xtream API Series Info Schema", +135 "type": "object", +136 "properties": { +137 "name": {"type": "string"}, +138 "cover": { +139 "type": "string", +140 "format": "uri", +141 "qt-uri-protocols": [ +142 "http", +143 "https" +144 ] +145 }, +146 "plot": {"type": "string"}, +147 "cast": {"type": "string"}, +148 "director": {"type": "string"}, +149 "genre": {"type": "string"}, +150 "releaseDate": {"type": "string", "format": "date"}, +151 "last_modified": {"type": "string", "format": "integer"}, +152 "rating": {"type": "string", "format": "integer"}, +153 "rating_5based": {"type": "number"}, +154 "backdrop_path": { +155 "anyOf": [ +156 { +157 "type": "array", +158 "items": { +159 "type": "string", +160 "format": "uri", +161 "qt-uri-protocols": [ +162 "http", +163 "https" +164 ] +165 } +166 }, +167 {"type": "string"} +168 ] +169 }, +170 "youtube_trailed": {"type": "string"}, +171 "episode_run_time": {"type": "string", "format": "integer"}, +172 "category_id": {"type": "string", "format": "integer"} +173 }, +174 "required": [ +175 "name", +176 "category_id" +177 ] +178} +179live_schema = { +180 "$schema": "https://json-schema.org/draft/2020-12/schema", +181 "$id": "https://example.com/product.schema.json", +182 "title": "Live", +183 "description": "xtream API Live Schema", +184 "type": "object", +185 "properties": { +186 "num": {"type": "integer"}, +187 "name": {"type": "string"}, +188 "stream_type": {"type": "string"}, +189 "stream_id": {"type": "integer"}, +190 "stream_icon": { +191 "anyOf": [ +192 { +193 "type": "string", +194 "format": "uri", +195 "qt-uri-protocols": [ +196 "http", +197 "https" +198 ] +199 }, +200 {"type": "null"} +201 ] +202 }, +203 "epg_channel_id": { +204 "anyOf": [ +205 {"type": "null"}, +206 {"type": "string"} +207 ] +208 }, +209 "added": {"type": "string", "format": "integer"}, +210 "is_adult": {"type": "string", "format": "number"}, +211 "category_id": {"type": "string"}, +212 "custom_sid": {"type": "string"}, +213 "tv_archive": {"type": "number"}, +214 "direct_source": {"type": "string"}, +215 "tv_archive_duration": { +216 "anyOf": [ +217 {"type": "number"}, +218 {"type": "string", "format": "integer"} +219 ] +220 } +221 } +222} +223vod_schema = { +224 "$schema": "https://json-schema.org/draft/2020-12/schema", +225 "$id": "https://example.com/product.schema.json", +226 "title": "VOD", +227 "description": "xtream API VOD Schema", +228 "type": "object", +229 "properties": { +230 "num": {"type": "integer"}, +231 "name": {"type": "string"}, +232 "stream_type": {"type": "string"}, +233 "stream_id": {"type": "integer"}, +234 "stream_icon": { +235 "anyOf": [ +236 { +237 "type": "string", +238 "format": "uri", +239 "qt-uri-protocols": [ +240 "http", +241 "https" +242 ] +243 }, +244 {"type": "null"} +245 ] +246 }, +247 "rating": { +248 "anyOf": [ +249 {"type": "null"}, +250 {"type": "string", "format": "integer"}, +251 {"type": "number"} +252 ] +253 }, +254 "rating_5based": {"type": "number"}, +255 "added": {"type": "string", "format": "integer"}, +256 "is_adult": {"type": "string", "format": "number"}, +257 "category_id": {"type": "string"}, +258 "container_extension": {"type": "string"}, +259 "custom_sid": {"type": "string"}, +260 "direct_source": {"type": "string"} +261 } +262} +263channel_schema = {} +264group_schema = { +265 "$schema": "https://json-schema.org/draft/2020-12/schema", +266 "$id": "https://example.com/product.schema.json", +267 "title": "Group", +268 "description": "xtream API Group Schema", +269 "type": "object", +270 "properties": { +271 "category_id": {"type": "string"}, +272 "category_name": {"type": "string"}, +273 "parent_id": {"type": "integer"} +274 } +275} +276 277 -278 if (schemaType == SchemaType.SERIES): -279 json_schema = series_schema -280 elif (schemaType == SchemaType.SERIES_INFO): -281 json_schema = series_info_schema -282 elif (schemaType == SchemaType.LIVE): -283 json_schema = live_schema -284 elif (schemaType == SchemaType.VOD): -285 json_schema = vod_schema -286 elif (schemaType == SchemaType.CHANNEL): -287 json_schema = channel_schema -288 elif (schemaType == SchemaType.GROUP): -289 json_schema = group_schema -290 else: -291 json_schema = "{}" -292 -293 -294 try: -295 validate(instance=jsonData, schema=json_schema) -296 except exceptions.ValidationError as err: -297 print(err) -298 return False -299 return True +278def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool: +279 +280 if (schemaType == SchemaType.SERIES): +281 json_schema = series_schema +282 elif (schemaType == SchemaType.SERIES_INFO): +283 json_schema = series_info_schema +284 elif (schemaType == SchemaType.LIVE): +285 json_schema = live_schema +286 elif (schemaType == SchemaType.VOD): +287 json_schema = vod_schema +288 elif (schemaType == SchemaType.CHANNEL): +289 json_schema = channel_schema +290 elif (schemaType == SchemaType.GROUP): +291 json_schema = group_schema +292 else: +293 json_schema = "{}" +294 +295 try: +296 validate(instance=jsonData, schema=json_schema) +297 except exceptions.ValidationError as err: +298 print(err) +299 return False +300 return True

    @@ -583,30 +584,29 @@

    -
    278def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
    -279
    -280    if (schemaType == SchemaType.SERIES):
    -281        json_schema = series_schema
    -282    elif (schemaType == SchemaType.SERIES_INFO):
    -283        json_schema = series_info_schema
    -284    elif (schemaType == SchemaType.LIVE):
    -285        json_schema = live_schema
    -286    elif (schemaType == SchemaType.VOD):
    -287        json_schema = vod_schema
    -288    elif (schemaType == SchemaType.CHANNEL):
    -289        json_schema = channel_schema
    -290    elif (schemaType == SchemaType.GROUP):
    -291        json_schema = group_schema
    -292    else:
    -293        json_schema = "{}"
    -294
    -295
    -296    try:
    -297        validate(instance=jsonData, schema=json_schema)
    -298    except exceptions.ValidationError as err:
    -299        print(err)
    -300        return False
    -301    return True
    +            
    280def schemaValidator(jsonData: str, schemaType: SchemaType) -> bool:
    +281
    +282    if (schemaType == SchemaType.SERIES):
    +283        json_schema = series_schema
    +284    elif (schemaType == SchemaType.SERIES_INFO):
    +285        json_schema = series_info_schema
    +286    elif (schemaType == SchemaType.LIVE):
    +287        json_schema = live_schema
    +288    elif (schemaType == SchemaType.VOD):
    +289        json_schema = vod_schema
    +290    elif (schemaType == SchemaType.CHANNEL):
    +291        json_schema = channel_schema
    +292    elif (schemaType == SchemaType.GROUP):
    +293        json_schema = group_schema
    +294    else:
    +295        json_schema = "{}"
    +296
    +297    try:
    +298        validate(instance=jsonData, schema=json_schema)
    +299    except exceptions.ValidationError as err:
    +300        print(err)
    +301        return False
    +302    return True
     
    diff --git a/docs/pyxtream/version.html b/docs/pyxtream/version.html index e224733..3e667c7 100644 --- a/docs/pyxtream/version.html +++ b/docs/pyxtream/version.html @@ -51,7 +51,7 @@

    -
    1__version__ = '0.7.1'
    +                        
    1__version__ = '0.7.3'
     2__author__ = 'Claudio Olmi'
     3__author_email__ = 'superolmo2@gmail.com'
     
    diff --git a/docs/search.js b/docs/search.js index fc41633..f638614 100644 --- a/docs/search.js +++ b/docs/search.js @@ -1,6 +1,6 @@ window.pdocSearch = (function(){ /** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();o

    \n"}, "pyxtream.progress": {"fullname": "pyxtream.progress", "modulename": "pyxtream.progress", "kind": "module", "doc": "

    \n"}, "pyxtream.progress.progress": {"fullname": "pyxtream.progress.progress", "modulename": "pyxtream.progress", "qualname": "progress", "kind": "function", "doc": "

    \n", "signature": "(count, total, status=''):", "funcdef": "def"}, "pyxtream.pyxtream": {"fullname": "pyxtream.pyxtream", "modulename": "pyxtream.pyxtream", "kind": "module", "doc": "

    pyxtream

    \n\n

    Module handles downloading xtream data.

    \n\n

    Part of this content comes from

    \n\n\n\n
    \n

    _Author_: Claudio Olmi\n _Github_: superolmo

    \n
    \n\n
    \n

    _Note_: It does not support M3U

    \n
    \n"}, "pyxtream.pyxtream.Channel": {"fullname": "pyxtream.pyxtream.Channel", "modulename": "pyxtream.pyxtream", "qualname": "Channel", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Channel.__init__": {"fullname": "pyxtream.pyxtream.Channel.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Channel.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, group_title, stream_info)"}, "pyxtream.pyxtream.Channel.info": {"fullname": "pyxtream.pyxtream.Channel.info", "modulename": "pyxtream.pyxtream", "qualname": "Channel.info", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.id": {"fullname": "pyxtream.pyxtream.Channel.id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.name": {"fullname": "pyxtream.pyxtream.Channel.name", "modulename": "pyxtream.pyxtream", "qualname": "Channel.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.logo": {"fullname": "pyxtream.pyxtream.Channel.logo", "modulename": "pyxtream.pyxtream", "qualname": "Channel.logo", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.logo_path": {"fullname": "pyxtream.pyxtream.Channel.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Channel.logo_path", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.group_title": {"fullname": "pyxtream.pyxtream.Channel.group_title", "modulename": "pyxtream.pyxtream", "qualname": "Channel.group_title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.title": {"fullname": "pyxtream.pyxtream.Channel.title", "modulename": "pyxtream.pyxtream", "qualname": "Channel.title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.url": {"fullname": "pyxtream.pyxtream.Channel.url", "modulename": "pyxtream.pyxtream", "qualname": "Channel.url", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.stream_type": {"fullname": "pyxtream.pyxtream.Channel.stream_type", "modulename": "pyxtream.pyxtream", "qualname": "Channel.stream_type", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.group_id": {"fullname": "pyxtream.pyxtream.Channel.group_id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.group_id", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.is_adult": {"fullname": "pyxtream.pyxtream.Channel.is_adult", "modulename": "pyxtream.pyxtream", "qualname": "Channel.is_adult", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.added": {"fullname": "pyxtream.pyxtream.Channel.added", "modulename": "pyxtream.pyxtream", "qualname": "Channel.added", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.epg_channel_id": {"fullname": "pyxtream.pyxtream.Channel.epg_channel_id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.epg_channel_id", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.age_days_from_added": {"fullname": "pyxtream.pyxtream.Channel.age_days_from_added", "modulename": "pyxtream.pyxtream", "qualname": "Channel.age_days_from_added", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.date_now": {"fullname": "pyxtream.pyxtream.Channel.date_now", "modulename": "pyxtream.pyxtream", "qualname": "Channel.date_now", "kind": "variable", "doc": "

    \n", "annotation": ": datetime.datetime"}, "pyxtream.pyxtream.Channel.raw": {"fullname": "pyxtream.pyxtream.Channel.raw", "modulename": "pyxtream.pyxtream", "qualname": "Channel.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.export_json": {"fullname": "pyxtream.pyxtream.Channel.export_json", "modulename": "pyxtream.pyxtream", "qualname": "Channel.export_json", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.Group": {"fullname": "pyxtream.pyxtream.Group", "modulename": "pyxtream.pyxtream", "qualname": "Group", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Group.__init__": {"fullname": "pyxtream.pyxtream.Group.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Group.__init__", "kind": "function", "doc": "

    \n", "signature": "(group_info: dict, stream_type: str)"}, "pyxtream.pyxtream.Group.name": {"fullname": "pyxtream.pyxtream.Group.name", "modulename": "pyxtream.pyxtream", "qualname": "Group.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.group_type": {"fullname": "pyxtream.pyxtream.Group.group_type", "modulename": "pyxtream.pyxtream", "qualname": "Group.group_type", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.group_id": {"fullname": "pyxtream.pyxtream.Group.group_id", "modulename": "pyxtream.pyxtream", "qualname": "Group.group_id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.raw": {"fullname": "pyxtream.pyxtream.Group.raw", "modulename": "pyxtream.pyxtream", "qualname": "Group.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"fullname": "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname", "modulename": "pyxtream.pyxtream", "qualname": "Group.convert_region_shortname_to_fullname", "kind": "function", "doc": "

    \n", "signature": "(self, shortname):", "funcdef": "def"}, "pyxtream.pyxtream.Group.channels": {"fullname": "pyxtream.pyxtream.Group.channels", "modulename": "pyxtream.pyxtream", "qualname": "Group.channels", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.series": {"fullname": "pyxtream.pyxtream.Group.series", "modulename": "pyxtream.pyxtream", "qualname": "Group.series", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.region_shortname": {"fullname": "pyxtream.pyxtream.Group.region_shortname", "modulename": "pyxtream.pyxtream", "qualname": "Group.region_shortname", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.region_longname": {"fullname": "pyxtream.pyxtream.Group.region_longname", "modulename": "pyxtream.pyxtream", "qualname": "Group.region_longname", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode": {"fullname": "pyxtream.pyxtream.Episode", "modulename": "pyxtream.pyxtream", "qualname": "Episode", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.__init__": {"fullname": "pyxtream.pyxtream.Episode.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Episode.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, series_info, group_title, episode_info)"}, "pyxtream.pyxtream.Episode.title": {"fullname": "pyxtream.pyxtream.Episode.title", "modulename": "pyxtream.pyxtream", "qualname": "Episode.title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.name": {"fullname": "pyxtream.pyxtream.Episode.name", "modulename": "pyxtream.pyxtream", "qualname": "Episode.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.info": {"fullname": "pyxtream.pyxtream.Episode.info", "modulename": "pyxtream.pyxtream", "qualname": "Episode.info", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.raw": {"fullname": "pyxtream.pyxtream.Episode.raw", "modulename": "pyxtream.pyxtream", "qualname": "Episode.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.group_title": {"fullname": "pyxtream.pyxtream.Episode.group_title", "modulename": "pyxtream.pyxtream", "qualname": "Episode.group_title", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.id": {"fullname": "pyxtream.pyxtream.Episode.id", "modulename": "pyxtream.pyxtream", "qualname": "Episode.id", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.container_extension": {"fullname": "pyxtream.pyxtream.Episode.container_extension", "modulename": "pyxtream.pyxtream", "qualname": "Episode.container_extension", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.episode_number": {"fullname": "pyxtream.pyxtream.Episode.episode_number", "modulename": "pyxtream.pyxtream", "qualname": "Episode.episode_number", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.av_info": {"fullname": "pyxtream.pyxtream.Episode.av_info", "modulename": "pyxtream.pyxtream", "qualname": "Episode.av_info", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.logo": {"fullname": "pyxtream.pyxtream.Episode.logo", "modulename": "pyxtream.pyxtream", "qualname": "Episode.logo", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.logo_path": {"fullname": "pyxtream.pyxtream.Episode.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Episode.logo_path", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.url": {"fullname": "pyxtream.pyxtream.Episode.url", "modulename": "pyxtream.pyxtream", "qualname": "Episode.url", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie": {"fullname": "pyxtream.pyxtream.Serie", "modulename": "pyxtream.pyxtream", "qualname": "Serie", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.__init__": {"fullname": "pyxtream.pyxtream.Serie.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Serie.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, series_info)"}, "pyxtream.pyxtream.Serie.name": {"fullname": "pyxtream.pyxtream.Serie.name", "modulename": "pyxtream.pyxtream", "qualname": "Serie.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.logo": {"fullname": "pyxtream.pyxtream.Serie.logo", "modulename": "pyxtream.pyxtream", "qualname": "Serie.logo", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.logo_path": {"fullname": "pyxtream.pyxtream.Serie.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Serie.logo_path", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.series_id": {"fullname": "pyxtream.pyxtream.Serie.series_id", "modulename": "pyxtream.pyxtream", "qualname": "Serie.series_id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.plot": {"fullname": "pyxtream.pyxtream.Serie.plot", "modulename": "pyxtream.pyxtream", "qualname": "Serie.plot", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.youtube_trailer": {"fullname": "pyxtream.pyxtream.Serie.youtube_trailer", "modulename": "pyxtream.pyxtream", "qualname": "Serie.youtube_trailer", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.genre": {"fullname": "pyxtream.pyxtream.Serie.genre", "modulename": "pyxtream.pyxtream", "qualname": "Serie.genre", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.raw": {"fullname": "pyxtream.pyxtream.Serie.raw", "modulename": "pyxtream.pyxtream", "qualname": "Serie.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.xtream": {"fullname": "pyxtream.pyxtream.Serie.xtream", "modulename": "pyxtream.pyxtream", "qualname": "Serie.xtream", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.seasons": {"fullname": "pyxtream.pyxtream.Serie.seasons", "modulename": "pyxtream.pyxtream", "qualname": "Serie.seasons", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.episodes": {"fullname": "pyxtream.pyxtream.Serie.episodes", "modulename": "pyxtream.pyxtream", "qualname": "Serie.episodes", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.export_json": {"fullname": "pyxtream.pyxtream.Serie.export_json", "modulename": "pyxtream.pyxtream", "qualname": "Serie.export_json", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.Season": {"fullname": "pyxtream.pyxtream.Season", "modulename": "pyxtream.pyxtream", "qualname": "Season", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Season.__init__": {"fullname": "pyxtream.pyxtream.Season.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Season.__init__", "kind": "function", "doc": "

    \n", "signature": "(name)"}, "pyxtream.pyxtream.Season.name": {"fullname": "pyxtream.pyxtream.Season.name", "modulename": "pyxtream.pyxtream", "qualname": "Season.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Season.episodes": {"fullname": "pyxtream.pyxtream.Season.episodes", "modulename": "pyxtream.pyxtream", "qualname": "Season.episodes", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream": {"fullname": "pyxtream.pyxtream.XTream", "modulename": "pyxtream.pyxtream", "qualname": "XTream", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.__init__": {"fullname": "pyxtream.pyxtream.XTream.__init__", "modulename": "pyxtream.pyxtream", "qualname": "XTream.__init__", "kind": "function", "doc": "

    Initialize Xtream Class

    \n\n

    Args:\n provider_name (str): Name of the IPTV provider\n provider_username (str): User name of the IPTV provider\n provider_password (str): Password of the IPTV provider\n provider_url (str): URL of the IPTV provider\n headers (dict): Requests Headers\n hide_adult_content(bool, optional): When True hide stream that are marked for adult\n cache_path (str, optional): Location where to save loaded files.\n Defaults to empty string.\n reload_time_sec (int, optional): Number of seconds before automatic reloading\n (-1 to turn it OFF)\n debug_flask (bool, optional): Enable the debug mode in Flask\n validate_json (bool, optional): Check Xtream API provided JSON for validity

    \n\n

    Returns: XTream Class Instance

    \n\n
      \n
    • Note 1: If it fails to authorize with provided username and password,\nauth_data will be an empty dictionary.
    • \n
    • Note 2: The JSON validation option will take considerable amount of time and it should be \nused only as a debug tool. The Xtream API JSON from the provider passes through a\nschema that represent the best available understanding of how the Xtream API \nworks.
    • \n
    \n", "signature": "(\tprovider_name: str,\tprovider_username: str,\tprovider_password: str,\tprovider_url: str,\theaders: dict = None,\thide_adult_content: bool = False,\tcache_path: str = '',\treload_time_sec: int = 28800,\tvalidate_json: bool = False,\tdebug_flask: bool = True)"}, "pyxtream.pyxtream.XTream.name": {"fullname": "pyxtream.pyxtream.XTream.name", "modulename": "pyxtream.pyxtream", "qualname": "XTream.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.server": {"fullname": "pyxtream.pyxtream.XTream.server", "modulename": "pyxtream.pyxtream", "qualname": "XTream.server", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.secure_server": {"fullname": "pyxtream.pyxtream.XTream.secure_server", "modulename": "pyxtream.pyxtream", "qualname": "XTream.secure_server", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.username": {"fullname": "pyxtream.pyxtream.XTream.username", "modulename": "pyxtream.pyxtream", "qualname": "XTream.username", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.password": {"fullname": "pyxtream.pyxtream.XTream.password", "modulename": "pyxtream.pyxtream", "qualname": "XTream.password", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.live_type": {"fullname": "pyxtream.pyxtream.XTream.live_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.live_type", "kind": "variable", "doc": "

    \n", "default_value": "'Live'"}, "pyxtream.pyxtream.XTream.vod_type": {"fullname": "pyxtream.pyxtream.XTream.vod_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vod_type", "kind": "variable", "doc": "

    \n", "default_value": "'VOD'"}, "pyxtream.pyxtream.XTream.series_type": {"fullname": "pyxtream.pyxtream.XTream.series_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series_type", "kind": "variable", "doc": "

    \n", "default_value": "'Series'"}, "pyxtream.pyxtream.XTream.auth_data": {"fullname": "pyxtream.pyxtream.XTream.auth_data", "modulename": "pyxtream.pyxtream", "qualname": "XTream.auth_data", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.pyxtream.XTream.authorization": {"fullname": "pyxtream.pyxtream.XTream.authorization", "modulename": "pyxtream.pyxtream", "qualname": "XTream.authorization", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.pyxtream.XTream.groups": {"fullname": "pyxtream.pyxtream.XTream.groups", "modulename": "pyxtream.pyxtream", "qualname": "XTream.groups", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.channels": {"fullname": "pyxtream.pyxtream.XTream.channels", "modulename": "pyxtream.pyxtream", "qualname": "XTream.channels", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.series": {"fullname": "pyxtream.pyxtream.XTream.series", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies": {"fullname": "pyxtream.pyxtream.XTream.movies", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies_30days": {"fullname": "pyxtream.pyxtream.XTream.movies_30days", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies_30days", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies_7days": {"fullname": "pyxtream.pyxtream.XTream.movies_7days", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies_7days", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.connection_headers": {"fullname": "pyxtream.pyxtream.XTream.connection_headers", "modulename": "pyxtream.pyxtream", "qualname": "XTream.connection_headers", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.pyxtream.XTream.state": {"fullname": "pyxtream.pyxtream.XTream.state", "modulename": "pyxtream.pyxtream", "qualname": "XTream.state", "kind": "variable", "doc": "

    \n", "default_value": "{'authenticated': False, 'loaded': False}"}, "pyxtream.pyxtream.XTream.hide_adult_content": {"fullname": "pyxtream.pyxtream.XTream.hide_adult_content", "modulename": "pyxtream.pyxtream", "qualname": "XTream.hide_adult_content", "kind": "variable", "doc": "

    \n", "default_value": "False"}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.live_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.live_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.vod_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vod_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.series_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"fullname": "pyxtream.pyxtream.XTream.threshold_time_sec", "modulename": "pyxtream.pyxtream", "qualname": "XTream.threshold_time_sec", "kind": "variable", "doc": "

    \n", "default_value": "-1"}, "pyxtream.pyxtream.XTream.cache_path": {"fullname": "pyxtream.pyxtream.XTream.cache_path", "modulename": "pyxtream.pyxtream", "qualname": "XTream.cache_path", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.validate_json": {"fullname": "pyxtream.pyxtream.XTream.validate_json", "modulename": "pyxtream.pyxtream", "qualname": "XTream.validate_json", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.app_fullpath": {"fullname": "pyxtream.pyxtream.XTream.app_fullpath", "modulename": "pyxtream.pyxtream", "qualname": "XTream.app_fullpath", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.html_template_folder": {"fullname": "pyxtream.pyxtream.XTream.html_template_folder", "modulename": "pyxtream.pyxtream", "qualname": "XTream.html_template_folder", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.search_stream": {"fullname": "pyxtream.pyxtream.XTream.search_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.search_stream", "kind": "function", "doc": "

    Search for streams

    \n\n

    Args:\n keyword (str): Keyword to search for. Supports REGEX\n ignore_case (bool, optional): True to ignore case during search. Defaults to \"True\".\n return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to \"LIST\".\n stream_type (list, optional): Search within specific stream type.

    \n\n

    Returns:\n list: List with all the results, it could be empty.

    \n", "signature": "(\tself,\tkeyword: str,\tignore_case: bool = True,\treturn_type: str = 'LIST',\tstream_type: list = ('series', 'movies', 'channels')) -> list:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.download_video": {"fullname": "pyxtream.pyxtream.XTream.download_video", "modulename": "pyxtream.pyxtream", "qualname": "XTream.download_video", "kind": "function", "doc": "

    Download Video from Stream ID

    \n\n

    Args:\n stream_id (int): Stirng identifing the stream ID

    \n\n

    Returns:\n str: Absolute Path Filename where the file was saved. Empty if could not download

    \n", "signature": "(self, stream_id: int) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.authenticate": {"fullname": "pyxtream.pyxtream.XTream.authenticate", "modulename": "pyxtream.pyxtream", "qualname": "XTream.authenticate", "kind": "function", "doc": "

    Login to provider

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.load_iptv": {"fullname": "pyxtream.pyxtream.XTream.load_iptv", "modulename": "pyxtream.pyxtream", "qualname": "XTream.load_iptv", "kind": "function", "doc": "

    Load XTream IPTV

    \n\n
      \n
    • Add all Live TV to XTream.channels
    • \n
    • Add all VOD to XTream.movies
    • \n
    • Add all Series to XTream.series\nSeries contains Seasons and Episodes. Those are not automatically\nretrieved from the server to reduce the loading time.
    • \n
    • Add all groups to XTream.groups\nGroups are for all three channel types, Live TV, VOD, and Series
    • \n
    \n\n

    Returns:\n bool: True if successfull, False if error

    \n", "signature": "(self) -> bool:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"fullname": "pyxtream.pyxtream.XTream.get_series_info_by_id", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_info_by_id", "kind": "function", "doc": "

    Get Seasons and Episodes for a Series

    \n\n

    Args:\n get_series (dict): Series dictionary

    \n", "signature": "(self, get_series: dict):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.vodInfoByID": {"fullname": "pyxtream.pyxtream.XTream.vodInfoByID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vodInfoByID", "kind": "function", "doc": "

    \n", "signature": "(self, vod_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"fullname": "pyxtream.pyxtream.XTream.liveEpgByStream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.liveEpgByStream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"fullname": "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit", "modulename": "pyxtream.pyxtream", "qualname": "XTream.liveEpgByStreamAndLimit", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id, limit):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"fullname": "pyxtream.pyxtream.XTream.allLiveEpgByStream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.allLiveEpgByStream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.allEpg": {"fullname": "pyxtream.pyxtream.XTream.allEpg", "modulename": "pyxtream.pyxtream", "qualname": "XTream.allEpg", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"fullname": "pyxtream.pyxtream.XTream.get_live_categories_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_categories_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"fullname": "pyxtream.pyxtream.XTream.get_live_streams_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_streams_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_streams_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"fullname": "pyxtream.pyxtream.XTream.get_vod_cat_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_cat_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"fullname": "pyxtream.pyxtream.XTream.get_vod_streams_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_streams_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_streams_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"fullname": "pyxtream.pyxtream.XTream.get_series_cat_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_cat_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_URL": {"fullname": "pyxtream.pyxtream.XTream.get_series_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_series_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"fullname": "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_info_URL_by_ID", "kind": "function", "doc": "

    \n", "signature": "(self, series_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"fullname": "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_VOD_info_URL_by_ID", "kind": "function", "doc": "

    \n", "signature": "(self, vod_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"fullname": "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_epg_URL_by_stream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"fullname": "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_epg_URL_by_stream_and_limit", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id, limit) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"fullname": "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_all_live_epg_URL_by_stream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"fullname": "pyxtream.pyxtream.XTream.get_all_epg_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_all_epg_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.rest_api": {"fullname": "pyxtream.rest_api", "modulename": "pyxtream.rest_api", "kind": "module", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction": {"fullname": "pyxtream.rest_api.EndpointAction", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction", "kind": "class", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction.__init__": {"fullname": "pyxtream.rest_api.EndpointAction.__init__", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.__init__", "kind": "function", "doc": "

    \n", "signature": "(action, function_name)"}, "pyxtream.rest_api.EndpointAction.function_name": {"fullname": "pyxtream.rest_api.EndpointAction.function_name", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.function_name", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction.action": {"fullname": "pyxtream.rest_api.EndpointAction.action", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.action", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap": {"fullname": "pyxtream.rest_api.FlaskWrap", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap", "kind": "class", "doc": "

    A class that represents a thread of control.

    \n\n

    This class can be safely subclassed in a limited fashion. There are two ways\nto specify the activity: by passing a callable object to the constructor, or\nby overriding the run() method in a subclass.

    \n", "bases": "threading.Thread"}, "pyxtream.rest_api.FlaskWrap.__init__": {"fullname": "pyxtream.rest_api.FlaskWrap.__init__", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.__init__", "kind": "function", "doc": "

    This constructor should always be called with keyword arguments. Arguments are:

    \n\n

    group should be None; reserved for future extension when a ThreadGroup\nclass is implemented.

    \n\n

    target is the callable object to be invoked by the run()\nmethod. Defaults to None, meaning nothing is called.

    \n\n

    name is the thread name. By default, a unique name is constructed of\nthe form \"Thread-N\" where N is a small decimal number.

    \n\n

    args is a list or tuple of arguments for the target invocation. Defaults to ().

    \n\n

    kwargs is a dictionary of keyword arguments for the target\ninvocation. Defaults to {}.

    \n\n

    If a subclass overrides the constructor, it must make sure to invoke\nthe base class constructor (Thread.__init__()) before doing anything\nelse to the thread.

    \n", "signature": "(\tname,\txtream: object,\thtml_template_folder: str = None,\thost: str = '0.0.0.0',\tport: int = 5000,\tdebug: bool = True)"}, "pyxtream.rest_api.FlaskWrap.home_template": {"fullname": "pyxtream.rest_api.FlaskWrap.home_template", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.home_template", "kind": "variable", "doc": "

    \n", "default_value": "'\\n<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>\\n '"}, "pyxtream.rest_api.FlaskWrap.host": {"fullname": "pyxtream.rest_api.FlaskWrap.host", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.host", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.rest_api.FlaskWrap.port": {"fullname": "pyxtream.rest_api.FlaskWrap.port", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.port", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.rest_api.FlaskWrap.debug": {"fullname": "pyxtream.rest_api.FlaskWrap.debug", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.debug", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.app": {"fullname": "pyxtream.rest_api.FlaskWrap.app", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.app", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.xt": {"fullname": "pyxtream.rest_api.FlaskWrap.xt", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.xt", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.name": {"fullname": "pyxtream.rest_api.FlaskWrap.name", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.name", "kind": "variable", "doc": "

    A string used for identification purposes only.

    \n\n

    It has no semantics. Multiple threads may be given the same name. The\ninitial name is set by the constructor.

    \n"}, "pyxtream.rest_api.FlaskWrap.daemon": {"fullname": "pyxtream.rest_api.FlaskWrap.daemon", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.daemon", "kind": "variable", "doc": "

    A boolean value indicating whether this thread is a daemon thread.

    \n\n

    This must be set before start() is called, otherwise RuntimeError is\nraised. Its initial value is inherited from the creating thread; the\nmain thread is not a daemon thread and therefore all threads created in\nthe main thread default to daemon = False.

    \n\n

    The entire Python program exits when only daemon threads are left.

    \n"}, "pyxtream.rest_api.FlaskWrap.run": {"fullname": "pyxtream.rest_api.FlaskWrap.run", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.run", "kind": "function", "doc": "

    Method representing the thread's activity.

    \n\n

    You may override this method in a subclass. The standard run() method\ninvokes the callable object passed to the object's constructor as the\ntarget argument, if any, with sequential and keyword arguments taken\nfrom the args and kwargs arguments, respectively.

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"fullname": "pyxtream.rest_api.FlaskWrap.add_endpoint", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.add_endpoint", "kind": "function", "doc": "

    \n", "signature": "(self, endpoint=None, endpoint_name=None, handler=None):", "funcdef": "def"}, "pyxtream.schemaValidator": {"fullname": "pyxtream.schemaValidator", "modulename": "pyxtream.schemaValidator", "kind": "module", "doc": "

    \n"}, "pyxtream.schemaValidator.SchemaType": {"fullname": "pyxtream.schemaValidator.SchemaType", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType", "kind": "class", "doc": "

    \n", "bases": "enum.Enum"}, "pyxtream.schemaValidator.SchemaType.SERIES": {"fullname": "pyxtream.schemaValidator.SchemaType.SERIES", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.SERIES", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.SERIES: 1>"}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"fullname": "pyxtream.schemaValidator.SchemaType.SERIES_INFO", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.SERIES_INFO", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.SERIES_INFO: 2>"}, "pyxtream.schemaValidator.SchemaType.LIVE": {"fullname": "pyxtream.schemaValidator.SchemaType.LIVE", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.LIVE", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.LIVE: 3>"}, "pyxtream.schemaValidator.SchemaType.VOD": {"fullname": "pyxtream.schemaValidator.SchemaType.VOD", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.VOD", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.VOD: 4>"}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"fullname": "pyxtream.schemaValidator.SchemaType.CHANNEL", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.CHANNEL", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.CHANNEL: 5>"}, "pyxtream.schemaValidator.SchemaType.GROUP": {"fullname": "pyxtream.schemaValidator.SchemaType.GROUP", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.GROUP", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.GROUP: 6>"}, "pyxtream.schemaValidator.series_schema": {"fullname": "pyxtream.schemaValidator.series_schema", "modulename": "pyxtream.schemaValidator", "qualname": "series_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Series', 'description': 'xtream API Series Schema', 'type': 'object', 'properties': {'seasons': {'type': 'array', 'items': {'properties': {'air_date': {'type': 'string', 'format': 'date'}, 'episode_count': {'type': 'integer'}, 'id': {'type': 'integer'}, 'name': {'type': 'string'}, 'overview': {'type': 'string'}, 'season_number': {'type': 'integer'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'cover_big': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, 'required': ['id'], 'title': 'Season'}}, 'info': {'properties': {'name': {'type': 'string'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'plot': {'type': 'string'}, 'cast': {'type': 'string'}, 'director': {'type': 'string'}, 'genre': {'type': 'string'}, 'releaseDate': {'type': 'string', 'format': 'date'}, 'last_modified': {'type': 'string', 'format': 'integer'}, 'rating': {'type': 'string', 'format': 'integer'}, 'rating_5based': {'type': 'number'}, 'backdrop_path': {'type': 'array', 'items': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, 'youtube_trailed': {'type': 'string'}, 'episode_run_time': {'type': 'string', 'format': 'integer'}, 'category_id': {'type': 'string', 'format': 'integer'}}, 'required': ['name'], 'title': 'Info'}, 'episodes': {'patternProperties': {'^\\\\d+$': {'type': 'array', 'items': {'properties': {'id': {'type': 'string', 'format': 'integer'}, 'episode_num': {'type': 'integer'}, 'title': {'type': 'string'}, 'container_extension': {'type': 'string'}, 'info': {'type': 'object', 'items': {'plot': {'type': 'string'}}}, 'customer_sid': {'type': 'string'}, 'added': {'type': 'string', 'format': 'integer'}, 'season': {'type': 'integer'}, 'direct_source': {'type': 'string'}}}}}}}, 'required': ['info', 'seasons', 'episodes']}"}, "pyxtream.schemaValidator.series_info_schema": {"fullname": "pyxtream.schemaValidator.series_info_schema", "modulename": "pyxtream.schemaValidator", "qualname": "series_info_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Series', 'description': 'xtream API Series Info Schema', 'type': 'object', 'properties': {'name': {'type': 'string'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'plot': {'type': 'string'}, 'cast': {'type': 'string'}, 'director': {'type': 'string'}, 'genre': {'type': 'string'}, 'releaseDate': {'type': 'string', 'format': 'date'}, 'last_modified': {'type': 'string', 'format': 'integer'}, 'rating': {'type': 'string', 'format': 'integer'}, 'rating_5based': {'type': 'number'}, 'backdrop_path': {'anyOf': [{'type': 'array', 'items': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, {'type': 'string'}]}, 'youtube_trailed': {'type': 'string'}, 'episode_run_time': {'type': 'string', 'format': 'integer'}, 'category_id': {'type': 'string', 'format': 'integer'}}, 'required': ['name', 'category_id']}"}, "pyxtream.schemaValidator.live_schema": {"fullname": "pyxtream.schemaValidator.live_schema", "modulename": "pyxtream.schemaValidator", "qualname": "live_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Live', 'description': 'xtream API Live Schema', 'type': 'object', 'properties': {'num': {'type': 'integer'}, 'name': {'type': 'string'}, 'stream_type': {'type': 'string'}, 'stream_id': {'type': 'integer'}, 'stream_icon': {'anyOf': [{'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, {'type': 'null'}]}, 'epg_channel_id': {'anyOf': [{'type': 'null'}, {'type': 'string'}]}, 'added': {'type': 'string', 'format': 'integer'}, 'is_adult': {'type': 'string', 'format': 'number'}, 'category_id': {'type': 'string'}, 'custom_sid': {'type': 'string'}, 'tv_archive': {'type': 'number'}, 'direct_source': {'type': 'string'}, 'tv_archive_duration': {'anyOf': [{'type': 'number'}, {'type': 'string', 'format': 'integer'}]}}}"}, "pyxtream.schemaValidator.vod_schema": {"fullname": "pyxtream.schemaValidator.vod_schema", "modulename": "pyxtream.schemaValidator", "qualname": "vod_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'VOD', 'description': 'xtream API VOD Schema', 'type': 'object', 'properties': {'num': {'type': 'integer'}, 'name': {'type': 'string'}, 'stream_type': {'type': 'string'}, 'stream_id': {'type': 'integer'}, 'stream_icon': {'anyOf': [{'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, {'type': 'null'}]}, 'rating': {'anyOf': [{'type': 'null'}, {'type': 'string', 'format': 'integer'}, {'type': 'number'}]}, 'rating_5based': {'type': 'number'}, 'added': {'type': 'string', 'format': 'integer'}, 'is_adult': {'type': 'string', 'format': 'number'}, 'category_id': {'type': 'string'}, 'container_extension': {'type': 'string'}, 'custom_sid': {'type': 'string'}, 'direct_source': {'type': 'string'}}}"}, "pyxtream.schemaValidator.channel_schema": {"fullname": "pyxtream.schemaValidator.channel_schema", "modulename": "pyxtream.schemaValidator", "qualname": "channel_schema", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.schemaValidator.group_schema": {"fullname": "pyxtream.schemaValidator.group_schema", "modulename": "pyxtream.schemaValidator", "qualname": "group_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Group', 'description': 'xtream API Group Schema', 'type': 'object', 'properties': {'category_id': {'type': 'string'}, 'category_name': {'type': 'string'}, 'parent_id': {'type': 'integer'}}}"}, "pyxtream.schemaValidator.schemaValidator": {"fullname": "pyxtream.schemaValidator.schemaValidator", "modulename": "pyxtream.schemaValidator", "qualname": "schemaValidator", "kind": "function", "doc": "

    \n", "signature": "(jsonData: str, schemaType: pyxtream.schemaValidator.SchemaType) -> bool:", "funcdef": "def"}, "pyxtream.version": {"fullname": "pyxtream.version", "modulename": "pyxtream.version", "kind": "module", "doc": "

    \n"}}, "docInfo": {"pyxtream": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.progress": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.progress.progress": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 3}, "pyxtream.pyxtream": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 67}, "pyxtream.pyxtream.Channel": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.info": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.id": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.group_title": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.title": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.url": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.stream_type": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.group_id": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.is_adult": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.added": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.epg_channel_id": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.age_days_from_added": {"qualname": 5, "fullname": 7, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.date_now": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.export_json": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.group_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.group_id": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 16, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.channels": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.series": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.region_shortname": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.region_longname": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 32, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.title": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.info": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.group_title": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.id": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.container_extension": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.episode_number": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.av_info": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.url": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.series_id": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.plot": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.youtube_trailer": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.genre": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.xtream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.seasons": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.episodes": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.export_json": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 9, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.episodes": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 170, "bases": 0, "doc": 202}, "pyxtream.pyxtream.XTream.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.server": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.secure_server": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.username": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.password": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.live_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.vod_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.auth_data": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.authorization": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.groups": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.channels": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies_30days": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies_7days": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.connection_headers": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.state": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 11, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.hide_adult_content": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.cache_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.validate_json": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.app_fullpath": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.html_template_folder": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.search_stream": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 117, "bases": 0, "doc": 69}, "pyxtream.pyxtream.XTream.download_video": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 36}, "pyxtream.pyxtream.XTream.authenticate": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 5}, "pyxtream.pyxtream.XTream.load_iptv": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 83}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 22, "bases": 0, "doc": 18}, "pyxtream.pyxtream.XTream.vodInfoByID": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 22, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.allEpg": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_URL": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"qualname": 9, "fullname": 11, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"qualname": 8, "fullname": 10, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.rest_api": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 15, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.function_name": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.action": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 49}, "pyxtream.rest_api.FlaskWrap.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 102, "bases": 0, "doc": 151}, "pyxtream.rest_api.FlaskWrap.home_template": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 34, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.host": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.port": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.debug": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.app": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.xt": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.name": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 33}, "pyxtream.rest_api.FlaskWrap.daemon": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 73}, "pyxtream.rest_api.FlaskWrap.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 53}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 3}, "pyxtream.schemaValidator": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "pyxtream.schemaValidator.SchemaType.SERIES": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.LIVE": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.VOD": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.GROUP": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.series_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 746, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.series_info_schema": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 354, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.live_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 314, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.vod_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 306, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.channel_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.group_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 92, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.schemaValidator": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 39, "bases": 0, "doc": 3}, "pyxtream.version": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}}, "length": 153, "save": true}, "index": {"qualname": {"root": {"3": {"0": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}}, "df": 1}}}}}, "docs": {}, "df": 0}, "7": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 1}}}}}, "docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 4}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.password": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Serie.plot": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel": {"tf": 1}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}}, "df": 21, "s": {"docs": {"pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8}}, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 8}}}, "d": {"docs": {"pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Group.group_id": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 9}, "s": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.episode_number": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}}, "df": 6}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 10, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Group": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 19, "s": {"docs": {"pyxtream.pyxtream.XTream.groups": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.genre": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 16}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}}, "df": 4}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}}, "df": 5}}}, "o": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 2}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 17}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.username": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 5, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}}, "df": 4}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.Serie.plot": {"tf": 1}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}, "pyxtream.pyxtream.Serie.genre": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 14}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Season": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.pyxtream.Serie.seasons": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 7}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 2}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "v": {"docs": {"pyxtream.pyxtream.Episode.av_info": {"tf": 1}}, "df": 1}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.allEpg": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 2}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 4}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}, "a": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 12}}}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 3}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 3}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.XTream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}, "pyxtream.pyxtream.XTream.username": {"tf": 1}, "pyxtream.pyxtream.XTream.password": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 55}}}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 8, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}}, "df": 1}}}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 3}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 1}}}}, "b": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 9}}}}, "fullname": {"root": {"3": {"0": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}}, "df": 1}}}}}, "docs": {}, "df": 0}, "7": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 1}}}}}, "docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8, "p": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream": {"tf": 1}, "pyxtream.progress": {"tf": 1}, "pyxtream.progress.progress": {"tf": 1}, "pyxtream.pyxtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.added": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.channels": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.series": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.episodes": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.groups": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.channels": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.state": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1.4142135623730951}, "pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}, "pyxtream.schemaValidator": {"tf": 1}, "pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}, "pyxtream.version": {"tf": 1}}, "df": 153}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress": {"tf": 1}, "pyxtream.progress.progress": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 4}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.password": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Serie.plot": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel": {"tf": 1}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}}, "df": 21, "s": {"docs": {"pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8}}, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 8}}}, "d": {"docs": {"pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Group.group_id": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 9}, "s": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.episode_number": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}}, "df": 6}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 10, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Group": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 19, "s": {"docs": {"pyxtream.pyxtream.XTream.groups": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.genre": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 16}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}}, "df": 4}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}}, "df": 5}}}, "o": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 2}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 17}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.username": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 5, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}}, "df": 4}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.Serie.plot": {"tf": 1}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}, "pyxtream.pyxtream.Serie.genre": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 14}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Season": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.pyxtream.Serie.seasons": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 1}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator": {"tf": 1}, "pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1.4142135623730951}}, "df": 15}}}}}}}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 7}}}}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 2}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "v": {"docs": {"pyxtream.pyxtream.Episode.av_info": {"tf": 1}}, "df": 1}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.allEpg": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}}, "df": 2}, "i": {"docs": {"pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 17}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 2}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 2}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 4}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}, "a": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 12}}}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 17}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 3}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.XTream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}, "pyxtream.pyxtream.XTream.username": {"tf": 1}, "pyxtream.pyxtream.XTream.password": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 55}}}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 8, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}}, "df": 1}}}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.version": {"tf": 1}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 3}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 1}}}}, "b": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 9}}}}, "annotation": {"root": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 9, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 4}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 4}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}, "default_value": {"root": {"0": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 4}, "1": {"2": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}, "docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}}, "df": 2}, "2": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}}, "df": 1}, "3": {"docs": {"pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}}, "df": 1}, "4": {"docs": {"pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}}, "df": 1}, "5": {"docs": {"pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}}, "df": 1, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}}}}, "6": {"docs": {"pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 1}, "docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_schema": {"tf": 13.96424004376894}, "pyxtream.schemaValidator.series_info_schema": {"tf": 9.273618495495704}, "pyxtream.schemaValidator.live_schema": {"tf": 9}, "pyxtream.schemaValidator.vod_schema": {"tf": 8.888194417315589}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 4.47213595499958}}, "df": 65, "x": {"2": {"7": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.state": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_schema": {"tf": 18.601075237738275}, "pyxtream.schemaValidator.series_info_schema": {"tf": 12.806248474865697}, "pyxtream.schemaValidator.live_schema": {"tf": 11.832159566199232}, "pyxtream.schemaValidator.vod_schema": {"tf": 11.74734012447073}, "pyxtream.schemaValidator.group_schema": {"tf": 6.324555320336759}}, "df": 45}, "docs": {}, "df": 0}, "docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 3}}}, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 10}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}, "pyxtream.schemaValidator.group_schema": {"tf": 2}}, "df": 5, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 6}}}}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 5}, "pyxtream.schemaValidator.series_info_schema": {"tf": 3.7416573867739413}, "pyxtream.schemaValidator.live_schema": {"tf": 3.1622776601683795}, "pyxtream.schemaValidator.vod_schema": {"tf": 3.1622776601683795}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}}, "df": 2}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}, "d": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 3.4641016151377544}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}}, "df": 4}}}}}}, "p": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 4}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 1}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 10}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 8}}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "/": {"2": {"0": {"2": {"0": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}}, "df": 4}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 2}}}}, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.7320508075688772}}, "df": 1}}, "t": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4, ":": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "g": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 1}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 3.3166247903554}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2.23606797749979}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.group_schema": {"tf": 1.7320508075688772}}, "df": 5}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 6}, "pyxtream.schemaValidator.series_info_schema": {"tf": 4.123105625617661}, "pyxtream.schemaValidator.live_schema": {"tf": 4.242640687119285}, "pyxtream.schemaValidator.vod_schema": {"tf": 4.242640687119285}, "pyxtream.schemaValidator.group_schema": {"tf": 2}}, "df": 5}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}, "v": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 1}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2.8284271247461903}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 4}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}}, "signature": {"root": {"0": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}}, "df": 1}, "2": {"8": {"8": {"0": {"0": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "3": {"9": {"docs": {"pyxtream.progress.progress": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 2.8284271247461903}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 4}, "docs": {}, "df": 0}, "5": {"0": {"0": {"0": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"pyxtream.progress.progress": {"tf": 4.898979485566356}, "pyxtream.pyxtream.Channel.__init__": {"tf": 4.47213595499958}, "pyxtream.pyxtream.Channel.export_json": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.Group.__init__": {"tf": 4.47213595499958}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.Episode.__init__": {"tf": 4.898979485566356}, "pyxtream.pyxtream.Serie.__init__": {"tf": 4}, "pyxtream.pyxtream.Serie.export_json": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.Season.__init__": {"tf": 2.8284271247461903}, "pyxtream.pyxtream.XTream.__init__": {"tf": 11.489125293076057}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 9.539392014169456}, "pyxtream.pyxtream.XTream.download_video": {"tf": 4.47213595499958}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 4.242640687119285}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 4.242640687119285}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 4}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 4}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 4}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 4.47213595499958}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 4}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 3.4641016151377544}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 3.4641016151377544}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 8.94427190999916}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 3.1622776601683795}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 5.830951894845301}, "pyxtream.schemaValidator.schemaValidator": {"tf": 5.656854249492381}}, "df": 40, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 2}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 21, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 10}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 30}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}}, "df": 5}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1.4142135623730951}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 4}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 4}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}}, "df": 4}}, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 13}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 5}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.7320508075688772}}, "df": 3}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 5}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 2}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}}, "df": 1}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 2}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}}, "bases": {"root": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "doc": {"root": {"1": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "2": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "docs": {"pyxtream": {"tf": 1.7320508075688772}, "pyxtream.progress": {"tf": 1.7320508075688772}, "pyxtream.progress.progress": {"tf": 1.7320508075688772}, "pyxtream.pyxtream": {"tf": 6.082762530298219}, "pyxtream.pyxtream.Channel": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.added": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.channels": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.series": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.episodes": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.__init__": {"tf": 5.291502622129181}, "pyxtream.pyxtream.XTream.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.server": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.username": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.password": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.groups": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.channels": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.state": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.download_video": {"tf": 2.6457513110645907}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 4.123105625617661}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1.7320508075688772}, "pyxtream.rest_api": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 5.5677643628300215}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 3}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.channel_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.group_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1.7320508075688772}, "pyxtream.version": {"tf": 1.7320508075688772}}, "df": 153, "p": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 3}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 2}, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}, "m": {"3": {"docs": {}, "df": 0, "u": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.7320508075688772}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {"pyxtream.pyxtream": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 2}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}}}}}}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}}, "df": 3}}}}}}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}}, "df": 3}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.6457513110645907}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}}, "df": 4, "f": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}}, "df": 2}}}}}}}, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 5}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 3}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 2.449489742783178}}, "df": 9, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 2}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 3}}}, "o": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 2}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 8, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 2}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}, "w": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": null}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": null}, "pyxtream.rest_api.FlaskWrap.name": {"tf": null}, "pyxtream.rest_api.FlaskWrap.run": {"tf": null}}, "df": 4}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "/": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "x": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 6}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 6, "m": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}}, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 7, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 6}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 5}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 2}}, "df": 1}}, "p": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 5}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.7320508075688772}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.4142135623730951}}, "df": 1}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 1, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.7320508075688772}}, "df": 3, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 2}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 2}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 2}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.7320508075688772}}, "df": 2}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1, "o": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 4, "e": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 2}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1.4142135623730951}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 5, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 2}}}, "f": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 5}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1.7320508075688772}}, "df": 1, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.8284271247461903}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2.23606797749979}}, "df": 3}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 4}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 6, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 3}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 2}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true}; + /** pdoc search index */const docs = {"version": "0.9.5", "fields": ["qualname", "fullname", "annotation", "default_value", "signature", "bases", "doc"], "ref": "fullname", "documentStore": {"docs": {"pyxtream": {"fullname": "pyxtream", "modulename": "pyxtream", "kind": "module", "doc": "

    \n"}, "pyxtream.progress": {"fullname": "pyxtream.progress", "modulename": "pyxtream.progress", "kind": "module", "doc": "

    \n"}, "pyxtream.progress.progress": {"fullname": "pyxtream.progress.progress", "modulename": "pyxtream.progress", "qualname": "progress", "kind": "function", "doc": "

    \n", "signature": "(count, total, status=''):", "funcdef": "def"}, "pyxtream.pyxtream": {"fullname": "pyxtream.pyxtream", "modulename": "pyxtream.pyxtream", "kind": "module", "doc": "

    pyxtream

    \n\n

    Module handles downloading xtream data.

    \n\n

    Part of this content comes from

    \n\n\n\n
    \n

    _Author_: Claudio Olmi\n _Github_: superolmo

    \n
    \n\n
    \n

    _Note_: It does not support M3U

    \n
    \n"}, "pyxtream.pyxtream.Channel": {"fullname": "pyxtream.pyxtream.Channel", "modulename": "pyxtream.pyxtream", "qualname": "Channel", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Channel.__init__": {"fullname": "pyxtream.pyxtream.Channel.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Channel.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, group_title, stream_info)"}, "pyxtream.pyxtream.Channel.info": {"fullname": "pyxtream.pyxtream.Channel.info", "modulename": "pyxtream.pyxtream", "qualname": "Channel.info", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.id": {"fullname": "pyxtream.pyxtream.Channel.id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.name": {"fullname": "pyxtream.pyxtream.Channel.name", "modulename": "pyxtream.pyxtream", "qualname": "Channel.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.logo": {"fullname": "pyxtream.pyxtream.Channel.logo", "modulename": "pyxtream.pyxtream", "qualname": "Channel.logo", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.logo_path": {"fullname": "pyxtream.pyxtream.Channel.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Channel.logo_path", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.group_title": {"fullname": "pyxtream.pyxtream.Channel.group_title", "modulename": "pyxtream.pyxtream", "qualname": "Channel.group_title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.title": {"fullname": "pyxtream.pyxtream.Channel.title", "modulename": "pyxtream.pyxtream", "qualname": "Channel.title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.url": {"fullname": "pyxtream.pyxtream.Channel.url", "modulename": "pyxtream.pyxtream", "qualname": "Channel.url", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.stream_type": {"fullname": "pyxtream.pyxtream.Channel.stream_type", "modulename": "pyxtream.pyxtream", "qualname": "Channel.stream_type", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.group_id": {"fullname": "pyxtream.pyxtream.Channel.group_id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.group_id", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.is_adult": {"fullname": "pyxtream.pyxtream.Channel.is_adult", "modulename": "pyxtream.pyxtream", "qualname": "Channel.is_adult", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.added": {"fullname": "pyxtream.pyxtream.Channel.added", "modulename": "pyxtream.pyxtream", "qualname": "Channel.added", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.epg_channel_id": {"fullname": "pyxtream.pyxtream.Channel.epg_channel_id", "modulename": "pyxtream.pyxtream", "qualname": "Channel.epg_channel_id", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.pyxtream.Channel.age_days_from_added": {"fullname": "pyxtream.pyxtream.Channel.age_days_from_added", "modulename": "pyxtream.pyxtream", "qualname": "Channel.age_days_from_added", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.pyxtream.Channel.date_now": {"fullname": "pyxtream.pyxtream.Channel.date_now", "modulename": "pyxtream.pyxtream", "qualname": "Channel.date_now", "kind": "variable", "doc": "

    \n", "annotation": ": datetime.datetime"}, "pyxtream.pyxtream.Channel.raw": {"fullname": "pyxtream.pyxtream.Channel.raw", "modulename": "pyxtream.pyxtream", "qualname": "Channel.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Channel.export_json": {"fullname": "pyxtream.pyxtream.Channel.export_json", "modulename": "pyxtream.pyxtream", "qualname": "Channel.export_json", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.Group": {"fullname": "pyxtream.pyxtream.Group", "modulename": "pyxtream.pyxtream", "qualname": "Group", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Group.__init__": {"fullname": "pyxtream.pyxtream.Group.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Group.__init__", "kind": "function", "doc": "

    \n", "signature": "(group_info: dict, stream_type: str)"}, "pyxtream.pyxtream.Group.name": {"fullname": "pyxtream.pyxtream.Group.name", "modulename": "pyxtream.pyxtream", "qualname": "Group.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.group_type": {"fullname": "pyxtream.pyxtream.Group.group_type", "modulename": "pyxtream.pyxtream", "qualname": "Group.group_type", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.group_id": {"fullname": "pyxtream.pyxtream.Group.group_id", "modulename": "pyxtream.pyxtream", "qualname": "Group.group_id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.raw": {"fullname": "pyxtream.pyxtream.Group.raw", "modulename": "pyxtream.pyxtream", "qualname": "Group.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"fullname": "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname", "modulename": "pyxtream.pyxtream", "qualname": "Group.convert_region_shortname_to_fullname", "kind": "function", "doc": "

    \n", "signature": "(self, shortname):", "funcdef": "def"}, "pyxtream.pyxtream.Group.channels": {"fullname": "pyxtream.pyxtream.Group.channels", "modulename": "pyxtream.pyxtream", "qualname": "Group.channels", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.series": {"fullname": "pyxtream.pyxtream.Group.series", "modulename": "pyxtream.pyxtream", "qualname": "Group.series", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.region_shortname": {"fullname": "pyxtream.pyxtream.Group.region_shortname", "modulename": "pyxtream.pyxtream", "qualname": "Group.region_shortname", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Group.region_longname": {"fullname": "pyxtream.pyxtream.Group.region_longname", "modulename": "pyxtream.pyxtream", "qualname": "Group.region_longname", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode": {"fullname": "pyxtream.pyxtream.Episode", "modulename": "pyxtream.pyxtream", "qualname": "Episode", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.__init__": {"fullname": "pyxtream.pyxtream.Episode.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Episode.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, series_info, group_title, episode_info)"}, "pyxtream.pyxtream.Episode.title": {"fullname": "pyxtream.pyxtream.Episode.title", "modulename": "pyxtream.pyxtream", "qualname": "Episode.title", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.name": {"fullname": "pyxtream.pyxtream.Episode.name", "modulename": "pyxtream.pyxtream", "qualname": "Episode.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.info": {"fullname": "pyxtream.pyxtream.Episode.info", "modulename": "pyxtream.pyxtream", "qualname": "Episode.info", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.raw": {"fullname": "pyxtream.pyxtream.Episode.raw", "modulename": "pyxtream.pyxtream", "qualname": "Episode.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Episode.group_title": {"fullname": "pyxtream.pyxtream.Episode.group_title", "modulename": "pyxtream.pyxtream", "qualname": "Episode.group_title", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.id": {"fullname": "pyxtream.pyxtream.Episode.id", "modulename": "pyxtream.pyxtream", "qualname": "Episode.id", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.container_extension": {"fullname": "pyxtream.pyxtream.Episode.container_extension", "modulename": "pyxtream.pyxtream", "qualname": "Episode.container_extension", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.episode_number": {"fullname": "pyxtream.pyxtream.Episode.episode_number", "modulename": "pyxtream.pyxtream", "qualname": "Episode.episode_number", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.av_info": {"fullname": "pyxtream.pyxtream.Episode.av_info", "modulename": "pyxtream.pyxtream", "qualname": "Episode.av_info", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.logo": {"fullname": "pyxtream.pyxtream.Episode.logo", "modulename": "pyxtream.pyxtream", "qualname": "Episode.logo", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.logo_path": {"fullname": "pyxtream.pyxtream.Episode.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Episode.logo_path", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Episode.url": {"fullname": "pyxtream.pyxtream.Episode.url", "modulename": "pyxtream.pyxtream", "qualname": "Episode.url", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie": {"fullname": "pyxtream.pyxtream.Serie", "modulename": "pyxtream.pyxtream", "qualname": "Serie", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.__init__": {"fullname": "pyxtream.pyxtream.Serie.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Serie.__init__", "kind": "function", "doc": "

    \n", "signature": "(xtream: object, series_info)"}, "pyxtream.pyxtream.Serie.name": {"fullname": "pyxtream.pyxtream.Serie.name", "modulename": "pyxtream.pyxtream", "qualname": "Serie.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.logo": {"fullname": "pyxtream.pyxtream.Serie.logo", "modulename": "pyxtream.pyxtream", "qualname": "Serie.logo", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.logo_path": {"fullname": "pyxtream.pyxtream.Serie.logo_path", "modulename": "pyxtream.pyxtream", "qualname": "Serie.logo_path", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.series_id": {"fullname": "pyxtream.pyxtream.Serie.series_id", "modulename": "pyxtream.pyxtream", "qualname": "Serie.series_id", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.plot": {"fullname": "pyxtream.pyxtream.Serie.plot", "modulename": "pyxtream.pyxtream", "qualname": "Serie.plot", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.youtube_trailer": {"fullname": "pyxtream.pyxtream.Serie.youtube_trailer", "modulename": "pyxtream.pyxtream", "qualname": "Serie.youtube_trailer", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.genre": {"fullname": "pyxtream.pyxtream.Serie.genre", "modulename": "pyxtream.pyxtream", "qualname": "Serie.genre", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.raw": {"fullname": "pyxtream.pyxtream.Serie.raw", "modulename": "pyxtream.pyxtream", "qualname": "Serie.raw", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Serie.xtream": {"fullname": "pyxtream.pyxtream.Serie.xtream", "modulename": "pyxtream.pyxtream", "qualname": "Serie.xtream", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.seasons": {"fullname": "pyxtream.pyxtream.Serie.seasons", "modulename": "pyxtream.pyxtream", "qualname": "Serie.seasons", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.episodes": {"fullname": "pyxtream.pyxtream.Serie.episodes", "modulename": "pyxtream.pyxtream", "qualname": "Serie.episodes", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.url": {"fullname": "pyxtream.pyxtream.Serie.url", "modulename": "pyxtream.pyxtream", "qualname": "Serie.url", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.Serie.export_json": {"fullname": "pyxtream.pyxtream.Serie.export_json", "modulename": "pyxtream.pyxtream", "qualname": "Serie.export_json", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.Season": {"fullname": "pyxtream.pyxtream.Season", "modulename": "pyxtream.pyxtream", "qualname": "Season", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.Season.__init__": {"fullname": "pyxtream.pyxtream.Season.__init__", "modulename": "pyxtream.pyxtream", "qualname": "Season.__init__", "kind": "function", "doc": "

    \n", "signature": "(name)"}, "pyxtream.pyxtream.Season.name": {"fullname": "pyxtream.pyxtream.Season.name", "modulename": "pyxtream.pyxtream", "qualname": "Season.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.Season.episodes": {"fullname": "pyxtream.pyxtream.Season.episodes", "modulename": "pyxtream.pyxtream", "qualname": "Season.episodes", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream": {"fullname": "pyxtream.pyxtream.XTream", "modulename": "pyxtream.pyxtream", "qualname": "XTream", "kind": "class", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.__init__": {"fullname": "pyxtream.pyxtream.XTream.__init__", "modulename": "pyxtream.pyxtream", "qualname": "XTream.__init__", "kind": "function", "doc": "

    Initialize Xtream Class

    \n\n

    Args:\n provider_name (str): Name of the IPTV provider\n provider_username (str): User name of the IPTV provider\n provider_password (str): Password of the IPTV provider\n provider_url (str): URL of the IPTV provider\n headers (dict): Requests Headers\n hide_adult_content(bool, optional): When True hide stream that are marked for adult\n cache_path (str, optional): Location where to save loaded files.\n Defaults to empty string.\n reload_time_sec (int, optional): Number of seconds before automatic reloading\n (-1 to turn it OFF)\n validate_json (bool, optional): Check Xtream API provided JSON for validity\n enable_flask (bool, optional): Enable Flask\n debug_flask (bool, optional): Enable the debug mode in Flask

    \n\n

    Returns: XTream Class Instance

    \n\n
      \n
    • Note 1: If it fails to authorize with provided username and password,\nauth_data will be an empty dictionary.
    • \n
    • Note 2: The JSON validation option will take considerable amount of time and it should be\nused only as a debug tool. The Xtream API JSON from the provider passes through a\nschema that represent the best available understanding of how the Xtream API\nworks.
    • \n
    \n", "signature": "(\tprovider_name: str,\tprovider_username: str,\tprovider_password: str,\tprovider_url: str,\theaders: dict = None,\thide_adult_content: bool = False,\tcache_path: str = '',\treload_time_sec: int = 28800,\tvalidate_json: bool = False,\tenable_flask: bool = False,\tdebug_flask: bool = True)"}, "pyxtream.pyxtream.XTream.name": {"fullname": "pyxtream.pyxtream.XTream.name", "modulename": "pyxtream.pyxtream", "qualname": "XTream.name", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.server": {"fullname": "pyxtream.pyxtream.XTream.server", "modulename": "pyxtream.pyxtream", "qualname": "XTream.server", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.secure_server": {"fullname": "pyxtream.pyxtream.XTream.secure_server", "modulename": "pyxtream.pyxtream", "qualname": "XTream.secure_server", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.username": {"fullname": "pyxtream.pyxtream.XTream.username", "modulename": "pyxtream.pyxtream", "qualname": "XTream.username", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.password": {"fullname": "pyxtream.pyxtream.XTream.password", "modulename": "pyxtream.pyxtream", "qualname": "XTream.password", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.base_url": {"fullname": "pyxtream.pyxtream.XTream.base_url", "modulename": "pyxtream.pyxtream", "qualname": "XTream.base_url", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.base_url_ssl": {"fullname": "pyxtream.pyxtream.XTream.base_url_ssl", "modulename": "pyxtream.pyxtream", "qualname": "XTream.base_url_ssl", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.cache_path": {"fullname": "pyxtream.pyxtream.XTream.cache_path", "modulename": "pyxtream.pyxtream", "qualname": "XTream.cache_path", "kind": "variable", "doc": "

    \n", "default_value": "''"}, "pyxtream.pyxtream.XTream.account_expiration": {"fullname": "pyxtream.pyxtream.XTream.account_expiration", "modulename": "pyxtream.pyxtream", "qualname": "XTream.account_expiration", "kind": "variable", "doc": "

    \n", "annotation": ": datetime.timedelta"}, "pyxtream.pyxtream.XTream.live_type": {"fullname": "pyxtream.pyxtream.XTream.live_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.live_type", "kind": "variable", "doc": "

    \n", "default_value": "'Live'"}, "pyxtream.pyxtream.XTream.vod_type": {"fullname": "pyxtream.pyxtream.XTream.vod_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vod_type", "kind": "variable", "doc": "

    \n", "default_value": "'VOD'"}, "pyxtream.pyxtream.XTream.series_type": {"fullname": "pyxtream.pyxtream.XTream.series_type", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series_type", "kind": "variable", "doc": "

    \n", "default_value": "'Series'"}, "pyxtream.pyxtream.XTream.auth_data": {"fullname": "pyxtream.pyxtream.XTream.auth_data", "modulename": "pyxtream.pyxtream", "qualname": "XTream.auth_data", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.pyxtream.XTream.authorization": {"fullname": "pyxtream.pyxtream.XTream.authorization", "modulename": "pyxtream.pyxtream", "qualname": "XTream.authorization", "kind": "variable", "doc": "

    \n", "default_value": "{'username': '', 'password': ''}"}, "pyxtream.pyxtream.XTream.groups": {"fullname": "pyxtream.pyxtream.XTream.groups", "modulename": "pyxtream.pyxtream", "qualname": "XTream.groups", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.channels": {"fullname": "pyxtream.pyxtream.XTream.channels", "modulename": "pyxtream.pyxtream", "qualname": "XTream.channels", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.series": {"fullname": "pyxtream.pyxtream.XTream.series", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies": {"fullname": "pyxtream.pyxtream.XTream.movies", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies_30days": {"fullname": "pyxtream.pyxtream.XTream.movies_30days", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies_30days", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.movies_7days": {"fullname": "pyxtream.pyxtream.XTream.movies_7days", "modulename": "pyxtream.pyxtream", "qualname": "XTream.movies_7days", "kind": "variable", "doc": "

    \n", "default_value": "[]"}, "pyxtream.pyxtream.XTream.connection_headers": {"fullname": "pyxtream.pyxtream.XTream.connection_headers", "modulename": "pyxtream.pyxtream", "qualname": "XTream.connection_headers", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.pyxtream.XTream.state": {"fullname": "pyxtream.pyxtream.XTream.state", "modulename": "pyxtream.pyxtream", "qualname": "XTream.state", "kind": "variable", "doc": "

    \n", "default_value": "{'authenticated': False, 'loaded': False}"}, "pyxtream.pyxtream.XTream.hide_adult_content": {"fullname": "pyxtream.pyxtream.XTream.hide_adult_content", "modulename": "pyxtream.pyxtream", "qualname": "XTream.hide_adult_content", "kind": "variable", "doc": "

    \n", "default_value": "False"}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.live_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.live_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.vod_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vod_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"fullname": "pyxtream.pyxtream.XTream.series_catch_all_group", "modulename": "pyxtream.pyxtream", "qualname": "XTream.series_catch_all_group", "kind": "variable", "doc": "

    \n", "default_value": "<pyxtream.pyxtream.Group object>"}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"fullname": "pyxtream.pyxtream.XTream.threshold_time_sec", "modulename": "pyxtream.pyxtream", "qualname": "XTream.threshold_time_sec", "kind": "variable", "doc": "

    \n", "default_value": "-1"}, "pyxtream.pyxtream.XTream.validate_json": {"fullname": "pyxtream.pyxtream.XTream.validate_json", "modulename": "pyxtream.pyxtream", "qualname": "XTream.validate_json", "kind": "variable", "doc": "

    \n", "annotation": ": bool", "default_value": "True"}, "pyxtream.pyxtream.XTream.download_progress": {"fullname": "pyxtream.pyxtream.XTream.download_progress", "modulename": "pyxtream.pyxtream", "qualname": "XTream.download_progress", "kind": "variable", "doc": "

    \n", "annotation": ": dict", "default_value": "{'StreamId': 0, 'Total': 0, 'Progress': 0}"}, "pyxtream.pyxtream.XTream.app_fullpath": {"fullname": "pyxtream.pyxtream.XTream.app_fullpath", "modulename": "pyxtream.pyxtream", "qualname": "XTream.app_fullpath", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.html_template_folder": {"fullname": "pyxtream.pyxtream.XTream.html_template_folder", "modulename": "pyxtream.pyxtream", "qualname": "XTream.html_template_folder", "kind": "variable", "doc": "

    \n"}, "pyxtream.pyxtream.XTream.get_download_progress": {"fullname": "pyxtream.pyxtream.XTream.get_download_progress", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_download_progress", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id: int = None):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_last_7days": {"fullname": "pyxtream.pyxtream.XTream.get_last_7days", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_last_7days", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.search_stream": {"fullname": "pyxtream.pyxtream.XTream.search_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.search_stream", "kind": "function", "doc": "

    Search for streams

    \n\n

    Args:\n keyword (str): Keyword to search for. Supports REGEX\n ignore_case (bool, optional): True to ignore case during search. Defaults to \"True\".\n return_type (str, optional): Output format, 'LIST' or 'JSON'. Defaults to \"LIST\".\n stream_type (list, optional): Search within specific stream type.\n added_after (datetime, optional): Search for items that have been added after a certain date.

    \n\n

    Returns:\n list: List with all the results, it could be empty.

    \n", "signature": "(\tself,\tkeyword: str,\tignore_case: bool = True,\treturn_type: str = 'LIST',\tstream_type: list = ('series', 'movies', 'channels'),\tadded_after: datetime.datetime = None) -> list:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.download_video": {"fullname": "pyxtream.pyxtream.XTream.download_video", "modulename": "pyxtream.pyxtream", "qualname": "XTream.download_video", "kind": "function", "doc": "

    Download Video from Stream ID

    \n\n

    Args:\n stream_id (int): String identifying the stream ID

    \n\n

    Returns:\n str: Absolute Path Filename where the file was saved. Empty if could not download

    \n", "signature": "(self, stream_id: int) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.authenticate": {"fullname": "pyxtream.pyxtream.XTream.authenticate", "modulename": "pyxtream.pyxtream", "qualname": "XTream.authenticate", "kind": "function", "doc": "

    Login to provider

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.load_iptv": {"fullname": "pyxtream.pyxtream.XTream.load_iptv", "modulename": "pyxtream.pyxtream", "qualname": "XTream.load_iptv", "kind": "function", "doc": "

    Load XTream IPTV

    \n\n
      \n
    • Add all Live TV to XTream.channels
    • \n
    • Add all VOD to XTream.movies
    • \n
    • Add all Series to XTream.series\nSeries contains Seasons and Episodes. Those are not automatically\nretrieved from the server to reduce the loading time.
    • \n
    • Add all groups to XTream.groups\nGroups are for all three channel types, Live TV, VOD, and Series
    • \n
    \n\n

    Returns:\n bool: True if successful, False if error

    \n", "signature": "(self) -> bool:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"fullname": "pyxtream.pyxtream.XTream.get_series_info_by_id", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_info_by_id", "kind": "function", "doc": "

    Get Seasons and Episodes for a Series

    \n\n

    Args:\n get_series (dict): Series dictionary

    \n", "signature": "(self, get_series: dict):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.vodInfoByID": {"fullname": "pyxtream.pyxtream.XTream.vodInfoByID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.vodInfoByID", "kind": "function", "doc": "

    \n", "signature": "(self, vod_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"fullname": "pyxtream.pyxtream.XTream.liveEpgByStream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.liveEpgByStream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"fullname": "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit", "modulename": "pyxtream.pyxtream", "qualname": "XTream.liveEpgByStreamAndLimit", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id, limit):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"fullname": "pyxtream.pyxtream.XTream.allLiveEpgByStream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.allLiveEpgByStream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.allEpg": {"fullname": "pyxtream.pyxtream.XTream.allEpg", "modulename": "pyxtream.pyxtream", "qualname": "XTream.allEpg", "kind": "function", "doc": "

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"fullname": "pyxtream.pyxtream.XTream.get_live_categories_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_categories_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"fullname": "pyxtream.pyxtream.XTream.get_live_streams_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_streams_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_streams_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"fullname": "pyxtream.pyxtream.XTream.get_vod_cat_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_cat_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"fullname": "pyxtream.pyxtream.XTream.get_vod_streams_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_streams_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_vod_streams_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"fullname": "pyxtream.pyxtream.XTream.get_series_cat_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_cat_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_URL": {"fullname": "pyxtream.pyxtream.XTream.get_series_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"fullname": "pyxtream.pyxtream.XTream.get_series_URL_by_category", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_URL_by_category", "kind": "function", "doc": "

    \n", "signature": "(self, category_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"fullname": "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_series_info_URL_by_ID", "kind": "function", "doc": "

    \n", "signature": "(self, series_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"fullname": "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_VOD_info_URL_by_ID", "kind": "function", "doc": "

    \n", "signature": "(self, vod_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"fullname": "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_epg_URL_by_stream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"fullname": "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_live_epg_URL_by_stream_and_limit", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id, limit) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"fullname": "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_all_live_epg_URL_by_stream", "kind": "function", "doc": "

    \n", "signature": "(self, stream_id) -> str:", "funcdef": "def"}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"fullname": "pyxtream.pyxtream.XTream.get_all_epg_URL", "modulename": "pyxtream.pyxtream", "qualname": "XTream.get_all_epg_URL", "kind": "function", "doc": "

    \n", "signature": "(self) -> str:", "funcdef": "def"}, "pyxtream.rest_api": {"fullname": "pyxtream.rest_api", "modulename": "pyxtream.rest_api", "kind": "module", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction": {"fullname": "pyxtream.rest_api.EndpointAction", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction", "kind": "class", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction.__init__": {"fullname": "pyxtream.rest_api.EndpointAction.__init__", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.__init__", "kind": "function", "doc": "

    \n", "signature": "(action, function_name)"}, "pyxtream.rest_api.EndpointAction.response": {"fullname": "pyxtream.rest_api.EndpointAction.response", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.response", "kind": "variable", "doc": "

    \n", "annotation": ": flask.wrappers.Response"}, "pyxtream.rest_api.EndpointAction.function_name": {"fullname": "pyxtream.rest_api.EndpointAction.function_name", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.function_name", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.EndpointAction.action": {"fullname": "pyxtream.rest_api.EndpointAction.action", "modulename": "pyxtream.rest_api", "qualname": "EndpointAction.action", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap": {"fullname": "pyxtream.rest_api.FlaskWrap", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap", "kind": "class", "doc": "

    A class that represents a thread of control.

    \n\n

    This class can be safely subclassed in a limited fashion. There are two ways\nto specify the activity: by passing a callable object to the constructor, or\nby overriding the run() method in a subclass.

    \n", "bases": "threading.Thread"}, "pyxtream.rest_api.FlaskWrap.__init__": {"fullname": "pyxtream.rest_api.FlaskWrap.__init__", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.__init__", "kind": "function", "doc": "

    This constructor should always be called with keyword arguments. Arguments are:

    \n\n

    group should be None; reserved for future extension when a ThreadGroup\nclass is implemented.

    \n\n

    target is the callable object to be invoked by the run()\nmethod. Defaults to None, meaning nothing is called.

    \n\n

    name is the thread name. By default, a unique name is constructed of\nthe form \"Thread-N\" where N is a small decimal number.

    \n\n

    args is a list or tuple of arguments for the target invocation. Defaults to ().

    \n\n

    kwargs is a dictionary of keyword arguments for the target\ninvocation. Defaults to {}.

    \n\n

    If a subclass overrides the constructor, it must make sure to invoke\nthe base class constructor (Thread.__init__()) before doing anything\nelse to the thread.

    \n", "signature": "(\tname,\txtream: object,\thtml_template_folder: str = None,\thost: str = '0.0.0.0',\tport: int = 5000,\tdebug: bool = True)"}, "pyxtream.rest_api.FlaskWrap.home_template": {"fullname": "pyxtream.rest_api.FlaskWrap.home_template", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.home_template", "kind": "variable", "doc": "

    \n", "default_value": "'\\n<!DOCTYPE html><html lang="en"><head></head><body>pyxtream API</body></html>\\n '"}, "pyxtream.rest_api.FlaskWrap.host": {"fullname": "pyxtream.rest_api.FlaskWrap.host", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.host", "kind": "variable", "doc": "

    \n", "annotation": ": str", "default_value": "''"}, "pyxtream.rest_api.FlaskWrap.port": {"fullname": "pyxtream.rest_api.FlaskWrap.port", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.port", "kind": "variable", "doc": "

    \n", "annotation": ": int", "default_value": "0"}, "pyxtream.rest_api.FlaskWrap.debug": {"fullname": "pyxtream.rest_api.FlaskWrap.debug", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.debug", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.app": {"fullname": "pyxtream.rest_api.FlaskWrap.app", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.app", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.xt": {"fullname": "pyxtream.rest_api.FlaskWrap.xt", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.xt", "kind": "variable", "doc": "

    \n"}, "pyxtream.rest_api.FlaskWrap.name": {"fullname": "pyxtream.rest_api.FlaskWrap.name", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.name", "kind": "variable", "doc": "

    A string used for identification purposes only.

    \n\n

    It has no semantics. Multiple threads may be given the same name. The\ninitial name is set by the constructor.

    \n"}, "pyxtream.rest_api.FlaskWrap.daemon": {"fullname": "pyxtream.rest_api.FlaskWrap.daemon", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.daemon", "kind": "variable", "doc": "

    A boolean value indicating whether this thread is a daemon thread.

    \n\n

    This must be set before start() is called, otherwise RuntimeError is\nraised. Its initial value is inherited from the creating thread; the\nmain thread is not a daemon thread and therefore all threads created in\nthe main thread default to daemon = False.

    \n\n

    The entire Python program exits when only daemon threads are left.

    \n"}, "pyxtream.rest_api.FlaskWrap.run": {"fullname": "pyxtream.rest_api.FlaskWrap.run", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.run", "kind": "function", "doc": "

    Method representing the thread's activity.

    \n\n

    You may override this method in a subclass. The standard run() method\ninvokes the callable object passed to the object's constructor as the\ntarget argument, if any, with sequential and keyword arguments taken\nfrom the args and kwargs arguments, respectively.

    \n", "signature": "(self):", "funcdef": "def"}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"fullname": "pyxtream.rest_api.FlaskWrap.add_endpoint", "modulename": "pyxtream.rest_api", "qualname": "FlaskWrap.add_endpoint", "kind": "function", "doc": "

    \n", "signature": "(self, endpoint=None, endpoint_name=None, handler=None):", "funcdef": "def"}, "pyxtream.schemaValidator": {"fullname": "pyxtream.schemaValidator", "modulename": "pyxtream.schemaValidator", "kind": "module", "doc": "

    \n"}, "pyxtream.schemaValidator.SchemaType": {"fullname": "pyxtream.schemaValidator.SchemaType", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType", "kind": "class", "doc": "

    \n", "bases": "enum.Enum"}, "pyxtream.schemaValidator.SchemaType.SERIES": {"fullname": "pyxtream.schemaValidator.SchemaType.SERIES", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.SERIES", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.SERIES: 1>"}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"fullname": "pyxtream.schemaValidator.SchemaType.SERIES_INFO", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.SERIES_INFO", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.SERIES_INFO: 2>"}, "pyxtream.schemaValidator.SchemaType.LIVE": {"fullname": "pyxtream.schemaValidator.SchemaType.LIVE", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.LIVE", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.LIVE: 3>"}, "pyxtream.schemaValidator.SchemaType.VOD": {"fullname": "pyxtream.schemaValidator.SchemaType.VOD", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.VOD", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.VOD: 4>"}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"fullname": "pyxtream.schemaValidator.SchemaType.CHANNEL", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.CHANNEL", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.CHANNEL: 5>"}, "pyxtream.schemaValidator.SchemaType.GROUP": {"fullname": "pyxtream.schemaValidator.SchemaType.GROUP", "modulename": "pyxtream.schemaValidator", "qualname": "SchemaType.GROUP", "kind": "variable", "doc": "

    \n", "default_value": "<SchemaType.GROUP: 6>"}, "pyxtream.schemaValidator.series_schema": {"fullname": "pyxtream.schemaValidator.series_schema", "modulename": "pyxtream.schemaValidator", "qualname": "series_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Series', 'description': 'xtream API Series Schema', 'type': 'object', 'properties': {'seasons': {'type': 'array', 'items': {'properties': {'air_date': {'type': 'string', 'format': 'date'}, 'episode_count': {'type': 'integer'}, 'id': {'type': 'integer'}, 'name': {'type': 'string'}, 'overview': {'type': 'string'}, 'season_number': {'type': 'integer'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'cover_big': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, 'required': ['id'], 'title': 'Season'}}, 'info': {'properties': {'name': {'type': 'string'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'plot': {'type': 'string'}, 'cast': {'type': 'string'}, 'director': {'type': 'string'}, 'genre': {'type': 'string'}, 'releaseDate': {'type': 'string', 'format': 'date'}, 'last_modified': {'type': 'string', 'format': 'integer'}, 'rating': {'type': 'string', 'format': 'integer'}, 'rating_5based': {'type': 'number'}, 'backdrop_path': {'type': 'array', 'items': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, 'youtube_trailed': {'type': 'string'}, 'episode_run_time': {'type': 'string', 'format': 'integer'}, 'category_id': {'type': 'string', 'format': 'integer'}}, 'required': ['name'], 'title': 'Info'}, 'episodes': {'patternProperties': {'^\\\\d+$': {'type': 'array', 'items': {'properties': {'id': {'type': 'string', 'format': 'integer'}, 'episode_num': {'type': 'integer'}, 'title': {'type': 'string'}, 'container_extension': {'type': 'string'}, 'info': {'type': 'object', 'items': {'plot': {'type': 'string'}}}, 'customer_sid': {'type': 'string'}, 'added': {'type': 'string', 'format': 'integer'}, 'season': {'type': 'integer'}, 'direct_source': {'type': 'string'}}}}}}}, 'required': ['info', 'seasons', 'episodes']}"}, "pyxtream.schemaValidator.series_info_schema": {"fullname": "pyxtream.schemaValidator.series_info_schema", "modulename": "pyxtream.schemaValidator", "qualname": "series_info_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Series', 'description': 'xtream API Series Info Schema', 'type': 'object', 'properties': {'name': {'type': 'string'}, 'cover': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, 'plot': {'type': 'string'}, 'cast': {'type': 'string'}, 'director': {'type': 'string'}, 'genre': {'type': 'string'}, 'releaseDate': {'type': 'string', 'format': 'date'}, 'last_modified': {'type': 'string', 'format': 'integer'}, 'rating': {'type': 'string', 'format': 'integer'}, 'rating_5based': {'type': 'number'}, 'backdrop_path': {'anyOf': [{'type': 'array', 'items': {'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}}, {'type': 'string'}]}, 'youtube_trailed': {'type': 'string'}, 'episode_run_time': {'type': 'string', 'format': 'integer'}, 'category_id': {'type': 'string', 'format': 'integer'}}, 'required': ['name', 'category_id']}"}, "pyxtream.schemaValidator.live_schema": {"fullname": "pyxtream.schemaValidator.live_schema", "modulename": "pyxtream.schemaValidator", "qualname": "live_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Live', 'description': 'xtream API Live Schema', 'type': 'object', 'properties': {'num': {'type': 'integer'}, 'name': {'type': 'string'}, 'stream_type': {'type': 'string'}, 'stream_id': {'type': 'integer'}, 'stream_icon': {'anyOf': [{'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, {'type': 'null'}]}, 'epg_channel_id': {'anyOf': [{'type': 'null'}, {'type': 'string'}]}, 'added': {'type': 'string', 'format': 'integer'}, 'is_adult': {'type': 'string', 'format': 'number'}, 'category_id': {'type': 'string'}, 'custom_sid': {'type': 'string'}, 'tv_archive': {'type': 'number'}, 'direct_source': {'type': 'string'}, 'tv_archive_duration': {'anyOf': [{'type': 'number'}, {'type': 'string', 'format': 'integer'}]}}}"}, "pyxtream.schemaValidator.vod_schema": {"fullname": "pyxtream.schemaValidator.vod_schema", "modulename": "pyxtream.schemaValidator", "qualname": "vod_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'VOD', 'description': 'xtream API VOD Schema', 'type': 'object', 'properties': {'num': {'type': 'integer'}, 'name': {'type': 'string'}, 'stream_type': {'type': 'string'}, 'stream_id': {'type': 'integer'}, 'stream_icon': {'anyOf': [{'type': 'string', 'format': 'uri', 'qt-uri-protocols': ['http', 'https']}, {'type': 'null'}]}, 'rating': {'anyOf': [{'type': 'null'}, {'type': 'string', 'format': 'integer'}, {'type': 'number'}]}, 'rating_5based': {'type': 'number'}, 'added': {'type': 'string', 'format': 'integer'}, 'is_adult': {'type': 'string', 'format': 'number'}, 'category_id': {'type': 'string'}, 'container_extension': {'type': 'string'}, 'custom_sid': {'type': 'string'}, 'direct_source': {'type': 'string'}}}"}, "pyxtream.schemaValidator.channel_schema": {"fullname": "pyxtream.schemaValidator.channel_schema", "modulename": "pyxtream.schemaValidator", "qualname": "channel_schema", "kind": "variable", "doc": "

    \n", "default_value": "{}"}, "pyxtream.schemaValidator.group_schema": {"fullname": "pyxtream.schemaValidator.group_schema", "modulename": "pyxtream.schemaValidator", "qualname": "group_schema", "kind": "variable", "doc": "

    \n", "default_value": "{'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'https://example.com/product.schema.json', 'title': 'Group', 'description': 'xtream API Group Schema', 'type': 'object', 'properties': {'category_id': {'type': 'string'}, 'category_name': {'type': 'string'}, 'parent_id': {'type': 'integer'}}}"}, "pyxtream.schemaValidator.schemaValidator": {"fullname": "pyxtream.schemaValidator.schemaValidator", "modulename": "pyxtream.schemaValidator", "qualname": "schemaValidator", "kind": "function", "doc": "

    \n", "signature": "(jsonData: str, schemaType: pyxtream.schemaValidator.SchemaType) -> bool:", "funcdef": "def"}, "pyxtream.version": {"fullname": "pyxtream.version", "modulename": "pyxtream.version", "kind": "module", "doc": "

    \n"}}, "docInfo": {"pyxtream": {"qualname": 0, "fullname": 1, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.progress": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.progress.progress": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 3}, "pyxtream.pyxtream": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 66}, "pyxtream.pyxtream.Channel": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.info": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.id": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.group_title": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.title": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.url": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.stream_type": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.group_id": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.is_adult": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.added": {"qualname": 2, "fullname": 4, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.epg_channel_id": {"qualname": 4, "fullname": 6, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.age_days_from_added": {"qualname": 5, "fullname": 7, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.date_now": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Channel.export_json": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 26, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.group_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.group_id": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 16, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.channels": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.series": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.region_shortname": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Group.region_longname": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 32, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.title": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.info": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.group_title": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.id": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.container_extension": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.episode_number": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.av_info": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Episode.url": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.logo": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.logo_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.series_id": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.plot": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.youtube_trailer": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.genre": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.raw": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.xtream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.seasons": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.episodes": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.url": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Serie.export_json": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 9, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.Season.episodes": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.__init__": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 189, "bases": 0, "doc": 209}, "pyxtream.pyxtream.XTream.name": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.server": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.secure_server": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.username": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.password": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.base_url": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.base_url_ssl": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.cache_path": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.account_expiration": {"qualname": 3, "fullname": 5, "annotation": 3, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.live_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.vod_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series_type": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 5, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.auth_data": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.authorization": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 14, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.groups": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.channels": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies_30days": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.movies_7days": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.connection_headers": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.state": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 11, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.hide_adult_content": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 2, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.validate_json": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.download_progress": {"qualname": 3, "fullname": 5, "annotation": 2, "default_value": 16, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.app_fullpath": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.html_template_folder": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_download_progress": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 29, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_last_7days": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.search_stream": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 141, "bases": 0, "doc": 85}, "pyxtream.pyxtream.XTream.download_video": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 36}, "pyxtream.pyxtream.XTream.authenticate": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 5}, "pyxtream.pyxtream.XTream.load_iptv": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 83}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 22, "bases": 0, "doc": 18}, "pyxtream.pyxtream.XTream.vodInfoByID": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 22, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 17, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.allEpg": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_URL": {"qualname": 4, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"qualname": 6, "fullname": 8, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"qualname": 7, "fullname": 9, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"qualname": 9, "fullname": 11, "annotation": 0, "default_value": 0, "signature": 25, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"qualname": 8, "fullname": 10, "annotation": 0, "default_value": 0, "signature": 20, "bases": 0, "doc": 3}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"qualname": 5, "fullname": 7, "annotation": 0, "default_value": 0, "signature": 14, "bases": 0, "doc": 3}, "pyxtream.rest_api": {"qualname": 0, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 15, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.response": {"qualname": 2, "fullname": 5, "annotation": 4, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.function_name": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.EndpointAction.action": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap": {"qualname": 1, "fullname": 4, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 49}, "pyxtream.rest_api.FlaskWrap.__init__": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 102, "bases": 0, "doc": 151}, "pyxtream.rest_api.FlaskWrap.home_template": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 34, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.host": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 4, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.port": {"qualname": 2, "fullname": 5, "annotation": 2, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.debug": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.app": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.xt": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.rest_api.FlaskWrap.name": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 33}, "pyxtream.rest_api.FlaskWrap.daemon": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 73}, "pyxtream.rest_api.FlaskWrap.run": {"qualname": 2, "fullname": 5, "annotation": 0, "default_value": 0, "signature": 11, "bases": 0, "doc": 53}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"qualname": 3, "fullname": 6, "annotation": 0, "default_value": 0, "signature": 42, "bases": 0, "doc": 3}, "pyxtream.schemaValidator": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 0, "bases": 2, "doc": 3}, "pyxtream.schemaValidator.SchemaType.SERIES": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 8, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.LIVE": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.VOD": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.SchemaType.GROUP": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 7, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.series_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 746, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.series_info_schema": {"qualname": 3, "fullname": 5, "annotation": 0, "default_value": 354, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.live_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 314, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.vod_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 306, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.channel_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 1, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.group_schema": {"qualname": 2, "fullname": 4, "annotation": 0, "default_value": 92, "signature": 0, "bases": 0, "doc": 3}, "pyxtream.schemaValidator.schemaValidator": {"qualname": 1, "fullname": 3, "annotation": 0, "default_value": 0, "signature": 39, "bases": 0, "doc": 3}, "pyxtream.version": {"qualname": 0, "fullname": 2, "annotation": 0, "default_value": 0, "signature": 0, "bases": 0, "doc": 3}}, "length": 161, "save": true}, "index": {"qualname": {"root": {"3": {"0": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}}, "df": 1}}}}}, "docs": {}, "df": 0}, "7": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}}, "df": 2}}}}}, "docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress.progress": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}}, "df": 3}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 4}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.password": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Serie.plot": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel": {"tf": 1}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}}, "df": 21, "s": {"docs": {"pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8}}, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 8}}}, "d": {"docs": {"pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Group.group_id": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 9}, "s": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.episode_number": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}}, "df": 6}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 10, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Group": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 19, "s": {"docs": {"pyxtream.pyxtream.XTream.groups": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.genre": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 18}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}}, "df": 4}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}}, "df": 5}}}, "o": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 2}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}, "pyxtream.pyxtream.Serie.url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 20}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.username": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 5, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}}, "df": 4}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.Serie.plot": {"tf": 1}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}, "pyxtream.pyxtream.Serie.genre": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Serie.url": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 15, "s": {"docs": {"pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 14}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Season": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.pyxtream.Serie.seasons": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 7}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 2}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "v": {"docs": {"pyxtream.pyxtream.Episode.av_info": {"tf": 1}}, "df": 1}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.allEpg": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}}, "df": 2}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 2}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}, "a": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3}}}}}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 12}}}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.EndpointAction.response": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 3}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.XTream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}, "pyxtream.pyxtream.XTream.username": {"tf": 1}, "pyxtream.pyxtream.XTream.password": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 61}}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}}, "df": 2}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 9}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 8, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}}, "df": 1}}}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 3}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 1}}}}}}, "fullname": {"root": {"3": {"0": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}}, "df": 1}}}}}, "docs": {}, "df": 0}, "7": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}}, "df": 2}}}}}, "docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8, "p": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream": {"tf": 1}, "pyxtream.progress": {"tf": 1}, "pyxtream.progress.progress": {"tf": 1}, "pyxtream.pyxtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.added": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.channels": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.series": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.episodes": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.groups": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.channels": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.state": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1.4142135623730951}, "pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}, "pyxtream.schemaValidator": {"tf": 1}, "pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}, "pyxtream.version": {"tf": 1}}, "df": 161}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress": {"tf": 1}, "pyxtream.progress.progress": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}}, "df": 4}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 4}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.password": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Serie.plot": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel": {"tf": 1}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}}, "df": 21, "s": {"docs": {"pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}}, "df": 2}}}}}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.cache_path": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}}, "df": 2, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}}, "df": 3}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 8}}, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 8}}}, "d": {"docs": {"pyxtream.pyxtream.Channel.id": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Group.group_id": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 9}, "s": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.name": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 8}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Episode.episode_number": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.logo": {"tf": 1}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}}, "df": 6}}, "n": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 10, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}}, "df": 1}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Group": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.name": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.channels": {"tf": 1}, "pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 19, "s": {"docs": {"pyxtream.pyxtream.XTream.groups": {"tf": 1}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.genre": {"tf": 1}}, "df": 1}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 18}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.group_title": {"tf": 1}, "pyxtream.pyxtream.Channel.title": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}}, "df": 4}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Group.group_type": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}}, "df": 5}}}, "o": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 2}}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.Channel.url": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}, "pyxtream.pyxtream.Serie.url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 20}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.username": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 5, "s": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}}, "df": 4}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}}, "df": 2}}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.name": {"tf": 1}, "pyxtream.pyxtream.Serie.logo": {"tf": 1}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.Serie.plot": {"tf": 1}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}, "pyxtream.pyxtream.Serie.genre": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Serie.url": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 15, "s": {"docs": {"pyxtream.pyxtream.Group.series": {"tf": 1}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 14}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Season": {"tf": 1}, "pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.Season.name": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.pyxtream.Serie.seasons": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}}, "df": 1, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.secure_server": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}}, "df": 1}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator": {"tf": 1}, "pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1.4142135623730951}}, "df": 15}}}}}}}}}, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 7}}}}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 2}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "v": {"docs": {"pyxtream.pyxtream.Episode.av_info": {"tf": 1}}, "df": 1}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.allEpg": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}}, "df": 2}, "i": {"docs": {"pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 18}}, "n": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 5}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.title": {"tf": 1}, "pyxtream.pyxtream.Episode.name": {"tf": 1}, "pyxtream.pyxtream.Episode.info": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1}, "pyxtream.pyxtream.Episode.id": {"tf": 1}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1}, "pyxtream.pyxtream.Episode.logo": {"tf": 1}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1}, "pyxtream.pyxtream.Episode.url": {"tf": 1}}, "df": 14, "s": {"docs": {"pyxtream.pyxtream.Serie.episodes": {"tf": 1}, "pyxtream.pyxtream.Season.episodes": {"tf": 1}}, "df": 2}}}}}}}, "x": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Episode.container_extension": {"tf": 1}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1}}, "df": 1}, "a": {"docs": {"pyxtream.pyxtream.XTream.auth_data": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3}}}}}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}}, "df": 1}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}}, "df": 1}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 12}}}}}}}}}, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.Channel.raw": {"tf": 1}, "pyxtream.pyxtream.Group.raw": {"tf": 1}, "pyxtream.pyxtream.Episode.raw": {"tf": 1}, "pyxtream.pyxtream.Serie.raw": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api": {"tf": 1}, "pyxtream.rest_api.EndpointAction": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 18}, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.EndpointAction.response": {"tf": 1}}, "df": 1}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 3}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1}}, "df": 1}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.xt": {"tf": 1}}, "df": 1, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Serie.xtream": {"tf": 1}, "pyxtream.pyxtream.XTream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.name": {"tf": 1}, "pyxtream.pyxtream.XTream.server": {"tf": 1}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1}, "pyxtream.pyxtream.XTream.username": {"tf": 1}, "pyxtream.pyxtream.XTream.password": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}}, "df": 61}}}}}}, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.base_url": {"tf": 1}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1}}, "df": 2}}}, "y": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 9}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 8, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}}, "df": 1}}}}}}}}}}, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.version": {"tf": 1}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}}, "df": 3}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 1}}}}}}, "annotation": {"root": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 13, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.Channel.stream_type": {"tf": 1}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1}}, "df": 4}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 4}}}, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.date_now": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 2}}}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.pyxtream.XTream.account_expiration": {"tf": 1}}, "df": 1}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.rest_api.EndpointAction.response": {"tf": 1}}, "df": 1}}}}}, "w": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.EndpointAction.response": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.EndpointAction.response": {"tf": 1}}, "df": 1}}}}}}}}}}, "default_value": {"root": {"0": {"docs": {"pyxtream.pyxtream.Channel.is_adult": {"tf": 1}, "pyxtream.pyxtream.Channel.added": {"tf": 1}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1}}, "df": 5}, "1": {"2": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}, "docs": {"pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}}, "df": 2}, "2": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}}, "df": 1}, "3": {"docs": {"pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}}, "df": 1}, "4": {"docs": {"pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}}, "df": 1}, "5": {"docs": {"pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}}, "df": 1, "b": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}}}}, "6": {"docs": {"pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 1}, "docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1}, "pyxtream.pyxtream.XTream.authorization": {"tf": 2}, "pyxtream.pyxtream.XTream.groups": {"tf": 1}, "pyxtream.pyxtream.XTream.channels": {"tf": 1}, "pyxtream.pyxtream.XTream.series": {"tf": 1}, "pyxtream.pyxtream.XTream.movies": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1}, "pyxtream.pyxtream.XTream.state": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_schema": {"tf": 13.96424004376894}, "pyxtream.schemaValidator.series_info_schema": {"tf": 9.273618495495704}, "pyxtream.schemaValidator.live_schema": {"tf": 9}, "pyxtream.schemaValidator.vod_schema": {"tf": 8.888194417315589}, "pyxtream.schemaValidator.channel_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 4.47213595499958}}, "df": 69, "x": {"2": {"7": {"docs": {"pyxtream.pyxtream.Channel.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Group.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.title": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.info": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Season.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.name": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.username": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.password": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.authorization": {"tf": 2.8284271247461903}, "pyxtream.pyxtream.XTream.state": {"tf": 2}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_schema": {"tf": 18.601075237738275}, "pyxtream.schemaValidator.series_info_schema": {"tf": 12.806248474865697}, "pyxtream.schemaValidator.live_schema": {"tf": 11.832159566199232}, "pyxtream.schemaValidator.vod_schema": {"tf": 11.74734012447073}, "pyxtream.schemaValidator.group_schema": {"tf": 6.324555320336759}}, "df": 50}, "docs": {}, "df": 0}, "docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.live_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 3}}}, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 10}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vod_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.series_type": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}}, "df": 2, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 5}, "pyxtream.schemaValidator.series_info_schema": {"tf": 3.7416573867739413}, "pyxtream.schemaValidator.live_schema": {"tf": 3.1622776601683795}, "pyxtream.schemaValidator.vod_schema": {"tf": 3.1622776601683795}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}, "pyxtream.schemaValidator.group_schema": {"tf": 2}}, "df": 5, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 6}}}}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2.8284271247461903}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 4}}}, "p": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.authorization": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 1}}}}}, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 4}}}}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4}}}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 6}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}, "d": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}, "n": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.state": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1}}, "df": 2}}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 3.4641016151377544}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}}, "df": 4}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 2.6457513110645907}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1}}, "df": 10}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 8}}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "/": {"2": {"0": {"2": {"0": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}}}}}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.validate_json": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.download_progress": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 6}, "pyxtream.schemaValidator.series_info_schema": {"tf": 4.123105625617661}, "pyxtream.schemaValidator.live_schema": {"tf": 4.242640687119285}, "pyxtream.schemaValidator.vod_schema": {"tf": 4.242640687119285}, "pyxtream.schemaValidator.group_schema": {"tf": 2}}, "df": 5}}}, "v": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}}, "df": 1}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}}, "df": 4}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 2}}}}, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 3, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.7320508075688772}}, "df": 1}}, "t": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4, ":": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}, "e": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 4}}, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1}}, "df": 1}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "g": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 1}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.4142135623730951}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1}, "pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 3.3166247903554}, "pyxtream.schemaValidator.series_info_schema": {"tf": 2}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 2}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2.23606797749979}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.live_schema": {"tf": 2}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.group_schema": {"tf": 1.7320508075688772}}, "df": 5}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 2}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}, "s": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}}, "df": 2}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2}}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1.4142135623730951}}, "df": 5}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}}, "df": 2, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}}, "df": 1}}}}}}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}, "pyxtream.schemaValidator.live_schema": {"tf": 1}, "pyxtream.schemaValidator.vod_schema": {"tf": 1}, "pyxtream.schemaValidator.group_schema": {"tf": 1}}, "df": 5}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.series_schema": {"tf": 1}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1}}, "df": 2}}}}}}}}}, "signature": {"root": {"0": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}}, "df": 1}, "2": {"8": {"8": {"0": {"0": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "3": {"9": {"docs": {"pyxtream.progress.progress": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 2.8284271247461903}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 4}, "docs": {}, "df": 0}, "5": {"0": {"0": {"0": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {}, "df": 0}, "docs": {"pyxtream.progress.progress": {"tf": 4.898979485566356}, "pyxtream.pyxtream.Channel.__init__": {"tf": 4.47213595499958}, "pyxtream.pyxtream.Channel.export_json": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.Group.__init__": {"tf": 4.47213595499958}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.Episode.__init__": {"tf": 4.898979485566356}, "pyxtream.pyxtream.Serie.__init__": {"tf": 4}, "pyxtream.pyxtream.Serie.export_json": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.Season.__init__": {"tf": 2.8284271247461903}, "pyxtream.pyxtream.XTream.__init__": {"tf": 12.12435565298214}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 4.898979485566356}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 10.488088481701515}, "pyxtream.pyxtream.XTream.download_video": {"tf": 4.47213595499958}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 4.242640687119285}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 4.242640687119285}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 3.7416573867739413}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 3.4641016151377544}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 4}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 4}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 4}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 4}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 4.47213595499958}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 4}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 3.4641016151377544}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 3.4641016151377544}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 8.94427190999916}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 3.1622776601683795}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 5.830951894845301}, "pyxtream.schemaValidator.schemaValidator": {"tf": 5.656854249492381}}, "df": 42, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}}, "df": 3}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 2}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.progress.progress": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 21, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 11}}}}}, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.pyxtream.Channel.export_json": {"tf": 1}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 32}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}}, "df": 5}}}}, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1}}, "df": 1}}}}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1.4142135623730951}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 4}}}}}}, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 4}}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.Channel.__init__": {"tf": 1}, "pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1}}, "df": 4}}, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 4}}, "d": {"docs": {"pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1}}, "df": 14}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.Group.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 3}}}, "e": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "e": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Episode.__init__": {"tf": 1}}, "df": 1}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "d": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.Season.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 5}}}, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.7320508075688772}}, "df": 5}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "y": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 5}}}}, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.EndpointAction.__init__": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1}}, "df": 2}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.schemaValidator.schemaValidator": {"tf": 1}}, "df": 1}}}}}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}}, "df": 1}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1}}, "df": 2}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}}}, "bases": {"root": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.schemaValidator.SchemaType": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "doc": {"root": {"1": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}, "2": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "docs": {"pyxtream": {"tf": 1.7320508075688772}, "pyxtream.progress": {"tf": 1.7320508075688772}, "pyxtream.progress.progress": {"tf": 1.7320508075688772}, "pyxtream.pyxtream": {"tf": 6}, "pyxtream.pyxtream.Channel": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.group_title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.stream_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.group_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.is_adult": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.added": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.epg_channel_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.age_days_from_added": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.date_now": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Channel.export_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.group_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.group_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.convert_region_shortname_to_fullname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.channels": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.series": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.region_shortname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Group.region_longname": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.group_title": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.container_extension": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.episode_number": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.av_info": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Episode.url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.logo": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.logo_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.series_id": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.plot": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.youtube_trailer": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.genre": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.raw": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.xtream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.seasons": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.episodes": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Serie.export_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.Season.episodes": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.__init__": {"tf": 5.385164807134504}, "pyxtream.pyxtream.XTream.name": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.server": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.secure_server": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.username": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.password": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.base_url": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.base_url_ssl": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.cache_path": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.account_expiration": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.vod_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series_type": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.auth_data": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.authorization": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.groups": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.channels": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies_30days": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.movies_7days": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.connection_headers": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.state": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.hide_adult_content": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.live_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.vod_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.series_catch_all_group": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.threshold_time_sec": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.validate_json": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.download_progress": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.app_fullpath": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.html_template_folder": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_download_progress": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_last_7days": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 3.605551275463989}, "pyxtream.pyxtream.XTream.download_video": {"tf": 2.6457513110645907}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 4.123105625617661}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.vodInfoByID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.liveEpgByStream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.liveEpgByStreamAndLimit": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.allLiveEpgByStream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.allEpg": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_categories_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_streams_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_streams_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_cat_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_streams_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_vod_streams_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_cat_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_URL": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_URL_by_category": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_series_info_URL_by_ID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_VOD_info_URL_by_ID": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_live_epg_URL_by_stream_and_limit": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_all_live_epg_URL_by_stream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.get_all_epg_URL": {"tf": 1.7320508075688772}, "pyxtream.rest_api": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.response": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.function_name": {"tf": 1.7320508075688772}, "pyxtream.rest_api.EndpointAction.action": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 5.5677643628300215}, "pyxtream.rest_api.FlaskWrap.home_template": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.host": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.port": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.debug": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.app": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.xt": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 3}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.add_endpoint": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.SERIES": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.SERIES_INFO": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.LIVE": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.VOD": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.CHANNEL": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.SchemaType.GROUP": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.series_info_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.live_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.vod_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.channel_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.group_schema": {"tf": 1.7320508075688772}, "pyxtream.schemaValidator.schemaValidator": {"tf": 1.7320508075688772}, "pyxtream.version": {"tf": 1.7320508075688772}}, "df": 161, "p": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 3}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 2}, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}, "m": {"3": {"docs": {}, "df": 0, "u": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.7320508075688772}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}, "t": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, ":": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {"pyxtream.pyxtream": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}}}}}}, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "w": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "d": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 2}, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}}}}}}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}}, "df": 3}}}}}}, "b": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}}, "df": 3}}}}}}, "o": {"docs": {}, "df": 0, "f": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 2.6457513110645907}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}}, "df": 4, "f": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "l": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2.449489742783178}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 2}}, "df": 2}}}}}}}, "n": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}, "r": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}, "b": {"docs": {}, "df": 0, "j": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 3}}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 5}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 3.1622776601683795}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 3}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 2.449489742783178}}, "df": 9, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 3}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "g": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "o": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 3}}}, "o": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 2}, "pyxtream.pyxtream.XTream.authenticate": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 8, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 2}}}, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}, "w": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 2}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": null}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": null}, "pyxtream.rest_api.FlaskWrap.name": {"tf": null}, "pyxtream.rest_api.FlaskWrap.run": {"tf": null}}, "df": 4}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}, "/": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "x": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "/": {"docs": {}, "df": 0, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}}}}}}}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3}}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}, "f": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 6}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 6, "m": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "k": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}}}, "s": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.449489742783178}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 8, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "o": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}}}}}}, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 6}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 2}}}}}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 5}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 2}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 5}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}, "m": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "f": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "b": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}}}}, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "b": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "p": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.7320508075688772}}, "df": 1}}}}}, "e": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.4142135623730951}}, "df": 1}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1.4142135623730951}}, "df": 1, "u": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1}}}}}}, "p": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}}}, "c": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}}, "b": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}, "t": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2.23606797749979}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1.7320508075688772}}, "df": 3, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 3}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}, "a": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}, "f": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}, "e": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 2.23606797749979}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2}}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 2}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1.7320508075688772}}, "df": 2}}}, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2}, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}, "h": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 2}}}}}, "c": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "a": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "m": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1, "o": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 4, "e": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 2}, "h": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "n": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1.4142135623730951}}, "df": 3}}}, "u": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream": {"tf": 1}, "pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 5, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 2, "i": {"docs": {}, "df": 0, "z": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 2}, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "v": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}}}}, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "v": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 2}}}, "f": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 5}, "g": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1.7320508075688772}}, "df": 1, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 1}}}}}}}}}}}}}, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 2.8284271247461903}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 2.23606797749979}}, "df": 3}, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "u": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}}}, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 2}}}, "r": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}}}}}, "i": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "q": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}}, "t": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 4}}}}, "r": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}}}, "p": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}}, "g": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "x": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}, "s": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "v": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}}}}}, "d": {"docs": {}, "df": 0, "u": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}}}}}}, "a": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "b": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 3, "e": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.7320508075688772}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 6, "f": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}}}}, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}, "y": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.name": {"tf": 1}}, "df": 3}, "a": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "w": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 3}, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 3}}, "t": {"docs": {}, "df": 0, "h": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "h": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 4, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "l": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}}, "df": 1}}}, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "k": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}, "a": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}, "y": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "c": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}}, "a": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}, "i": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "g": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}}, "g": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.authenticate": {"tf": 1}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 2.23606797749979}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 2}}, "v": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}}, "m": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.rest_api.FlaskWrap": {"tf": 1}}, "df": 1}}}}}}, "e": {"docs": {}, "df": 0, "f": {"docs": {}, "df": 0, "t": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}, "e": {"docs": {}, "df": 0, "m": {"docs": {}, "df": 0, "p": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.4142135623730951}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}, "pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 3}}}}, "n": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "b": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1.7320508075688772}}, "df": 1}}}}, "t": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}}, "p": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}, "pyxtream.pyxtream.XTream.get_series_info_by_id": {"tf": 1}}, "df": 2}}}}}}}, "r": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1}}, "df": 1}}}}, "x": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "n": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1}}, "df": 1}}}}, "l": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}}, "df": 1}}}}, "v": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "l": {"docs": {}, "df": 0, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}, "i": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "i": {"docs": {}, "df": 0, "t": {"docs": {}, "df": 0, "y": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 1}}, "df": 1}}}}}, "u": {"docs": {}, "df": 0, "e": {"docs": {"pyxtream.rest_api.FlaskWrap.daemon": {"tf": 1.4142135623730951}}, "df": 1}}}}, "i": {"docs": {}, "df": 0, "d": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "o": {"docs": {"pyxtream.pyxtream.XTream.download_video": {"tf": 1}}, "df": 1}}}}, "o": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.load_iptv": {"tf": 1.4142135623730951}}, "df": 1}}}, "j": {"docs": {}, "df": 0, "s": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "n": {"docs": {"pyxtream.pyxtream.XTream.__init__": {"tf": 2}, "pyxtream.pyxtream.XTream.search_stream": {"tf": 1}}, "df": 2}}}}, "k": {"docs": {}, "df": 0, "e": {"docs": {}, "df": 0, "y": {"docs": {}, "df": 0, "w": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "d": {"docs": {"pyxtream.pyxtream.XTream.search_stream": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1.4142135623730951}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 3}}}}}}, "w": {"docs": {}, "df": 0, "a": {"docs": {}, "df": 0, "r": {"docs": {}, "df": 0, "g": {"docs": {}, "df": 0, "s": {"docs": {"pyxtream.rest_api.FlaskWrap.__init__": {"tf": 1}, "pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 2}}}}}}, "y": {"docs": {}, "df": 0, "o": {"docs": {}, "df": 0, "u": {"docs": {"pyxtream.rest_api.FlaskWrap.run": {"tf": 1}}, "df": 1}}}}}}, "pipeline": ["trimmer"], "_isPrebuiltIndex": true}; // mirrored in build-search-index.js (part 1) // Also split on html tags. this is a cheap heuristic, but good enough. diff --git a/run_tests.sh b/run_tests.sh index ce16506..19d2e3d 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -1 +1 @@ -poetry run pytest --cov=pyxtream pyxtream \ No newline at end of file +poetry run pytest --cov=pyxtream test/test_pyxtream.py \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index a0c1079..0000000 --- a/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -import os -from setuptools import setup, find_packages - -with open("README.md", "r", encoding='utf-8') as fh: - long_description = fh.read() - -main_ns = {} -ver_path = os.path.join('pyxtream', 'version.py') -with open(ver_path, encoding='utf-8') as ver_file: - exec(ver_file.read(), main_ns) - -setup( - name='pyxtream', - version=main_ns['__version__'], - author=main_ns['__author__'], - author_email=main_ns['__author_email__'], - description="xtream IPTV loader", - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/superolmo/pyxtream", - packages=find_packages(), - license="GPL3", - classifiers=[ - "Development Status :: 4 - Beta", - "Environment :: Console", - "Intended Audience :: Developers", - "Programming Language :: Python :: 3 :: Only", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Operating System :: OS Independent", - "Natural Language :: English" - ], - install_requires=[ - 'jsonschema', - 'requests', - ], - extras_require={ - "REST_API": ["Flask>=1.1.2",], - } - ) \ No newline at end of file From 12943bb62394880b33a98e9eab5ec6df31fa2dfd Mon Sep 17 00:00:00 2001 From: Claudio Olmi Date: Sun, 23 Feb 2025 12:10:19 -0600 Subject: [PATCH 41/41] Updated extra info --- PYPI.md | 2 ++ README.md | 39 ++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/PYPI.md b/PYPI.md index e8fe20c..f23fc70 100644 --- a/PYPI.md +++ b/PYPI.md @@ -36,6 +36,8 @@ poetry publish -u __token__ -p # Upload to PYPI +References: https://www.digitalocean.com/community/tutorials/how-to-publish-python-packages-to-pypi-using-poetry-on-ubuntu-22-04 + ```shell poetry publish ``` diff --git a/README.md b/README.md index 94537fe..42cee64 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ PyXtream loads the xtream IPTV content from a provider server. Groups, Channels, Series are all organized in dictionaries. Season and Episodes are retrieved as needed. It includes functions for searching streams and downloading. -This library was originally designed to work with Hypnotix at https://github.com/linuxmint/hypnotix +This library was originally designed to work with Hypnotix at https://github.com/linuxmint/hypnotix, so its compatibility with Hypnotix takes precedence. # Installing @@ -34,19 +34,17 @@ else: print("Could not connect") ``` -Once completed, all the data can be found in xTream.groups, xTream.channels, xTream.movies, xTream.series. Series do not contains the information for all the Seasons and Episodes. Those are loaded separately when needed by calling the following function using a Series object from xTream.series array of dictionaries. +Once completed, all the data can be found in `xTream.groups`, `xTream.channels`, `xTream.movies`, `xTream.series`. Series do not contains the information for all the Seasons and Episodes. Those are loaded separately when needed by calling the following function using a Series object from `xTream.series` array of dictionaries. ```python xt.get_series_info_by_id(series_obj) ``` -At this point, the series_obj will have both Seasons and Episodes populated. - -If you have installed Flask, the REST Api will be turned ON automatically. At this point, there is no method to turn it off. Maybe in a future version. +At this point, the `series_obj` will have both Seasons and Episodes populated. ## Functional Test -Please modify the functional_test.py file with your provider information, then start the application. +Please modify the `functional_test.py` file with your provider information, then start the application. ```shell python3 functional_test.py @@ -54,21 +52,15 @@ python3 functional_test.py The functional test will allow you to authenticate on startup, load and search streams. If Flask is installed, a simple website will be available at http://localhost:5000 to allow you to search and play streams. -## Interesting Work by somebody else - -- xtreamPOC - https://github.com/sght500/xtreamPOC - Project is a Proof of Concept (POC) that leverages pyxtream, MPV, and NiceGUI to demonstrate the use of Xtream Portal Codes. - -So far there is no ready to use Transport Stream library for playing live stream. +## Applications using PyXtream -- This is the library to convert TS to MP4 - - https://github.com/videojs/mux.js/ +Applications using PyXtream PYPI package -- More on above, but same problem. XMLHttpRequest waits until the whole TS file is completely loaded. It does not work for live video streams - - https://developpaper.com/play-ts-video-directly-on-the-web/ +- xtreamPOC - https://github.com/sght500/xtreamPOC - Project is a Proof of Concept (POC) that leverages pyxtream, MPV, and NiceGUI to demonstrate the use of Xtream Portal Codes. -- This below will allow me to process chunks of data - - https://stackoverflow.com/questions/37402716/handle-xmlhttprequest-response-with-large-data +Applications using PyXtream files +- Hypnotix - https://github.com/linuxmint/hypnotix - Hypnotix is an IPTV streaming application with support for live TV, movies and series. # API @@ -128,3 +120,16 @@ Follows the Semantic Versioning from https://semver.org/ | 2021-06-05 | 0.1.2 | - Fixed Server Name | | 2021-06-04 | 0.1.1 | - Updated README.md | | 2021-06-04 | 0.1.0 | - Initial Release | + +## Interesting content that could be used for future development + +So far there is no ready to use Transport Stream library for playing live stream. + +- This is the library to convert TS to MP4 + - https://github.com/videojs/mux.js/ + +- More on above, but same problem. XMLHttpRequest waits until the whole TS file is completely loaded. It does not work for live video streams + - https://developpaper.com/play-ts-video-directly-on-the-web/ + +- This below will allow me to process chunks of data + - https://stackoverflow.com/questions/37402716/handle-xmlhttprequest-response-with-large-data