1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
# -*- coding: utf-8 -*-
import datetime
import logging
from bs4 import BeautifulSoup as bso
from requests import Session
from subliminal.cache import region as cache_region
from subliminal.exceptions import AuthenticationError
from subliminal.exceptions import ConfigurationError
from subliminal_patch.core import Movie
from subliminal_patch.providers import Provider
from subliminal_patch.providers.utils import update_matches
from subliminal_patch.subtitle import Subtitle
from subzero.language import Language
logger = logging.getLogger(__name__)
_PROVIDER_NAME = "karagarga"
_BASE_URL = "https://karagarga.in"
class KaragargaSubtitle(Subtitle):
provider_name = _PROVIDER_NAME
hash_verifiable = False
def __init__(self, language, page_link, release_info, downloads):
super().__init__(language, page_link=page_link)
self.release_info = release_info
self.downloads = downloads
self._matches = {"title", "year"}
def get_matches(self, video):
update_matches(self._matches, video, self.release_info, type="movie")
return self._matches
@property
def id(self):
return self.page_link
_NO_LOGGED_IN_REDIRECT = 302
_EXPIRATION_TIME = datetime.timedelta(weeks=1).total_seconds()
class KaragargaProvider(Provider):
provider_name = _PROVIDER_NAME
# Only english for now
languages = {Language.fromietf("en")}
video_types = (Movie,)
subtitle_class = KaragargaSubtitle
_session: Session
def __init__(self, username: str, password: str, f_username=None, f_password=None):
if not username or not password:
raise ConfigurationError("Username/password not provided")
self._username = username
self._password = password
self._f_username = f_username or username
self._f_password = f_password or password
def initialize(self):
self._session = Session()
self._session.headers.update(
{"authority": "karagarga.in", "user-agent": "Bazarr"}
)
self._login()
def terminate(self):
self._session.close()
def _login(self):
self._login_main()
self._login_forum()
def _login_main(self):
data = {
"username": self._username,
"password": self._password,
}
self._session.post(f"{_BASE_URL}/takelogin.php", data=data)
if "pass" not in self._session.cookies:
raise AuthenticationError("Invalid username/password")
logger.debug("Karagarga login: OK")
def _login_forum(self):
params = {
"app": "core",
"module": "global",
"section": "login",
"do": "process",
}
data = {
# What's the origin of this key?
"auth_key": "880ea6a14ea49e853634fbdc5015a024",
#
"referer": "https://forum.karagarga.in/",
"ips_username": self._username,
"ips_password": self._password,
"rememberMe": "1",
"anonymous": "1",
}
self._session.post(
"https://forum.karagarga.in/index.php", params=params, data=data
)
if not {"session_id", "pass_hash"}.issubset(self._session.cookies.keys()):
raise AuthenticationError("Invalid forum username/password")
logger.debug("Karagarga forum login: OK")
@cache_region.cache_on_arguments(expiration_time=_EXPIRATION_TIME)
def _cached_get(self, url, params):
response = self._session.get(url, params=params)
if response.status_code == _NO_LOGGED_IN_REDIRECT:
raise AuthenticationError("Not logged in")
return response.content
def _search_movie(self, title, year):
params = {"search": title, "status": "completed"}
content = self._cached_get(f"{_BASE_URL}/pots.php", params)
soup = bso(content, "html.parser")
table = soup.find("table", {"cellspacing": "5"})
if table is None:
logger.debug("Failed to get table. Returning []")
return []
subtitles = []
scans = 0
for tr_ in table.find_all("tr"): # type: ignore
if "forum.karagarga" not in str(tr_):
continue
found_tds = tr_.find_all("td")
if len(found_tds) != 11:
continue
title = found_tds[1].text
if f"({year}" not in title:
logger.debug("Year doesn't match: %s", title)
continue
logger.debug("Movie matched: %s", title)
requested_language = found_tds[5].text
if "English" not in requested_language:
continue
forum_item = found_tds[9]
if "approved" not in str(forum_item):
logger.debug("Non-approved subtitle: %s", title)
continue
try:
forum_url = str(forum_item.find("a").get("href"))
except AttributeError:
continue
if scans > 2:
logger.debug("Forum scans limit exceeded")
break
subtitles += self._parse_from_forum(forum_url, Language.fromietf("en"))
scans += 1
return subtitles
def _parse_from_forum(self, url, language):
logger.debug("Scanning forum for subs: %s", url)
content = self._cached_get(url, {})
soup = bso(content, "html.parser")
for post in soup.find_all("div", {"class": "post entry-content"}):
yield from _gen_subtitles(post, language)
def list_subtitles(self, video, languages):
subtitles = self._search_movie(video.title, video.year)
if not subtitles:
return []
subtitles.sort(key=lambda x: x.downloads, reverse=True)
# Always return the most downloaded subtitle from the forum
return [subtitles[0]]
def download_subtitle(self, subtitle):
response = self._session.get(subtitle.page_link, allow_redirects=True)
response.raise_for_status()
subtitle.content = response.content
def _gen_subtitles(post, language):
seen_urls = set()
for potential in post.select("p,li.attachment,div"):
downloads = potential.find("span", {"class": "desc lighter"})
if not downloads:
continue
try:
download_count = int(downloads.text.split()[0].strip())
item = [a_ for a_ in potential.find_all("a") if a_.find("strong")][0]
release_info = item.find("strong").text
except (AttributeError, KeyError, ValueError) as error:
logger.debug("Error parsing post: %s", error)
continue
url = item.get("href")
if not url or url in seen_urls:
continue
seen_urls.add(url)
subtitle = KaragargaSubtitle(language, url, release_info, download_count)
logger.debug("Valid subtitle found: %s - %s", release_info, subtitle)
yield subtitle
|