quasarr 1.21.1__tar.gz → 1.21.2__tar.gz

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.

Potentially problematic release.


This version of quasarr might be problematic. Click here for more details.

Files changed (83) hide show
  1. {quasarr-1.21.1 → quasarr-1.21.2}/PKG-INFO +1 -1
  2. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/arr/__init__.py +11 -0
  3. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/version.py +1 -1
  4. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/by.py +2 -1
  5. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/dl.py +38 -28
  6. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/PKG-INFO +1 -1
  7. {quasarr-1.21.1 → quasarr-1.21.2}/LICENSE +0 -0
  8. {quasarr-1.21.1 → quasarr-1.21.2}/README.md +0 -0
  9. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/__init__.py +0 -0
  10. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/__init__.py +0 -0
  11. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/captcha/__init__.py +0 -0
  12. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/config/__init__.py +0 -0
  13. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/sponsors_helper/__init__.py +0 -0
  14. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/api/statistics/__init__.py +0 -0
  15. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/__init__.py +0 -0
  16. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/linkcrypters/__init__.py +0 -0
  17. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/linkcrypters/al.py +0 -0
  18. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/linkcrypters/filecrypt.py +0 -0
  19. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/linkcrypters/hide.py +0 -0
  20. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/packages/__init__.py +0 -0
  21. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/__init__.py +0 -0
  22. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/al.py +0 -0
  23. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/by.py +0 -0
  24. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/dd.py +0 -0
  25. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/dj.py +0 -0
  26. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/dl.py +0 -0
  27. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/dt.py +0 -0
  28. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/dw.py +0 -0
  29. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/he.py +0 -0
  30. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/mb.py +0 -0
  31. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/nk.py +0 -0
  32. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/nx.py +0 -0
  33. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/sf.py +0 -0
  34. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/sj.py +0 -0
  35. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/sl.py +0 -0
  36. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/wd.py +0 -0
  37. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/downloads/sources/wx.py +0 -0
  38. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/__init__.py +0 -0
  39. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/cloudflare.py +0 -0
  40. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/html_images.py +0 -0
  41. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/html_templates.py +0 -0
  42. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/imdb_metadata.py +0 -0
  43. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/log.py +0 -0
  44. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/myjd_api.py +0 -0
  45. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/notifications.py +0 -0
  46. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/obfuscated.py +0 -0
  47. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/sessions/__init__.py +0 -0
  48. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/sessions/al.py +0 -0
  49. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/sessions/dd.py +0 -0
  50. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/sessions/dl.py +0 -0
  51. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/sessions/nx.py +0 -0
  52. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/shared_state.py +0 -0
  53. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/statistics.py +0 -0
  54. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/providers/web_server.py +0 -0
  55. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/__init__.py +0 -0
  56. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/__init__.py +0 -0
  57. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/al.py +0 -0
  58. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/dd.py +0 -0
  59. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/dj.py +0 -0
  60. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/dt.py +0 -0
  61. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/dw.py +0 -0
  62. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/fx.py +0 -0
  63. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/he.py +0 -0
  64. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/mb.py +0 -0
  65. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/nk.py +0 -0
  66. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/nx.py +0 -0
  67. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/sf.py +0 -0
  68. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/sj.py +0 -0
  69. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/sl.py +0 -0
  70. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/wd.py +0 -0
  71. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/search/sources/wx.py +0 -0
  72. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/storage/__init__.py +0 -0
  73. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/storage/config.py +0 -0
  74. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/storage/setup.py +0 -0
  75. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr/storage/sqlite_database.py +0 -0
  76. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/SOURCES.txt +0 -0
  77. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/dependency_links.txt +0 -0
  78. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/entry_points.txt +0 -0
  79. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/not-zip-safe +0 -0
  80. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/requires.txt +0 -0
  81. {quasarr-1.21.1 → quasarr-1.21.2}/quasarr.egg-info/top_level.txt +0 -0
  82. {quasarr-1.21.1 → quasarr-1.21.2}/setup.cfg +0 -0
  83. {quasarr-1.21.1 → quasarr-1.21.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.21.1
3
+ Version: 1.21.2
4
4
  Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
5
5
  Home-page: https://github.com/rix1337/Quasarr
6
6
  Author: rix1337
@@ -353,6 +353,17 @@ def setup_arr_routes(app):
353
353
  <enclosure url="{release.get("link", "")}" length="{release.get("size", 0)}" type="application/x-nzb" />
354
354
  </item>'''
355
355
 
356
+ if not items:
357
+ items = f'''
358
+ <item>
359
+ <title>No results found</title>
360
+ <guid isPermaLink="False">0</guid>
361
+ <link>https://github.com/rix1337/Quasarr</link>
362
+ <comments>No results matched your search criteria.</comments>
363
+ <pubDate>{datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")}</pubDate>
364
+ <enclosure url="https://github.com/rix1337/Quasarr" length="0" type="application/x-nzb" />
365
+ </item>'''
366
+
356
367
  return f'''<?xml version="1.0" encoding="UTF-8"?>
357
368
  <rss>
358
369
  <channel>
@@ -8,7 +8,7 @@ import requests
8
8
 
9
9
 
10
10
  def get_version():
11
- return "1.21.1"
11
+ return "1.21.2"
12
12
 
13
13
 
14
14
  def get_latest_version():
@@ -17,9 +17,10 @@ from quasarr.providers.log import info, debug
17
17
 
18
18
  hostname = "by"
19
19
  supported_mirrors = ["rapidgator", "ddownload", "nitroflare"]
20
- XXX_REGEX = re.compile(r"\.xxx\.", re.I)
20
+
21
21
  RESOLUTION_REGEX = re.compile(r"\d{3,4}p", re.I)
22
22
  CODEC_REGEX = re.compile(r"x264|x265|h264|h265|hevc|avc", re.I)
23
+ XXX_REGEX = re.compile(r"\.xxx\.", re.I)
23
24
  IMDB_REGEX = re.compile(r"imdb\.com/title/(tt\d+)")
24
25
 
25
26
 
@@ -5,7 +5,6 @@
5
5
  import re
6
6
  import time
7
7
  from base64 import urlsafe_b64encode
8
- from concurrent.futures import ThreadPoolExecutor, as_completed
9
8
  from datetime import datetime
10
9
  from html import unescape
11
10
 
@@ -16,7 +15,27 @@ from quasarr.providers.log import info, debug
16
15
  from quasarr.providers.sessions.dl import retrieve_and_validate_session, invalidate_session, fetch_via_requests_session
17
16
 
18
17
  hostname = "dl"
19
- supported_mirrors = []
18
+
19
+ RESOLUTION_REGEX = re.compile(r"\d{3,4}p", re.I)
20
+ CODEC_REGEX = re.compile(r"x264|x265|h264|h265|hevc|avc", re.I)
21
+ XXX_REGEX = re.compile(r"\.xxx\.", re.I)
22
+
23
+
24
+ def convert_to_rss_date(iso_date_str):
25
+ """
26
+ Convert ISO format datetime to RSS date format.
27
+ DL date format: '2025-12-15T20:43:06+0100'
28
+ Returns: 'Sun, 15 Dec 2025 20:43:06 +0100'
29
+ Falls back to current time if conversion fails.
30
+ """
31
+ if not iso_date_str:
32
+ return datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
33
+
34
+ try:
35
+ dt_obj = datetime.fromisoformat(iso_date_str)
36
+ return dt_obj.strftime("%a, %d %b %Y %H:%M:%S %z")
37
+ except Exception:
38
+ return datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
20
39
 
21
40
 
22
41
  def normalize_title_for_sonarr(title):
@@ -95,21 +114,9 @@ def dl_feed(shared_state, start_time, request_from, mirror=None):
95
114
  thread_url = f"https://www.{host}{thread_url}"
96
115
 
97
116
  # Extract date and convert to RFC 2822 format
98
- date_str = None
99
117
  date_elem = item.select_one('time.u-dt')
100
- if date_elem:
101
- iso_date = date_elem.get('datetime', '')
102
- if iso_date:
103
- try:
104
- # Parse ISO format and convert to RFC 2822
105
- dt = datetime.fromisoformat(iso_date.replace('Z', '+00:00'))
106
- date_str = dt.strftime("%a, %d %b %Y %H:%M:%S %z")
107
- except Exception:
108
- date_str = None
109
-
110
- # Fallback: use current time if no date found
111
- if not date_str:
112
- date_str = datetime.now().strftime("%a, %d %b %Y %H:%M:%S %z")
118
+ iso_date = date_elem.get('datetime', '') if date_elem else ''
119
+ published = convert_to_rss_date(iso_date)
113
120
 
114
121
  mb = 0
115
122
  imdb_id = None
@@ -128,7 +135,7 @@ def dl_feed(shared_state, start_time, request_from, mirror=None):
128
135
  "link": link,
129
136
  "mirror": mirror,
130
137
  "size": mb * 1024 * 1024,
131
- "date": date_str,
138
+ "date": published,
132
139
  "source": thread_url
133
140
  },
134
141
  "type": "protected"
@@ -228,6 +235,15 @@ def _search_single_page(shared_state, host, search_string, search_id, page_num,
228
235
  title = unescape(title)
229
236
  title_normalized = normalize_title_for_sonarr(title)
230
237
 
238
+ # Filter: Skip if no resolution or codec info (unless LazyLibrarian)
239
+ if 'lazylibrarian' not in request_from.lower():
240
+ if not (RESOLUTION_REGEX.search(title_normalized) or CODEC_REGEX.search(title_normalized)):
241
+ continue
242
+
243
+ # Filter: Skip XXX content unless explicitly searched for
244
+ if XXX_REGEX.search(title_normalized) and 'xxx' not in search_string.lower():
245
+ continue
246
+
231
247
  thread_url = title_elem.get('href')
232
248
  if thread_url.startswith('/'):
233
249
  thread_url = f"https://www.{host}{thread_url}"
@@ -235,16 +251,10 @@ def _search_single_page(shared_state, host, search_string, search_id, page_num,
235
251
  if not shared_state.is_valid_release(title_normalized, request_from, search_string, season, episode):
236
252
  continue
237
253
 
238
- minor_info = item.select_one('div.contentRow-minor')
239
- date_str = ""
240
- if minor_info:
241
- date_elem = minor_info.select_one('time.u-dt')
242
- if date_elem:
243
- date_str = date_elem.get('datetime', '')
244
-
245
- # Fallback: use current time if no date found
246
- if not date_str:
247
- date_str = datetime.now().strftime("%a, %d %b %Y %H:%M:%S +0000")
254
+ # Extract date and convert to RFC 2822 format
255
+ date_elem = item.select_one('time.u-dt')
256
+ iso_date = date_elem.get('datetime', '') if date_elem else ''
257
+ published = convert_to_rss_date(iso_date)
248
258
 
249
259
  mb = 0
250
260
  password = ""
@@ -262,7 +272,7 @@ def _search_single_page(shared_state, host, search_string, search_id, page_num,
262
272
  "link": link,
263
273
  "mirror": mirror,
264
274
  "size": mb * 1024 * 1024,
265
- "date": date_str,
275
+ "date": published,
266
276
  "source": thread_url
267
277
  },
268
278
  "type": "protected"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quasarr
3
- Version: 1.21.1
3
+ Version: 1.21.2
4
4
  Summary: Quasarr connects JDownloader with Radarr, Sonarr and LazyLibrarian. It also decrypts links protected by CAPTCHAs.
5
5
  Home-page: https://github.com/rix1337/Quasarr
6
6
  Author: rix1337
File without changes
File without changes
File without changes
File without changes
File without changes