Skip to content
This repository has been archived by the owner on Sep 19, 2024. It is now read-only.

Commit

Permalink
Merge pull request #8 from bemble/aria2
Browse files Browse the repository at this point in the history
Add Aria2
  • Loading branch information
bemble authored May 11, 2024
2 parents 81f4bf8 + c26e5dd commit a6d7291
Show file tree
Hide file tree
Showing 15 changed files with 333 additions and 25 deletions.
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ COPY --from=front-builder /app/public /app/public

# copy server files
COPY ./server /app/server
RUN chmod +x /app/server/entrypoint.sh

WORKDIR /app/server
RUN pip install -r requirements.txt
ENTRYPOINT ["/app/server/entrypoint.sh"]

WORKDIR /app
COPY entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh

ENTRYPOINT ["/app/entrypoint.sh"]

EXPOSE 8765
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ holerr:
**Downloaders:**
- Synology Download Station
- Aria2 (using JSON-RPC over HTTP)
## Migrate from v1
Expand All @@ -67,18 +68,24 @@ debrider:
downloader:
# Synology
synology_download_station:
# Your Synology endpoint (example: "http://192.168.1.1:5000")
endpoint: synology endpoint # string
# Your Synology endpoint
endpoint: synology endpoint # string (example: "http://192.168.1.1:5000")
# DSM username (this user must not have 2FA)
username: dsm user # string
# DSM password
password: use password # string
# Aria2
aria2_jsonrpc:
# Your aria2 JSON-RPC endpoint (http)
endpoint: aria2 endpoint # string (example: "http://192.168.1.1:6000")
# Aria2 JSONRPC secret
secret: aria2 secret # optional, string
# Presets list
presets:
- name: Downloads # string
# Directory that holerr will watch
watch_dir: holes/downloads # string
# Downloader output directory
# Downloader output directory, relative to the downloader
output_dir: Downloads # string
# Whether the file should be downloaded in a subdoctory or not
create_sub_dir: false # optional, boolean
Expand All @@ -88,6 +95,8 @@ presets:
min_file_size: # human readbale string, example: 3.0B, 12KB, 432.2MB, 4.5GB, 1TB
```

:warning: currently, you can only have one debrider and one downloader.

### Synology specifics

You should have a user dedicated to this. Create a user (_Configuration_ > _Users and groups_), for this project it only needs access to DownloadStation application and write/read access to the output directories.
Expand All @@ -102,6 +111,10 @@ Before being able to start a download in Download Station you **must**:
- log out
- remove access to DSM for this user (_Configuration_ > _Users and groups_ > _[the user], edit_ > _Applications_ > _DSM_)

### Aria2 specifics

aria2 does not push any progress event over websocket, it has to be pulled, so for now there's real advantage to implement websocket connection.

## Development

### Folder structure
Expand Down
2 changes: 2 additions & 0 deletions server/entrypoint.sh → entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/bin/sh

cd server

if [ -d ".venv" ]
then
source ".venv/bin/activate"
Expand Down
2 changes: 2 additions & 0 deletions front/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"settings": {
"title": "Settings",
"language": "Language",
"configuration_downloader": "Downloader",
"configuration_subtitle": "Configuration",
"configuration_debug": "Debug",
"configuration_base_path": "Base path",
Expand All @@ -57,6 +58,7 @@
"configuration_endpoint": "Endpoint",
"configuration_username": "Username",
"configuration_password": "Password",
"configuration_secret": "Secret",
"configuration_save": "Save",
"configuration_restart_required": "A restart will be necessary for the changes to take effect."
},
Expand Down
2 changes: 2 additions & 0 deletions front/src/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"settings": {
"title": "Paramètres",
"language": "Langue",
"configuration_downloader": "Downloader",
"configuration_subtitle": "Configuration",
"configuration_debug": "Debug",
"configuration_base_path": "Chemin de base",
Expand All @@ -56,6 +57,7 @@
"configuration_endpoint": "Endpoint",
"configuration_username": "Nom d'utilisateur",
"configuration_password": "Mot de passe",
"configuration_secret": "Secret",
"configuration_save": "Enregistrer",
"configuration_restart_required": "Un redémarrage sera nécessaire pour que les changements soient effectifs."
},
Expand Down
8 changes: 7 additions & 1 deletion front/src/models/configuration.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ export type RealDebrid = {
};

export type Downloader = {
synology_download_station: SynologyDownloadStation;
synology_download_station?: SynologyDownloadStation;
aria2_jsonrpc?: Aria2JsonRpc;
};

export type SynologyDownloadStation = {
endpoint: string;
username: string;
password: string;
};

export type Aria2JsonRpc = {
endpoint: string;
secret?: string;
};
102 changes: 95 additions & 7 deletions front/src/pages/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ const Settings = () => {
}: ChangeEvent<HTMLInputElement>) => {
if (config) {
const tmpConf = Object.assign({}, config);
if (!tmpConf.downloader) {
if (
!tmpConf.downloader ||
!tmpConf.downloader.synology_download_station
) {
tmpConf.downloader = {
synology_download_station: {
endpoint: "",
Expand All @@ -90,13 +93,16 @@ const Settings = () => {
},
};
}

// @ts-ignore
tmpConf.downloader.synology_download_station.endpoint = target.value;

const tmpNewConfig = Object.assign({}, newConfig);
if (!tmpNewConfig.downloader) {
tmpNewConfig.downloader = { synology_download_station: {} };
}
tmpNewConfig.downloader.synology_download_station.endpoint =
// @ts-ignore
tmpConf.downloader.synology_download_station.endpoint;

setConfig(tmpConf);
Expand All @@ -109,7 +115,10 @@ const Settings = () => {
}: ChangeEvent<HTMLInputElement>) => {
if (config) {
const tmpConf = Object.assign({}, config);
if (!tmpConf.downloader) {
if (
!tmpConf.downloader ||
!tmpConf.downloader.synology_download_station
) {
tmpConf.downloader = {
synology_download_station: {
endpoint: "",
Expand All @@ -118,13 +127,16 @@ const Settings = () => {
},
};
}
// @ts-ignore
tmpConf.downloader.synology_download_station.username = target.value;

const tmpNewConfig = Object.assign({}, newConfig);
if (!tmpNewConfig.downloader) {
tmpNewConfig.downloader = { synology_download_station: {} };
}

tmpNewConfig.downloader.synology_download_station.username =
// @ts-ignore
tmpConf.downloader.synology_download_station.username;

setConfig(tmpConf);
Expand All @@ -137,7 +149,10 @@ const Settings = () => {
}: ChangeEvent<HTMLInputElement>) => {
if (config) {
const tmpConf = Object.assign({}, config);
if (!tmpConf.downloader) {
if (
!tmpConf.downloader ||
!tmpConf.downloader.synology_download_station
) {
tmpConf.downloader = {
synology_download_station: {
endpoint: "",
Expand All @@ -146,20 +161,81 @@ const Settings = () => {
},
};
}
// @ts-ignore
tmpConf.downloader.synology_download_station.password = target.value;

const tmpNewConfig = Object.assign({}, newConfig);
if (!tmpNewConfig.downloader) {
tmpNewConfig.downloader = { synology_download_station: {} };
}
tmpNewConfig.downloader.synology_download_station.password =
// @ts-ignore
tmpConf.downloader.synology_download_station.password;

setConfig(tmpConf);
setNewConfig(tmpNewConfig);
}
};

const handleChangeAria2Endpoint = ({
target,
}: ChangeEvent<HTMLInputElement>) => {
if (config) {
const tmpConf = Object.assign({}, config);
if (!tmpConf.downloader || !tmpConf.downloader.aria2_jsonrpc) {
tmpConf.downloader = {
aria2_jsonrpc: {
endpoint: "",
},
};
}

// @ts-ignore
tmpConf.downloader.aria2_jsonrpc.endpoint = target.value;

const tmpNewConfig = Object.assign({}, newConfig);
if (!tmpNewConfig.downloader) {
tmpNewConfig.downloader = { aria2_jsonrpc: {} };
}
tmpNewConfig.downloader.aria2_jsonrpc.endpoint =
// @ts-ignore
tmpConf.downloader.aria2_jsonrpc.endpoint;

setConfig(tmpConf);
setNewConfig(tmpNewConfig);
}
};

const handleChangeAria2Secret = ({
target,
}: ChangeEvent<HTMLInputElement>) => {
if (config) {
const tmpConf = Object.assign({}, config);
if (!tmpConf.downloader || !tmpConf.downloader.aria2_jsonrpc) {
tmpConf.downloader = {
aria2_jsonrpc: {
endpoint: "",
secret: "",
},
};
}

// @ts-ignore
tmpConf.downloader.aria2_jsonrpc.secret = target.value;

const tmpNewConfig = Object.assign({}, newConfig);
if (!tmpNewConfig.downloader) {
tmpNewConfig.downloader = { aria2_jsonrpc: {} };
}
tmpNewConfig.downloader.aria2_jsonrpc.secret =
// @ts-ignore
tmpConf.downloader.aria2_jsonrpc.secret;

setConfig(tmpConf);
setNewConfig(tmpNewConfig);
}
};

const handleSave = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

Expand Down Expand Up @@ -233,31 +309,43 @@ const Settings = () => {
{t("settings.real_debrid_website")}
</a>
</div>
<h3>Synology Download Station</h3>
<h3>{t("settings.configuration_downloader")}</h3>
<h4>Synology Download Station</h4>
<TextField
value={
config.downloader?.synology_download_station
.endpoint
?.endpoint
}
onChange={handleChangeSynoEndpoint}
label={t("settings.configuration_endpoint")}
/>
<TextField
value={
config.downloader?.synology_download_station
.username
?.username
}
onChange={handleChangeSynoUsername}
label={t("settings.configuration_username")}
/>
<TextField
value={
config.downloader?.synology_download_station
.password
?.password
}
onChange={handleChangeSynoPassword}
label={t("settings.configuration_password")}
/>
<h4>Aria2 JSON-RPC</h4>
<TextField
value={config.downloader?.aria2_jsonrpc?.endpoint}
onChange={handleChangeAria2Endpoint}
label={t("settings.configuration_endpoint")}
/>
<TextField
value={config.downloader?.aria2_jsonrpc?.secret}
onChange={handleChangeAria2Secret}
label={t("settings.configuration_secret")}
/>
<div className={classes.spacer} />
<Button
type="submit"
Expand Down
5 changes: 5 additions & 0 deletions server/holerr/api/routers/routers_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,14 @@ class PartialSynologyDownloadStation(BaseModel):
username: Optional[str] = None
password: Optional[SecretStr] = None

class PartialAria2JsonRpc(BaseModel):
endpoint: Optional[str] = None
secret: Optional[SecretStr] = None


class PartialDownloader(BaseModel):
synology_download_station: Optional[PartialSynologyDownloadStation] = None
aria2_jsonrpc: Optional[PartialAria2JsonRpc] = None


class PartialConfig(BaseModel):
Expand Down
11 changes: 10 additions & 1 deletion server/holerr/core/config_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,22 @@ class SynologyDownloadStation(Model):
def dump_secret(self, v, info):
return self._dump_secret(v, info)

class Aria2JsonRpc(Model):
endpoint: str
secret: Optional[SecretStr]

@field_serializer("secret")
def dump_secret(self, v, info):
return self._dump_secret(v, info)


class Downloader(Model):
synology_download_station: Optional[SynologyDownloadStation] = None
aria2_jsonrpc: Optional[Aria2JsonRpc] = None

@model_validator(mode="after")
def verify_any_of(self):
if not self.synology_download_station:
if not (self.synology_download_station or self.aria2_jsonrpc):
raise ValidationError("A downloader needs to be set.")
return self

Expand Down
6 changes: 4 additions & 2 deletions server/holerr/downloaders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ def __init__(self) -> None:
def update(self):
if config.downloader.synology_download_station:
from .synology_download_station import SynologyDownloadStation

self._downloader = SynologyDownloadStation(config.downloader.synology_download_station)
self._downloader = SynologyDownloadStation()
elif config.downloader.aria2_jsonrpc:
from .aria2_jsonrpc import Aria2JsonRpc
self._downloader = Aria2JsonRpc()
else:
self._downloader = None

Expand Down
Loading

0 comments on commit a6d7291

Please sign in to comment.