Skip to content

Commit 5a2453e

Browse files
authored
Add FileLions extractor and improve eval_solver (#135)
* Add FileLions extractor and support for p.a.c.k.e.d pattern array * Improve eval_solver URL creation * Support also hls3 link * Improve eval_resolver failing when no pattern matches * Improve URL generation a bit more * Apply more review suggestions * Further simplify URL creation and fix error message * Remove hls3 link again because it's not always working
1 parent 840b75c commit 5a2453e

7 files changed

Lines changed: 47 additions & 14 deletions

File tree

mediaflow_proxy/extractors/factory.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from mediaflow_proxy.extractors.base import BaseExtractor, ExtractorError
44
from mediaflow_proxy.extractors.dlhd import DLHDExtractor
55
from mediaflow_proxy.extractors.doodstream import DoodStreamExtractor
6+
from mediaflow_proxy.extractors.filelions import FileLionsExtractor
67
from mediaflow_proxy.extractors.livetv import LiveTVExtractor
78
from mediaflow_proxy.extractors.maxstream import MaxstreamExtractor
89
from mediaflow_proxy.extractors.mixdrop import MixdropExtractor
@@ -19,6 +20,7 @@ class ExtractorFactory:
1920

2021
_extractors: Dict[str, Type[BaseExtractor]] = {
2122
"Doodstream": DoodStreamExtractor,
23+
"FileLions": FileLionsExtractor,
2224
"Uqload": UqloadExtractor,
2325
"Mixdrop": MixdropExtractor,
2426
"Streamtape": StreamtapeExtractor,

mediaflow_proxy/extractors/fastream.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ def __init__(self, *args, **kwargs):
1414

1515
async def extract(self, url: str, **kwargs) -> Dict[str, Any]:
1616
headers = {'Accept': '*/*', 'Connection': 'keep-alive','Accept-Language': 'en-US,en;q=0.5','Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:138.0) Gecko/20100101 Firefox/138.0'}
17-
pattern = r'file:"(.*?)"'
17+
patterns = [r'file:"(.*?)"']
1818

19-
final_url = await eval_solver(self, url, headers, pattern)
19+
final_url = await eval_solver(self, url, headers, patterns)
2020

2121
self.base_headers["referer"] = f'https://{url.replace("https://","").split("/")[0]}/'
2222
self.base_headers["origin"] = f'https://{url.replace("https://","").split("/")[0]}'
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from typing import Dict, Any
2+
3+
from mediaflow_proxy.extractors.base import BaseExtractor
4+
from mediaflow_proxy.utils.packed import eval_solver
5+
6+
class FileLionsExtractor(BaseExtractor):
7+
def __init__(self, *args, **kwargs):
8+
super().__init__(*args, **kwargs)
9+
self.mediaflow_endpoint = "hls_manifest_proxy"
10+
11+
async def extract(self, url: str, **kwargs) -> Dict[str, Any]:
12+
headers = {}
13+
patterns = [ # See https://github.com/Gujal00/ResolveURL/blob/master/script.module.resolveurl/lib/resolveurl/plugins/filelions.py
14+
r'''sources:\s*\[{file:\s*["'](?P<url>[^"']+)''',
15+
r'''["']hls[24]["']:\s*["'](?P<url>[^"']+)'''
16+
]
17+
18+
final_url = await eval_solver(self, url, headers, patterns)
19+
20+
self.base_headers["referer"] = url
21+
return {
22+
"destination_url": final_url,
23+
"request_headers": self.base_headers,
24+
"mediaflow_endpoint": self.mediaflow_endpoint,
25+
}

mediaflow_proxy/extractors/mixdrop.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ async def extract(self, url: str, **kwargs) -> Dict[str, Any]:
1313
url = url.replace("club", "ps").split("/2")[0]
1414

1515
headers = {"accept-language": "en-US,en;q=0.5"}
16-
pattern = r'MDCore.wurl ?= ?"(.*?)"'
16+
patterns = [r'MDCore.wurl ?= ?"(.*?)"']
1717

18-
final_url = f"https:{await eval_solver(self, url, headers, pattern)}"
18+
final_url = await eval_solver(self, url, headers, patterns)
1919

2020
self.base_headers["referer"] = url
2121
return {

mediaflow_proxy/extractors/supervideo.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ def __init__(self, *args, **kwargs):
1515

1616
async def extract(self, url: str, **kwargs) -> Dict[str, Any]:
1717
headers = {'Accept': '*/*', 'Connection': 'keep-alive', 'User-Agent': 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.71 Mobile Safari/537.36', 'user-agent': 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.71 Mobile Safari/537.36'}
18-
pattern = r'file:"(.*?)"'
18+
patterns = [r'file:"(.*?)"']
1919

20-
final_url = await eval_solver(self, url, headers, pattern)
20+
final_url = await eval_solver(self, url, headers, patterns)
2121

2222
self.base_headers["referer"] = url
2323
return {

mediaflow_proxy/schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ class MPDSegmentParams(GenericParams):
9696

9797
class ExtractorURLParams(GenericParams):
9898
host: Literal[
99-
"Doodstream", "Mixdrop", "Uqload", "Streamtape", "Supervideo", "VixCloud", "Okru", "Maxstream", "LiveTV", "DLHD", "Fastream"
99+
"Doodstream", "FileLions", "Mixdrop", "Uqload", "Streamtape", "Supervideo", "VixCloud", "Okru", "Maxstream", "LiveTV", "DLHD", "Fastream"
100100
] = Field(..., description="The host to extract the URL from.")
101101
destination: str = Field(..., description="The URL of the stream.", alias="d")
102102
redirect_stream: bool = Field(False, description="Whether to redirect to the stream endpoint automatically.")

mediaflow_proxy/utils/packed.py

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

1515
import re
1616
from bs4 import BeautifulSoup, SoupStrainer
17+
from urllib.parse import urljoin, urlparse
1718
import logging
1819

1920

@@ -142,18 +143,23 @@ class UnpackingError(Exception):
142143

143144

144145

145-
async def eval_solver(self, url: str, headers, pattern: str) -> str:
146+
async def eval_solver(self, url: str, headers: dict[str, str] | None, patterns: list[str]) -> str:
146147
try:
147148
response = await self._make_request(url, headers=headers)
148149
soup = BeautifulSoup(response.text, "lxml",parse_only=SoupStrainer("script"))
149150
script_all = soup.find_all("script")
150151
for i in script_all:
151152
if detect(i.text):
152153
unpacked_code = unpack(i.text)
153-
match = re.search(pattern, unpacked_code)
154-
if match:
155-
m3u8_url = match.group(1)
156-
return m3u8_url
154+
for pattern in patterns:
155+
match = re.search(pattern, unpacked_code)
156+
if match:
157+
extracted_url = match.group(1)
158+
if not urlparse(extracted_url).scheme:
159+
extracted_url = urljoin(url, extracted_url)
160+
161+
return extracted_url
162+
raise UnpackingError("No p.a.c.k.e.d JS found or no pattern matched.")
157163
except Exception as e:
158-
logger.error("Eval solver error\n",e)
159-
raise Exception("Error in eval_solver")
164+
logger.exception("Eval solver error for %s", url)
165+
raise UnpackingError("Error in eval_solver") from e

0 commit comments

Comments
 (0)