Skip to content

Commit

Permalink
Merge pull request #9 from rix1337/dev
Browse files Browse the repository at this point in the history
Fix complete download handling
  • Loading branch information
rix1337 authored Aug 23, 2024
2 parents eaf87db + 3df35b5 commit dc67acf
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 40 deletions.
Binary file added Quasarr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
# Quasarr

[![PyPI version](https://badge.fury.io/py/quasarr.svg)](https://badge.fury.io/py/quasarr)
[![GitHub Sponsorship](https://img.shields.io/badge/support-me-red.svg)](https://github.com/users/rix1337/sponsorship)
<img src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" data-canonical-src="https://raw.githubusercontent.com/rix1337/Quasarr/main/Quasarr.png" width="64" height="64" />

Quasarr is a Bridge to use JDownloader in Radarr and (later also) Sonarr.

Quasarr includes a solution to quickly and easily decrypt protected links.
Just follow the link from the console output and solve the CAPTCHA.
Quasarr will confidently handle the rest.
[![PyPI version](https://badge.fury.io/py/quasarr.svg)](https://badge.fury.io/py/quasarr)
[![GitHub Sponsorship](https://img.shields.io/badge/support-me-red.svg)](https://github.com/users/rix1337/sponsorship)

Quasarr poses as a Newznab Indexer and a SABnzbd client.
It will thus never work in parallel with a real NZB indexer and download client set up.
Torrents are unaffected.

Quasarr includes a solution to quickly and easily decrypt protected links.
Just follow the link from the console output (or discord notification) and solve the CAPTCHA.
Quasarr will confidently handle the rest.

# Instructions

* Follow instructions to set up at least one hostname for Quasarr
* Provide your [MyJDownloader credentials](https://my.jdownloader.org)
* Set up Quasarr's URL as 'Newznab Indexer' and 'SABnzbd Download Client' in Sonarr/Radarr.
Expand Down
2 changes: 1 addition & 1 deletion quasarr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def run():
discord_webhook_pattern = r'^https://discord\.com/api/webhooks/\d+/[\w-]+$'
if re.match(discord_webhook_pattern, arguments.discord):
shared_state.update("webhook", arguments.discord)
print(f"Using Discord Webhook URL: {arguments.discord}")
print(f"Using Discord Webhook URL for CAPTCHA notifications")
discord_url = arguments.discord
else:
print(f"Invalid Discord Webhook URL provided: {arguments.discord}")
Expand Down
14 changes: 1 addition & 13 deletions quasarr/arr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,7 @@ def submit_token():

print(f"Decrypted {len(links)} download links for {title}")

shared_state.get_device().linkgrabber.add_links(params=[
{
"autostart": True,
"links": str(links).replace(" ", ""),
"packageName": title,
"extractPassword": password,
"priority": "DEFAULT",
"downloadPassword": password,
"destinationFolder": "Quasarr/<jd:packagename>",
"comment": package_id,
"overwritePackagizerRules": True
}
])
shared_state.download_package(links, title, password, package_id)

shared_state.get_db("protected").delete(package_id)

Expand Down
29 changes: 12 additions & 17 deletions quasarr/downloads/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,16 @@ def get_packages(shared_state):
comment = get_first_matching_comment(package, shared_state.get_device().downloads.query_links())
finished = False
try:
finished = package["finished"]
status = package["status"]
except KeyError:
pass
status = ""
if status and "entpacken" in status.lower() or "extracting" in status.lower():
finished = False
else:
try:
finished = package["finished"]
except KeyError:
pass
packages.append({
"details": package,
"location": "history" if finished else "queue",
Expand Down Expand Up @@ -166,7 +173,7 @@ def get_packages(shared_state):
queue_index += 1
elif package["location"] == "history":
details = package["details"]
name = f"[Finished] {details["name"]}"
name = details["name"]
size = int(details["bytesLoaded"])
storage = details["saveTo"]
package_id = package["comment"]
Expand Down Expand Up @@ -207,19 +214,7 @@ def download_package(shared_state, request_from, title, url, size_mb, password):
print(f"Decrypted {len(links)} download links for {title}")
package_id = f"Quasarr_{category}_{str(hash(title + url)).replace('-', '')}"

added = shared_state.get_device().linkgrabber.add_links(params=[
{
"autostart": True,
"links": str(links).replace(" ", ""),
"packageName": title,
"extractPassword": nx,
"priority": "DEFAULT",
"downloadPassword": nx,
"destinationFolder": "Quasarr/<jd:packagename>",
"comment": package_id,
"overwritePackagizerRules": True
}
])
added = shared_state.download_package(links, title, password, package_id)

if not added:
print(f"Failed to add {title} to linkgrabber")
Expand Down Expand Up @@ -255,7 +250,7 @@ def delete_package(shared_state, package_id):
else:
package_name_field = "name"

deleted = package[package_name_field].split("] ")[1]
deleted = package[package_name_field]
break
if deleted:
break
Expand Down
54 changes: 54 additions & 0 deletions quasarr/providers/myjd_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ def __init__(self, device):
self.device = device
self.url = '/linkgrabberv2'

def is_collecting(self):
resp = self.device.action("/linkgrabberv2/isCollecting")
return resp

def add_links(self,
params=[{
"autostart": True,
Expand Down Expand Up @@ -127,6 +131,18 @@ def remove_links(self, links_ids, packages_ids):
resp = self.device.action(self.url + "/removeLinks", params)
return resp

def move_to_downloadlist(self, link_ids, package_ids):
"""
Moves packages and/or links to download list.
:param package_ids: Package UUID's.
:type: list of strings.
:param link_ids: Link UUID's.
"""
params = [link_ids, package_ids]
resp = self.device.action(self.url + "/moveToDownloadlist", params)
return resp

def query_links(self,
params=[{
"bytesTotal": True,
Expand Down Expand Up @@ -248,6 +264,43 @@ def remove_links(self, links_ids, packages_ids):
return resp


class Extraction:
"""
Class that represents the extraction functionalities of a Device.
"""

def __init__(self, device):
self.device = device
self.url = '/extraction'

def get_archive_info(self, link_ids, package_ids):
params = [link_ids, package_ids]
resp = self.device.action(self.url + "/getArchiveInfo", params)
return resp

def set_archive_settings(self, archive_id, archive_settings=None):
"""
Sets the extraction settings for a specific archive.
:param archive_id: The ID of the archive.
:type archive_id: string
:param archive_settings: Dictionary of archive settings.
:type archive_settings: dict
:rtype: boolean indicating success or failure
"""
if archive_settings is None:
archive_settings = {
"autoExtract": True,
"removeDownloadLinksAfterExtraction": True,
"removeFilesAfterExtraction": True
}

params = [archive_id, archive_settings]

resp = self.device.action(self.url + "/setArchiveSettings", params)
return resp


class Jddevice:
"""
Class that represents a JDownloader device and it's functions
Expand All @@ -266,6 +319,7 @@ def __init__(self, jd, device_dict):
self.linkgrabber = Linkgrabber(self)
self.downloads = Downloads(self)
self.downloadcontroller = DownloadController(self)
self.extraction = Extraction(self)
self.__direct_connection_info = None
self.__refresh_direct_connections()
self.__direct_connection_enabled = True
Expand Down
81 changes: 81 additions & 0 deletions quasarr/providers/shared_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,84 @@ def convert_to_mb(item):

size_mb = size_b / (1024 * 1024)
return int(size_mb)


def download_package(links, title, password, package_id):
device = get_device()
added = device.linkgrabber.add_links(params=[
{
"autostart": False,
"links": str(links).replace(" ", ""),
"packageName": title,
"extractPassword": password,
"priority": "DEFAULT",
"downloadPassword": password,
"destinationFolder": "Quasarr/<jd:packagename>",
"comment": package_id,
"overwritePackagizerRules": True
}
])

package_uuids = []
link_ids = []
archive_id = None

for _ in range(30):
try:
collecting = device.linkgrabber.is_collecting()
if not collecting:
links = device.linkgrabber.query_links()
for link in links:
if link["comment"] == package_id:
link_id = link["uuid"]
if link_id not in link_ids:
link_ids.append(link_id)
package_uuid = link["packageUUID"]
if package_uuid not in package_uuids:
package_uuids.append(package_uuid)

if link_ids and package_uuids:
archive = device.extraction.get_archive_info(link_ids=link_ids, package_ids=package_uuids)
if archive:
archive_id = archive[0].get("archiveId", None)
if archive_id:
break # Exit the loop as archive_id is found

except Exception as e:
print(f"An error occurred: {e}")

time.sleep(1)

if not link_ids and not package_uuids:
print(f"No links or packages found within 30 seconds! Adding {title} package failed.")
return False

if not archive_id:
print(f"Archive ID for {title} not found! Release may not be compressed.")
else:
settings = {
"autoExtract": True,
"removeDownloadLinksAfterExtraction": False,
"removeFilesAfterExtraction": True
}
settings_set = device.extraction.set_archive_settings(archive_id, archive_settings=settings)
if not settings_set:
print(f"Failed to set archive settings for {title}!")

time.sleep(3)
links = device.linkgrabber.query_links()
for link in links:
if link["comment"] == package_id:
link_id = link["uuid"]
if link_id not in link_ids:
link_ids.append(link_id)
package_uuid = link["packageUUID"]
if package_uuid not in package_uuids:
package_uuids.append(package_uuid)

try:
device.linkgrabber.move_to_downloadlist(link_ids, package_uuids)
except Exception as e:
print(f"Failed to start download for {title}: {e}")
return False
return True
2 changes: 1 addition & 1 deletion quasarr/providers/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@


def get_version():
return "0.1.2"
return "0.1.3"


def create_version_file():
Expand Down
6 changes: 3 additions & 3 deletions quasarr/search/sources/nx.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
def nx_feed(shared_state, request_from):
releases = []

password = ""
nx = shared_state.values["config"]("Hostnames").get("nx")

password = nx

if "Radarr" in request_from:
category = "movie"
else:
category = "episode"

nx = shared_state.values["config"]("Hostnames").get("nx")

url = f'https://{nx}/api/frontend/releases/category/{category}/tag/all/1/51?sort=date'
headers = {
'User-Agent': shared_state.values["user_agent"],
Expand Down

0 comments on commit dc67acf

Please sign in to comment.