seansfkelley / nas-download-manager

An open source browser extension for adding/managing download tasks to your Synology DiskStation.
256 stars 46 forks source link

Failed to add download with Download Station 3.8.16-3566 (update from 2021-03-09) #177

Closed szabomarcihu closed 3 years ago

szabomarcihu commented 3 years ago

current status (maintainer edit)

This appears to be an unannounced change/breakage on Synology's part. I have a support ticket open with them now and will post any updates/next steps here. The current workaround is to downgrade DownloadStation to 3.8.15-3563. https://github.com/Sonarr/Sonarr/issues/4388#issuecomment-803004078 has some advice for how to do this effectively.

original issue follows

Description

Failed to add download with Download Station 3.8.16-3566 Invalid parameter

Steps to Reproduce the Issue

Failed to add download: Invalid parameter

  1. Do something.
  2. Do a different thing.
  3. Expected error.

Failure Logs

Check the bottom of the extension's setting page for a "Debugging Output" section and paste it over this text. If there is no such section in the settings, please say so.

Versions

Extension version: (visible in Synology Download Manager's settings (Firefox) or in the extension list (Chrome)) 0.9.3 Browser version: (usually found in the browser's "About") 86.0.1 (64 bites) DSM version: (DSM > Control Panel > Info Center > General > DSM version) 86.0.1 (64 bites)

seansfkelley commented 3 years ago

You copy-pasted your browser version twice instead of including the DSM version. I take it you're on DSM 7? Your error message is the same as https://github.com/seansfkelley/synology-download-manager/issues/166.

szabomarcihu commented 3 years ago

Sorry.

This error has occurred after the Download Station 3.8.16-3566 update.

DSM version: (DSM > Control Panel > Info Center > General > DSM version)

DSM 6.2.3-25426 Update 3

szabomarcihu commented 3 years ago

Trying on another torrent site, again has the same error: Invalid parameter.

Speckbrot3000 commented 3 years ago

Yep, same for me. Same extension version, but whenever trying to add a torrent via rightclick - "Invalid parameter" error pops up. DSM version: DSM 6.2.4-25556

hambone43 commented 3 years ago

Rolled back Download Station to 3.8.15-3563 and it works fine.

https://archive.synology.com/download/Package/DownloadStation/3.8.15-3563

seansfkelley commented 3 years ago

Thanks for the information and workaround! I'm currently away from my NAS for some time so I won't be able to investigate for a few weeks.

If you've got a download link you're willing to share that didn't work, that would help a lot to reproduce the issue.

szabomarcihu commented 3 years ago

https://cdimage.debian.org/debian-cd/current/amd64/bt-cd/debian-10.8.0-amd64-netinst.iso.torrent

lordvandal commented 3 years ago

Same issue for me, same version of Download Station 3.8.16-3566, on DSM 6.2.3-25426 Update 3.

I do not have a Debugging Output section in my Mozilla extension.

On Synology I get the following message repeatedly in the logs: 2021-03-25T13:17:45+02:00 MyDiskStation task.cgi: WebAPI.cpp:199 UnHandled Upload File Operation.

It seems that Download Station 3.8.16-3566 breaks the addition of downloads: Sonarr/Sonarr#4388

seansfkelley commented 3 years ago

Thank you @lordvandal for the logs and the link to another issue -- happy to know that this is not a fault on my end.

I've pinned this issue, retitled it slightly, and I sent a support request to Synology notifying them of the issue and requesting a fix. I'll post any response/updates here when I get them.

xhiena commented 3 years ago

Same issue here (DS 3..8.16-3566 and Sonar v3.0.6.1196) Failed to add task from data - Reason: Invalid parameter

Downgrading to DS 3.8.15 workaround worked.

[v3.0.6.1196]` NzbDrone.Core.Download.Clients.DownloadClientException: Failed to add task from data The Flash 2014 S07E05 720p HEVC x265-MeGusta.torrent. Reason: Invalid parameter
  at NzbDrone.Core.Download.Clients.DownloadStation.Proxies.DiskStationProxyBase.ProcessRequest[T] (NzbDrone.Common.Http.HttpRequestBuilder requestBuilder, System.String operation, NzbDrone.Core.Download.Clients.DownloadStation.DiskStationApi api, NzbDrone.Core.Download.Clients.DownloadStation.DownloadStationSettings settings) [0x000e8] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\Clients\DownloadStation\Proxies\DiskStationProxyBase.cs:125 
  at NzbDrone.Core.Download.Clients.DownloadStation.Proxies.DiskStationProxyBase.ProcessRequest[T] (NzbDrone.Common.Http.HttpRequestBuilder requestBuilder, System.String operation, NzbDrone.Core.Download.Clients.DownloadStation.DownloadStationSettings settings) [0x00000] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\Clients\DownloadStation\Proxies\DiskStationProxyBase.cs:66 
  at NzbDrone.Core.Download.Clients.DownloadStation.Proxies.DownloadStationTaskProxy.AddTaskFromData (System.Byte[] data, System.String filename, System.String downloadDirectory, NzbDrone.Core.Download.Clients.DownloadStation.DownloadStationSettings settings) [0x00038] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\Clients\DownloadStation\Proxies\DownloadStationTaskProxy.cs:36 
  at NzbDrone.Core.Download.Clients.DownloadStation.TorrentDownloadStation.AddFromTorrentFile (NzbDrone.Core.Parser.Model.RemoteEpisode remoteEpisode, System.String hash, System.String filename, System.Byte[] fileContent) [0x0001f] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\Clients\DownloadStation\TorrentDownloadStation.cs:177 
  at NzbDrone.Core.Download.TorrentClientBase`1[TSettings].DownloadFromWebUrl (NzbDrone.Core.Parser.Model.RemoteEpisode remoteEpisode, System.String torrentUrl) [0x00230] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\TorrentClientBase.cs:190 
  at NzbDrone.Core.Download.TorrentClientBase`1[TSettings].Download (NzbDrone.Core.Parser.Model.RemoteEpisode remoteEpisode) [0x00148] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\TorrentClientBase.cs:117 
  at NzbDrone.Core.Download.DownloadService.DownloadReport (NzbDrone.Core.Parser.Model.RemoteEpisode remoteEpisode) [0x0018f] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\DownloadService.cs:75 
  at NzbDrone.Core.Download.ProcessDownloadDecisions.ProcessDecisions (System.Collections.Generic.List`1[T] decisions) [0x00104] in M:\BuildAgent\work\63739567f01dbcc2\src\NzbDrone.Core\Download\ProcessDownloadDecisions.cs:77 
seansfkelley commented 3 years ago

@xhiena I think you're looking for https://github.com/Sonarr/Sonarr.

samzlab commented 3 years ago

The problem occurs when you pass the torrent file contents directly and send it to the API ("metadata-file" mode).

I disabled the auto-download functionality (just for testing) and now it's working for simple torrent file downloads (forced "direct-download" mode).

src/common/apis/protocols.ts

export const AUTO_DOWNLOAD_TORRENT_FILE_PROTOCOLS = []; // ["http", "https"];
seansfkelley commented 3 years ago

Interesting, I just confirmed that that appears to work with 3.8.16, though the progress bar moves backwards after it finishes the .torrent and realizes that there's more to download. I'll have to do some more thorough checking when I have the opportunity to make sure it doesn't break with earlier versions of DownloadStation though. Thanks for the find!

Edit: ah, but doing so will break private trackers, because the NAS can't authenticate with them like the browser can. I think I still have to rely on Synology to unbreak what they broke.

crion66 commented 3 years ago

Probably Synology will just sneak in updated application from the DSM7 into the DSM6 as time progresses. You will probably start designing for DSM7 and DSM6 will magically start to work...

seansfkelley commented 3 years ago

Inspecting the traffic on the DSM page when I went to upload a .torrent manually, I noticed that it uses the Syno.DownloadStation2.Task API, which is definitely undocumented and might be brand new.

There might be a backwards-compatible workaround I can implement in the extension here if I can figure out how it works and which versions of DSM support it.

seansfkelley commented 3 years ago

Response received. This seems promising, but I don't have time to try implementing it right this moment. I've lightly edited the response to make it prettier in Markdown.

Synology's response/documentation (verbatim from support person)

I have received an update from the developers regarding this matter. They reported that in DSM 6.2.4 and DSM 7.0, the API for uploading .torrent or .nzb have been removed. The new version of the API is below, as they have provided:

link: /webapi/entry.cgi/

API: SYNO.DownloadStation2.Task

method: create

version: 2

Parameters:

Name type Description Required Condition
type string Source type true One of "file", "url" or "local".
create_list boolean Create file list if there are multiple files. true
destination string Download destination path starting with a shared folder true
file '["torrent"]' false It is required when type is "file".
url array of string Array of links to download.

Accepts HTTP/FTP/magnet/ED2K links.
false It is required when type is "url".
local_path string Download destination path starting with a shared folder false It is required when type is "local".
extract_password string Password for extracting download tasks. false

Example to create BT task in Python

#!/usr/bin/env python
import argparse
import os
import sys

import requests

def do_login(session, ip, port, account, password):
    url = "http://{}:{}/webapi/auth.cgi".format(ip, port)
    data = {
        "api": "SYNO.API.Auth",
        "method": "login",
        "version": 6,
        "account": account,
        "passwd": password,
        "session": "DownloadStation",
        "format": "cookie",
    }
    resp = session.post(url, data=data)
    resp_json = resp.json()
    if not resp_json["success"]:
        print("login resp: {}".format(resp.text))
        raise PermissionError

def create_bt_task(session, ip, port, file_path):
    cgi_path = "/webapi/entry.cgi/"
    url = "http://{}:{}{}".format(ip, port, cgi_path)

    if not os.path.exists(file_path):
        print("host path {} not exists".format(file_path))
        return

    data = {
        "api": "SYNO.DownloadStation2.Task",
        "method": "create",
        "version": 2,
        "type": '"file"',
        "destination": '"home"',
        "file": '["torrent"]',
        "create_list": "false",
    }
    files = {
        "torrent": open(file_path, "rb"),
    }
    resp = session.post(url, data=data, files=files)
    print("resp: {}".format(resp.text))

if __name__ == "__main__":
    ip = "ip to your machine"
    port = "port number"
    account = "account"
    password = "password"
    file_path = "path to .torrent or .nzb or .txt"

    session = requests.Session()
    do_login(session, ip, port, account, password)
    create_bt_task(session, ip, port, file_path)

a few notes

I played around with this API and it's worth noting a few things:

Taloth commented 3 years ago

Thx for letting us know, appreciate it.

They reported that in DSM 6.2.4 and DSM 7.0, the API for uploading .torrent or .nzb have been removed

Did they provide a reason for the breaking change?

The example code looks like it's still support uploading something, just changed slightly. Also, same apiVersion as current.

seansfkelley commented 3 years ago

Did they provide a reason for the breaking change?

No, that response is all the useful information I have. I'm guessing they were redesigning it anyway for DSM 7 and then chose to handle whatever security issue they found in the previous version by just removing the non-7 version of the API. Or something along those lines.

Also, same apiVersion as current.

Yeah, but it's using DownloadStation2 instead of DownloadStation.

snechaev commented 3 years ago

@seansfkelley, I can confirm that a sid is supported by DownloadStation2. But the sid should be passed as query string parameter, so POST /webapi/entry.cgi?_sid=<sid goes here>

Other parameters mentioned in a synology's answer at the same time should be passed as part of formdata (as shown in python sample).

It is also required to call Auth with format=sid to allow use the returned sid as parameter (and not as cookie value).

seansfkelley commented 3 years ago

Thanks @snechaev! I've updated that comment to reflect that so all the notes are in one place.

seansfkelley commented 3 years ago

This should be fixed by version 0.10.0 that I just published.

If you're using Firefox, you should be able to update right now.

If you're using Chrome, I don't know when Google will approve the changes, but watch for any updates there.

Thank you to everyone that chimed in! You all made this easier to debug.

Speckbrot3000 commented 3 years ago

Glad you found a solution, thank you very much!

BenjV commented 3 years ago

You can use sid as part of the data dict but the syntax is "_sid" (with an underscore at the front) So you have to get a sid with the login and use that in the data dict so it would become:

data = {
        "api": "SYNO.DownloadStation2.Task",
        "method": "create",
        "version": 2,
        "type": '"file"',
        "destination": '"home"',
        "file": '["torrent"]',
        "create_list": "false",
        "_sid": sid
    }
snechaev commented 3 years ago

@BenjV, could you please provide extended sample? I tried to pass _sid as you suggested, but got error 119. I used sid, received with Auth request with format=sid. My download station version is 3.8.16-3566. Than you!

BenjV commented 3 years ago

Ok here an complete Python example. Python 3 is used and the requests module must be present.

EDIT: ~~I found out that this Download Station only start downloading when the authorization is done via the "admin" account, no other acount works, not even when it is in de administrators group. If you use another user then "admin", torrents are added to Download Station but they don't ever start working.~~ This was a faulty conclusion. You can use any account as long as it has application privileges to DownLoadStation and you have configured the download location in DownloadStation for that user. If you fail to do that the download will be added but never starts.


import requests

class DownloadStationAPI():
    """Synology Download Station API class."""

    def __init__(self, host='http://192.168.1.105:5000'):
        self.host = host
        self.session = requests.Session()
        self.sid = None
        self.dest = None

        self.error_map = {
            100: 'Unknown error',
            101: 'Invalid parameter',
            102: 'The requested API does not exist',
            103: 'The requested method does not exist',
            104: 'The requested version does not support the functionality',
            105: 'The logged in session does not have permission',
            106: 'Session timeout', 
            107: 'Session interrupted by duplicate login',
            119: 'SID not found (not logged in)',
            400: 'File upload failed',
            401: 'Max number of tasks reached',
            402: 'Destination denied',
            403: 'Destination does not exist',
            404: 'Invalid task id',
            405: 'Invalid task action',
            406: 'No default destination',
            407: 'Set destination failed',
            408: 'File does not exist'
        }

    def Auth(self, User, Passwd):
        Url = f'{self.host}/webapi/auth.cgi'
        data = {    'method' : 'login',
                    'api'    : 'SYNO.API.Auth',
                    'version': 3,
                    'session': 'DownloadStation',
                    'account': User,
                    'passwd' : Passwd
                 }

        try:
            response = self.session.get(Url, params=data, timeout=30)
        except RequestException as error:
            print(error)
            return False
        if response.ok:
            jdata = response.json()
            if jdata.get('success'):
                self.sid = jdata['data']['sid']
            else:
                print (self.error_map[jdata.get('error',{}).get('code', 100)])
                return False
        return True

    def Add(self, Uri=None):
        Url = f'{self.host}/webapi/entry.cgi'
        if not Uri:
            Uri = 'https://linuxtracker.org/index.php?page=downloadcheck&id=ffc9e93bfa23b8039abe591403ed17ba4154d236'
        Dest = 'BT' # Must be a valid share 
        params = {
            'api'         : 'SYNO.DownloadStation2.Task',
            'version'     : 2,
            'method'      : 'create',
            'create_list' : 'false',
            'type'        : '"url"',
            'url'         : Uri,
            'destination' : f'"{Dest}"',
            '_sid'        : self.sid
        }
        try:
            response = self.session.post(Url, data=params, timeout=30 )
            if response.ok:
                jdata = response.json()
                if jdata.get('success'):
                    print (f"Torrent added with task id: {jdata['data']['task_id'][0]}")
                    return True
                else:
                    print (self.error_map[jdata.get('error',{}).get('code', 100)])
                    return False
        except Exception as error:
            print(error)
            return False

# Change the adress to the correct Syno adress:port and fill in the login credentials
SynoAdress = 'http://192.168.1.105:5000'
UserName = 'admin'
Password = '*****'
Uri = None  # Link to a torrent url or magnetlink, if left empty a test torrent will be used

SynoApi = DownloadStationAPI(SynoAdress)
if not SynoApi.Auth(UserName, Password):
    print ('Authorization failed')
    sys.exit()

SynoApi.Add(Uri)