plexflow 0.0.64__py3-none-any.whl → 0.0.65__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plexflow/core/torrents/auto/auto_torrents.py +4 -4
- plexflow/core/torrents/providers/ext/ext.py +1 -1
- plexflow/core/torrents/providers/therarbg/therarbg.py +4 -4
- plexflow/core/torrents/providers/tpb/tpb.py +10 -8
- plexflow/core/torrents/providers/yts/yts.py +14 -7
- plexflow/utils/antibot/human_like_requests.py +10 -0
- plexflow/utils/api/context/http.py +32 -5
- plexflow/utils/api/rest/plexful.py +64 -0
- plexflow/utils/api/rest/url_builder.py +9 -0
- plexflow/utils/llm/mistral.py +139 -0
- plexflow/utils/pubsub/produce.py +22 -1
- plexflow/utils/strings/json_extract.py +434 -0
- plexflow/utils/subtitle/search.py +1 -0
- plexflow/utils/torrent/extract/common.py +1 -0
- plexflow/utils/torrent/files.py +49 -5
- plexflow/utils/torrent/hash.py +7 -1
- plexflow/utils/transcribe/speech2text.py +83 -17
- plexflow/utils/video/audio.py +98 -0
- plexflow/utils/video/subtitle.py +32 -61
- {plexflow-0.0.64.dist-info → plexflow-0.0.65.dist-info}/METADATA +5 -3
- {plexflow-0.0.64.dist-info → plexflow-0.0.65.dist-info}/RECORD +23 -18
- {plexflow-0.0.64.dist-info → plexflow-0.0.65.dist-info}/entry_points.txt +3 -0
- {plexflow-0.0.64.dist-info → plexflow-0.0.65.dist-info}/WHEEL +0 -0
@@ -10,17 +10,17 @@ from plexflow.core.torrents.results.torrent import Torrent
|
|
10
10
|
|
11
11
|
class AutoTorrents:
|
12
12
|
@staticmethod
|
13
|
-
def movie(imdb_id: str = None, query: str = None, source: str = 'yts', **kwargs) -> List[Torrent]:
|
13
|
+
def movie(imdb_id: str = None, query: str = None, source: str = 'yts', headless: bool = True, **kwargs) -> List[Torrent]:
|
14
14
|
if source == 'tpb':
|
15
|
-
return TPB(**kwargs).search(query=imdb_id)
|
15
|
+
return TPB(**kwargs).search(query=imdb_id, headless=headless, **kwargs)
|
16
16
|
elif source == 'yts':
|
17
|
-
return YTS(**kwargs).search(query=imdb_id)
|
17
|
+
return YTS(**kwargs).search(query=imdb_id, headless=headless, **kwargs)
|
18
18
|
elif source == 'torrentquest':
|
19
19
|
return TorrentQuest(**kwargs).search(query=query)
|
20
20
|
elif source == 'extratorrent':
|
21
21
|
return ExtraTorrent(**kwargs).search(query=query)
|
22
22
|
elif source == 'therarbg':
|
23
|
-
return TheRarbg(**kwargs).search(query=imdb_id)
|
23
|
+
return TheRarbg(**kwargs).search(query=imdb_id, headless=headless, **kwargs)
|
24
24
|
elif source == 'ext':
|
25
25
|
return Ext(**kwargs).search(query=query)
|
26
26
|
elif source == 'snowfl':
|
@@ -1,17 +1,17 @@
|
|
1
|
-
from plexflow.utils.api.rest.
|
1
|
+
from plexflow.utils.api.rest.plexful import Plexful
|
2
2
|
from plexflow.utils.api.rest.restful import Restful
|
3
3
|
from plexflow.core.torrents.providers.therarbg.utils import TheRarbgSearchResult
|
4
4
|
from plexflow.utils.torrent.extract.therarbg import extract_torrent_results
|
5
5
|
from typing import List
|
6
6
|
|
7
|
-
class TheRarbg(
|
7
|
+
class TheRarbg(Plexful):
|
8
8
|
def __init__(self, base_url: str = 'https://therarbg.com'):
|
9
9
|
super().__init__(base_url=base_url)
|
10
10
|
|
11
|
-
def search(self, query: str) -> List[TheRarbgSearchResult]:
|
11
|
+
def search(self, query: str, headless: bool = True, **kwargs) -> List[TheRarbgSearchResult]:
|
12
12
|
response = self.get(f'/get-posts/keywords:{query}/')
|
13
13
|
|
14
14
|
response.raise_for_status
|
15
15
|
|
16
|
-
data = extract_torrent_results(html=response.text)
|
16
|
+
data = extract_torrent_results(html=response.text if not headless else response.html)
|
17
17
|
return list(map(lambda t: TheRarbgSearchResult(**t), data))
|
@@ -1,17 +1,19 @@
|
|
1
|
-
from plexflow.utils.api.rest.
|
1
|
+
from plexflow.utils.api.rest.plexful import Plexful
|
2
2
|
from plexflow.core.torrents.providers.tpb.utils import TPBSearchResult
|
3
3
|
|
4
|
-
class TPB(
|
4
|
+
class TPB(Plexful):
|
5
5
|
def __init__(self, base_url: str = 'https://apibay.org'):
|
6
6
|
super().__init__(base_url=base_url)
|
7
7
|
|
8
|
-
def search(self, query: str):
|
8
|
+
def search(self, query: str, headless: bool = True, **kwargs) -> list[TPBSearchResult]:
|
9
9
|
response = self.get('/q.php', query_params={
|
10
10
|
'q': query,
|
11
|
-
})
|
12
|
-
|
13
|
-
response.raise_for_status()
|
14
|
-
|
15
|
-
data = response.json()
|
11
|
+
}, headless=headless, **kwargs)
|
16
12
|
|
13
|
+
if headless:
|
14
|
+
response.raise_for_status()
|
15
|
+
data = response.json()
|
16
|
+
else:
|
17
|
+
data = response.json
|
18
|
+
|
17
19
|
return list(map(lambda x: TPBSearchResult(**x), data))
|
@@ -1,18 +1,25 @@
|
|
1
|
-
from plexflow.utils.api.rest.
|
1
|
+
from plexflow.utils.api.rest.plexful import Plexful
|
2
2
|
from plexflow.core.torrents.providers.yts.utils import YTSSearchResult
|
3
3
|
|
4
|
-
class YTS(
|
4
|
+
class YTS(Plexful):
|
5
5
|
def __init__(self, base_url: str = 'https://yts.mx'):
|
6
6
|
super().__init__(base_url=base_url)
|
7
7
|
|
8
|
-
def search(self, query: str):
|
8
|
+
def search(self, query: str, headless: bool = True, **kwargs) -> list[YTSSearchResult]:
|
9
|
+
if 'wait_value' not in kwargs:
|
10
|
+
kwargs['wait_value'] = 'Query was successful'
|
9
11
|
response = self.get(url='/api/v2/list_movies.json', query_params={
|
10
12
|
'query_term': query,
|
11
|
-
})
|
13
|
+
}, headless=headless, **kwargs)
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
if headless:
|
16
|
+
print(response.content)
|
17
|
+
|
18
|
+
response.raise_for_status()
|
19
|
+
|
20
|
+
data = response.json()
|
21
|
+
else:
|
22
|
+
data = response.json
|
16
23
|
|
17
24
|
data = data.get("data", {})
|
18
25
|
movies = data.get("movies", [])
|
@@ -8,6 +8,8 @@ from selenium.webdriver.support.ui import WebDriverWait
|
|
8
8
|
from selenium.webdriver.support import expected_conditions as EC
|
9
9
|
from selenium.common.exceptions import TimeoutException
|
10
10
|
from seleniumbase import SB
|
11
|
+
from bs4 import BeautifulSoup
|
12
|
+
import json
|
11
13
|
|
12
14
|
class HumanLikeRequestCapture:
|
13
15
|
def __init__(self, url, html, screenshot, cookies):
|
@@ -16,6 +18,14 @@ class HumanLikeRequestCapture:
|
|
16
18
|
self.screenshot = screenshot
|
17
19
|
self.cookies = cookies
|
18
20
|
|
21
|
+
@property
|
22
|
+
def json(self):
|
23
|
+
try:
|
24
|
+
soup = BeautifulSoup(self.html, 'html.parser')
|
25
|
+
return json.loads(soup.get_text())
|
26
|
+
except json.decoder.JSONDecodeError:
|
27
|
+
return None
|
28
|
+
|
19
29
|
class HumanLikeRequestSession:
|
20
30
|
def __init__(self, use_xvfb=False):
|
21
31
|
self.use_xvfb = use_xvfb
|
@@ -1,21 +1,47 @@
|
|
1
1
|
import requests
|
2
|
+
from requests.adapters import HTTPAdapter
|
2
3
|
from typing import Optional, Dict
|
4
|
+
import dns.resolver
|
5
|
+
|
6
|
+
class DNSHTTPAdapter(HTTPAdapter):
|
7
|
+
def __init__(self, dns_servers, *args, **kwargs):
|
8
|
+
self.dns_servers = dns_servers
|
9
|
+
super().__init__(*args, **kwargs)
|
10
|
+
|
11
|
+
def resolve(self, hostname):
|
12
|
+
resolver = dns.resolver.Resolver()
|
13
|
+
resolver.nameservers = self.dns_servers
|
14
|
+
answers = resolver.resolve(hostname, 'A')
|
15
|
+
return [answer.to_text() for answer in answers]
|
16
|
+
|
17
|
+
def send(self, request, **kwargs):
|
18
|
+
# Resolve the hostname using the custom DNS servers
|
19
|
+
hostname = request.url.split('/')[2].split(':')[0]
|
20
|
+
ip_addresses = self.resolve(hostname)
|
21
|
+
if ip_addresses:
|
22
|
+
request.url = request.url.replace(hostname, ip_addresses[0])
|
23
|
+
return super().send(request, **kwargs)
|
3
24
|
|
4
25
|
class HttpRequestContext:
|
5
26
|
"""
|
6
27
|
A base class for setting up a default request context for headers, params, etc.
|
7
|
-
|
28
|
+
|
8
29
|
Args:
|
9
30
|
base_url (str): The base URL for the API.
|
10
31
|
default_headers (dict, optional): The default headers for the API. Defaults to None.
|
11
32
|
default_params (dict, optional): The default parameters for the API. Defaults to None.
|
12
|
-
|
33
|
+
dns_servers (list, optional): A list of DNS servers to use for custom DNS resolution. Defaults to None.
|
34
|
+
|
13
35
|
Attributes:
|
14
36
|
session (requests.Session): The requests Session instance.
|
15
37
|
"""
|
16
|
-
|
17
|
-
def __init__(self, base_url: str, default_headers: Optional[Dict[str, str]] = None, default_params: Optional[Dict[str, str]] = None):
|
38
|
+
|
39
|
+
def __init__(self, base_url: str, default_headers: Optional[Dict[str, str]] = None, default_params: Optional[Dict[str, str]] = None, dns_servers: Optional[list] = None):
|
18
40
|
self.session = requests.Session()
|
41
|
+
if dns_servers:
|
42
|
+
adapter = DNSHTTPAdapter(list(dns_servers))
|
43
|
+
self.session.mount('http://', adapter)
|
44
|
+
self.session.mount('https://', adapter)
|
19
45
|
self.session.headers.update(default_headers or {})
|
20
46
|
self.session.params.update(default_params or {})
|
21
47
|
self.base_url = base_url
|
@@ -58,5 +84,6 @@ class HttpRequestContext:
|
|
58
84
|
# Reset headers and params to defaults after each request
|
59
85
|
self.session.headers = self.default_headers or {}
|
60
86
|
self.session.params = self.default_params or {}
|
61
|
-
|
87
|
+
|
88
|
+
return response
|
62
89
|
return response
|
@@ -0,0 +1,64 @@
|
|
1
|
+
from typing import Optional, Dict, Any,Union
|
2
|
+
import requests
|
3
|
+
from plexflow.utils.api.context.http import HttpRequestContext
|
4
|
+
import plexflow.utils.antibot.human_like_requests as human_like_requests
|
5
|
+
from plexflow.utils.api.rest.url_builder import build_url
|
6
|
+
|
7
|
+
class Plexful:
|
8
|
+
"""
|
9
|
+
A class that uses UniversalHttpHook and UniversalPostgresqlHook to create RESTful API interfaces and interact with a PostgreSQL database.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
http_conn_id (str, optional): The connection ID, used as Airflow connection ID or as the name for the YAML file. Defaults to None.
|
13
|
+
postgres_conn_id (str, optional): The connection ID, used as Airflow connection ID or as the name for the YAML file. Defaults to None.
|
14
|
+
config_folder (str, optional): The folder where the YAML configuration file is located. Defaults to None.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, base_url: str):
|
18
|
+
self._base_url = base_url
|
19
|
+
|
20
|
+
def get(self, url: str, headers: Optional[Dict[str, str]] = None, query_params: Optional[Dict[str, str]] = None, headless: bool = True, **kwargs) -> Union[requests.Response, human_like_requests.HumanLikeRequestCapture]:
|
21
|
+
"""
|
22
|
+
Makes a GET request to the resource.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
url (str): The full URL for the GET request.
|
26
|
+
headers (dict, optional): The headers for the GET request. Defaults to None.
|
27
|
+
query_params (dict, optional): The query parameters for the GET request. Defaults to None.
|
28
|
+
**kwargs: Additional keyword arguments for the GET request.
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
The response from the GET request.
|
32
|
+
"""
|
33
|
+
if headless:
|
34
|
+
context = HttpRequestContext(self._base_url)
|
35
|
+
return context.get(url, headers=headers, params=query_params, **kwargs)
|
36
|
+
else:
|
37
|
+
full_url = build_url(self._base_url, url, query_params)
|
38
|
+
print(full_url)
|
39
|
+
capture = human_like_requests.get(
|
40
|
+
url=full_url,
|
41
|
+
take_screenshot=kwargs.get('take_screenshot', False),
|
42
|
+
use_xvfb=kwargs.get('use_xvfb', False),
|
43
|
+
wait_condition=kwargs.get('wait_condition', 'regex'),
|
44
|
+
wait_value=kwargs.get('wait_value', r'magnet:'),
|
45
|
+
wait_until_not=kwargs.get('wait_until_not', False)
|
46
|
+
)
|
47
|
+
return capture
|
48
|
+
|
49
|
+
def post(self, url: str, data: Dict[str, Any], headers: Optional[Dict[str, str]] = None, query_params: Optional[Dict[str, str]] = None, **kwargs) -> requests.Response:
|
50
|
+
"""
|
51
|
+
Makes a POST request to the resource.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
url (str): The full URL for the POST request.
|
55
|
+
data (dict): The data for the POST request.
|
56
|
+
headers (dict, optional): The headers for the POST request. Defaults to None.
|
57
|
+
query_params (dict, optional): The query parameters for the POST request. Defaults to None.
|
58
|
+
**kwargs: Additional keyword arguments for the POST request.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
The response from the POST request.
|
62
|
+
"""
|
63
|
+
context = HttpRequestContext(self._base_url)
|
64
|
+
return context.post(url, headers=headers, params=query_params, **kwargs)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import urllib.parse
|
2
|
+
|
3
|
+
def build_url(base_url: str, path: str, query_params: dict) -> str:
|
4
|
+
# Construct the full URL
|
5
|
+
url = urllib.parse.urljoin(base_url, path)
|
6
|
+
# Add query parameters
|
7
|
+
query_string = urllib.parse.urlencode(query_params if query_params else {})
|
8
|
+
full_url = f"{url}?{query_string}"
|
9
|
+
return full_url
|
@@ -0,0 +1,139 @@
|
|
1
|
+
from mistralai import Mistral
|
2
|
+
from io import StringIO
|
3
|
+
from plexflow.utils.strings.json_extract import extract_json_from_string
|
4
|
+
|
5
|
+
def extract_torrents(content: str, **kwargs):
|
6
|
+
api_key = kwargs.get("api_key", "bZ6vS0p30hiEqpyy6fBZUMyuZuiqaS91")
|
7
|
+
model = kwargs.get("model", "mistral-large-latest")
|
8
|
+
|
9
|
+
client = Mistral(api_key=api_key)
|
10
|
+
|
11
|
+
stream_response = client.chat.stream(
|
12
|
+
model = model,
|
13
|
+
messages = [
|
14
|
+
{
|
15
|
+
"role": "user",
|
16
|
+
"content": f"""
|
17
|
+
Parse the torrents as JSON, designate the total seeds as the field 'seeds', the total peers as the field 'peers', the date added as the field 'date', the torrent title as 'title', the torrent size as 'size', the torrent category as 'category', keep all other fields the same:
|
18
|
+
|
19
|
+
{content}
|
20
|
+
""",
|
21
|
+
},
|
22
|
+
]
|
23
|
+
)
|
24
|
+
|
25
|
+
buffer = StringIO()
|
26
|
+
|
27
|
+
for chunk in stream_response:
|
28
|
+
chunk = chunk.data.choices[0].delta.content
|
29
|
+
if kwargs.get("log", False):
|
30
|
+
print(chunk, end="")
|
31
|
+
|
32
|
+
buffer.write(chunk)
|
33
|
+
|
34
|
+
json_data = buffer.getvalue()
|
35
|
+
|
36
|
+
return extract_json_from_string(json_data)
|
37
|
+
|
38
|
+
|
39
|
+
torrents = extract_torrents(
|
40
|
+
"""
|
41
|
+
|
42
|
+
|
43
|
+
Login
|
44
|
+
Home
|
45
|
+
Catalog
|
46
|
+
Box Office
|
47
|
+
New Selection
|
48
|
+
4K Movies
|
49
|
+
4K XXX
|
50
|
+
Home Movies TV Games Music Anime Apps Other Books XXX Pages
|
51
|
+
XXX content
|
52
|
+
Search title or IMDB ID like tt27932269 ...
|
53
|
+
|
54
|
+
»
|
55
|
+
Reset
|
56
|
+
My 30 Books March 3 2 mature maturenl tt0051532 girlsoutwest Audiobooks heavier trip matrix language issue mummy The Office nacho libre the gentlemen playboyplus the flash 2023 The Comeback The Lord of the Rings twin peaks 720p the work 1080p vikings Windows All 7 8 1 10 11 All Editions With Updates AIO 46in1 glass 2019 Thunderbolts ma the invasion Becastled v0 6002 naughtyamericavr DesignOptimal Editable font and
|
57
|
+
Mufasa: The Lion King 2024
|
58
|
+
Play
|
59
|
+
|
60
|
+
Title: Mufasa: The Lion King
|
61
|
+
EXTERNAL URL: IMDB | TMDB
|
62
|
+
Languages: English
|
63
|
+
Genres: Animation, Adventure, Drama, Family, Fantasy, Musical,
|
64
|
+
Runtime: 2:00:00 Hour
|
65
|
+
Cast: Aaron Pierre, Kelvin Harrison Jr., Tiffany Boone
|
66
|
+
Directors: Barry Jenkins
|
67
|
+
IMDB Rating Rating: 6.8 (1406 Votes)
|
68
|
+
Year: 2024
|
69
|
+
Summary: Mufasa, a cub lost and alone, meets a sympathetic lion named Taka, the heir to a royal bloodline. The chance meeting sets in motion an expansive journey of a group of misfits searching for their destiny.
|
70
|
+
:TRAILER:
|
71
|
+
|
72
|
+
Search table...
|
73
|
+
C File Category Added Time Size Se. Le.
|
74
|
+
|
75
|
+
Mufasa The Lion King 2024 720p WEBRiP x264 XoXo M.Q.A
|
76
|
+
Movies 2025-02-02 8 hours 4.0 GB 26 50
|
77
|
+
|
78
|
+
Mufasa The Lion King 2024 1080p WEBRiP x264 XoXo M.Q.A
|
79
|
+
Movies 2025-02-02 8 hours 8.4 GB 40 34
|
80
|
+
|
81
|
+
Mufasa The Lion King 2024 720p WEB DL 2000MB Feranki1980 M.Q.A
|
82
|
+
Movies 2025-01-09 3 weeks, 2 days 2.0 GB 44 22
|
83
|
+
|
84
|
+
Mufasa The Lion King 2024 1080p WEBRip Multi AAC x265 HNY M.Q.A
|
85
|
+
Movies 2025-01-02 1 month 1.5 GB 43 48
|
86
|
+
|
87
|
+
Mufasa The Lion King 2024 1080P WEBRiP 24 bit Stereo HEVC X265 POOTLED mkv M.Q.A
|
88
|
+
Movies 2025-01-02 1 month 3.1 GB 23 33
|
89
|
+
|
90
|
+
Mufasa The Lion King 2024 1080p WEBRip x265 10bit YTS M.Q.A
|
91
|
+
Movies 2025-01-02 1 month 1.7 GB 294 621
|
92
|
+
|
93
|
+
Mufasa The Lion King 2024 1080p WEBRIP H264 HAPPYNEWYEAR COLLECTi M.Q.A
|
94
|
+
Movies 2025-01-02 1 month 3.7 GB 35 42
|
95
|
+
|
96
|
+
Mufasa The Lion King 2024 1080p WEBRip YTS M.Q.A
|
97
|
+
Movies 2025-01-01 1 month 1.8 GB 424 1301
|
98
|
+
|
99
|
+
Mufasa Il Re Leone 2024 1080p HDRip HQ h264 ADS MD Ita iDN_Cr M.Q.A
|
100
|
+
Movies 2025-01-01 1 month 2.5 GB 36 10
|
101
|
+
|
102
|
+
Mufasa The Lion King 2024 720p WEBRip YTS M.Q.A
|
103
|
+
Movies 2025-01-01 1 month 1020.6 MB 422 796
|
104
|
+
|
105
|
+
Mufasa The Lion King 2024 1080p WEBRIP H264 HAPPYNEWYEAR COLLECTiVE M.Q.A
|
106
|
+
Movies 2025-01-01 1 month 3.7 GB 45 37
|
107
|
+
|
108
|
+
Mufasa The Lion King 2024 1080p WEBRip READNFO x265 AC3 AOC M.Q.A
|
109
|
+
Movies 2024-12-31 1 month 5.9 GB 13 13
|
110
|
+
|
111
|
+
Mufasa The Lion King 2024 1080p HDRip ENG HappyNEWYear M.Q.A
|
112
|
+
Movies 2024-12-31 1 month 2.5 GB 95 714
|
113
|
+
|
114
|
+
Mufasa The Lion King 2024 1080p Cam X264 COLLECTiVE M.Q.A
|
115
|
+
Movies 2024-12-23 1 month, 1 week 3.7 GB 27 21
|
116
|
+
|
117
|
+
Mufasa The Lion King 2024 V 2 1080p HDTS C1NEM4 M.Q.A
|
118
|
+
Movies 2024-12-20 1 month, 2 weeks 2.1 GB 14 45
|
119
|
+
|
120
|
+
Mufasa The Lion King 2024 HDCAM c1nem4 x264 SUNSCREEN TGx M.Q.A
|
121
|
+
Movies 2024-12-20 1 month, 2 weeks 995.1 MB 12 25
|
122
|
+
|
123
|
+
Mufasa The Lion King 2024 720p HDCAM C1NEM4 M.Q.A
|
124
|
+
Movies 2024-12-20 1 month, 2 weeks 1.7 GB 14 19
|
125
|
+
Showing 1 to 17 of 17 entries
|
126
|
+
""",
|
127
|
+
log=False
|
128
|
+
)
|
129
|
+
|
130
|
+
if len(torrents) == 0:
|
131
|
+
print("No torrents found")
|
132
|
+
else:
|
133
|
+
if type(torrents[0]) == list:
|
134
|
+
torrents = torrents[0]
|
135
|
+
|
136
|
+
print(len(torrents), "torrents found")
|
137
|
+
|
138
|
+
for torrent in torrents:
|
139
|
+
print(torrent)
|
plexflow/utils/pubsub/produce.py
CHANGED
@@ -4,12 +4,26 @@ from confluent_kafka import Producer
|
|
4
4
|
import os
|
5
5
|
|
6
6
|
def delivery_report(err, msg):
|
7
|
+
"""
|
8
|
+
Callback function to handle the delivery report of a message.
|
9
|
+
|
10
|
+
Args:
|
11
|
+
err: Error object if message delivery failed, otherwise None.
|
12
|
+
msg: Message object that was delivered.
|
13
|
+
"""
|
7
14
|
if err is not None:
|
8
15
|
logging.error(f"Message delivery failed: {err}")
|
9
16
|
else:
|
10
17
|
logging.info(f"Message delivered to {msg.topic()} [{msg.partition()}] at offset {msg.offset()}")
|
11
18
|
|
12
19
|
def produce_message(topic: str, message):
|
20
|
+
"""
|
21
|
+
Produce a single message to the specified Kafka topic.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
topic (str): The Kafka topic to publish the message to.
|
25
|
+
message: The message to be published.
|
26
|
+
"""
|
13
27
|
logging.info(f"Publishing message to {topic}...")
|
14
28
|
producer = Producer({'bootstrap.servers': os.getenv('KAFKA_BOOTSTRAP_SERVERS', 'localhost:9092')})
|
15
29
|
producer.produce(topic, json.dumps(message).encode('utf-8'), callback=delivery_report)
|
@@ -17,9 +31,16 @@ def produce_message(topic: str, message):
|
|
17
31
|
logging.info(f"Message published to {topic}.")
|
18
32
|
|
19
33
|
def produce_messages(topic: str, messages):
|
34
|
+
"""
|
35
|
+
Produce multiple messages to the specified Kafka topic.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
topic (str): The Kafka topic to publish the messages to.
|
39
|
+
messages: A list of messages to be published.
|
40
|
+
"""
|
20
41
|
logging.info(f"Publishing messages to {topic}...")
|
21
42
|
producer = Producer({'bootstrap.servers': os.getenv('KAFKA_BOOTSTRAP_SERVERS', 'localhost:9092')})
|
22
43
|
for message in messages:
|
23
44
|
producer.produce(topic, json.dumps(message).encode('utf-8'), callback=delivery_report)
|
24
45
|
producer.flush()
|
25
|
-
logging.info(f"All messages published to {topic}.")
|
46
|
+
logging.info(f"All messages published to {topic}.")
|