mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-30 19:21:16 -05:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11ba9ae12a | ||
|
|
a61a5539a7 | ||
|
|
77f7490aea | ||
|
|
a7198b6a81 | ||
|
|
4c77954526 | ||
|
|
a229a2a5ea | ||
|
|
0a2f3865ee | ||
|
|
900e68bb9a | ||
|
|
977dbc805f | ||
|
|
abcca19820 | ||
|
|
52a7b5dcff | ||
|
|
9518714885 | ||
|
|
1de674a532 | ||
|
|
e1dad3e4c4 | ||
|
|
44f2eb8620 | ||
|
|
70945a9c5b | ||
|
|
fdfca97dfa | ||
|
|
b84900dcb5 | ||
|
|
d989ec928a | ||
|
|
4a89fcf8ea | ||
|
|
d7fa3e1f7b | ||
|
|
d11e757c6e | ||
|
|
c1417c319d | ||
|
|
6689939cc9 | ||
|
|
09347d0766 | ||
|
|
41db09057c | ||
|
|
6983058f49 | ||
|
|
fb2d412c97 | ||
|
|
1c0b1205b2 | ||
|
|
f556cea488 | ||
|
|
a2447253a0 | ||
|
|
3393d7c976 | ||
|
|
06572bdf7d | ||
|
|
4f9ed7803f | ||
|
|
95bc069af9 | ||
|
|
d4411f1b8f | ||
|
|
1bfd1b8f41 | ||
|
|
c47dbfdc26 | ||
|
|
b5e55cd9b2 | ||
|
|
85c98d7203 | ||
|
|
9e95717619 | ||
|
|
90b4ff2720 | ||
|
|
0f97a9fdfc | ||
|
|
90caf0c164 | ||
|
|
9b3fe470a0 | ||
|
|
ab318729ab | ||
|
|
9576554426 | ||
|
|
3cd819b78d | ||
|
|
bb24f3f04e | ||
|
|
6f4416236d | ||
|
|
47dcccd17f | ||
|
|
6b026d8274 | ||
|
|
ec18606557 | ||
|
|
d1d9bab65a | ||
|
|
e2560bf214 | ||
|
|
895c8549ba | ||
|
|
0d80efb898 | ||
|
|
deace9f8ae | ||
|
|
1c96dff133 | ||
|
|
1734b11338 | ||
|
|
5f3c4d17da | ||
|
|
4ffe0e27fb | ||
|
|
951bc0c957 | ||
|
|
60f985ba00 | ||
|
|
a42a2db196 | ||
|
|
64034c5636 | ||
|
|
e03a031342 | ||
|
|
825322baa4 | ||
|
|
7a5ca5b226 | ||
|
|
cb4f022d17 | ||
|
|
da3d72b484 | ||
|
|
e3042a6106 | ||
|
|
55f1253a56 | ||
|
|
913e4ea02e | ||
|
|
aa0d44a60b | ||
|
|
5e432bea37 | ||
|
|
2d0cc08987 | ||
|
|
627797f8c7 | ||
|
|
e37a777f29 | ||
|
|
13a76e5824 | ||
|
|
e4c64cac12 | ||
|
|
c6694483e4 | ||
|
|
8b5f29df8f | ||
|
|
82954f5930 | ||
|
|
bc793e11c4 | ||
|
|
4980fc70a0 | ||
|
|
8afac4f6fb | ||
|
|
c78b633da8 | ||
|
|
a1ee1677dc | ||
|
|
511bb153d7 | ||
|
|
c1af36f6b0 | ||
|
|
6028824966 | ||
|
|
49a7300ad6 | ||
|
|
8e8e560eac | ||
|
|
e51da569ca | ||
|
|
6ce43eed5f | ||
|
|
73ec6d8323 | ||
|
|
27a7531f79 | ||
|
|
423bdb4f81 | ||
|
|
7f8081e2cc | ||
|
|
50c2d5e2ab | ||
|
|
552bfd4b72 | ||
|
|
3d522c8205 | ||
|
|
b6b0d10367 | ||
|
|
67f1858315 | ||
|
|
55bb81ceef | ||
|
|
8d4d69d56b | ||
|
|
0e475e593a | ||
|
|
78424318ce | ||
|
|
57c90b2554 | ||
|
|
9b94d22621 | ||
|
|
5c30b0ee29 | ||
|
|
3a1c60a3ed | ||
|
|
bd07a79c97 | ||
|
|
c562c9a468 | ||
|
|
fdd3f590cd | ||
|
|
77e9627e64 | ||
|
|
c40d1274d2 | ||
|
|
d1948071fc | ||
|
|
4f79d924e6 | ||
|
|
872cf835df | ||
|
|
69bb1a87a4 | ||
|
|
e3339a1ab4 |
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -3,7 +3,7 @@ description: >
|
||||
Did you discover a bug in SABnzbd? Report it here!
|
||||
If you are not 100% certain this is a bug please go to our forums, Reddit or Discord server first.
|
||||
labels:
|
||||
- Bug
|
||||
- Support
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
2
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: Feature request
|
||||
description: What new feature would you like to have added to SABnzbd?
|
||||
labels:
|
||||
- Feature request
|
||||
- Support
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
||||
5
.github/renovate.json
vendored
5
.github/renovate.json
vendored
@@ -26,6 +26,11 @@
|
||||
"werkzeug"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchPackageNames": ["windows", "macos"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
|
||||
157
.github/workflows/build_release.yml
vendored
157
.github/workflows/build_release.yml
vendored
@@ -9,14 +9,14 @@ env:
|
||||
jobs:
|
||||
build_windows:
|
||||
name: Build Windows binary
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.13
|
||||
uses: actions/setup-python@v5
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
python-version: "3.14"
|
||||
architecture: "x64"
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
@@ -27,18 +27,59 @@ jobs:
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt --no-dependencies
|
||||
pip install --upgrade -r builder/requirements.txt --no-dependencies
|
||||
- name: Build Windows standalone binary and installer
|
||||
run: python builder/package.py installer
|
||||
- name: Upload Windows standalone binary
|
||||
- name: Build Windows standalone binary
|
||||
id: windows_binary
|
||||
run: python builder/package.py binary
|
||||
- name: Upload Windows standalone binary (unsigned)
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload-unsigned-binary
|
||||
with:
|
||||
path: "*-win64-bin.zip"
|
||||
name: Windows standalone binary
|
||||
- name: Sign Windows standalone binary
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
if: contains(github.ref, 'refs/tags/')
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ secrets.SIGNPATH_ORG_ID }}
|
||||
project-slug: "sabnzbd"
|
||||
artifact-configuration-slug: "sabnzbd-binary"
|
||||
signing-policy-slug: "release-signing"
|
||||
github-artifact-id: ${{ steps.upload-unsigned-binary.outputs.artifact-id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: "signed"
|
||||
- name: Upload Windows standalone binary (signed)
|
||||
uses: actions/upload-artifact@v4
|
||||
if: contains(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: Windows standalone binary (signed)
|
||||
path: "signed"
|
||||
- name: Build Windows installer
|
||||
run: python builder/package.py installer
|
||||
- name: Upload Windows installer
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload-unsigned-installer
|
||||
with:
|
||||
path: "*-win-setup.exe"
|
||||
name: Windows installer
|
||||
- name: Sign Windows installer
|
||||
uses: signpath/github-action-submit-signing-request@v1
|
||||
if: contains(github.ref, 'refs/tags/')
|
||||
with:
|
||||
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
|
||||
organization-id: ${{ secrets.SIGNPATH_ORG_ID }}
|
||||
project-slug: "sabnzbd"
|
||||
artifact-configuration-slug: "sabnzbd-installer"
|
||||
signing-policy-slug: "release-signing"
|
||||
github-artifact-id: ${{ steps.upload-unsigned-installer.outputs.artifact-id }}
|
||||
wait-for-completion: true
|
||||
output-artifact-directory: "signed"
|
||||
- name: Upload Windows installer (signed)
|
||||
if: contains(github.ref, 'refs/tags/')
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows installer (signed)
|
||||
path: "signed/*-win-setup.exe"
|
||||
|
||||
build_macos:
|
||||
name: Build macOS binary
|
||||
@@ -48,18 +89,18 @@ jobs:
|
||||
# We need the official Python, because the GA ones only support newer macOS versions
|
||||
# The deployment target is picked up by the Python build tools automatically
|
||||
# If updated, make sure to also set LSMinimumSystemVersion in SABnzbd.spec
|
||||
PYTHON_VERSION: "3.13.5"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.13"
|
||||
PYTHON_VERSION: "3.14.0"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.15"
|
||||
# We need to force compile for universal2 support
|
||||
CFLAGS: -arch x86_64 -arch arm64
|
||||
ARCHFLAGS: -arch x86_64 -arch arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up Python 3.13
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
# Only use this for the caching of pip packages!
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.13"
|
||||
python-version: "3.14"
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
- name: Cache Python download
|
||||
@@ -89,8 +130,8 @@ jobs:
|
||||
if: env.CERTIFICATES_P12
|
||||
run: |
|
||||
echo $CERTIFICATES_P12 | base64 --decode > certificate.p12
|
||||
security create-keychain -p "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
security default-keychain -s build.keychain
|
||||
security create-keychain -p "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
security default-keychain -s build.keychain
|
||||
security unlock-keychain -p "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
security set-keychain-settings -lut 21600 build.keychain
|
||||
security import certificate.p12 -k build.keychain -P "$CERTIFICATES_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign -T /usr/bin/xcrun
|
||||
@@ -117,22 +158,83 @@ jobs:
|
||||
path: "*-macos.dmg"
|
||||
name: macOS binary
|
||||
|
||||
build-snap:
|
||||
name: Build Snap Packages (${{ matrix.linux_arch }})
|
||||
timeout-minutes: 30
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
linux_arch: amd64
|
||||
- os: ubuntu-24.04-arm
|
||||
linux_arch: arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Cache par2cmdline-turbo tarball
|
||||
uses: actions/cache@v4
|
||||
id: cache-par2cmdline
|
||||
# Clearing the cache in case of new version requires manual clearing in GitHub!
|
||||
with:
|
||||
path: snap/par2cmdline.tar.gz
|
||||
key: cache-par2cmdline
|
||||
- name: Download par2cmdline-turbo tarball
|
||||
if: steps.cache-par2cmdline.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
PAR2_TARBALL=$(curl -sL https://api.github.com/repos/animetosho/par2cmdline-turbo/releases/latest | jq -r '.tarball_url')
|
||||
curl -o snap/par2cmdline.tar.gz -L "$PAR2_TARBALL"
|
||||
- uses: snapcore/action-build@v1
|
||||
name: Build snap
|
||||
id: snapcraft
|
||||
- name: Test snap installation
|
||||
run: |
|
||||
sudo snap install --dangerous *.snap
|
||||
sudo snap connect sabnzbd:removable-media
|
||||
# Basic smoke test - check that the binary exists and can show help
|
||||
timeout 10s snap run sabnzbd --help || true
|
||||
sudo snap remove sabnzbd
|
||||
- name: Upload snap
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Snap package (${{ matrix.linux_arch }})
|
||||
path: ${{ steps.snapcraft.outputs.snap }}
|
||||
- name: Publish snap
|
||||
uses: snapcore/action-publish@v1
|
||||
if: contains(github.ref, 'refs/tags/')
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_TOKEN }}
|
||||
with:
|
||||
store_login: ${{ secrets.SNAP_TOKEN }}
|
||||
snap: ${{ steps.snapcraft.outputs.snap }}
|
||||
release: stable
|
||||
|
||||
release:
|
||||
name: Prepare Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_windows, build_macos]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
python-version: "3.14"
|
||||
cache: pip
|
||||
cache-dependency-path: "builder/release-requirements.txt"
|
||||
- name: Download Source distribution artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
path: dist
|
||||
- name: Move all artifacts to main folder
|
||||
run: find dist -type f -exec mv {} . \;
|
||||
name: Source distribution
|
||||
- name: Download macOS artifact
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: macOS binary
|
||||
- name: Download Windows artifacts
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
pattern: ${{ (contains(github.ref, 'refs/tags/')) && '*signed*' || '*Windows*' }}
|
||||
merge-multiple: true
|
||||
- name: Prepare official release
|
||||
env:
|
||||
AUTOMATION_GITHUB_TOKEN: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
@@ -140,10 +242,3 @@ jobs:
|
||||
run: |
|
||||
pip3 install -r builder/release-requirements.txt
|
||||
python3 builder/release.py
|
||||
- name: Release latest available Snap
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_TOKEN }}
|
||||
run: |
|
||||
sudo snap install snapcraft --classic
|
||||
python3 snap/local/release_snap.py
|
||||
|
||||
17
.github/workflows/integration_testing.yml
vendored
17
.github/workflows/integration_testing.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Black Code Formatter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Black Code Formatter
|
||||
uses: lgeiger/black-action@master
|
||||
with:
|
||||
@@ -31,24 +31,23 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
name: ["Linux"]
|
||||
os: [ubuntu-latest]
|
||||
include:
|
||||
- name: macOS
|
||||
os: macos-latest
|
||||
python-version: "3.13"
|
||||
os: macos-13
|
||||
python-version: "3.14"
|
||||
- name: Windows
|
||||
os: windows-latest
|
||||
python-version: "3.13"
|
||||
os: windows-2022
|
||||
python-version: "3.14"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: "x64"
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
- name: Install system dependencies
|
||||
|
||||
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
if: github.repository_owner == 'sabnzbd'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
|
||||
4
.github/workflows/translations.yml
vendored
4
.github/workflows/translations.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TX_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
- name: Generate translatable texts
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
run: |
|
||||
python3 tools/make_mo.py
|
||||
- name: Push translatable and translated texts back to repo
|
||||
uses: stefanzweifel/git-auto-commit-action@v6.0.1
|
||||
uses: stefanzweifel/git-auto-commit-action@v7.0.0
|
||||
if: env.TX_TOKEN
|
||||
with:
|
||||
commit_message: |
|
||||
|
||||
@@ -66,3 +66,12 @@ Conditions:
|
||||
- Bugfixes created specifically for a release branch are done there (because they are specific, they're not cherry-picked to `develop`).
|
||||
- Bugfixes done on `develop` may be cherry-picked to a release branch.
|
||||
- We will not release a 1.0.2 if a 1.1.0 has already been released.
|
||||
|
||||
## Privacy Policy
|
||||
|
||||
This program will not transfer any information to other networked systems unless
|
||||
specifically requested by the user or the person installing or operating it.
|
||||
|
||||
## Code Signing Policy
|
||||
|
||||
For our Windows release, free code signing is provided by [SignPath.io](https://signpath.io), certificate by [SignPath Foundation](https://signpath.org).
|
||||
|
||||
64
README.mkd
64
README.mkd
@@ -1,6 +1,57 @@
|
||||
Release Notes - SABnzbd 4.5.2
|
||||
Release Notes - SABnzbd 4.5.5
|
||||
=========================================================
|
||||
|
||||
## Bug fixes and changes in 4.5.5
|
||||
|
||||
* macOS: Failed to start on versions of macOS older than 11.
|
||||
Python 3.14 dropped support for macOS 10.13 and 10.14.
|
||||
Because of that macOS 10.15 is required to run 4.5.5.
|
||||
|
||||
## Bug fixes and changes in 4.5.4
|
||||
|
||||
### New Features
|
||||
* History details now includes option to mark job as `Completed`.
|
||||
* `Quota` notifications available for all notification services.
|
||||
- Sends alerts at 75%, 90%, and 100% quota usage.
|
||||
* Multi-Operations now supports Move to Top/Bottom.
|
||||
* New `outgoing_nntp_ip` option to bind outgoing NNTP connections to specific IP address.
|
||||
|
||||
### Improvements
|
||||
* Setup wizard now requires successful Server Test before proceeding.
|
||||
* Anime episode notation `S04 - 10` now supported for Sorting and Duplicate Detection.
|
||||
* Multi-Operations: Play/Resume button unselects on second click for better usability.
|
||||
* Unrar now handles renaming of invalid characters on Windows filesystem.
|
||||
* Switched from vendored `sabnzbd.rarfile` module to `rarfile>=4.2`.
|
||||
* Warning displayed when removing all Orphaned jobs (clears Temporary Download folder).
|
||||
|
||||
### Bug Fixes
|
||||
* Active connections counter in Status window now updates correctly.
|
||||
* Job setting changes during URL-grabbing no longer ignored.
|
||||
* Incomplete `.par2` file parsing no longer leaves files behind.
|
||||
* `Local IPv4 address` now detectable when using Socks5 proxy.
|
||||
* Server configuration changes no longer show `Failure` message during page reload.
|
||||
|
||||
### Platform-Specific
|
||||
* Linux: `Make Windows compatible` automatically enabled when needed.
|
||||
* Windows: Executables are now signed using SignPath Foundation certificate.
|
||||
* Windows: Can now start SABnzbd directly from installer.
|
||||
* Windows and macOS: Binaries now use Python 3.14.
|
||||
|
||||
## Bug fixes and changes in 4.5.3
|
||||
|
||||
* Remember if `Permanently delete` was previously checked.
|
||||
* All available IP-addresses will be included when selecting the fastest.
|
||||
* Pre-queue script rejected NZBs were sometimes reported as `URL Fetching failed`.
|
||||
* RSS `Next scan` time was not adjusted after manual `Read All Feeds Now`.
|
||||
* Prevent renaming of `.cbr` files during verification.
|
||||
* If `--disable-file-log` was enabled, `Show Logging` would crash.
|
||||
* API: Added `time_added`, timestamp of when the job was added to the queue.
|
||||
* API: History output could contain duplicate items.
|
||||
* Snap: Updated packages and changed build process for reliability.
|
||||
* macOS: Repair would fail on macOS 10.13 High Sierra.
|
||||
* Windows: Unable to start on Windows 8.
|
||||
* Windows: Updated Unrar to 7.13, which resolves CVE-2025-8088.
|
||||
|
||||
## Bug fixes and changes in 4.5.2
|
||||
|
||||
* Added Tab and Shift+Tab navigation to move between rename fields in queue.
|
||||
@@ -37,16 +88,19 @@ Release Notes - SABnzbd 4.5.2
|
||||
|
||||
## Upgrade notices
|
||||
|
||||
* You can directly upgrade from version 3.0.0 and newer.
|
||||
* Upgrading from older versions will require performing a `Queue repair`.
|
||||
* Downgrading from version 4.2.0 or newer to 3.7.2 or older will require
|
||||
performing a `Queue repair` due to changes in the internal data format.
|
||||
* Direct upgrade supported from version 3.0.0 and newer.
|
||||
* Older versions require performing a `Queue repair` after upgrading.
|
||||
|
||||
## Known problems and solutions
|
||||
|
||||
* Read `ISSUES.txt` or https://sabnzbd.org/wiki/introduction/known-issues
|
||||
|
||||
## Code Signing Policy
|
||||
|
||||
Windows code signing is provided by SignPath.io using a SignPath Foundation certificate.
|
||||
|
||||
## About
|
||||
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically, thanks to its web-based
|
||||
user interface and advanced built-in post-processing options that automatically verify, repair,
|
||||
|
||||
@@ -426,10 +426,7 @@ def print_modules():
|
||||
# Check if we managed to link, warning for now
|
||||
# It won't work on OpenSSL < 1.1.1 anyway, so we skip the check there
|
||||
if not sabnzbd.decoder.SABCTOOLS_OPENSSL_LINKED and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1):
|
||||
logging.warning(
|
||||
"Could not link to OpenSSL library, please report here: "
|
||||
"https://github.com/sabnzbd/sabnzbd/issues/2421"
|
||||
)
|
||||
helpful_warning(T("Unable to link to OpenSSL, optimized SSL connection functions will not be used."))
|
||||
else:
|
||||
# Wrong SABCTools version, if it was fully missing it would fail to start due to check at the very top
|
||||
logging.error(
|
||||
@@ -1103,12 +1100,13 @@ def main():
|
||||
logging_level = sabnzbd.cfg.log_level()
|
||||
else:
|
||||
sabnzbd.cfg.log_level.set(logging_level)
|
||||
sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)
|
||||
|
||||
logformat = "%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s"
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
|
||||
try:
|
||||
if not no_file_log:
|
||||
sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)
|
||||
rollover_log = logging.handlers.RotatingFileHandler(
|
||||
sabnzbd.LOGFILE, "a+", sabnzbd.cfg.log_size(), sabnzbd.cfg.log_backups()
|
||||
)
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import glob
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
@@ -28,6 +27,7 @@ import tarfile
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import configobj
|
||||
import packaging.version
|
||||
from typing import List
|
||||
|
||||
from constants import (
|
||||
@@ -70,9 +70,9 @@ def delete_files_glob(glob_pattern: str, allow_no_matches: bool = False):
|
||||
raise FileNotFoundError(f"No files found that match '{glob_pattern}'")
|
||||
|
||||
|
||||
def run_external_command(command: List[str], print_output: bool = True):
|
||||
def run_external_command(command: List[str], print_output: bool = True, **kwargs):
|
||||
"""Wrapper to ease the use of calling external programs"""
|
||||
process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||
output, _ = process.communicate()
|
||||
ret = process.wait()
|
||||
if (output and print_output) or ret != 0:
|
||||
@@ -109,6 +109,52 @@ def patch_version_file(release_name):
|
||||
ver.write(version_file)
|
||||
|
||||
|
||||
def test_macos_min_version(binary_path: str):
|
||||
# Skip check if nothing was set
|
||||
if macos_min_version := os.environ.get("MACOSX_DEPLOYMENT_TARGET"):
|
||||
# Skip any arm64 specific files
|
||||
if "arm64" in binary_path:
|
||||
print(f"Skipping arm64 binary {binary_path}")
|
||||
return
|
||||
|
||||
# Check minimum macOS version is at least mac OS10.13
|
||||
# We only check the x86_64 since for arm64 it's always macOS 11+
|
||||
print(f"Checking if binary supports macOS {macos_min_version} and above: {binary_path}")
|
||||
otool_output = run_external_command(
|
||||
[
|
||||
"otool",
|
||||
"-arch",
|
||||
"x86_64",
|
||||
"-l",
|
||||
binary_path,
|
||||
],
|
||||
print_output=False,
|
||||
)
|
||||
|
||||
# Parse the output for LC_BUILD_VERSION minos
|
||||
# The output is very large, so that's why we enumerate over it
|
||||
req_version = packaging.version.parse(macos_min_version)
|
||||
bin_version = None
|
||||
lines = otool_output.split("\n")
|
||||
for line_nr, line in enumerate(lines):
|
||||
if "LC_VERSION_MIN_MACOSX" in line:
|
||||
# Display the version in the next lines
|
||||
bin_version = packaging.version.parse(lines[line_nr + 2].split()[1])
|
||||
elif "minos" in line:
|
||||
bin_version = packaging.version.parse(line.split()[1])
|
||||
|
||||
if bin_version and bin_version > req_version:
|
||||
raise ValueError(f"{binary_path} requires {bin_version}, we want {req_version}")
|
||||
else:
|
||||
# We got the information we need
|
||||
break
|
||||
else:
|
||||
print(lines)
|
||||
raise RuntimeError(f"Could not determine minimum macOS version for {binary_path}")
|
||||
else:
|
||||
print(f"Skipping macOS version check, MACOSX_DEPLOYMENT_TARGET not set")
|
||||
|
||||
|
||||
def test_sab_binary(binary_path: str):
|
||||
"""Wrapper to have a simple start-up test for the binary"""
|
||||
with tempfile.TemporaryDirectory() as config_dir:
|
||||
@@ -201,23 +247,21 @@ if __name__ == "__main__":
|
||||
if not os.path.exists("locale"):
|
||||
raise FileNotFoundError("Failed to compile language files")
|
||||
|
||||
# Make sure we remove any existing build-folders
|
||||
safe_remove("build")
|
||||
safe_remove("dist")
|
||||
safe_remove(RELEASE_NAME)
|
||||
|
||||
# Copy the specification
|
||||
shutil.copyfile("builder/SABnzbd.spec", "SABnzbd.spec")
|
||||
|
||||
if "binary" in sys.argv or "installer" in sys.argv:
|
||||
if "binary" in sys.argv:
|
||||
# Must be run on Windows
|
||||
if sys.platform != "win32":
|
||||
raise RuntimeError("Binary should be created on Windows")
|
||||
|
||||
# Make sure we remove any existing build-folders
|
||||
safe_remove("build")
|
||||
safe_remove("dist")
|
||||
|
||||
# Remove any leftovers
|
||||
safe_remove(RELEASE_NAME)
|
||||
safe_remove(RELEASE_BINARY)
|
||||
|
||||
# Run PyInstaller and check output
|
||||
shutil.copyfile("builder/SABnzbd.spec", "SABnzbd.spec")
|
||||
run_external_command([sys.executable, "-O", "-m", "PyInstaller", "SABnzbd.spec"])
|
||||
|
||||
shutil.copytree("dist/SABnzbd-console", "dist/SABnzbd", dirs_exist_ok=True)
|
||||
@@ -228,33 +272,49 @@ if __name__ == "__main__":
|
||||
delete_files_glob("dist/SABnzbd/api-ms-win*.dll", allow_no_matches=True)
|
||||
delete_files_glob("dist/SABnzbd/ucrtbase.dll", allow_no_matches=True)
|
||||
|
||||
if "installer" in sys.argv:
|
||||
# Compile NSIS translations
|
||||
safe_remove("NSIS_Installer.nsi")
|
||||
safe_remove("NSIS_Installer.nsi.tmp")
|
||||
shutil.copyfile("builder/win/NSIS_Installer.nsi", "NSIS_Installer.nsi")
|
||||
run_external_command([sys.executable, "tools/make_mo.py", "nsis"])
|
||||
|
||||
# Run NSIS to build installer
|
||||
run_external_command(
|
||||
[
|
||||
"makensis.exe",
|
||||
"/V3",
|
||||
"/DSAB_VERSION=%s" % RELEASE_VERSION,
|
||||
"/DSAB_VERSIONKEY=%s" % ".".join(map(str, RELEASE_VERSION_TUPLE)),
|
||||
"/DSAB_FILE=%s" % RELEASE_INSTALLER,
|
||||
"NSIS_Installer.nsi.tmp",
|
||||
]
|
||||
)
|
||||
|
||||
# Rename the folder
|
||||
shutil.copytree("dist/SABnzbd", RELEASE_NAME)
|
||||
# Test the release
|
||||
test_sab_binary("dist/SABnzbd/SABnzbd.exe")
|
||||
|
||||
# Create the archive
|
||||
run_external_command(["win/7zip/7za.exe", "a", RELEASE_BINARY, RELEASE_NAME])
|
||||
run_external_command(["win/7zip/7za.exe", "a", RELEASE_BINARY, "SABnzbd"], cwd="dist")
|
||||
shutil.move(f"dist/{RELEASE_BINARY}", RELEASE_BINARY)
|
||||
|
||||
# Test the release, as the very last step to not mess with any release code
|
||||
test_sab_binary("dist/SABnzbd/SABnzbd.exe")
|
||||
if "installer" in sys.argv:
|
||||
# Check if we have the dist folder
|
||||
if not os.path.exists("dist/SABnzbd/SABnzbd.exe"):
|
||||
raise FileNotFoundError("SABnzbd executable not found, run binary creation first")
|
||||
|
||||
# Check if we have a signed version
|
||||
if os.path.exists(f"signed/{RELEASE_BINARY}"):
|
||||
print("Using signed version of SABnzbd binaries")
|
||||
safe_remove("dist/SABnzbd")
|
||||
run_external_command(["win/7zip/7za.exe", "x", "-odist", f"signed/{RELEASE_BINARY}"])
|
||||
|
||||
# Make sure it exists
|
||||
if not os.path.exists("dist/SABnzbd/SABnzbd.exe"):
|
||||
raise FileNotFoundError("SABnzbd executable not found, signed zip extraction failed")
|
||||
elif RELEASE_THIS:
|
||||
raise FileNotFoundError("Signed SABnzbd executable not found, required for release!")
|
||||
else:
|
||||
print("Using unsigned version of SABnzbd binaries")
|
||||
|
||||
# Compile NSIS translations
|
||||
safe_remove("NSIS_Installer.nsi")
|
||||
safe_remove("NSIS_Installer.nsi.tmp")
|
||||
shutil.copyfile("builder/win/NSIS_Installer.nsi", "NSIS_Installer.nsi")
|
||||
run_external_command([sys.executable, "tools/make_mo.py", "nsis"])
|
||||
|
||||
# Run NSIS to build installer
|
||||
run_external_command(
|
||||
[
|
||||
"makensis.exe",
|
||||
"/V3",
|
||||
"/DSAB_VERSION=%s" % RELEASE_VERSION,
|
||||
"/DSAB_VERSIONKEY=%s" % ".".join(map(str, RELEASE_VERSION_TUPLE)),
|
||||
"/DSAB_FILE=%s" % RELEASE_INSTALLER,
|
||||
"NSIS_Installer.nsi.tmp",
|
||||
]
|
||||
)
|
||||
|
||||
if "app" in sys.argv:
|
||||
# Must be run on macOS
|
||||
@@ -271,13 +331,16 @@ if __name__ == "__main__":
|
||||
if authority:
|
||||
files_to_sign = [
|
||||
"macos/par2/par2",
|
||||
"macos/par2/arm64/par2",
|
||||
"macos/unrar/unrar",
|
||||
"macos/unrar/arm64/unrar",
|
||||
"macos/7zip/7zz",
|
||||
]
|
||||
for file_to_sign in files_to_sign:
|
||||
print("Signing %s with hardended runtime" % file_to_sign)
|
||||
# Make sure it supports the macOS versions we want first
|
||||
test_macos_min_version(file_to_sign)
|
||||
|
||||
# Then sign in
|
||||
print("Signing %s with hardened runtime" % file_to_sign)
|
||||
run_external_command(
|
||||
[
|
||||
"codesign",
|
||||
@@ -297,17 +360,21 @@ if __name__ == "__main__":
|
||||
print("Signed %s!" % file_to_sign)
|
||||
|
||||
# Run PyInstaller and check output
|
||||
shutil.copyfile("builder/SABnzbd.spec", "SABnzbd.spec")
|
||||
run_external_command([sys.executable, "-O", "-m", "PyInstaller", "SABnzbd.spec"])
|
||||
|
||||
# Make sure we created a fully universal2 release when releasing or during CI
|
||||
if RELEASE_THIS or ON_GITHUB_ACTIONS:
|
||||
for bin_to_check in glob.glob("dist/SABnzbd.app/Contents/MacOS/**/*.so", recursive=True):
|
||||
for bin_to_check in glob.glob("dist/SABnzbd.app/**/*.so", recursive=True):
|
||||
print("Checking if binary is universal2: %s" % bin_to_check)
|
||||
file_output = run_external_command(["file", bin_to_check], print_output=False)
|
||||
# Make sure we have both arm64 and x86
|
||||
if not ("x86_64" in file_output and "arm64" in file_output):
|
||||
raise RuntimeError("Non-universal2 binary found!")
|
||||
|
||||
# Make sure it supports the macOS versions we want
|
||||
test_macos_min_version(bin_to_check)
|
||||
|
||||
# Only continue if we can sign
|
||||
if authority:
|
||||
# We use PyInstaller to sign the main SABnzbd executable and the SABnzbd.app
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
PyGithub==2.6.1
|
||||
PyGithub==2.8.1
|
||||
praw==7.8.1
|
||||
@@ -75,7 +75,7 @@ print("----")
|
||||
# Check if tagged as release and check for token
|
||||
gh_token = os.environ.get("AUTOMATION_GITHUB_TOKEN", "")
|
||||
if RELEASE_THIS and gh_token:
|
||||
gh_obj = github.Github(gh_token)
|
||||
gh_obj = github.Github(auth=github.Auth.Token(gh_token))
|
||||
gh_repo = gh_obj.get_repo("sabnzbd/sabnzbd")
|
||||
|
||||
# Read the release notes
|
||||
@@ -86,7 +86,7 @@ if RELEASE_THIS and gh_token:
|
||||
for release in gh_repo.get_releases():
|
||||
if release.tag_name == RELEASE_VERSION:
|
||||
gh_release = release
|
||||
print("Found existing release %s" % gh_release.title)
|
||||
print("Found existing release %s" % gh_release.name)
|
||||
break
|
||||
else:
|
||||
# Did not find it, so create the release, use the GitHub tag we got as input
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# Basic build requirements
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
pyinstaller==6.14.1
|
||||
pyinstaller==6.16.0
|
||||
packaging==25.0
|
||||
pyinstaller-hooks-contrib==2025.5
|
||||
pyinstaller-hooks-contrib==2025.9
|
||||
altgraph==0.17.4
|
||||
wrapt==1.17.2
|
||||
wrapt==2.0.0
|
||||
setuptools==80.9.0
|
||||
|
||||
# For the Windows build
|
||||
@@ -16,4 +16,4 @@ dmgbuild==1.6.5; sys_platform == 'darwin'
|
||||
mac-alias==2.2.2; sys_platform == 'darwin'
|
||||
macholib==1.16.3; sys_platform == 'darwin'
|
||||
ds-store==1.3.1; sys_platform == 'darwin'
|
||||
PyNaCl==1.5.0; sys_platform == 'darwin'
|
||||
PyNaCl==1.6.0; sys_platform == 'darwin'
|
||||
|
||||
@@ -29,6 +29,7 @@ Unicode true
|
||||
!include "nsProcess.nsh"
|
||||
!include "x64.nsh"
|
||||
!include "servicelib.nsh"
|
||||
!include "StdUtils.nsh"
|
||||
|
||||
;------------------------------------------------------------------
|
||||
;
|
||||
@@ -139,9 +140,9 @@ Unicode true
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
; !define MUI_FINISHPAGE_RUN
|
||||
; !define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
; !define MUI_FINISHPAGE_RUN_TEXT $(MsgRunSAB)
|
||||
!define MUI_FINISHPAGE_RUN
|
||||
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun
|
||||
!define MUI_FINISHPAGE_RUN_TEXT $(MsgRunSAB)
|
||||
!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\README.txt"
|
||||
!define MUI_FINISHPAGE_SHOWREADME_TEXT $(MsgShowRelNote)
|
||||
!define MUI_FINISHPAGE_LINK $(MsgSupportUs)
|
||||
@@ -154,12 +155,21 @@ Unicode true
|
||||
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Run as user-level at end of install
|
||||
; DOES NOT WORK
|
||||
; Function PageFinishRun
|
||||
; !insertmacro UAC_AsUser_ExecShell "" "$INSTDIR\SABnzbd.exe" "" "" ""
|
||||
; FunctionEnd
|
||||
Function PageFinishRun
|
||||
; Check if SABnzbd service is installed
|
||||
!insertmacro SERVICE "installed" "SABnzbd" ""
|
||||
Pop $0 ;response
|
||||
${If} $0 == true
|
||||
; Service is installed, start the service
|
||||
!insertmacro SERVICE "start" "SABnzbd" ""
|
||||
${Else}
|
||||
; Service not installed, run executable as user
|
||||
${StdUtils.ExecShellAsUser} $0 "$INSTDIR\SABnzbd.exe" "" ""
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
@@ -188,7 +198,6 @@ Unicode true
|
||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
;Reserve Files
|
||||
;If you are using solid compression, files that are required before
|
||||
@@ -361,14 +370,6 @@ Function .onInit
|
||||
|
||||
FunctionEnd
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Show the shortcuts at end of install so user can start SABnzbd
|
||||
; This is instead of us trying to run SAB from the installer
|
||||
;
|
||||
Function .onInstSuccess
|
||||
ExecShell "open" "$SMPROGRAMS\$STARTMENU_FOLDER"
|
||||
FunctionEnd
|
||||
|
||||
;--------------------------------
|
||||
; begin uninstall settings/section
|
||||
UninstallText $(MsgUninstall)
|
||||
@@ -410,6 +411,8 @@ SectionEnd
|
||||
;Language strings
|
||||
LangString MsgShowRelNote ${LANG_ENGLISH} "Show Release Notes"
|
||||
|
||||
LangString MsgRunSAB ${LANG_ENGLISH} "Run SABnzbd"
|
||||
|
||||
LangString MsgSupportUs ${LANG_ENGLISH} "Support the project, Donate!"
|
||||
|
||||
LangString MsgServChange ${LANG_ENGLISH} "The SABnzbd Windows Service changed in SABnzbd 3.0.0. $\nYou will need to reinstall the SABnzbd service. $\n$\nClick `OK` to remove the existing services or `Cancel` to cancel this upgrade."
|
||||
|
||||
501
builder/win/nsis/Include/StdUtils.nsh
Normal file
501
builder/win/nsis/Include/StdUtils.nsh
Normal file
@@ -0,0 +1,501 @@
|
||||
#################################################################################
|
||||
# StdUtils plug-in for NSIS
|
||||
# Copyright (C) 2004-2018 LoRd_MuldeR <MuldeR2@GMX.de>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
# http://www.gnu.org/licenses/lgpl-2.1.txt
|
||||
#################################################################################
|
||||
|
||||
# DEVELOPER NOTES:
|
||||
# - Please see "https://github.com/lordmulder/stdutils/" for news and updates!
|
||||
# - Please see "Docs\StdUtils\StdUtils.html" for detailed function descriptions!
|
||||
# - Please see "Examples\StdUtils\StdUtilsTest.nsi" for usage examples!
|
||||
|
||||
#################################################################################
|
||||
# FUNCTION DECLARTIONS
|
||||
#################################################################################
|
||||
|
||||
!ifndef ___STDUTILS__NSH___
|
||||
!define ___STDUTILS__NSH___
|
||||
|
||||
!define StdUtils.Time '!insertmacro _StdU_Time' #time(), as in C standard library
|
||||
!define StdUtils.GetMinutes '!insertmacro _StdU_GetMinutes' #GetSystemTimeAsFileTime(), returns the number of minutes
|
||||
!define StdUtils.GetHours '!insertmacro _StdU_GetHours' #GetSystemTimeAsFileTime(), returns the number of hours
|
||||
!define StdUtils.GetDays '!insertmacro _StdU_GetDays' #GetSystemTimeAsFileTime(), returns the number of days
|
||||
!define StdUtils.Rand '!insertmacro _StdU_Rand' #rand(), as in C standard library
|
||||
!define StdUtils.RandMax '!insertmacro _StdU_RandMax' #rand(), as in C standard library, with maximum value
|
||||
!define StdUtils.RandMinMax '!insertmacro _StdU_RandMinMax' #rand(), as in C standard library, with minimum/maximum value
|
||||
!define StdUtils.RandList '!insertmacro _StdU_RandList' #rand(), as in C standard library, with list support
|
||||
!define StdUtils.RandBytes '!insertmacro _StdU_RandBytes' #Generates random bytes, returned as Base64-encoded string
|
||||
!define StdUtils.FormatStr '!insertmacro _StdU_FormatStr' #sprintf(), as in C standard library, one '%d' placeholder
|
||||
!define StdUtils.FormatStr2 '!insertmacro _StdU_FormatStr2' #sprintf(), as in C standard library, two '%d' placeholders
|
||||
!define StdUtils.FormatStr3 '!insertmacro _StdU_FormatStr3' #sprintf(), as in C standard library, three '%d' placeholders
|
||||
!define StdUtils.ScanStr '!insertmacro _StdU_ScanStr' #sscanf(), as in C standard library, one '%d' placeholder
|
||||
!define StdUtils.ScanStr2 '!insertmacro _StdU_ScanStr2' #sscanf(), as in C standard library, two '%d' placeholders
|
||||
!define StdUtils.ScanStr3 '!insertmacro _StdU_ScanStr3' #sscanf(), as in C standard library, three '%d' placeholders
|
||||
!define StdUtils.TrimStr '!insertmacro _StdU_TrimStr' #Remove whitspaces from string, left and right
|
||||
!define StdUtils.TrimStrLeft '!insertmacro _StdU_TrimStrLeft' #Remove whitspaces from string, left side only
|
||||
!define StdUtils.TrimStrRight '!insertmacro _StdU_TrimStrRight' #Remove whitspaces from string, right side only
|
||||
!define StdUtils.RevStr '!insertmacro _StdU_RevStr' #Reverse a string, e.g. "reverse me" <-> "em esrever"
|
||||
!define StdUtils.ValidFileName '!insertmacro _StdU_ValidFileName' #Test whether string is a valid file name - no paths allowed
|
||||
!define StdUtils.ValidPathSpec '!insertmacro _StdU_ValidPathSpec' #Test whether string is a valid full(!) path specification
|
||||
!define StdUtils.ValidDomainName '!insertmacro _StdU_ValidDomain' #Test whether string is a valid host name or domain name
|
||||
!define StdUtils.StrToUtf8 '!insertmacro _StdU_StrToUtf8' #Convert string from Unicode (UTF-16) or ANSI to UTF-8 bytes
|
||||
!define StdUtils.StrFromUtf8 '!insertmacro _StdU_StrFromUtf8' #Convert string from UTF-8 bytes to Unicode (UTF-16) or ANSI
|
||||
!define StdUtils.SHFileMove '!insertmacro _StdU_SHFileMove' #SHFileOperation(), using the FO_MOVE operation
|
||||
!define StdUtils.SHFileCopy '!insertmacro _StdU_SHFileCopy' #SHFileOperation(), using the FO_COPY operation
|
||||
!define StdUtils.AppendToFile '!insertmacro _StdU_AppendToFile' #Append contents of an existing file to another file
|
||||
!define StdUtils.ExecShellAsUser '!insertmacro _StdU_ExecShlUser' #ShellExecute() as NON-elevated user from elevated installer
|
||||
!define StdUtils.InvokeShellVerb '!insertmacro _StdU_InvkeShlVrb' #Invokes a "shell verb", e.g. for pinning items to the taskbar
|
||||
!define StdUtils.ExecShellWaitEx '!insertmacro _StdU_ExecShlWaitEx' #ShellExecuteEx(), returns the handle of the new process
|
||||
!define StdUtils.WaitForProcEx '!insertmacro _StdU_WaitForProcEx' #WaitForSingleObject(), e.g. to wait for a running process
|
||||
!define StdUtils.GetParameter '!insertmacro _StdU_GetParameter' #Get the value of a specific command-line option
|
||||
!define StdUtils.TestParameter '!insertmacro _StdU_TestParameter' #Test whether a specific command-line option has been set
|
||||
!define StdUtils.ParameterCnt '!insertmacro _StdU_ParameterCnt' #Get number of command-line tokens, similar to argc in main()
|
||||
!define StdUtils.ParameterStr '!insertmacro _StdU_ParameterStr' #Get the n-th command-line token, similar to argv[i] in main()
|
||||
!define StdUtils.GetAllParameters '!insertmacro _StdU_GetAllParams' #Get complete command-line, but without executable name
|
||||
!define StdUtils.GetRealOSVersion '!insertmacro _StdU_GetRealOSVer' #Get the *real* Windows version number, even on Windows 8.1+
|
||||
!define StdUtils.GetRealOSBuildNo '!insertmacro _StdU_GetRealOSBld' #Get the *real* Windows build number, even on Windows 8.1+
|
||||
!define StdUtils.GetRealOSName '!insertmacro _StdU_GetRealOSStr' #Get the *real* Windows version, as a "friendly" name
|
||||
!define StdUtils.GetOSEdition '!insertmacro _StdU_GetOSEdition' #Get the Windows edition, i.e. "workstation" or "server"
|
||||
!define StdUtils.GetOSReleaseId '!insertmacro _StdU_GetOSRelIdNo' #Get the Windows release identifier (on Windows 10)
|
||||
!define StdUtils.GetOSReleaseName '!insertmacro _StdU_GetOSRelIdStr' #Get the Windows release (on Windows 10), as a "friendly" name
|
||||
!define StdUtils.VerifyOSVersion '!insertmacro _StdU_VrfyRealOSVer' #Compare *real* operating system to an expected version number
|
||||
!define StdUtils.VerifyOSBuildNo '!insertmacro _StdU_VrfyRealOSBld' #Compare *real* operating system to an expected build number
|
||||
!define StdUtils.HashText '!insertmacro _StdU_HashText' #Compute hash from text string (CRC32, MD5, SHA1/2/3, BLAKE2)
|
||||
!define StdUtils.HashFile '!insertmacro _StdU_HashFile' #Compute hash from file (CRC32, MD5, SHA1/2/3, BLAKE2)
|
||||
!define StdUtils.NormalizePath '!insertmacro _StdU_NormalizePath' #Simplifies the path to produce a direct, well-formed path
|
||||
!define StdUtils.GetParentPath '!insertmacro _StdU_GetParentPath' #Get parent path by removing the last component from the path
|
||||
!define StdUtils.SplitPath '!insertmacro _StdU_SplitPath' #Split the components of the given path
|
||||
!define StdUtils.GetDrivePart '!insertmacro _StdU_GetDrivePart' #Get drive component of path
|
||||
!define StdUtils.GetDirectoryPart '!insertmacro _StdU_GetDirPart' #Get directory component of path
|
||||
!define StdUtils.GetFileNamePart '!insertmacro _StdU_GetFNamePart' #Get file name component of path
|
||||
!define StdUtils.GetExtensionPart '!insertmacro _StdU_GetExtnPart' #Get file extension component of path
|
||||
!define StdUtils.TimerCreate '!insertmacro _StdU_TimerCreate' #Create a new event-timer that will be triggered periodically
|
||||
!define StdUtils.TimerDestroy '!insertmacro _StdU_TimerDestroy' #Destroy a running timer created with TimerCreate()
|
||||
!define StdUtils.ProtectStr '!insertmacro _StdU_PrtctStr' #Protect a given String using Windows' DPAPI
|
||||
!define StdUtils.UnprotectStr '!insertmacro _StdU_UnprtctStr' #Unprotect a string that was protected via ProtectStr()
|
||||
!define StdUtils.GetLibVersion '!insertmacro _StdU_GetLibVersion' #Get the current StdUtils library version (for debugging)
|
||||
!define StdUtils.SetVerbose '!insertmacro _StdU_SetVerbose' #Enable or disable "verbose" mode (for debugging)
|
||||
|
||||
|
||||
#################################################################################
|
||||
# MACRO DEFINITIONS
|
||||
#################################################################################
|
||||
|
||||
!macro _StdU_Time out
|
||||
StdUtils::Time /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetMinutes out
|
||||
StdUtils::GetMinutes /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetHours out
|
||||
StdUtils::GetHours /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetDays out
|
||||
StdUtils::GetDays /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_Rand out
|
||||
StdUtils::Rand /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_RandMax out max
|
||||
push ${max}
|
||||
StdUtils::RandMax /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_RandMinMax out min max
|
||||
push ${min}
|
||||
push ${max}
|
||||
StdUtils::RandMinMax /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_RandList count max
|
||||
push ${max}
|
||||
push ${count}
|
||||
StdUtils::RandList /NOUNLOAD
|
||||
!macroend
|
||||
|
||||
!macro _StdU_RandBytes out count
|
||||
push ${count}
|
||||
StdUtils::RandBytes /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_FormatStr out format val
|
||||
push `${format}`
|
||||
push ${val}
|
||||
StdUtils::FormatStr /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_FormatStr2 out format val1 val2
|
||||
push `${format}`
|
||||
push ${val1}
|
||||
push ${val2}
|
||||
StdUtils::FormatStr2 /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_FormatStr3 out format val1 val2 val3
|
||||
push `${format}`
|
||||
push ${val1}
|
||||
push ${val2}
|
||||
push ${val3}
|
||||
StdUtils::FormatStr3 /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ScanStr out format input default
|
||||
push `${format}`
|
||||
push `${input}`
|
||||
push ${default}
|
||||
StdUtils::ScanStr /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ScanStr2 out1 out2 format input default1 default2
|
||||
push `${format}`
|
||||
push `${input}`
|
||||
push ${default1}
|
||||
push ${default2}
|
||||
StdUtils::ScanStr2 /NOUNLOAD
|
||||
pop ${out1}
|
||||
pop ${out2}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ScanStr3 out1 out2 out3 format input default1 default2 default3
|
||||
push `${format}`
|
||||
push `${input}`
|
||||
push ${default1}
|
||||
push ${default2}
|
||||
push ${default3}
|
||||
StdUtils::ScanStr3 /NOUNLOAD
|
||||
pop ${out1}
|
||||
pop ${out2}
|
||||
pop ${out3}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TrimStr var
|
||||
push ${var}
|
||||
StdUtils::TrimStr /NOUNLOAD
|
||||
pop ${var}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TrimStrLeft var
|
||||
push ${var}
|
||||
StdUtils::TrimStrLeft /NOUNLOAD
|
||||
pop ${var}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TrimStrRight var
|
||||
push ${var}
|
||||
StdUtils::TrimStrRight /NOUNLOAD
|
||||
pop ${var}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_RevStr var
|
||||
push ${var}
|
||||
StdUtils::RevStr /NOUNLOAD
|
||||
pop ${var}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ValidFileName out test
|
||||
push `${test}`
|
||||
StdUtils::ValidFileName /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ValidPathSpec out test
|
||||
push `${test}`
|
||||
StdUtils::ValidPathSpec /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ValidDomain out test
|
||||
push `${test}`
|
||||
StdUtils::ValidDomainName /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
|
||||
!macro _StdU_StrToUtf8 out str
|
||||
push `${str}`
|
||||
StdUtils::StrToUtf8 /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_StrFromUtf8 out trnc str
|
||||
push ${trnc}
|
||||
push `${str}`
|
||||
StdUtils::StrFromUtf8 /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_SHFileMove out from to hwnd
|
||||
push `${from}`
|
||||
push `${to}`
|
||||
push ${hwnd}
|
||||
StdUtils::SHFileMove /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_SHFileCopy out from to hwnd
|
||||
push `${from}`
|
||||
push `${to}`
|
||||
push ${hwnd}
|
||||
StdUtils::SHFileCopy /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_AppendToFile out from dest offset maxlen
|
||||
push `${from}`
|
||||
push `${dest}`
|
||||
push ${offset}
|
||||
push ${maxlen}
|
||||
StdUtils::AppendToFile /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ExecShlUser out file verb args
|
||||
push `${file}`
|
||||
push `${verb}`
|
||||
push `${args}`
|
||||
StdUtils::ExecShellAsUser /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_InvkeShlVrb out path file verb_id
|
||||
push "${path}"
|
||||
push "${file}"
|
||||
push ${verb_id}
|
||||
StdUtils::InvokeShellVerb /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ExecShlWaitEx out_res out_val file verb args
|
||||
push `${file}`
|
||||
push `${verb}`
|
||||
push `${args}`
|
||||
StdUtils::ExecShellWaitEx /NOUNLOAD
|
||||
pop ${out_res}
|
||||
pop ${out_val}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_WaitForProcEx out handle
|
||||
push `${handle}`
|
||||
StdUtils::WaitForProcEx /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetParameter out name default
|
||||
push `${name}`
|
||||
push `${default}`
|
||||
StdUtils::GetParameter /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TestParameter out name
|
||||
push `${name}`
|
||||
StdUtils::TestParameter /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ParameterCnt out
|
||||
StdUtils::ParameterCnt /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_ParameterStr out index
|
||||
push ${index}
|
||||
StdUtils::ParameterStr /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetAllParams out truncate
|
||||
push `${truncate}`
|
||||
StdUtils::GetAllParameters /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetRealOSVer out_major out_minor out_spack
|
||||
StdUtils::GetRealOsVersion /NOUNLOAD
|
||||
pop ${out_major}
|
||||
pop ${out_minor}
|
||||
pop ${out_spack}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetRealOSBld out
|
||||
StdUtils::GetRealOsBuildNo /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetRealOSStr out
|
||||
StdUtils::GetRealOsName /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_VrfyRealOSVer out major minor spack
|
||||
push `${major}`
|
||||
push `${minor}`
|
||||
push `${spack}`
|
||||
StdUtils::VerifyRealOsVersion /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_VrfyRealOSBld out build
|
||||
push `${build}`
|
||||
StdUtils::VerifyRealOsBuildNo /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetOSEdition out
|
||||
StdUtils::GetOsEdition /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetOSRelIdNo out
|
||||
StdUtils::GetOsReleaseId /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetOSRelIdStr out
|
||||
StdUtils::GetOsReleaseName /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_HashText out type text
|
||||
push `${type}`
|
||||
push `${text}`
|
||||
StdUtils::HashText /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_HashFile out type file
|
||||
push `${type}`
|
||||
push `${file}`
|
||||
StdUtils::HashFile /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_NormalizePath out path
|
||||
push `${path}`
|
||||
StdUtils::NormalizePath /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetParentPath out path
|
||||
push `${path}`
|
||||
StdUtils::GetParentPath /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_SplitPath out_drive out_dir out_fname out_ext path
|
||||
push `${path}`
|
||||
StdUtils::SplitPath /NOUNLOAD
|
||||
pop ${out_drive}
|
||||
pop ${out_dir}
|
||||
pop ${out_fname}
|
||||
pop ${out_ext}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetDrivePart out path
|
||||
push `${path}`
|
||||
StdUtils::GetDrivePart /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetDirPart out path
|
||||
push `${path}`
|
||||
StdUtils::GetDirectoryPart /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetFNamePart out path
|
||||
push `${path}`
|
||||
StdUtils::GetFileNamePart /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetExtnPart out path
|
||||
push `${path}`
|
||||
StdUtils::GetExtensionPart /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TimerCreate out callback interval
|
||||
GetFunctionAddress ${out} ${callback}
|
||||
push ${out}
|
||||
push ${interval}
|
||||
StdUtils::TimerCreate /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_TimerDestroy out timer_id
|
||||
push ${timer_id}
|
||||
StdUtils::TimerDestroy /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_PrtctStr out dpsc salt text
|
||||
push `${dpsc}`
|
||||
push `${salt}`
|
||||
push `${text}`
|
||||
StdUtils::ProtectStr /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_UnprtctStr out trnc salt data
|
||||
push `${trnc}`
|
||||
push `${salt}`
|
||||
push `${data}`
|
||||
StdUtils::UnprotectStr /NOUNLOAD
|
||||
pop ${out}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_GetLibVersion out_ver out_tst
|
||||
StdUtils::GetLibVersion /NOUNLOAD
|
||||
pop ${out_ver}
|
||||
pop ${out_tst}
|
||||
!macroend
|
||||
|
||||
!macro _StdU_SetVerbose enable
|
||||
Push ${enable}
|
||||
StdUtils::SetVerboseMode /NOUNLOAD
|
||||
!macroend
|
||||
|
||||
|
||||
#################################################################################
|
||||
# MAGIC NUMBERS
|
||||
#################################################################################
|
||||
|
||||
!define StdUtils.Const.ShellVerb.PinToTaskbar 0
|
||||
!define StdUtils.Const.ShellVerb.UnpinFromTaskbar 1
|
||||
!define StdUtils.Const.ShellVerb.PinToStart 2
|
||||
!define StdUtils.Const.ShellVerb.UnpinFromStart 3
|
||||
|
||||
!endif # !___STDUTILS__NSH___
|
||||
BIN
builder/win/nsis/Plugins/StdUtils.dll
Normal file
BIN
builder/win/nsis/Plugins/StdUtils.dll
Normal file
Binary file not shown.
@@ -132,7 +132,7 @@
|
||||
<textarea name="notes" id="notes" rows="3" cols="50"></textarea>
|
||||
</div>
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
<button class="btn btn-default addNewServer" disabled><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
|
||||
<button class="btn btn-default addNewServer" disabled data-toggle="tooltip" data-placement="top" title="$T('wizard-test-server-required')"><span class="glyphicon glyphicon-plus"></span> $T('button-addServer')</button>
|
||||
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
|
||||
</div>
|
||||
<div class="field-pair result-box">
|
||||
@@ -464,14 +464,14 @@
|
||||
When finished loading
|
||||
**/
|
||||
jQuery(document).ready(function(){
|
||||
// Initialize tooltips
|
||||
jQuery('[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
// Reload form in case we change items that make the servers appear different
|
||||
jQuery('input[name="priority"], input[name="displayname"], textarea[name="notes"]').on('change', function() {
|
||||
jQuery('.fullform').submit(function() {
|
||||
// No ajax this time
|
||||
jQuery('input[name="ajax"]').val('')
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
jQuery('input[name="priority"], input[name="displayname"], textarea[name="notes"]').on('change', function(event) {
|
||||
var parentForm = jQuery(event.target).parents("form")
|
||||
parentForm.unbind("submit")
|
||||
parentForm.find('input[name="ajax"]').val('')
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -563,6 +563,9 @@
|
||||
// Allow adding the new server if we are in the new-server section
|
||||
if(theButton.parents("form[action='addServer']").length) {
|
||||
jQuery(".addNewServer").removeAttr("disabled")
|
||||
jQuery(".addNewServer").removeAttr("data-toggle")
|
||||
jQuery(".addNewServer").removeAttr("title")
|
||||
jQuery(".addNewServer").tooltip("destroy")
|
||||
}
|
||||
} else {
|
||||
resultBox.addClass('alert-danger')
|
||||
@@ -571,6 +574,10 @@
|
||||
// Disable the adding of new server, just to be sure
|
||||
if(theButton.parents("form[action='addServer']").length) {
|
||||
jQuery(".addNewServer").attr("disabled", "disabled")
|
||||
jQuery(".addNewServer").attr("data-toggle", "tooltip")
|
||||
jQuery(".addNewServer").attr("data-placement", "top")
|
||||
jQuery(".addNewServer").attr("title", "$T('wizard-test-server-required')")
|
||||
jQuery(".addNewServer").tooltip()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -50,7 +50,6 @@ textarea,
|
||||
.navbar-default .navbar-nav>li>a:focus,
|
||||
.navbar-logo:hover,
|
||||
.quoteBlock,
|
||||
.selected,
|
||||
.server-disabled,
|
||||
#serverResponse,
|
||||
.table>tbody>tr:nth-child(odd),
|
||||
@@ -62,30 +61,10 @@ select:hover {
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
|
||||
.correct {
|
||||
border: 2px solid #00cc22 !important;
|
||||
}
|
||||
|
||||
.failed,
|
||||
.required-star,
|
||||
.error-text {
|
||||
.failed {
|
||||
color: #ff3333 !important;
|
||||
}
|
||||
|
||||
.unselected,
|
||||
.selected {
|
||||
border: 1px solid #EBEBEB !important;
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
|
||||
.incorrect {
|
||||
border: 2px solid #ff3333 !important;
|
||||
}
|
||||
|
||||
.disabled-text {
|
||||
color: #777 !important;
|
||||
}
|
||||
|
||||
#rightGreyText,
|
||||
small {
|
||||
color: #c7c7c7 !important;
|
||||
@@ -306,6 +285,14 @@ col2 h3 a,
|
||||
border-top-color: #E4E4E4 !important;
|
||||
}
|
||||
|
||||
.tooltip.left .tooltip-arrow {
|
||||
border-left-color: #E4E4E4 !important;
|
||||
}
|
||||
|
||||
.tooltip.right .tooltip-arrow {
|
||||
border-right-color: #E4E4E4 !important;
|
||||
}
|
||||
|
||||
.Special .glyphicon-asterisk {
|
||||
color: #E4E4E4 !important;
|
||||
}
|
||||
|
||||
@@ -83,13 +83,20 @@
|
||||
<div class="col-sm-2">$T('name')</div>
|
||||
<div class="col-sm-10" data-bind="text: historyStatus.name"></div>
|
||||
</div>
|
||||
<div class="row" data-bind="visible: historyStatus.time_added">
|
||||
<div class="col-sm-2">$T('rss-added')</div>
|
||||
<div class="col-sm-10" data-bind="text: timeAdded(), attr: { 'data-timestamp': historyStatus.time_added }"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">$T('post-Completed')</div>
|
||||
<div class="col-sm-10" data-bind="text: completedOn, attr: { 'data-timestamp': completed }"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">$T('status')</div>
|
||||
<div class="col-sm-10" data-bind="text: glitterTranslate.status[historyStatus.status()] ? glitterTranslate.status[historyStatus.status()] : statusText()"></div>
|
||||
<div class="col-sm-10">
|
||||
<span data-bind="text: glitterTranslate.status[historyStatus.status()] ? glitterTranslate.status[historyStatus.status()] : statusText()"></span>
|
||||
<a href="#" class="mark-completed-link" data-bind="visible: failed(), click: markAsCompleted" title="$T('button-mark-completed')"><span class="glyphicon glyphicon-ok"></span> $T('post-Completed')</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">$T('size')</div>
|
||||
@@ -142,6 +149,7 @@
|
||||
<a href="#" class="hover-button history-archive" title="$T('showArchive') / $T('showAllHis')" data-tooltip="true" data-placement="top" data-bind="click: history.toggleShowArchive, css: { 'history-options-show-failed': history.showArchive }"><svg viewBox="6 6 36 36" height="14" width="14" class="archive-icon"><path d="M41.09 10.45l-2.77-3.36c-.56-.66-1.39-1.09-2.32-1.09h-24c-.93 0-1.76.43-2.31 1.09l-2.77 3.36c-.58.7-.92 1.58-.92 2.55v25c0 2.21 1.79 4 4 4h28c2.21 0 4-1.79 4-4v-25c0-.97-.34-1.85-.91-2.55zm-17.09 24.55l-11-11h7v-4h8v4h7l-11 11zm-13.75-25l1.63-2h24l1.87 2h-27.5z"/></svg></a>
|
||||
<a href="#" class="hover-button" title="$T('showFailedHis') / $T('showAllHis')" data-tooltip="true" data-placement="top" data-bind="click: history.toggleShowFailed, css: { 'history-options-show-failed': history.showFailed }"><span class="glyphicon glyphicon-exclamation-sign"></span></a>
|
||||
<a href="#" class="hover-button" title="$T('link-retryAll')" data-tooltip="true" data-placement="top" data-bind="click: history.retryAllFailed"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="hover-button" title="$T('button-mark-completed')" data-bind="visible: (history.isMultiEditing() && hasHistory()), click: history.doMultiMarkCompleted" data-tooltip="true" data-placement="top"><span class="glyphicon glyphicon-ok"></span></a>
|
||||
|
||||
<div data-bind="visible: (history.isMultiEditing() && hasHistory())">
|
||||
<span class="label label-default" data-bind="text: history.multiEditItems().length">0</span>
|
||||
|
||||
@@ -681,9 +681,9 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="checkbox">
|
||||
<div class="checkbox" data-bind="visible: !history.showArchive()">
|
||||
<label>
|
||||
<input type="checkbox" data-bind="checked: history.showArchive()"> <span>$T('permanently-delete')</span>
|
||||
<input type="checkbox" data-bind="checked: history.permanentlyDelete"> <span>$T('permanently-delete')</span>
|
||||
</label>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">$T('cancel')</button>
|
||||
@@ -802,7 +802,7 @@
|
||||
<div class="modal-footer">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-bind="checked: history.showArchive()"> <span>$T('permanently-delete')</span>
|
||||
<input type="checkbox" data-bind="checked: history.permanentlyDelete"> <span>$T('permanently-delete')</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -179,14 +179,20 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="add-nzb-inputbox add-nzb-inputbox-small">
|
||||
<label for="multiedit-play">
|
||||
<label for="multiedit-play" data-bind="event: { mousedown: queue.handleMultiEditStatusMouseDown }">
|
||||
<input type="radio" name="multiedit-status" value="resume" id="multiedit-play" data-bind="event: { change: queue.doMultiEditUpdate }" />
|
||||
<span class="glyphicon glyphicon-play" title="$T('link-resume')" data-tooltip="true" data-placement="top"></span>
|
||||
</label>
|
||||
<label for="multiedit-pause">
|
||||
<label for="multiedit-pause" data-bind="event: { mousedown: queue.handleMultiEditStatusMouseDown }">
|
||||
<input type="radio" name="multiedit-status" value="pause" id="multiedit-pause" data-bind="event: { change: queue.doMultiEditUpdate }" />
|
||||
<span class="glyphicon glyphicon-pause" title="$T('link-pause')" data-tooltip="true" data-placement="top"></span>
|
||||
</label>
|
||||
<a href="#" class="hover-button" title="$T('Glitter-top')" data-bind="click: queue.doMultiMoveToTop" data-tooltip="true" data-placement="top">
|
||||
<span class="glyphicon glyphicon-chevron-up"></span>
|
||||
</a>
|
||||
<a href="#" class="hover-button" title="$T('Glitter-bottom')" data-bind="click: queue.doMultiMoveToBottom" data-tooltip="true" data-placement="top">
|
||||
<span class="glyphicon glyphicon-chevron-down"></span>
|
||||
</a>
|
||||
<span class="label label-default" data-bind="text: queue.multiEditItems().length">0</span>
|
||||
</div>
|
||||
<div class="add-nzb-inputbox-clear"></div>
|
||||
|
||||
@@ -52,16 +52,15 @@
|
||||
var glitterTranslate = new Object();
|
||||
glitterTranslate.paused = "$T('post-Paused')";
|
||||
glitterTranslate.left = "$T('Glitter-left')";
|
||||
glitterTranslate.clearWarn = "$T('confirm')";
|
||||
glitterTranslate.clearOrphanWarning = "$T('Glitter-clearOrphanWarning')";
|
||||
glitterTranslate.pausePromptFail = "$T('Glitter-pausePromptFail')"
|
||||
glitterTranslate.pauseFor = "$T('pauseFor')"
|
||||
glitterTranslate.minutes = "$T('mins')"
|
||||
glitterTranslate.shutdown = "$T('shutdownOK?')";
|
||||
glitterTranslate.restart = "$T('explain-Restart') $T('explain-needNewLogin')".replace(/\<br(\s*\/|)\>/g, '\n');
|
||||
glitterTranslate.repair = "$T('explain-Repair')".replace(/<br \/>/g, "\n").replace(/"/g,'"');
|
||||
glitterTranslate.deleteMsg = "$T('nzo-delete')";
|
||||
glitterTranslate.removeDown = "$T('confirm')";
|
||||
glitterTranslate.removeDow1 = "$T('confirm')";
|
||||
glitterTranslate.confirm = "$T('confirm')";
|
||||
glitterTranslate.markComplete = "$T('button-mark-completed')";
|
||||
glitterTranslate.renameAbort = "$T('Glitter-confirmAbortDirectUnpack')\n$T('confirm')";
|
||||
glitterTranslate.retryAll = "$T('link-retryAll')?";
|
||||
glitterTranslate.fetch = "$T('Glitter-fetch')";
|
||||
|
||||
@@ -10,6 +10,7 @@ function HistoryListModel(parent) {
|
||||
self.historyItems = ko.observableArray([])
|
||||
self.showFailed = ko.observable(false).extend({ persist: 'historyShowFailed' });
|
||||
self.showArchive = ko.observable(false).extend({ persist: 'historyShowArchive' });
|
||||
self.permanentlyDelete = ko.observable(false).extend({ persist: 'permanentlyDelete' });
|
||||
self.isLoading = ko.observable(false).extend({ rateLimit: 100 });
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 400, method: "notifyWhenChangesStop" } });
|
||||
self.paginationLimit = ko.observable(10).extend({ persist: 'historyPaginationLimit' });
|
||||
@@ -391,6 +392,10 @@ function HistoryListModel(parent) {
|
||||
}
|
||||
if(strIDsHistory !== "") {
|
||||
var skipArchive = $('#modal-delete-history-job input[type="checkbox"]').prop("checked")
|
||||
|
||||
// Permanently delete if we are on the Archive page
|
||||
if(self.showArchive()) skipArchive = true
|
||||
|
||||
callAPI({
|
||||
mode: 'history',
|
||||
name: 'delete',
|
||||
@@ -415,6 +420,42 @@ function HistoryListModel(parent) {
|
||||
self.triggerRemoveDownload(self.multiEditItems())
|
||||
}
|
||||
|
||||
// Mark jobs as completed
|
||||
self.markAsCompleted = function(items) {
|
||||
// Confirm
|
||||
if(!confirm(glitterTranslate.markComplete)) {
|
||||
return
|
||||
}
|
||||
// Single or multiple items?
|
||||
var strIDs = '';
|
||||
if(items.length) {
|
||||
$.each(items, function(index) {
|
||||
strIDs = strIDs + this.id + ',';
|
||||
})
|
||||
} else {
|
||||
strIDs = items.id
|
||||
}
|
||||
|
||||
// Send the API call
|
||||
callAPI({
|
||||
mode: 'history',
|
||||
name: 'mark_as_completed',
|
||||
value: strIDs
|
||||
}).then(function(response) {
|
||||
// Force refresh to update the UI
|
||||
self.parent.refresh(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Mark all selected as completed
|
||||
self.doMultiMarkCompleted = function() {
|
||||
// Anything selected?
|
||||
if(self.multiEditItems().length < 1) return;
|
||||
|
||||
// Mark them
|
||||
self.markAsCompleted(self.multiEditItems());
|
||||
}
|
||||
|
||||
// Focus on the confirm button
|
||||
$('#modal-delete-history-job').on("shown.bs.modal", function() {
|
||||
$('#modal-delete-history-job .btn[type="submit"]').focus()
|
||||
@@ -546,6 +587,11 @@ function HistoryModel(parent, data) {
|
||||
return displayDateTime(self.completed(), parent.parent.dateFormat(), 'X')
|
||||
});
|
||||
|
||||
// Format time added
|
||||
self.timeAdded = ko.pureComputed(function() {
|
||||
return displayDateTime(self.historyStatus.time_added(), parent.parent.dateFormat(), 'X')
|
||||
});
|
||||
|
||||
// Subscribe to retryEvent so we can load the password
|
||||
self.canRetry.subscribe(function() {
|
||||
self.updateAllHistory = true;
|
||||
@@ -561,6 +607,11 @@ function HistoryModel(parent, data) {
|
||||
$('#modal-retry-job').modal("show")
|
||||
};
|
||||
|
||||
// Mark as completed button
|
||||
self.markAsCompleted = function() {
|
||||
parent.markAsCompleted(self);
|
||||
};
|
||||
|
||||
// Update information only on click
|
||||
self.updateAllHistoryInfo = function(data, event) {
|
||||
// Show
|
||||
|
||||
@@ -704,7 +704,6 @@ function ViewModel() {
|
||||
data.append("apikey", apiKey);
|
||||
|
||||
// Add this one
|
||||
debugger
|
||||
$.ajax({
|
||||
url: "./api",
|
||||
type: "POST",
|
||||
@@ -896,7 +895,7 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder deletion of all
|
||||
self.removeAllOrphaned = function() {
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
if (confirm(glitterTranslate.clearOrphanWarning)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-removing-multiple', 0, self.statusInfo.folders().length)
|
||||
// Delete them all
|
||||
@@ -913,7 +912,7 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder adding of all
|
||||
self.addAllOrphaned = function() {
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
if (confirm(glitterTranslate.confirm)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-sendback')
|
||||
// Delete them all
|
||||
|
||||
@@ -423,6 +423,21 @@ function QueueListModel(parent) {
|
||||
|
||||
}
|
||||
|
||||
// Handle mousedown to capture state before change
|
||||
self.handleMultiEditStatusMouseDown = function(item, event) {
|
||||
var clickedValue = $(event.currentTarget).find("input").val();
|
||||
|
||||
// If this radio was already selected (same value as previous), clear it
|
||||
if ($('.multioperations-selector input[name="multiedit-status"]:checked').val() === clickedValue) {
|
||||
// Clear all radio buttons in this group after the click finished
|
||||
// Hacky, but it works
|
||||
setTimeout(function () {
|
||||
$('.multioperations-selector input[name="multiedit-status"]').prop('checked', false);
|
||||
}, 200)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove downloads from queue
|
||||
self.removeDownloads = function(form) {
|
||||
// Hide modal and show notification
|
||||
@@ -456,6 +471,50 @@ function QueueListModel(parent) {
|
||||
self.triggerRemoveDownload(self.multiEditItems())
|
||||
}
|
||||
|
||||
// Move all selected to top
|
||||
self.doMultiMoveToTop = function() {
|
||||
// Anything selected?
|
||||
if(self.multiEditItems().length < 1) return;
|
||||
|
||||
// Move each item to the top, starting from the last one in the sorted list
|
||||
var arrayList = self.multiEditItems()
|
||||
var movePromises = [];
|
||||
for(var i = arrayList.length - 1; i >= 0; i--) {
|
||||
movePromises.push(callAPI({
|
||||
mode: "switch",
|
||||
value: arrayList[i].id,
|
||||
value2: 0
|
||||
}));
|
||||
}
|
||||
|
||||
// Wait for all moves to complete then refresh
|
||||
Promise.all(movePromises).then(function() {
|
||||
self.parent.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
// Move all selected to bottom
|
||||
self.doMultiMoveToBottom = function() {
|
||||
// Anything selected?
|
||||
if(self.multiEditItems().length < 1) return;
|
||||
|
||||
// Move each item to the bottom, starting from the first one in the sorted list
|
||||
var arrayList = self.multiEditItems()
|
||||
var movePromises = [];
|
||||
for(var i = 0; i < arrayList.length; i++) {
|
||||
movePromises.push(callAPI({
|
||||
mode: "switch",
|
||||
value: arrayList[i].id,
|
||||
value2: self.totalItems() - 1
|
||||
}));
|
||||
}
|
||||
|
||||
// Wait for all moves to complete then refresh
|
||||
Promise.all(movePromises).then(function() {
|
||||
self.parent.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
// Focus on the confirm button
|
||||
$('#modal-delete-queue-job').on("shown.bs.modal", function() {
|
||||
$('#modal-delete-queue-job .btn[type="submit"]').focus()
|
||||
|
||||
@@ -860,7 +860,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox {
|
||||
width: 20%;
|
||||
width: 19%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
@@ -871,7 +871,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-small {
|
||||
width: 80px;
|
||||
width: 115px;
|
||||
float: right;
|
||||
padding-left: 0;
|
||||
padding-top: 12px;
|
||||
@@ -1097,6 +1097,13 @@ tr.queue-item>td:first-child>a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mark-completed-link {
|
||||
font-weight: bold !important;
|
||||
color: #28a745 !important;
|
||||
text-decoration: underline;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.history-status-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-small {
|
||||
width: 72px;
|
||||
width: 115px;
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-clear {
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
<h1>$T('wizard-quickstart')</h1>
|
||||
<hr />
|
||||
<script type="text/javascript">
|
||||
var txtTestServer = "$T('wizard-server-text')";
|
||||
var txtChecking = "$T('srv-testing')";
|
||||
var txtTestRequired = "$T('wizard-test-server-required')";
|
||||
<!--#include raw $webdir + "/static/javascript/checkserver.js"#-->
|
||||
</script>
|
||||
<h3>$T('wizard-server')</h3>
|
||||
@@ -22,7 +24,7 @@
|
||||
<div class="form-group">
|
||||
<label for="host" class="col-sm-4 control-label">$T('srv-host')</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" name="host" id="host" value="$host" placeholder="$T('wizard-example') news.newshosting.com" />
|
||||
<input type="text" class="form-control" name="host" id="host" value="$host" placeholder="$T('wizard-example') news.newshosting.com" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -57,13 +59,13 @@
|
||||
<div class="form-group">
|
||||
<label for="port" class="col-sm-4 control-label">$T('srv-port')</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" class="form-control" name="port" id="port" value="<!--#if $port then $port else '563' #-->" min="0" max="65535" />
|
||||
<input type="number" class="form-control" name="port" id="port" value="<!--#if $port then $port else '563' #-->" min="0" max="65535" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="connections" class="col-sm-4 control-label">$T('srv-connections')</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" min="1" max="500" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" />
|
||||
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" min="1" max="500" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -81,7 +83,7 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button id="serverTest" class="btn btn-default"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
|
||||
<button id="serverTest" class="btn btn-default" data-toggle="tooltip" data-placement="left"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div id="serverResponse" class="well well-sm">$T('wizard-server-text')</div>
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
// Variable to track server test results
|
||||
var serverTestSuccessful = false;
|
||||
|
||||
function resetTestResult() {
|
||||
serverTestSuccessful = false;
|
||||
$('#serverResponse').html(txtTestServer);
|
||||
checkRequired();
|
||||
}
|
||||
|
||||
function setTestResult(success) {
|
||||
serverTestSuccessful = success;
|
||||
checkRequired();
|
||||
}
|
||||
|
||||
function checkRequired() {
|
||||
if ($("#host").val() && $("#connections").val()) {
|
||||
// Check if form is valid using HTML5 validation and if server test passed
|
||||
if ($("form").get(0).checkValidity() && serverTestSuccessful) {
|
||||
$("#next-button").removeClass('disabled')
|
||||
$("#next-button").removeAttr('data-toggle')
|
||||
$("#next-button").removeAttr('title')
|
||||
$("#next-button").tooltip('destroy')
|
||||
return true;
|
||||
} else {
|
||||
$("#next-button").addClass('disabled')
|
||||
$("#next-button").attr('data-toggle', 'tooltip')
|
||||
$("#next-button").attr('data-placement', 'left')
|
||||
$("#next-button").attr('title', txtTestRequired)
|
||||
$("#next-button").tooltip()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +34,13 @@ $(document).ready(function() {
|
||||
// Add tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
// On form-submit
|
||||
// On server test button click
|
||||
$("#serverTest").click(function() {
|
||||
// Check HTML5 form validation before testing server
|
||||
if (!$("form").get(0).reportValidity()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#serverResponse').html(txtChecking);
|
||||
$.getJSON(
|
||||
"../api?mode=config&name=test_server&output=json",
|
||||
@@ -21,8 +48,10 @@ $(document).ready(function() {
|
||||
function(result) {
|
||||
if (result.value.result) {
|
||||
r = '<span class="success"><span class="glyphicon glyphicon-ok"></span> ' + result.value.message + '</span>';
|
||||
setTestResult(true);
|
||||
} else {
|
||||
r = '<span class="failed"><span class="glyphicon glyphicon-minus-sign"></span> ' + result.value.message + '</span>';
|
||||
setTestResult(false);
|
||||
}
|
||||
r = r.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="failed" target="_blank">https://sabnzbd.org/certificate-errors</a>')
|
||||
$('#serverResponse').html(r);
|
||||
@@ -31,26 +60,9 @@ $(document).ready(function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#port, #connections").bind('keyup blur', function() {
|
||||
if (this.value > 0) {
|
||||
$(this).removeClass("incorrect");
|
||||
$(this).addClass("correct");
|
||||
} else {
|
||||
$(this).removeClass("correct");
|
||||
$(this).addClass("incorrect");
|
||||
}
|
||||
checkRequired()
|
||||
});
|
||||
|
||||
$("#host, #username, #password").bind('keyup blur', function() {
|
||||
if (this.value) {
|
||||
$(this).removeClass("incorrect");
|
||||
$(this).addClass("correct");
|
||||
} else {
|
||||
$(this).removeClass("correct");
|
||||
$(this).addClass("incorrect");
|
||||
}
|
||||
checkRequired();
|
||||
// Reset test result when any form field changes
|
||||
$("#host, #username, #password, #port, #connections, #ssl_verify").bind('input change', function() {
|
||||
resetTestResult();
|
||||
});
|
||||
|
||||
$('#ssl').click(function() {
|
||||
@@ -65,13 +77,14 @@ $(document).ready(function() {
|
||||
$('#port').val('119')
|
||||
}
|
||||
}
|
||||
resetTestResult();
|
||||
})
|
||||
|
||||
checkRequired()
|
||||
|
||||
$('form').submit(function(event) {
|
||||
// Double check
|
||||
if(!checkRequired()) {
|
||||
// Check if server test passed (HTML5 validation is automatic)
|
||||
if(!serverTestSuccessful) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -98,43 +98,15 @@ label {
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.unselected,
|
||||
.selected {
|
||||
display: inline-block;
|
||||
}
|
||||
.unselected {
|
||||
padding: 6px 10px 6px 10px;
|
||||
border: 1px solid #636363;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
color: #636363;
|
||||
}
|
||||
.selected {
|
||||
padding: 6px 10px 6px 10px;
|
||||
color: white;
|
||||
background-color: #636363;
|
||||
border: 1px solid #636363;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.bigger {
|
||||
font-size: 14px;
|
||||
}
|
||||
.bigger input {
|
||||
font-size: 16px;
|
||||
}
|
||||
.required-star {
|
||||
color: red;
|
||||
}
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.correct {
|
||||
border: 2px solid #00cc22;
|
||||
}
|
||||
.incorrect {
|
||||
border: 2px solid red;
|
||||
}
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -150,20 +122,12 @@ label {
|
||||
.input-group-bw {
|
||||
width: 150px;
|
||||
}
|
||||
.disabled-text {
|
||||
text-decoration: line-through;
|
||||
color: #ccc;
|
||||
}
|
||||
#serverResponse {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
#host-tip {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.error-text {
|
||||
display: inline;
|
||||
color: red;
|
||||
}
|
||||
#bandwidth {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
<url type="faq">https://sabnzbd.org/wiki/faq</url>
|
||||
<url type="contact">https://sabnzbd.org/live-chat.html</url>
|
||||
<releases>
|
||||
<release version="4.5.5" date="2025-10-24" type="stable"/>
|
||||
<release version="4.5.4" date="2025-10-22" type="stable"/>
|
||||
<release version="4.5.3" date="2025-08-25" type="stable"/>
|
||||
<release version="4.5.2" date="2025-07-09" type="stable"/>
|
||||
<release version="4.5.1" date="2025-04-11" type="stable"/>
|
||||
<release version="4.5.0" date="2025-04-01" type="stable"/>
|
||||
|
||||
BIN
macos/7zip/7zz
BIN
macos/7zip/7zz
Binary file not shown.
Binary file not shown.
BIN
macos/par2/par2
BIN
macos/par2/par2
Binary file not shown.
@@ -32,6 +32,11 @@ msgstr ""
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid "Unable to link to OpenSSL, optimized SSL connection functions will not be used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid "SABCTools disabled: no correct version found! (Found v%s, expecting v%s)"
|
||||
@@ -293,6 +298,19 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py, sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr ""
|
||||
@@ -670,6 +688,14 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "API Key missing, please enter the api key from Config->General into your 3rd party program:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "API Key incorrect, Use the api key from Config->General in your 3rd party program:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
@@ -679,14 +705,6 @@ msgstr ""
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "API Key missing, please enter the api key from Config->General into your 3rd party program:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "API Key incorrect, Use the api key from Config->General in your 3rd party program:"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -996,10 +1014,6 @@ msgstr ""
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr ""
|
||||
@@ -2239,6 +2253,11 @@ msgstr ""
|
||||
msgid "Retry"
|
||||
msgstr ""
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3296,10 +3315,6 @@ msgstr ""
|
||||
msgid "Naming"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr ""
|
||||
@@ -3397,7 +3412,7 @@ msgid "Warn 5 days in advance of account expiration date."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota for this account, counted from the time it is set. In bytes, optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few minutes."
|
||||
msgid "Quota for this server, counted from the time it is set. In bytes, optionally follow with K,M,G.<br />Checked every few minutes. Notification is sent when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4303,6 +4318,10 @@ msgstr ""
|
||||
msgid "Retry all"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Are you sure you want to delete all folders in your Temporary Download Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr ""
|
||||
@@ -4541,6 +4560,11 @@ msgstr ""
|
||||
msgid "Start Wizard"
|
||||
msgstr ""
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -37,6 +37,13 @@ msgstr "Nezdařilo se spustit webové rozhraní"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Šablona pro web nebyla nalezena: %s, zkouším standardní šablonu"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -328,6 +335,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kvóta přesažena, pozastavuji stahování"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Nesprávný parametr"
|
||||
@@ -723,15 +744,6 @@ msgstr "Odmítnuto spojení z:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Odmítnuté spojení s hostem \"%s\" z:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Uživatel přihlášen do webového rozhraní"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Uživatel přihlášen"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -748,6 +760,15 @@ msgstr ""
|
||||
"Nesprávný API klíč, použijte api klíč z Nastavení->Obecné ve vašem programu "
|
||||
"třetí strany:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Uživatel přihlášen do webového rozhraní"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Uživatel přihlášen"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1069,10 +1090,6 @@ msgstr "Rozbalování selhalo, chyba zápisu nebo plný disk?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Rozbalování selhalo, cesta k souboru je příliš dlouhá."
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Rozbalení selhalo, archiv vyžaduje heslo"
|
||||
@@ -2327,6 +2344,11 @@ msgstr "Jméno"
|
||||
msgid "Retry"
|
||||
msgstr "Opakovat"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3466,10 +3488,6 @@ msgstr ""
|
||||
msgid "Naming"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr ""
|
||||
@@ -3572,9 +3590,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4517,6 +4535,12 @@ msgstr ""
|
||||
msgid "Retry all"
|
||||
msgstr "Opakovat vše"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Získat NZB z URL"
|
||||
@@ -4763,6 +4787,11 @@ msgstr ""
|
||||
msgid "Start Wizard"
|
||||
msgstr ""
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
256
po/main/da.po
256
po/main/da.po
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2023
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -36,6 +36,15 @@ msgstr "Kunne ikke starte web-interface"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Kan ikke finde webskabeloner: %s, forsøger med standardskabelon"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
"Kan ikke linke til OpenSSL, optimerede SSL-forbindelsesfunktioner vil ikke "
|
||||
"blive brugt."
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -67,7 +76,7 @@ msgstr "7za binær... IKKE fundet!"
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid "Essential modules are missing, downloading cannot start."
|
||||
msgstr ""
|
||||
msgstr "Nødvendige moduler mangler, download kan ikke starte."
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
@@ -86,7 +95,7 @@ msgstr "HTTP og HTTPS porte kan ikke være de samme"
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid "Could not load additional certificates from certifi package"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke indlæse yderligere certifikater fra certifi-pakken"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
@@ -96,7 +105,7 @@ msgstr "HTTPS fejlede på grund af manglende CERT og KEY filer"
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid "Disabled HTTPS because of invalid CERT and KEY files"
|
||||
msgstr ""
|
||||
msgstr "HTTPS deaktiveret på grund af ugyldige CERT og KEY filer"
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
@@ -136,6 +145,7 @@ msgid ""
|
||||
"Current umask (%o) might deny SABnzbd access to the files and folders it "
|
||||
"creates."
|
||||
msgstr ""
|
||||
"Aktuel umask (%o) kan nægte SABnzbd adgang til filer og mapper den opretter."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/__init__.py
|
||||
@@ -143,26 +153,28 @@ msgid ""
|
||||
"Completed Download Folder %s is on FAT file system, limiting maximum file "
|
||||
"size to 4GB"
|
||||
msgstr ""
|
||||
"Færdig Download Mappe %s er på FAT filsystem, begrænser maksimal "
|
||||
"filstørrelse til 4GB"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/__init__.py
|
||||
msgid "Restarting because of crashed postprocessor"
|
||||
msgstr ""
|
||||
msgstr "Genstarter på grund af styrt efterbehandler"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/__init__.py
|
||||
msgid "Restarting because of crashed downloader"
|
||||
msgstr ""
|
||||
msgstr "Genstarter på grund af styrt downloader"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/__init__.py
|
||||
msgid "Restarting because of crashed assembler"
|
||||
msgstr ""
|
||||
msgstr "Genstarter på grund af styrt assembler"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/__init__.py
|
||||
msgid "Cannot access PID file %s"
|
||||
msgstr ""
|
||||
msgstr "Kan ikke få adgang til PID-fil %s"
|
||||
|
||||
#: sabnzbd/api.py, sabnzbd/emailer.py
|
||||
msgid "Email succeeded"
|
||||
@@ -194,18 +206,25 @@ msgid ""
|
||||
"server (port 80), possibly an indexer, not a usenet server. You have to fill"
|
||||
" a usenet server."
|
||||
msgstr ""
|
||||
"Kunne ikke forbinde til %s på port %s. Det ser ud til at %s fungerer som en "
|
||||
"webserver (port 80), muligvis en indexer, ikke en usenet server. Du skal "
|
||||
"angive en usenet server."
|
||||
|
||||
#: sabnzbd/api.py
|
||||
msgid ""
|
||||
"Could not connect to %s on port %s. Use the default usenet settings: port "
|
||||
"563 and SSL turned on"
|
||||
msgstr ""
|
||||
"Kunne ikke forbinde til %s på port %s. Brug standard usenet-indstillinger: "
|
||||
"port 563 og SSL slået til"
|
||||
|
||||
#: sabnzbd/api.py
|
||||
msgid ""
|
||||
"Could not connect to %s on port %s. Use the default usenet settings: port "
|
||||
"119 and SSL turned off"
|
||||
msgstr ""
|
||||
"Kunne ikke forbinde til %s på port %s. Brug standard usenet-indstillinger: "
|
||||
"port 119 og SSL slået fra"
|
||||
|
||||
#: sabnzbd/api.py, sabnzbd/interface.py
|
||||
msgid "Server address \"%s:%s\" is not valid."
|
||||
@@ -320,17 +339,33 @@ msgstr "Afbrudt, uønsket extension fundet"
|
||||
#: sabnzbd/assembler.py
|
||||
msgid "Job \"%s\" is probably encrypted due to RAR with same name inside this RAR"
|
||||
msgstr ""
|
||||
"Job \"%s\" er sandsynligvis krypteret pga. en RAR med samme navn inde i "
|
||||
"denne RAR"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/assembler.py
|
||||
msgid "Job \"%s\" is probably encrypted: \"password\" in filename \"%s\""
|
||||
msgstr ""
|
||||
msgstr "Job \"%s\" er sandsynligvis krypteret: \"password\" i filnavnet \"%s\""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kvote brugt, pause downloading"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvota"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fejl parameter"
|
||||
@@ -349,7 +384,7 @@ msgstr "Ugyldig server adresse."
|
||||
|
||||
#: sabnzbd/cfg.py
|
||||
msgid "%s is not a valid script"
|
||||
msgstr ""
|
||||
msgstr "%s er ikke et gyldigt script"
|
||||
|
||||
#: sabnzbd/cfg.py
|
||||
msgid "%s is not a correct octal value"
|
||||
@@ -361,11 +396,13 @@ msgid ""
|
||||
"Permissions setting of %s might deny SABnzbd access to the files and folders"
|
||||
" it creates."
|
||||
msgstr ""
|
||||
"Tilladelsesindstillingen for %s kan nægte SABnzbd adgang til de filer og "
|
||||
"mapper, den opretter."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/cfg.py
|
||||
msgid "Network path \"%s\" should not be used here"
|
||||
msgstr ""
|
||||
msgstr "Netværksstien \"%s\" bør ikke bruges her"
|
||||
|
||||
#: sabnzbd/cfg.py
|
||||
msgid "Queue not empty, cannot change folder."
|
||||
@@ -376,6 +413,8 @@ msgid ""
|
||||
"The Completed Download Folder cannot be the same or a subfolder of the "
|
||||
"Temporary Download Folder"
|
||||
msgstr ""
|
||||
"Mappen Færdige downloads kan ikke være den samme som eller en undermappe af "
|
||||
"den Midlertidige download-mappe"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/cfg.py
|
||||
@@ -383,17 +422,21 @@ msgid ""
|
||||
"Do not use a folder in the application folder as your Scripts Folder, it "
|
||||
"might be emptied during updates."
|
||||
msgstr ""
|
||||
"Brug ikke en mappe i programmappen som din Scripts-mappe, den kan blive tømt"
|
||||
" under opdateringer."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/cfg.py
|
||||
msgid ""
|
||||
"The par2 application was switched, any custom par2 parameters were removed"
|
||||
msgstr ""
|
||||
"par2-programmet blev skiftet, eventuelle brugerdefinerede par2-parametre er "
|
||||
"fjernet"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/config.py
|
||||
msgid "Configuration locked, cannot save settings"
|
||||
msgstr ""
|
||||
msgstr "Konfiguration låst, kan ikke gemme indstillinger"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/config.py
|
||||
@@ -408,7 +451,7 @@ msgstr "Kan ikke oprette backup fil for %s"
|
||||
#. Warning message
|
||||
#: sabnzbd/config.py
|
||||
msgid "Could not restore backup"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke gendanne backup"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/config.py
|
||||
@@ -452,23 +495,23 @@ msgstr "Ukendt fejl under afkodning af %s"
|
||||
|
||||
#: sabnzbd/deobfuscate_filenames.py
|
||||
msgid "Deobfuscate skipped due to DVD/Bluray directories"
|
||||
msgstr ""
|
||||
msgstr "Dekryptering sprang over på grund af DVD/Blu-ray-mapper"
|
||||
|
||||
#: sabnzbd/deobfuscate_filenames.py
|
||||
msgid "Deobfuscate corrected the extension of %d file(s)"
|
||||
msgstr ""
|
||||
msgstr "Dekryptering rettede filendelsen på %d fil(er)"
|
||||
|
||||
#: sabnzbd/deobfuscate_filenames.py
|
||||
msgid "Deobfuscate renamed %d file(s)"
|
||||
msgstr ""
|
||||
msgstr "Dekryptering omdøbte %d fil(er)"
|
||||
|
||||
#: sabnzbd/deobfuscate_filenames.py
|
||||
msgid "Deobfuscate renamed %d subtitle file(s)"
|
||||
msgstr ""
|
||||
msgstr "Dekryptering omdøbte %d undertekst-fil(er)"
|
||||
|
||||
#: sabnzbd/directunpacker.py, sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
msgstr "Direkte udpakning"
|
||||
|
||||
#. PP status
|
||||
#: sabnzbd/directunpacker.py, sabnzbd/skintext.py
|
||||
@@ -482,13 +525,15 @@ msgstr "Udpakket %s filer/mapper i %s"
|
||||
#. Warning message
|
||||
#: sabnzbd/directunpacker.py
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
msgstr "Direkte udpakning blev automatisk aktiveret."
|
||||
|
||||
#: sabnzbd/directunpacker.py, sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
"Jobs vil begynde at blive udpakket under download for at reducere "
|
||||
"efterbehandlingstiden. Virker kun for jobs, der ikke kræver reparation."
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/dirscanner.py
|
||||
@@ -528,7 +573,7 @@ msgstr "Server %s vil blive ignoreret for i %s minutter"
|
||||
#. Warning message
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "There are no active servers!"
|
||||
msgstr ""
|
||||
msgstr "Der er ingen aktive servere!"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/downloader.py
|
||||
@@ -538,12 +583,12 @@ msgstr "Det lykkedes ikke at initialisere %s@%s med begrundelse %s"
|
||||
#. Error message
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "Fatal error in Downloader"
|
||||
msgstr ""
|
||||
msgstr "Alvorlig fejl i Downloader"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "%s@%s: Received unknown status code %s for article %s"
|
||||
msgstr ""
|
||||
msgstr "%s@%s: Modtog ukendt statuskode %s for artikel %s"
|
||||
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "Too many connections to server %s [%s]"
|
||||
@@ -554,6 +599,8 @@ msgid ""
|
||||
"Login from too many different IP addresses to server %s [%s] - "
|
||||
"https://sabnzbd.org/multiple-adresses"
|
||||
msgstr ""
|
||||
"Login fra for mange forskellige IP-adresser til server %s [%s] - "
|
||||
"https://sabnzbd.org/multiple-adresses"
|
||||
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "Failed login for server %s [%s]"
|
||||
@@ -576,12 +623,12 @@ msgstr "Påbegynder lukning af SABnzbd"
|
||||
#. Warning message
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "Server %s is expiring in %s day(s)"
|
||||
msgstr ""
|
||||
msgstr "Server %s udløber om %s dag(e)"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/downloader.py
|
||||
msgid "Server %s has used the specified quota"
|
||||
msgstr ""
|
||||
msgstr "Server %s har brugt den angivne kvota"
|
||||
|
||||
#: sabnzbd/emailer.py
|
||||
msgid "Failed to connect to mail server"
|
||||
@@ -687,7 +734,7 @@ msgstr "Det lykkedes ikke at flytte %s til %s"
|
||||
#. Error message
|
||||
#: sabnzbd/filesystem.py
|
||||
msgid "Blocked attempt to create directory %s"
|
||||
msgstr ""
|
||||
msgstr "Blokerede forsøg på at oprette mappe %s"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/filesystem.py
|
||||
@@ -707,17 +754,17 @@ msgstr "Downloadning af %s mislykkedes"
|
||||
#. Warning message
|
||||
#: sabnzbd/filesystem.py
|
||||
msgid "%s is not writable at all. This blocks downloads."
|
||||
msgstr ""
|
||||
msgstr "%s er slet ikke skrivbar. Dette blokerer downloads."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/filesystem.py
|
||||
msgid "Cannot write a long filename to %s. This can cause problems."
|
||||
msgstr ""
|
||||
msgstr "Kan ikke skrive et langt filnavn til %s. Dette kan give problemer."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/filesystem.py
|
||||
msgid "Cannot write a unicode filename to %s. This can cause problems."
|
||||
msgstr ""
|
||||
msgstr "Kan ikke skrive et Unicode-filnavn til %s. Dette kan give problemer."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/filesystem.py
|
||||
@@ -725,23 +772,15 @@ msgid ""
|
||||
"%s is not writable with special character filenames. This can cause "
|
||||
"problems."
|
||||
msgstr ""
|
||||
"%s er ikke skrivbar med filnavne med specialtegn. Dette kan give problemer."
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Refused connection from:"
|
||||
msgstr ""
|
||||
msgstr "Afviste forbindelse fra:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Bruger logget på webgrænsefladen"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Bruger logget ind"
|
||||
msgstr "Afviste forbindelse med værtsnavn \"%s\" fra:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
@@ -759,6 +798,15 @@ msgstr ""
|
||||
"Forkert API-nøgle, anvend api-nøglen fra Konfiguration->Generelt i dit "
|
||||
"tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Bruger logget på webgrænsefladen"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Bruger logget ind"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -766,7 +814,7 @@ msgstr "Mislykkede login forsøg fra %s"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Invalid backup archive"
|
||||
msgstr ""
|
||||
msgstr "Ugyldigt backup-arkiv"
|
||||
|
||||
#. Config->RSS, tab header
|
||||
#: sabnzbd/interface.py, sabnzbd/skintext.py
|
||||
@@ -817,6 +865,7 @@ msgstr "Udefineret server!"
|
||||
msgid ""
|
||||
"Category folder cannot be a subfolder of the Temporary Download Folder."
|
||||
msgstr ""
|
||||
"Kategorimappe kan ikke være en undermappe i den midlertidige download-mappe."
|
||||
|
||||
#: sabnzbd/interface.py, sabnzbd/skintext.py
|
||||
msgid "ERROR:"
|
||||
@@ -937,6 +986,8 @@ msgstr "Standser..."
|
||||
msgid ""
|
||||
"To prevent all helpful warnings, disable Special setting 'helpful_warnings'."
|
||||
msgstr ""
|
||||
"For at undertrykke alle hjælpsomme advarsler, deaktiver Special-"
|
||||
"indstillingen 'helpful_warnings'."
|
||||
|
||||
#: sabnzbd/misc.py
|
||||
msgid "d"
|
||||
@@ -957,7 +1008,7 @@ msgstr "Opdatering tilgængelig!"
|
||||
#. Error message
|
||||
#: sabnzbd/misc.py
|
||||
msgid "Failed to upload file: %s"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke uploade fil: %s"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/misc.py
|
||||
@@ -970,16 +1021,18 @@ msgid ""
|
||||
"Your password file contains more than 30 passwords, testing all these "
|
||||
"passwords takes a lot of time. Try to only list useful passwords."
|
||||
msgstr ""
|
||||
"Din adgangskodefil indeholder over 30 adgangskoder, at afprøve dem alle "
|
||||
"tager lang tid. Prøv kun at liste nyttige adgangskoder."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/misc.py
|
||||
msgid "Failed to read the password file %s"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke læse adgangskodefilen %s"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/misc.py
|
||||
msgid "[%s] The command in build_command is undefined."
|
||||
msgstr ""
|
||||
msgstr "[%s] Kommandoen i build_command er ikke defineret."
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/misc.py
|
||||
@@ -1075,11 +1128,7 @@ msgstr "Udpakning mislykkedes, skrivefejl eller disken fuld?"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Udpakningen mislykkedes, stien er for lang"
|
||||
msgstr "Udpakning mislykkedes, disken er fuld"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
@@ -1176,7 +1225,7 @@ msgstr "[%s] Repareret i %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
msgstr "Verificerer reparation"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/newsunpack.py, sabnzbd/notifier.py
|
||||
@@ -1208,6 +1257,8 @@ msgid ""
|
||||
"Certificate hostname mismatch: the server hostname is not listed in the "
|
||||
"certificate. This is a server issue."
|
||||
msgstr ""
|
||||
"Certifikatets værtsnavn stemmer ikke: serverens værtsnavn er ikke angivet i "
|
||||
"certifikatet. Dette er et serverproblem."
|
||||
|
||||
#: sabnzbd/newswrapper.py
|
||||
msgid ""
|
||||
@@ -1215,6 +1266,9 @@ msgid ""
|
||||
" locally injected certificate (for example by firewall or virus scanner). "
|
||||
"Try setting Certificate verification to Medium."
|
||||
msgstr ""
|
||||
"Certifikatet kunne ikke valideres. Dette kan være et serverproblem eller "
|
||||
"skyldes et lokalt indsat certifikat (f.eks. af firewall eller virus-"
|
||||
"scanner). Prøv at sætte Certifikatkontrol til Medium."
|
||||
|
||||
#: sabnzbd/newswrapper.py
|
||||
msgid "Server %s uses an untrusted certificate [%s]"
|
||||
@@ -1227,7 +1281,7 @@ msgstr "Wiki"
|
||||
|
||||
#: sabnzbd/newswrapper.py
|
||||
msgid "Failed to connect: %s %s@%s:%s (%s)"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke oprette forbindelse: %s %s@%s:%s (%s)"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/notifier.py
|
||||
@@ -1266,7 +1320,7 @@ msgstr "Andre beskeder"
|
||||
#. Notification action
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Open folder"
|
||||
msgstr ""
|
||||
msgstr "Åbn mappe"
|
||||
|
||||
#. Notification action
|
||||
#: sabnzbd/notifier.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py
|
||||
@@ -1285,7 +1339,7 @@ msgstr "Ikke tilgængelig"
|
||||
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Failed to send macOS notification"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke sende macOS-notifikation"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/notifier.py
|
||||
@@ -1295,21 +1349,21 @@ msgstr "Kunne ikke sende Prowl besked"
|
||||
#. Warning message
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Failed to send Apprise message - no URLs defined"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke sende Apprise-besked - ingen URL'er angivet"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "One or more Apprise URLs could not be loaded."
|
||||
msgstr ""
|
||||
msgstr "En eller flere Apprise-URL'er kunne ikke indlæses."
|
||||
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Failed to send one or more Apprise Notifications"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke sende en eller flere Apprise-notifikationer"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/notifier.py
|
||||
msgid "Failed to send Apprise message"
|
||||
msgstr ""
|
||||
msgstr "Kunne ikke sende Apprise-besked"
|
||||
|
||||
#. Error message
|
||||
#: sabnzbd/notifier.py
|
||||
@@ -1585,6 +1639,8 @@ msgid ""
|
||||
"Unable to bind to port %s on %s. Some other software uses the port or "
|
||||
"SABnzbd is already running."
|
||||
msgstr ""
|
||||
"Kan ikke binde til port %s på %s. En anden software bruger porten eller "
|
||||
"SABnzbd kører allerede."
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/panic.py
|
||||
@@ -1860,7 +1916,7 @@ msgstr "Udpak"
|
||||
#. PP phase "deobfuscate"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Deobfuscate"
|
||||
msgstr ""
|
||||
msgstr "Af-obfuskér"
|
||||
|
||||
#. PP phase "script" - Notification Script settings
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -2362,6 +2418,11 @@ msgstr "Navn"
|
||||
msgid "Retry"
|
||||
msgstr "Forsøg igen"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -2576,15 +2637,15 @@ msgstr "Nameserver/DNS Lookup"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Download speed limited by"
|
||||
msgstr ""
|
||||
msgstr "Downloadhastighed begrænset af"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Disk speed"
|
||||
msgstr ""
|
||||
msgstr "Diskhastighed"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "System load"
|
||||
msgstr ""
|
||||
msgstr "Systembelastning"
|
||||
|
||||
#. Do not translate Pystone
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -2601,7 +2662,7 @@ msgstr "Komplet mappe hastighed"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Internet Bandwidth"
|
||||
msgstr ""
|
||||
msgstr "Internetbåndbredde"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Repeat test"
|
||||
@@ -2616,6 +2677,8 @@ msgid ""
|
||||
"Adds a verified test NZB of the specified size, filled with random data. Can"
|
||||
" be used to verify your setup."
|
||||
msgstr ""
|
||||
"Tilføjer en verificeret test-NZB af den angivne størrelse, fyldt med "
|
||||
"tilfældige data. Kan bruges til at verificere din opsætning."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Config File"
|
||||
@@ -2629,7 +2692,7 @@ msgstr "Brugt chace"
|
||||
#. What platform we are on (e.g. Windows/macOS/Ubuntu/UnRaid/etc)
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Platform"
|
||||
msgstr ""
|
||||
msgstr "Platform"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -2735,11 +2798,11 @@ msgstr "Port som SABnzbd ska lytte på."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Web Interface Theme"
|
||||
msgstr ""
|
||||
msgstr "Tema for webgrænseflade"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Choose a theme."
|
||||
msgstr ""
|
||||
msgstr "Vælg et tema."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "SABnzbd Username"
|
||||
@@ -2780,6 +2843,8 @@ msgid ""
|
||||
"Modern web browsers and other clients will not accept self-signed "
|
||||
"certificates and will give a warning and/or won't connect at all."
|
||||
msgstr ""
|
||||
"Moderne webbrowsere og andre klienter accepterer ikke selvsignerede "
|
||||
"certifikater og vil give en advarsel og/eller slet ikke forbinde."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "HTTPS Port"
|
||||
@@ -2861,7 +2926,7 @@ msgstr ""
|
||||
#. Config->Scheduling
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Create backup"
|
||||
msgstr ""
|
||||
msgstr "Opret backup"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -2873,7 +2938,7 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Cleanup List"
|
||||
msgstr "Ryd listen"
|
||||
msgstr "Rydningsliste"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -2885,7 +2950,7 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
msgstr "Bevarelse af historik"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
@@ -2895,28 +2960,29 @@ msgstr "Behold alle jobs"
|
||||
msgid ""
|
||||
"Move jobs to the archive if the history exceeds specified number of jobs"
|
||||
msgstr ""
|
||||
"Flyt jobs til arkivet hvis historikken overstiger det angivne antal jobs"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Delete jobs if the history and archive exceeds specified number of jobs"
|
||||
msgstr ""
|
||||
msgstr "Slet jobs hvis historik og arkiv overstiger det angivne antal jobs"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Move jobs to the archive after specified number of days"
|
||||
msgstr ""
|
||||
msgstr "Flyt jobs til arkivet efter angivet antal dage"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Delete jobs from the history and archive after specified number of days"
|
||||
msgstr ""
|
||||
msgstr "Slet jobs fra historik og arkiv efter angivet antal dage"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Move all completed jobs to archive"
|
||||
msgstr ""
|
||||
msgstr "Flyt alle fuldførte jobs til arkivet"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Delete all completed jobs"
|
||||
msgstr ""
|
||||
msgstr "Slet alle fuldførte jobs"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
@@ -3064,14 +3130,16 @@ msgstr ""
|
||||
msgid ""
|
||||
"Use Sorting to automatically organize and rename your completed downloads."
|
||||
msgstr ""
|
||||
"Brug Sortering til automatisk at organisere og omdøbe dine fuldførte "
|
||||
"downloads."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Minimum Free Space for Completed Download Folder"
|
||||
msgstr ""
|
||||
msgstr "Minimum fri plads for mappen Færdige downloads"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Will not work if a category folder is on a different disk."
|
||||
msgstr ""
|
||||
msgstr "Virker ikke hvis en kategorimappe er på en anden disk."
|
||||
|
||||
#. Auto-resume download on the reset day
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3145,7 +3213,7 @@ msgstr "Systemmapper"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Hidden Folders"
|
||||
msgstr ""
|
||||
msgstr "Skjulte mapper"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Administrative Folder"
|
||||
@@ -3161,7 +3229,7 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Backup Folder"
|
||||
msgstr ""
|
||||
msgstr "Backup-mappe"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -3373,11 +3441,11 @@ msgstr "Brugt før, en NZB kommer ind i køen."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "On queue finish script"
|
||||
msgstr ""
|
||||
msgstr "Script når køen afsluttes"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Executed after the queue finishes downloading."
|
||||
msgstr ""
|
||||
msgstr "Køres når køens downloads er afsluttet."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Extra PAR2 Parameters"
|
||||
@@ -3547,10 +3615,6 @@ msgstr "Efterbehandling"
|
||||
msgid "Naming"
|
||||
msgstr "Navngivning"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvota"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Hvor meget der kan downloades i denne måned (K/M/G)"
|
||||
@@ -3658,9 +3722,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4150,26 +4214,29 @@ msgstr "Enhed som meddelse skal sendes til"
|
||||
#. Apprise settings
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Apprise notifications"
|
||||
msgstr ""
|
||||
msgstr "Aktiver Apprise-notifikationer"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Send notifications using Apprise to almost any notification service"
|
||||
msgstr ""
|
||||
"Send notifikationer via Apprise til næsten enhver notifikationstjeneste"
|
||||
|
||||
#. Apprise settings
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Default Apprise URLs"
|
||||
msgstr ""
|
||||
msgstr "Standard Apprise-URL'er"
|
||||
|
||||
#. Apprise settings
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use a comma and/or space to identify more than one URL."
|
||||
msgstr ""
|
||||
msgstr "Brug komma og/eller mellemrum for at angive flere URL'er."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Override the default URLs for specific notification types below, if desired."
|
||||
msgstr ""
|
||||
"Tilsidesæt standard-URL'er for specifikke notifikationstyper nedenfor, hvis "
|
||||
"ønsket."
|
||||
|
||||
#. Header for Notification Script notification section
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4618,6 +4685,12 @@ msgstr "Fjern alle"
|
||||
msgid "Retry all"
|
||||
msgstr "Prøv igen alle"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Hent NZB fra URL"
|
||||
@@ -4868,6 +4941,11 @@ msgstr "Afslut SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Start guide"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -52,6 +52,13 @@ msgstr ""
|
||||
"Konnte Web-Vorlage nicht finden: %s Versuche die Standard-Vorlage zu "
|
||||
"verwenden."
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -369,6 +376,20 @@ msgstr "Aufgabe \"%s\" ist wahrscheinlich verschlüsselt: \"Passwort\" im Datein
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kontingent aufgebraucht, Downloads werden angehalten"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kontingent"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fehlerhafter Parameter"
|
||||
@@ -798,15 +819,6 @@ msgstr "Abgelehnte Verbindung von:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Verbindung vom Host \"%s\" abgelehnt von:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Benutzer im Web-Interface angemeldet"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Benutzer angemeldet"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -823,6 +835,15 @@ msgstr ""
|
||||
"API-Schlüssel ungültig. Bitte API-Schlüssel aus Einstellungen->Allgemein in "
|
||||
"die externe Anwendung eingeben:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Benutzer im Web-Interface angemeldet"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Benutzer angemeldet"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1150,10 +1171,6 @@ msgstr ""
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr "Fehler beim Entpacken: Festplatte voll"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Entpacken fehlgeschlagen, Pfad ist zu lang"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Entpacken fehlgeschlagen. Archiv benötigt ein Passwort."
|
||||
@@ -2458,6 +2475,11 @@ msgstr "Name"
|
||||
msgid "Retry"
|
||||
msgstr "Erneut versuchen"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3722,10 +3744,6 @@ msgstr "Nachbearbeitung"
|
||||
msgid "Naming"
|
||||
msgstr "Benennung"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kontingent"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Wie viel kann in diesem Monat heruntergeladen werden (K/M/G)?"
|
||||
@@ -3839,13 +3857,10 @@ msgstr "5 Tage vor dem Ablauf des Accounts warnen."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"Kontingent für dieses Konto, gezählt ab dem Zeitpunkt, an dem es festgelegt "
|
||||
"wird. In Bytes, optional gefolgt von K, M, G.<br />Warne, wenn es 0 "
|
||||
"erreicht, wird alle paar Minuten überprüft."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4836,6 +4851,12 @@ msgstr "Alle löschen"
|
||||
msgid "Retry all"
|
||||
msgstr "Alle wiederholen"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "NZB aus URL laden"
|
||||
@@ -5091,6 +5112,11 @@ msgstr "SABnzbd beenden"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Assistenten starten"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "Backup wiederherstellen"
|
||||
|
||||
361
po/main/es.po
361
po/main/es.po
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,13 @@ msgstr "Web-käyttöliittymän käynnistys epäonnistui"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Web-mallia %s ei löydy, yritetään käyttää oletusmallia"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -327,6 +334,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Latausrajoitus saavutettu, keskeytetään lataukset"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Latausrajoitus"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Virheellinen parametri"
|
||||
@@ -729,15 +750,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Käyttäjä kirjautui sisään web-käyttöliittymään"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Käyttäjä kirjautui sisään"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -754,6 +766,15 @@ msgstr ""
|
||||
"API avain virheellinen, käytä Asetukset->Yleiset löytyvää api avainta "
|
||||
"käyttämääsi kolmannen osapuolen ohjelmaan:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Käyttäjä kirjautui sisään web-käyttöliittymään"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Käyttäjä kirjautui sisään"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1072,10 +1093,6 @@ msgstr "Purkaminen epäonnistui, kirjoitusvirhe tai levy täynnä?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Purkaminen epäonnistui, polku on liian pitkä"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Purkaminen epäonnistui, arkisto vaatii salasanan"
|
||||
@@ -2354,6 +2371,11 @@ msgstr "Nimi"
|
||||
msgid "Retry"
|
||||
msgstr "Yritä uudelleen"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3554,10 +3576,6 @@ msgstr "Jälkikäsittely"
|
||||
msgid "Naming"
|
||||
msgstr "Nimeäminen"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Latausrajoitus"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Kuinka paljon voidaan ladata tässä kuussa (K/M/G)"
|
||||
@@ -3663,9 +3681,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4624,6 +4642,12 @@ msgstr "Poista kaikki"
|
||||
msgid "Retry all"
|
||||
msgstr "Yritä uudelleen kaikki"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Nouda NZB osoitteesta"
|
||||
@@ -4876,6 +4900,11 @@ msgstr "Poistu SABnzbd:stä"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Käynnistä velho"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2023
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
# Fred L <88com88@gmail.com>, 2025
|
||||
#
|
||||
msgid ""
|
||||
@@ -39,6 +39,15 @@ msgstr ""
|
||||
"Impossible de trouver le template de l'interface web : %s, nouvelle "
|
||||
"tentative avec le template standard"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
"Impossible d'établir une connexion avec OpenSSL, les fonctions de connexion "
|
||||
"SSL optimisées ne seront pas utilisées."
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -361,6 +370,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Quota atteint, téléchargement mis en pause"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr "Avertissement de limite de quota (%d%%)"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr "Le téléchargement a repris après la réinitialisation du quota."
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Paramètre incorrect"
|
||||
@@ -787,15 +810,6 @@ msgstr "Connexion refusée de:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Connexion refusée avec le nom d'hôte \"%s\" à partir de :"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utilisateur connecté à l'interface web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utilisateur connecté"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -812,6 +826,15 @@ msgstr ""
|
||||
"Clé API incorrecte, utilisez la clé API de la configuration générale dans "
|
||||
"votre application tierce :"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utilisateur connecté à l'interface web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utilisateur connecté"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1142,10 +1165,6 @@ msgstr ""
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr "Échec de l'extraction, disque plein"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Extraction échoué, le chemin est trop long"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Échec de l'extraction, l'archive nécessite un mot de passe"
|
||||
@@ -2442,6 +2461,11 @@ msgstr "Nom"
|
||||
msgid "Retry"
|
||||
msgstr "Réessayer"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr "Marquer comme terminé & supprimer les fichiers temporaires"
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3704,10 +3728,6 @@ msgstr "Post-traitement"
|
||||
msgid "Naming"
|
||||
msgstr "Appellation"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Combien peut-être télécharger ce mois (K/M/G)"
|
||||
@@ -3818,13 +3838,13 @@ msgstr "Avertir 5 jours avant la date d'expiration du compte."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"Quota pour ce compte calculé à partir du moment où il est défini. En octets,"
|
||||
" éventuellement suivi de K,M,G.<br />Avertir quand il atteint 0, vérifié "
|
||||
"toutes les quelques minutes."
|
||||
"Quota pour ce serveur, calculé à partir du moment où il est défini. En "
|
||||
"octets, suivi éventuellement de K,M,G.<br />Vérifié toutes les quelques "
|
||||
"minutes. Une notification est envoyée lorsque le quota est atteint."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4818,6 +4838,14 @@ msgstr "Tout supprimer"
|
||||
msgid "Retry all"
|
||||
msgstr "Réessayer tous"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
"Êtes-vous sûr de vouloir supprimer tous les dossiers de votre Dossier de "
|
||||
"Téléchargement Temporaire ? Cette opération ne peut pas être annulée !"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Importer le NZB depuis l'URL"
|
||||
@@ -5076,6 +5104,11 @@ msgstr "Quitter SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Lancer l'assistant"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr "Cliquez sur Tester le Serveur avant de continuer"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "Restaurer la sauvegarde"
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2023
|
||||
# ION, 2024
|
||||
# ION, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
|
||||
"Last-Translator: ION, 2024\n"
|
||||
"Last-Translator: ION, 2025\n"
|
||||
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -37,6 +37,13 @@ msgstr "כישלון בהתחלת ממשק רשת"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "לא ניתן למצוא תבניות רשת: %s, מנסה תבנית תקנית"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -328,6 +335,20 @@ msgstr "העבודה \"%s\" כנראה מוצפנת: \"סיסמה\" בשם הק
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "מכסה נוצלה, משהה הורדה"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "מכסה"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "פרמטר שגוי"
|
||||
@@ -738,15 +759,6 @@ msgstr "חיבור מסורב מאת:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "חיבור מסורב עם שם המארח \"%s\" מאת:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "משתמש התחבר לממשק הרשת"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "משתמש התחבר"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -761,6 +773,15 @@ msgid ""
|
||||
"program:"
|
||||
msgstr "מפתח API שגוי, השתמש במפתח ה־API מתצורה->כללי בתוכנית הצד השלישי שלך:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "משתמש התחבר לממשק הרשת"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "משתמש התחבר"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1080,11 +1101,7 @@ msgstr "פריקה נכשלה, שגיאת כתיבה או דיסק מלא?"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "פריקה נכשלה, נתיב ארוך מדי"
|
||||
msgstr "פריקה נכשלה, דיסק מלא"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
@@ -2367,6 +2384,11 @@ msgstr "שם"
|
||||
msgid "Retry"
|
||||
msgstr "נסה שוב"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -2638,7 +2660,7 @@ msgstr "מטמון בשימוש"
|
||||
#. What platform we are on (e.g. Windows/macOS/Ubuntu/UnRaid/etc)
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Platform"
|
||||
msgstr ""
|
||||
msgstr "פלטפורמה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -3558,10 +3580,6 @@ msgstr "בתר־עיבוד"
|
||||
msgid "Naming"
|
||||
msgstr "מתן שמות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "מכסה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "כמה ניתן להוריד החודש (ק״ב/מ״ב/ג״ב)"
|
||||
@@ -3666,12 +3684,10 @@ msgstr "הזהר 5 ימים טרם תאריך תפוגת החשבון."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"מכסה עבור חשבון זה, נספרת מהזמן שהיא הוגדרה. בבתים, יכולה לבוא עם K,M,G.<br "
|
||||
"/>הזהר כאשר המכסה מגיעה אל 0, היא נבדקת כל כמה דקות."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4634,6 +4650,12 @@ msgstr "מחק הכל"
|
||||
msgid "Retry all"
|
||||
msgstr "נסה שוב הכל"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "משוך NZB מכתובת"
|
||||
@@ -4888,6 +4910,11 @@ msgstr "צא מן SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "התחל אשף"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "שחזר גיבוי"
|
||||
|
||||
@@ -36,6 +36,13 @@ msgstr "Impossibile avviare l'interfaccia web"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Impossibile trovare il modello web: %s, si prova il modello standard"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -354,6 +361,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Quota esaurita, download in pausa"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parametro non corretto"
|
||||
@@ -778,15 +799,6 @@ msgstr "Connessione rifiutata da:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Connessione rifiutata con hostname \"%s\" da:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utente ha effettuato l'accesso all'interfaccia web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utente ha effettuato l'accesso"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -803,6 +815,15 @@ msgstr ""
|
||||
"Chiave API non corretta, Usa la chiave API da Config->Generale nel tuo "
|
||||
"programma di terze parti:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utente ha effettuato l'accesso all'interfaccia web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utente ha effettuato l'accesso"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1127,10 +1148,6 @@ msgstr "Estrazione fallita, errore di scrittura o disco pieno?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr "Estrazione fallita, disco pieno"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Estrazione fallita, percorso troppo lungo"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Estrazione fallita, l'archivio richiede una password"
|
||||
@@ -2422,6 +2439,11 @@ msgstr "Nome"
|
||||
msgid "Retry"
|
||||
msgstr "Riprova"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3665,10 +3687,6 @@ msgstr "Post-elaborazione"
|
||||
msgid "Naming"
|
||||
msgstr "Denominazione"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Quanto può essere scaricato questo mese (K/M/G)"
|
||||
@@ -3778,13 +3796,10 @@ msgstr "Avvisa 5 giorni prima della data di scadenza dell'account."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"Quota per questo account, contata dal momento in cui è impostata. In byte, "
|
||||
"opzionalmente seguito da K,M,G.<br />Avvisa quando raggiunge 0, controllato "
|
||||
"ogni pochi minuti."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4772,6 +4787,12 @@ msgstr "Elimina tutto"
|
||||
msgid "Retry all"
|
||||
msgstr "Riprova tutto"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Recupera NZB da URL"
|
||||
@@ -5028,6 +5049,11 @@ msgstr "Esci da SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Avvia procedura guidata"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "Ripristina backup"
|
||||
|
||||
@@ -36,6 +36,13 @@ msgstr "Kunne ikke starte webgrensesnittet"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Kan ikke finne webmal: %s, prøver standardmal"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -325,6 +332,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kvote oppbrukt, setter nedlasting på pause"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvote"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Feil parameter"
|
||||
@@ -726,15 +747,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Bruker logget inn i webgrensesnitt"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Bruker pålogget"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -751,6 +763,15 @@ msgstr ""
|
||||
"API-nøkkel er feil, bruk API-nøkkel fra Konfigurasjon->Generelt i ditt "
|
||||
"tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Bruker logget inn i webgrensesnitt"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Bruker pålogget"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1069,10 +1090,6 @@ msgstr "Utpakking mislyktes, skrivefeil eller er disken full?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Utpakking feilet, stien er for lang"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Utpakking mislyktes, arkivet krever passord"
|
||||
@@ -2352,6 +2369,11 @@ msgstr "Navn"
|
||||
msgid "Retry"
|
||||
msgstr "Prøv igjen"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3531,10 +3553,6 @@ msgstr "Postprosessering"
|
||||
msgid "Naming"
|
||||
msgstr "Filnavn"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvote"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Hvor mye can lastes ned denne måneden (K/M/G)"
|
||||
@@ -3642,9 +3660,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4598,6 +4616,12 @@ msgstr "Ta bort alle"
|
||||
msgid "Retry all"
|
||||
msgstr "Prøv alle på nytt"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Hent NZB fra URL"
|
||||
@@ -4848,6 +4872,11 @@ msgstr "Avslutt SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Start Veiviser"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -38,6 +38,13 @@ msgstr "Webinterface kan niet gestart worden"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Websjabloon %s niet te vinden; het standaardsjabloon wordt gebruikt."
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -351,6 +358,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Quotum verbruikt, download is gestopt"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quotum"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Incorrecte parameter"
|
||||
@@ -781,15 +802,6 @@ msgstr "Verbinding geweigerd van: "
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Verbinding met hostnaam \"%s\" geweigerd van:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Gebruiker heeft ingelogd"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Gebruiker ingelogd"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -806,6 +818,15 @@ msgstr ""
|
||||
"API-sleutel incorrect; vul de API-sleutel van 'Configuratie' => 'Algemeen' "
|
||||
"in bij het externe programma:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Gebruiker heeft ingelogd"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Gebruiker ingelogd"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1131,10 +1152,6 @@ msgstr "Uitpakken mislukt, schrijffout of schijf vol?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr "Uitpakken mislukt, de schijf is vol"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Uitpakken mislukt, bestandspad is te lang"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Uitpakken mislukt, archief vereist wachtwoord"
|
||||
@@ -2425,6 +2442,11 @@ msgstr "Naam"
|
||||
msgid "Retry"
|
||||
msgstr "Opnieuw"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3665,10 +3687,6 @@ msgstr "Nabewerking"
|
||||
msgid "Naming"
|
||||
msgstr "Naamgeving"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quotum"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Hoeval mag deze maand worden gedownload (K/M/G)"
|
||||
@@ -3780,14 +3798,10 @@ msgstr "Ontvang 5 dagen voor de verloopdatum een waarschuwing."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"Quotum voor dit account, wordt geteld vanaf het moment dat het voor het "
|
||||
"eerst ingesteld wordt. In bytes, in K,M,G notatie.<br />Er wordt een "
|
||||
"waarschuwing gegeven als het quotum bereikt is, dit wordt elke paar minuten "
|
||||
"gecontroleerd."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4770,6 +4784,12 @@ msgstr "Alles wissen"
|
||||
msgid "Retry all"
|
||||
msgstr "Alles opnieuw proberen"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Haal NZB op via URL"
|
||||
@@ -5025,6 +5045,11 @@ msgstr "Stop SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Wizard starten"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "Backup herstellen"
|
||||
|
||||
@@ -36,6 +36,13 @@ msgstr "Nie udało się uruchomić interfejsu WWW"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Nie znaleziono szablonu: %s, próbuję użyć standardowego szablonu"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -324,6 +331,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Przekroczono limit, wstrzymywanie pobierania"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Limit pobierania"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Błędny parametr"
|
||||
@@ -729,15 +750,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -754,6 +766,15 @@ msgstr ""
|
||||
"Klucz API jest nieprawidłowy, użyj klucza API z sekcji Konfiguracja->Ogólne "
|
||||
"w zewnętrznym programie:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1072,10 +1093,6 @@ msgstr "Rozpakowywanie nie powiodło się, błąd zapisu lub zapełniony dysk?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Rozpakowywanie nie powiodło się, zbyt długa ścieżka"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Rozpakowywanie nie powiodło się, archiwum wymaga podania hasła"
|
||||
@@ -2361,6 +2378,11 @@ msgstr "Nazwa"
|
||||
msgid "Retry"
|
||||
msgstr "Ponów"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3542,10 +3564,6 @@ msgstr "Przetwarzanie końcowe"
|
||||
msgid "Naming"
|
||||
msgstr "Nazwy"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Limit pobierania"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Ile danych można pobrać w miesiącu (K/M/G)"
|
||||
@@ -3654,9 +3672,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4610,6 +4628,12 @@ msgstr "Usuń wszystko"
|
||||
msgid "Retry all"
|
||||
msgstr "Ponów wszystkie"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Pobierz NZB z URL"
|
||||
@@ -4858,6 +4882,11 @@ msgstr "Wyjście z SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Uruchom kreatora konfiguracji"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -38,6 +38,13 @@ msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr ""
|
||||
"Não foi possível encontrar o template web: %s. Tentando o template padrão"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -336,6 +343,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Quota esgotada, pausando o download"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parâmetro incorreto"
|
||||
@@ -741,15 +762,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -766,6 +778,15 @@ msgstr ""
|
||||
"Chave de API incorreta. Use a chave de API de Configuração->Geral em seu "
|
||||
"programa de terceiros:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1084,10 +1105,6 @@ msgstr "A descompactação falhou. Erro de escrita ou disco cheio?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Descompactação falhou, o caminho é muito extenso"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "A descompactação falhou. O arquivo exige uma senha"
|
||||
@@ -2372,6 +2389,11 @@ msgstr "Nome"
|
||||
msgid "Retry"
|
||||
msgstr "Repetir"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3554,10 +3576,6 @@ msgstr "Pós-processamento"
|
||||
msgid "Naming"
|
||||
msgstr "Nomeando"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Quota"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Quanto pode ser baixado neste mês (K/M/G)"
|
||||
@@ -3665,9 +3683,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4621,6 +4639,12 @@ msgstr "Excluir Todos"
|
||||
msgid "Retry all"
|
||||
msgstr "Repetir todos"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Buscar NZB de uma URL"
|
||||
@@ -4869,6 +4893,11 @@ msgstr "Sair do SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Iniciar o Assistente"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -37,6 +37,13 @@ msgstr "Pornirea interfeţei-web nereuşită"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Nu se poate găsi şablon web:%s, se încearcă şablon standard"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -340,6 +347,20 @@ msgstr "Sarcina „%s” este probabil criptată: „parolă” în fișierul
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Cotă epuizată, întrerupem descărcarea"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Cotă"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parametru Incorect"
|
||||
@@ -749,15 +770,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Conectare refuzată cu gazda „%s” de la:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utilizatorul s-a autentificat în interfața web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utilizator logat"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -774,6 +786,15 @@ msgstr ""
|
||||
"Cheie API incorectă, Folosiţi cheia api din Configurare->General în "
|
||||
"programul dumneavoastră terţ:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Utilizatorul s-a autentificat în interfața web"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Utilizator logat"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1097,10 +1118,6 @@ msgstr "Dezarhivare nereuşită, eroare scriere sau disc plin?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Dezarhivare eșuată, calea este prea lungă"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Dezarhivare nereuşită, arhiva necesită o parolă"
|
||||
@@ -2390,6 +2407,11 @@ msgstr "Nume"
|
||||
msgid "Retry"
|
||||
msgstr "Reîncearcă"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3573,10 +3595,6 @@ msgstr "Post procesare"
|
||||
msgid "Naming"
|
||||
msgstr "Redenumire"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Cotă"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Cât de mult poate fi descărcat în acestă lună (K/M/G)"
|
||||
@@ -3685,9 +3703,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4640,6 +4658,12 @@ msgstr "Șterge tot"
|
||||
msgid "Retry all"
|
||||
msgstr "Reîncearcă toate"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Descarcă NZB din URL"
|
||||
@@ -4891,6 +4915,11 @@ msgstr "Închide SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Porneşte Vrăjitor"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -38,6 +38,13 @@ msgstr ""
|
||||
"Не удаётся найти шаблон веб-интерфейса: %s. Выполняется попытка использовать"
|
||||
" стандартный шаблон"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -324,6 +331,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Квота исчерпана. Загрузка приостановлена"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Квота"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Неправильный параметр"
|
||||
@@ -725,15 +746,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -750,6 +762,15 @@ msgstr ""
|
||||
"Неправильный ключ API. Используйте в сторонней программе ключ API из раздела"
|
||||
" «Настройка -> Общие»:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1068,10 +1089,6 @@ msgstr "Не удалось распаковать: ошибка записи и
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Ошибка распаковки: архив защищён паролем"
|
||||
@@ -2354,6 +2371,11 @@ msgstr "Название"
|
||||
msgid "Retry"
|
||||
msgstr "Повторить"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3533,10 +3555,6 @@ msgstr "Пост-обработка"
|
||||
msgid "Naming"
|
||||
msgstr "Именование"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Квота"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Объем, который можно загрузить в месяц (K/M/G)"
|
||||
@@ -3643,9 +3661,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4604,6 +4622,12 @@ msgstr "Удалить всё"
|
||||
msgid "Retry all"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr ""
|
||||
@@ -4854,6 +4878,11 @@ msgstr ""
|
||||
msgid "Start Wizard"
|
||||
msgstr ""
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -36,6 +36,13 @@ msgstr "Neuspešno pokretanje web interfejsa"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Немогуће наћи веб модел: %s, програм покушава са стандардним моделом"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -321,6 +328,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kvota utrošena, pauziram preuzimanja"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Квота"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Погрешан параметар"
|
||||
@@ -723,15 +744,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -746,6 +758,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"API кључ је погрешан, унети у спољни програм API кључ из Подешавања->Опште:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1064,10 +1085,6 @@ msgstr "Neuspašno raspakivanje, greška u pisanju ili je disk pun?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Neuspešno raspakivanje, putanja je predugačka"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Neuspešno raspakivanje, arhiva zahteva lozinku"
|
||||
@@ -2347,6 +2364,11 @@ msgstr "Име"
|
||||
msgid "Retry"
|
||||
msgstr "Покушај опет"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3518,10 +3540,6 @@ msgstr "Накнадна обрада"
|
||||
msgid "Naming"
|
||||
msgstr "Именовање"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Квота"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Колико може да се преузме овог месеца (К/М/Г)"
|
||||
@@ -3629,9 +3647,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4583,6 +4601,12 @@ msgstr "Избриши све"
|
||||
msgid "Retry all"
|
||||
msgstr "Ponovo pokušaj sve"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Povuci NZB sa URL"
|
||||
@@ -4831,6 +4855,11 @@ msgstr "Затвори SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Покрени чаробњака"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -36,6 +36,13 @@ msgstr "Det gick inte att starta webbgränssnittet"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Hittar inte webbmall: %s, försöker med standardmall"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -321,6 +328,20 @@ msgstr ""
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Din kvot är uppnådd, pausar nerladdning"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvot"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fel parameter"
|
||||
@@ -723,15 +744,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -748,6 +760,15 @@ msgstr ""
|
||||
"API-nyckel felaktig, använd api-nyckeln från Konfiguration-> Allmänt i ditt "
|
||||
"tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr ""
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr ""
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1066,10 +1087,6 @@ msgstr "Uppackning misslyckades, skrivfel eller disken full?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Uppackning misslyckades, sökvägen är för lång"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Uppackning misslyckades, arkivet kräver lösenord"
|
||||
@@ -2353,6 +2370,11 @@ msgstr "Namn"
|
||||
msgid "Retry"
|
||||
msgstr "Försök igen"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3530,10 +3552,6 @@ msgstr "Efterbehandling"
|
||||
msgid "Naming"
|
||||
msgstr "Döpning"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kvot"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Hur mycket kan laddas ner denna månad (K/M/G)"
|
||||
@@ -3641,9 +3659,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4595,6 +4613,12 @@ msgstr "Ta bort alla"
|
||||
msgid "Retry all"
|
||||
msgstr "Starta om alla"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "Hämta NZB från URL"
|
||||
@@ -4845,6 +4869,11 @@ msgstr "Avsluta SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Starta guide"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -37,6 +37,15 @@ msgstr "Web arayüzünün başlatılması başarısız oldu"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "Web şablonu bulunamadı: %s, standart şablon denenecek"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
"OpenSSL unsuruna bağlanılamıyor, en uygun hale getirilmiş SSL bağlantı "
|
||||
"işlevleri kullanılmayacaktır."
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -353,6 +362,20 @@ msgstr "\"%s\" işi muhtemelen şifrelenmiştir: \"parola\", \"%s\" dosya ismind
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "Kota kullanıldı, indirme duraklatılıyor"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kota"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr "Kota sınır ikazı (%d%%)"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr "İndirme kota sıfırlamasının ardından devam etti"
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Yanlış parametre"
|
||||
@@ -771,15 +794,6 @@ msgstr "Şuradan bağlantı reddedildi:"
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr "Şuradan \"%s\" makine ismi ile bağlantı reddedildi:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Kullanıcı web arayüzünde oturum açtı"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Kullanıcı oturum açtı"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -796,6 +810,15 @@ msgstr ""
|
||||
"API anahtarı yanlış, 3. taraf programınızda Yapılandırma->Genel'den api "
|
||||
"anahtarını kullanın:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "Kullanıcı web arayüzünde oturum açtı"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "Kullanıcı oturum açtı"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1118,10 +1141,6 @@ msgstr "Açma başarısız oldu, yazma hatası mı yoksa disk doldu mu?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr "Açma başarısız oldu, disk dolu"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "Açma başarısız oldu, yok çok uzun"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "Açma başarısız oldu, arşiv bir parola gerektiriyor"
|
||||
@@ -2413,6 +2432,11 @@ msgstr "İsim"
|
||||
msgid "Retry"
|
||||
msgstr "Tekrar dene"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr "Tamamlanmış Olarak İşaretle ve Geçici Dosyaları Kaldır"
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3651,10 +3675,6 @@ msgstr "Post processing"
|
||||
msgid "Naming"
|
||||
msgstr "İsimlendirme"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "Kota"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "Bu ay ne kadar indirme yapılabileceği (K/M/G)"
|
||||
@@ -3762,13 +3782,14 @@ msgstr "Sonlanma tarihinden 5 gün evvel ikaz et."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
"Bu hesap için kota, bu seçeneğin ayarlanmasından itibaren hesaplanır. Bayt "
|
||||
"olarak, seçime dayalı bir şekilde K,M,G takip edebilir.<br />0 değerine "
|
||||
"ulaştığında ikazda bulun, her birkaç dakikada bir kontrol edilir."
|
||||
"Bu sunucu için, ayarlandığı zamandan itibaren hesaplanan kota. Bayt olarak, "
|
||||
"seçime dayalı bir şekilde K, M, G takip edebilir. <br /> Her birkaç "
|
||||
"dakikada bir kontrol edilir. Kota sonuna ulaşıldığında bir bildirim "
|
||||
"gönderilir."
|
||||
|
||||
#. Server's retention time in days
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4759,6 +4780,14 @@ msgstr "Tümünü Sil"
|
||||
msgid "Retry all"
|
||||
msgstr "Tümünü tekrar dene"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
"Geçici İndirme Dizini'ndeki tüm dizinleri silmek istediğinizden emin "
|
||||
"misiniz? Bu geri alınamaz!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "URL konumundan NZB al"
|
||||
@@ -5015,6 +5044,11 @@ msgstr "SABnzb'den çık"
|
||||
msgid "Start Wizard"
|
||||
msgstr "Sihirbazı Başlat"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr "Devam etmeden evvel Sunucuyu Dene unsuruna tıklayın"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr "Yedeklemeyi geri getir"
|
||||
|
||||
@@ -38,6 +38,13 @@ msgstr "web 界面启动失败"
|
||||
msgid "Cannot find web template: %s, trying standard template"
|
||||
msgstr "无法找到 web 模板: %s,正在尝试标准模板"
|
||||
|
||||
#. Warning message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
"Unable to link to OpenSSL, optimized SSL connection functions will not be "
|
||||
"used."
|
||||
msgstr ""
|
||||
|
||||
#. Error message
|
||||
#: SABnzbd.py
|
||||
msgid ""
|
||||
@@ -320,6 +327,20 @@ msgstr "任务 \"%s\" 可能受加密保护:文件名 \"%s\" 中有 \"password
|
||||
msgid "Quota spent, pausing downloading"
|
||||
msgstr "配额已耗尽,暂停下载"
|
||||
|
||||
#. Warning message - Notification
|
||||
#: sabnzbd/bpsmeter.py, sabnzbd/downloader.py, sabnzbd/notifier.py,
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "配额"
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Quota limit warning (%d%%)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/bpsmeter.py
|
||||
msgid "Downloading resumed after quota reset"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/cfg.py, sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "参数不正确"
|
||||
@@ -721,15 +742,6 @@ msgstr ""
|
||||
msgid "Refused connection with hostname \"%s\" from:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "用户已在 web 界面登录"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "用户已登录"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd"
|
||||
@@ -742,6 +754,15 @@ msgid ""
|
||||
"program:"
|
||||
msgstr "API Key 不正确,请在第三方程序中使用“配置”->“常规”中的 api key:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid "User logged in to the web interface"
|
||||
msgstr "用户已在 web 界面登录"
|
||||
|
||||
#. Notification
|
||||
#: sabnzbd/interface.py, sabnzbd/notifier.py
|
||||
msgid "User logged in"
|
||||
msgstr "用户已登录"
|
||||
|
||||
#. Warning message
|
||||
#: sabnzbd/interface.py
|
||||
msgid "Unsuccessful login attempt from %s"
|
||||
@@ -1060,10 +1081,6 @@ msgstr "解压失败,写入出错或磁盘已满?"
|
||||
msgid "Unpacking failed, disk full"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, path is too long"
|
||||
msgstr "解压失败,路径过长"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacking failed, archive requires a password"
|
||||
msgstr "解压失败,压缩文件需要密码"
|
||||
@@ -2341,6 +2358,11 @@ msgstr "名称"
|
||||
msgid "Retry"
|
||||
msgstr "重试"
|
||||
|
||||
#. History page button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Mark as Completed & Remove Temporary Files"
|
||||
msgstr ""
|
||||
|
||||
#. Queue page table, script selection menu
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Scripts"
|
||||
@@ -3478,10 +3500,6 @@ msgstr "后期处理"
|
||||
msgid "Naming"
|
||||
msgstr "命名"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Quota"
|
||||
msgstr "配额"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "How much can be downloaded this month (K/M/G)"
|
||||
msgstr "本月能下载多少数据量 (K/M/G)"
|
||||
@@ -3584,9 +3602,9 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Quota for this account, counted from the time it is set. In bytes, "
|
||||
"optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few"
|
||||
" minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally"
|
||||
" follow with K,M,G.<br />Checked every few minutes. Notification is sent "
|
||||
"when quota is spent."
|
||||
msgstr ""
|
||||
|
||||
#. Server's retention time in days
|
||||
@@ -4535,6 +4553,12 @@ msgstr "全部删除"
|
||||
msgid "Retry all"
|
||||
msgstr "全部重试"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Are you sure you want to delete all folders in your Temporary Download "
|
||||
"Folder? This cannot be undone!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Fetch NZB from URL"
|
||||
msgstr "从 URL 装取 NZB"
|
||||
@@ -4781,6 +4805,11 @@ msgstr "退出 SABnzbd"
|
||||
msgid "Start Wizard"
|
||||
msgstr "启动向导"
|
||||
|
||||
#. Tooltip for disabled Next button
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Click on Test Server before continuing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Restore backup"
|
||||
msgstr ""
|
||||
|
||||
@@ -16,6 +16,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr ""
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr ""
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#
|
||||
# Translators:
|
||||
# Pavel C <quoing_transifex@mess.cz>, 2022
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Pavel C <quoing_transifex@mess.cz>, 2022\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -18,7 +19,11 @@ msgstr ""
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
msgstr ""
|
||||
msgstr "Zobrazit poznámky k vydání"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Spustit SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
@@ -30,39 +35,42 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Služba SABnzbd pro Windows byla ve verzi SABnzbd 3.0.0 změněna.\\nBudete "
|
||||
"muset znovu nainstalovat službu SABnzbd.\\n\\nKlikněte na `OK` pro "
|
||||
"odstranění stávajících služeb nebo na `Zrušit` pro zrušení této aktualizace."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd podporuje pouze 64bitové Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd podporuje pouze Windows 8.1 a novější."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Vypínání SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr ""
|
||||
msgstr "Tímto odinstalujete SABnzbd z vašeho systému"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run at startup"
|
||||
msgstr ""
|
||||
msgstr "Spouštět při startu systému"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Desktop Icon"
|
||||
msgstr ""
|
||||
msgstr "Ikona na ploše"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "NZB File association"
|
||||
msgstr ""
|
||||
msgstr "Přiřazení souborů NZB"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Delete Program"
|
||||
msgstr ""
|
||||
msgstr "Odstranit program"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Delete Settings"
|
||||
msgstr ""
|
||||
msgstr "Smazat nastavení"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Vis udgivelsesbemærkninger"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Kør SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Støt projektet, donér!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"SABnzbd Windows-tjenesten blev ændret i SABnzbd 3.0.0.\\nDu skal "
|
||||
"geninstallere SABnzbd-tjenesten.\\n\\nKlik på `OK` for at fjerne de "
|
||||
"eksisterende tjenester eller `Annuller` for at afbryde denne opgradering."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd understøtter kun 64-bit Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd understøtter kun Windows 8.1 og nyere."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Lukker SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# HandyDandy04, 2024
|
||||
# Gjelbrim Haskaj, 2024
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Gjelbrim Haskaj, 2024\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: German (https://app.transifex.com/sabnzbd/teams/111101/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -22,6 +22,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Versionshinweise anzeigen"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "SABnzbd starten"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Bitte unterstützen Sie das Projekt durch eine Spende!"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Ester Molla Aragones <moarages@gmail.com>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Ester Molla Aragones <moarages@gmail.com>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/sabnzbd/teams/111101/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -21,6 +21,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Mostrar notas de la versión"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Ejecutar SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "¡Apoye el proyecto, haga una donación!"
|
||||
@@ -38,15 +42,15 @@ msgstr ""
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd solo es compatible con Windows de 64 bits."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd solo es compatible con Windows 8.1 y superiores."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Apagando SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/sabnzbd/teams/111101/fi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Näytä julkaisutiedot"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Käynnistä SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Tue projektia, lahjoita!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"SABnzbdin Windows-palvelu muuttui versiossa SABnzbd 3.0.0.\\nSinun täytyy "
|
||||
"asentaa SABnzbd-palvelu uudelleen.\\n\\nNapsauta `OK` poistaaksesi olemassa "
|
||||
"olevat palvelut tai `Peruuta` peruuttaaksesi tämän päivityksen."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd tukee vain 64-bittistä Windowsia."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd tukee vain Windows 8.1:tä ja uudempia."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Sammutetaan SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Fred L <88com88@gmail.com>, 2024
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Fred L <88com88@gmail.com>, 2024\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: French (https://app.transifex.com/sabnzbd/teams/111101/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -21,6 +21,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Afficher les notes de version"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Lancer SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Soutenez le projet, faites un don !"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# ION, 2024
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: ION, 2024\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -21,6 +21,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "הראה הערות שחרור"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "הפעל את SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "תמוך במיזם, תרום!"
|
||||
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Mostra note di rilascio"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Avvia SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Sostieni il progetto, dona!"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Norwegian Bokmål (https://app.transifex.com/sabnzbd/teams/111101/nb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Vis versjonsmerknader"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Kjør SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Støtt prosjektet, donèr!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"SABnzbd Windows-tjenesten ble endret i SABnzbd 3.0.0.\\nDu må installere "
|
||||
"SABnzbd-tjenesten på nytt.\\n\\nKlikk `OK` for å fjerne eksisterende "
|
||||
"tjenester eller `Avbryt` for å avbryte denne oppgraderingen."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd støtter kun 64-bit Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd støtter kun Windows 8.1 og nyere."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Slår av SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2024
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2024\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/sabnzbd/teams/111101/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Toon opmerkingen bij deze uitgave"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "SABnzbd starten"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Steun het project, doneer!"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/sabnzbd/teams/111101/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Pokaż informacje o wydaniu"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Uruchom SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Wspomóż projekt!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Usługa SABnzbd dla Windows została zmieniona w wersji SABnzbd "
|
||||
"3.0.0.\\nMusisz ponownie zainstalować usługę SABnzbd.\\n\\nKliknij `OK`, aby"
|
||||
" usunąć istniejące usługi, lub `Anuluj`, aby przerwać tę aktualizację."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd obsługuje tylko system Windows 64-bit."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd obsługuje tylko Windows 8.1 i nowsze."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Zamykanie SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Mostrar Notas de Lançamento"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Executar o SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Apoie o projeto. Faça uma doação!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"O Serviço do Windows do SABnzbd mudou no SABnzbd 3.0.0.\\nVocê precisará "
|
||||
"reinstalar o serviço do SABnzbd.\\n\\nClique em `OK` para remover os "
|
||||
"serviços existentes ou em `Cancelar` para cancelar esta atualização."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "O SABnzbd oferece suporte apenas ao Windows de 64 bits."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "O SABnzbd oferece suporte apenas ao Windows 8.1 e superior."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Encerrando o SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Romanian (https://app.transifex.com/sabnzbd/teams/111101/ro/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Arată Notele de Publicare"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Rulați SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Susţine proiectul, Donează!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Serviciul SABnzbd pentru Windows s-a schimbat în SABnzbd 3.0.0.\\nVa trebui "
|
||||
"să reinstalați serviciul SABnzbd.\\n\\nFaceți clic pe `OK` pentru a elimina "
|
||||
"serviciile existente sau pe `Anulare` pentru a anula această actualizare."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd acceptă doar Windows pe 64 de biți."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd acceptă doar Windows 8.1 și versiunile ulterioare."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Se oprește SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/sabnzbd/teams/111101/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Показать заметки о выпуске"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Запустить SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Поддержите проект. Сделайте пожертвование!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Служба SABnzbd для Windows была изменена в SABnzbd 3.0.0.\\nВам необходимо "
|
||||
"переустановить службу SABnzbd.\\n\\nНажмите «ОК», чтобы удалить существующие"
|
||||
" службы, или «Отмена», чтобы прервать это обновление."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd поддерживает только 64-битные версии Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd поддерживает только Windows 8.1 и более новые."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Завершение работы SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Serbian (https://app.transifex.com/sabnzbd/teams/111101/sr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Прикажи белешке о издању"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Покрени SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Подржите пројекат, дајте добровољан прилог!"
|
||||
@@ -30,18 +34,21 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Windows услуга за SABnzbd је измењена у верзији SABnzbd 3.0.0.\\nМораћете "
|
||||
"поново да инсталирате SABnzbd услугу.\\n\\nКликните „У реду“ да уклоните "
|
||||
"постојеће услуге или „Откажи“ да откажете ово ажурирање."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd подржава само 64‑битни Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd подржава само Windows 8.1 и новије."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Искључивање SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Petter Ramme, 2024
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Petter Ramme, 2024\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Swedish (https://app.transifex.com/sabnzbd/teams/111101/sv/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -21,6 +21,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Visa releasenoteringar"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "Kör SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Donera och stöd detta projekt!"
|
||||
@@ -32,16 +36,16 @@ msgid ""
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"SABnzbd Windows tjänsten ändrades i SABnzbd 3.0.0.\\nSABnzbd tjänsten "
|
||||
"behöver installeras om.\\n\\Välj OK` för att ta bort den befintliga "
|
||||
"behöver installeras om.\\n\\nVälj OK` för att ta bort den befintliga "
|
||||
"tjänsten, eller välj `Cancel`för att avbryta uppdateringen."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd stöder endast 64-bitars Windows."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd stöder endast Windows 8.1 och senare."
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
#
|
||||
# Translators:
|
||||
# mauron, 2025
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: mauron, 2025\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Turkish (https://app.transifex.com/sabnzbd/teams/111101/tr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +21,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Yayın Notlarını Göster"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "SABnzbd'yi çalıştır"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Projeye destek olun, Bağış Yapın!"
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -20,6 +20,10 @@ msgstr ""
|
||||
msgid "Show Release Notes"
|
||||
msgstr "显示版本说明"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Run SABnzbd"
|
||||
msgstr "运行 SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "支持该项目,捐助!"
|
||||
@@ -30,18 +34,20 @@ msgid ""
|
||||
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
|
||||
"services or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"SABnzbd 的 Windows 服务在 SABnzbd 3.0.0 中发生了变化。\\n您需要重新安装 SABnzbd "
|
||||
"服务。\\n\\n点击“确定”以移除现有服务,或点击“取消”以取消此次升级。"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports 64-bit Windows."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd 仅支持 64 位 Windows。"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "SABnzbd only supports Windows 8.1 and above."
|
||||
msgstr ""
|
||||
msgstr "SABnzbd 仅支持 Windows 8.1 及更高版本。"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Shutting down SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "正在关闭 SABnzbd"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
# Main requirements
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
apprise==1.9.3
|
||||
sabctools==8.2.5
|
||||
apprise==1.9.5
|
||||
sabctools==8.2.6
|
||||
CT3==3.4.0
|
||||
cffi==1.17.1
|
||||
pycparser==2.22
|
||||
feedparser==6.0.11
|
||||
cffi==2.0.0
|
||||
pycparser==2.23
|
||||
feedparser==6.0.12
|
||||
configobj==5.0.9
|
||||
cheroot==10.0.1
|
||||
cheroot==11.0.0
|
||||
six==1.17.0
|
||||
cherrypy==18.10.0
|
||||
jaraco.functools==4.2.1
|
||||
jaraco.functools==4.3.0
|
||||
jaraco.collections==5.0.0
|
||||
jaraco.text==3.8.1 # Newer version introduces irrelevant extra dependencies
|
||||
jaraco.classes==3.4.0
|
||||
jaraco.context==4.3.0
|
||||
more-itertools==10.7.0
|
||||
zc.lockfile==3.0.post1
|
||||
more-itertools==10.8.0
|
||||
zc.lockfile==4.0
|
||||
python-dateutil==2.9.0.post0
|
||||
tempora==5.8.1
|
||||
pytz==2025.2
|
||||
@@ -24,49 +24,50 @@ sgmllib3k==1.0.0
|
||||
portend==3.2.1
|
||||
chardet==5.2.0
|
||||
PySocks==1.7.1
|
||||
puremagic==1.29
|
||||
puremagic==1.30
|
||||
rarfile==4.2
|
||||
guessit==3.8.0
|
||||
babelfish==0.6.1
|
||||
rebulk==3.2.0
|
||||
|
||||
# Recent cryptography versions require Rust. If you run into issues compiling this
|
||||
# SABnzbd will also work with older pre-Rust versions such as cryptography==3.3.2
|
||||
cryptography==45.0.4
|
||||
cryptography==46.0.3
|
||||
|
||||
# We recommend using "orjson" as it is 2x as fast as "ujson". However, it requires
|
||||
# Rust so SABnzbd works just as well with "ujson" or the Python built in "json" module
|
||||
ujson==5.10.0
|
||||
orjson==3.10.18
|
||||
ujson==5.11.0
|
||||
orjson==3.11.3
|
||||
|
||||
# Windows system integration
|
||||
pywin32==310; sys_platform == 'win32'
|
||||
pywin32==311; sys_platform == 'win32'
|
||||
windows-toasts==1.3.1; sys_platform == 'win32'
|
||||
winrt-runtime==3.2.1; sys_platform == 'win32'
|
||||
winrt-Windows.Data.Xml.Dom==3.2.1; sys_platform == 'win32'
|
||||
winrt-Windows.Foundation==3.2.1; sys_platform == 'win32'
|
||||
winrt-Windows.Foundation.Collections==3.2.1; sys_platform == 'win32'
|
||||
winrt-Windows.UI.Notifications==3.2.1; sys_platform == 'win32'
|
||||
typing_extensions==4.14.0; sys_platform == 'win32'
|
||||
typing_extensions==4.15.0; sys_platform == 'win32'
|
||||
|
||||
# macOS system calls
|
||||
pyobjc-core==11.1; sys_platform == 'darwin'
|
||||
pyobjc-framework-Cocoa==11.1; sys_platform == 'darwin'
|
||||
pyobjc-core==12.0; sys_platform == 'darwin'
|
||||
pyobjc-framework-Cocoa==12.0; sys_platform == 'darwin'
|
||||
|
||||
# Linux notifications
|
||||
notify2==0.3.1; sys_platform != 'win32' and sys_platform != 'darwin'
|
||||
|
||||
# Apprise Requirements
|
||||
requests==2.32.4
|
||||
requests==2.32.5
|
||||
requests-oauthlib==2.0.0
|
||||
PyYAML==6.0.2
|
||||
markdown==3.8.2
|
||||
PyYAML==6.0.3
|
||||
markdown==3.9
|
||||
paho-mqtt==1.6.1 # Pinned, newer versions don't work with AppRise yet
|
||||
|
||||
# Requests Requirements
|
||||
charset_normalizer==3.4.2
|
||||
idna==3.10
|
||||
charset_normalizer==3.4.4
|
||||
idna==3.11
|
||||
urllib3==2.5.0
|
||||
certifi==2025.6.15
|
||||
certifi==2025.10.5
|
||||
oauthlib==3.3.1
|
||||
PyJWT==2.10.1
|
||||
blinker==1.9.0
|
||||
|
||||
201
sabnzbd/api.py
201
sabnzbd/api.py
@@ -480,8 +480,78 @@ def _api_add_all_orphan(value: str, kwargs: Dict[str, Union[str, List[str]]]) ->
|
||||
|
||||
|
||||
def _api_history(name: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
"""API: accepts value(=nzo_id), start, limit, search, nzo_ids"""
|
||||
"""API: Dispatcher for mode=history"""
|
||||
value = kwargs.get("value", "")
|
||||
return _api_history_table.get(name, (_api_history_default, 2))[0](value, kwargs)
|
||||
|
||||
|
||||
def _api_history_delete(value: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
"""API: accepts value(=nzo_id or special), search, archive, del_files"""
|
||||
search = kwargs.get("search")
|
||||
archive = True
|
||||
|
||||
# Only skip archive if specifically requested
|
||||
if kwargs.get("archive") == "0" or cfg.disable_archive():
|
||||
archive = False
|
||||
|
||||
special = value.lower()
|
||||
del_files = bool_conv(kwargs.get("del_files"))
|
||||
if special in ("all", "failed", "completed"):
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if special in ("all", "failed"):
|
||||
if del_files:
|
||||
del_job_files(history_db.get_failed_paths(search))
|
||||
if archive:
|
||||
history_db.archive_with_status(Status.FAILED, search)
|
||||
else:
|
||||
history_db.remove_with_status(Status.FAILED, search)
|
||||
if special in ("all", "completed"):
|
||||
if archive:
|
||||
history_db.archive_with_status(Status.COMPLETED, search)
|
||||
else:
|
||||
history_db.remove_with_status(Status.COMPLETED, search)
|
||||
history_updated()
|
||||
return report()
|
||||
elif value:
|
||||
for job in clean_comma_separated_list(value):
|
||||
if sabnzbd.PostProcessor.get_path(job):
|
||||
# This is always a permanent delete, no archiving
|
||||
sabnzbd.PostProcessor.delete(job, del_files=del_files)
|
||||
else:
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if del_files:
|
||||
remove_all(history_db.get_incomplete_path(job), recursive=True)
|
||||
if archive:
|
||||
history_db.archive(job)
|
||||
else:
|
||||
history_db.remove(job)
|
||||
history_updated()
|
||||
return report()
|
||||
else:
|
||||
return report(_MSG_NO_VALUE)
|
||||
|
||||
|
||||
def _api_history_mark_as_completed(value: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
"""API: accepts value(=nzo_id)"""
|
||||
if value:
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
for job in clean_comma_separated_list(value):
|
||||
# Get incomplete path before marking as completed
|
||||
incomplete_path = history_db.get_incomplete_path(job)
|
||||
history_db.mark_as_completed(job)
|
||||
|
||||
# Remove incomplete folder if it exists
|
||||
if incomplete_path:
|
||||
remove_all(incomplete_path, recursive=True)
|
||||
|
||||
history_updated()
|
||||
return report()
|
||||
else:
|
||||
return report(_MSG_NO_VALUE)
|
||||
|
||||
|
||||
def _api_history_default(value: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
"""API: accepts start, limit, search, failed_only, archive, cat, status, nzo_ids"""
|
||||
start = int_conv(kwargs.get("start"))
|
||||
limit = int_conv(kwargs.get("limit"))
|
||||
last_history_update = int_conv(kwargs.get("last_history_update", 0))
|
||||
@@ -491,84 +561,38 @@ def _api_history(name: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
failed_only = bool_conv(kwargs.get("failed_only"))
|
||||
nzo_ids = clean_comma_separated_list(kwargs.get("nzo_ids"))
|
||||
|
||||
archive = True
|
||||
# Do we need to send anything?
|
||||
if last_history_update == sabnzbd.LAST_HISTORY_UPDATE:
|
||||
return report(keyword="history", data=False)
|
||||
|
||||
if name == "delete":
|
||||
# Only skip archive if specifically requested
|
||||
if kwargs.get("archive") == "0" or cfg.disable_archive():
|
||||
archive = False
|
||||
if failed_only:
|
||||
# We ignore any other statuses, having both doesn't make sense
|
||||
statuses = [Status.FAILED]
|
||||
|
||||
special = value.lower()
|
||||
del_files = bool_conv(kwargs.get("del_files"))
|
||||
if special in ("all", "failed", "completed"):
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if special in ("all", "failed"):
|
||||
if del_files:
|
||||
del_job_files(history_db.get_failed_paths(search))
|
||||
if archive:
|
||||
history_db.archive_with_status(Status.FAILED, search)
|
||||
else:
|
||||
history_db.remove_with_status(Status.FAILED, search)
|
||||
if special in ("all", "completed"):
|
||||
if archive:
|
||||
history_db.archive_with_status(Status.COMPLETED, search)
|
||||
else:
|
||||
history_db.remove_with_status(Status.COMPLETED, search)
|
||||
history_updated()
|
||||
return report()
|
||||
elif value:
|
||||
for job in clean_comma_separated_list(value):
|
||||
if sabnzbd.PostProcessor.get_path(job):
|
||||
# This is always a permanent delete, no archiving
|
||||
sabnzbd.PostProcessor.delete(job, del_files=del_files)
|
||||
else:
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if del_files:
|
||||
remove_all(history_db.get_incomplete_path(job), recursive=True)
|
||||
if archive:
|
||||
history_db.archive(job)
|
||||
else:
|
||||
history_db.remove(job)
|
||||
history_updated()
|
||||
return report()
|
||||
else:
|
||||
return report(_MSG_NO_VALUE)
|
||||
elif not name:
|
||||
# Do we need to send anything?
|
||||
if last_history_update == sabnzbd.LAST_HISTORY_UPDATE:
|
||||
return report(keyword="history", data=False)
|
||||
if not limit:
|
||||
limit = cfg.history_limit()
|
||||
|
||||
if failed_only:
|
||||
# We ignore any other statuses, having both doesn't make sense
|
||||
statuses = [Status.FAILED]
|
||||
# Only show archive if specifically requested
|
||||
archive = bool(int_conv(kwargs.get("archive")))
|
||||
|
||||
if not limit:
|
||||
limit = cfg.history_limit()
|
||||
|
||||
# Only show archive if specifically requested
|
||||
if not int_conv(kwargs.get("archive")):
|
||||
archive = False
|
||||
|
||||
history = {}
|
||||
grand, month, week, day = sabnzbd.BPSMeter.get_sums()
|
||||
history["total_size"] = to_units(grand)
|
||||
history["month_size"] = to_units(month)
|
||||
history["week_size"] = to_units(week)
|
||||
history["day_size"] = to_units(day)
|
||||
history["slots"], history["ppslots"], history["noofslots"] = build_history(
|
||||
start=start,
|
||||
limit=limit,
|
||||
archive=archive,
|
||||
search=search,
|
||||
categories=categories,
|
||||
statuses=statuses,
|
||||
nzo_ids=nzo_ids,
|
||||
)
|
||||
history["last_history_update"] = sabnzbd.LAST_HISTORY_UPDATE
|
||||
history["version"] = sabnzbd.__version__
|
||||
return report(keyword="history", data=history)
|
||||
else:
|
||||
return report(_MSG_NOT_IMPLEMENTED)
|
||||
history = {}
|
||||
grand, month, week, day = sabnzbd.BPSMeter.get_sums()
|
||||
history["total_size"] = to_units(grand)
|
||||
history["month_size"] = to_units(month)
|
||||
history["week_size"] = to_units(week)
|
||||
history["day_size"] = to_units(day)
|
||||
history["slots"], history["ppslots"], history["noofslots"] = build_history(
|
||||
start=start,
|
||||
limit=limit,
|
||||
archive=archive,
|
||||
search=search,
|
||||
categories=categories,
|
||||
statuses=statuses,
|
||||
nzo_ids=nzo_ids,
|
||||
)
|
||||
history["last_history_update"] = sabnzbd.LAST_HISTORY_UPDATE
|
||||
history["version"] = sabnzbd.__version__
|
||||
return report(keyword="history", data=history)
|
||||
|
||||
|
||||
def _api_get_files(name: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
@@ -667,8 +691,11 @@ def _api_showlog(name: str, kwargs: Dict[str, Union[str, List[str]]]) -> bytes:
|
||||
log_data += b"The log includes a copy of your sabnzbd.ini with\nall usernames, passwords and API-keys removed."
|
||||
log_data += b"\n\n--------------------------------\n"
|
||||
|
||||
with open(sabnzbd.LOGFILE, "rb") as f:
|
||||
log_data += f.read()
|
||||
if sabnzbd.LOGFILE and os.path.exists(sabnzbd.LOGFILE):
|
||||
with open(sabnzbd.LOGFILE, "rb") as f:
|
||||
log_data += f.read()
|
||||
else:
|
||||
log_data += b"\nFile log disabled or not found.\n\n"
|
||||
|
||||
with open(config.get_filename(), "rb") as f:
|
||||
log_data += f.read()
|
||||
@@ -1048,6 +1075,12 @@ _api_queue_table = {
|
||||
"sort": (_api_queue_sort, 2),
|
||||
}
|
||||
|
||||
_api_history_table = {
|
||||
"delete": (_api_history_delete, 2),
|
||||
"mark_as_completed": (_api_history_mark_as_completed, 2),
|
||||
}
|
||||
|
||||
|
||||
_api_status_table = {
|
||||
"unblock_server": (_api_unblock_server, 2),
|
||||
"delete_orphan": (_api_delete_orphan, 2),
|
||||
@@ -1072,6 +1105,8 @@ def api_level(mode: str, name: str) -> int:
|
||||
"""Return access level required for this API call"""
|
||||
if mode == "queue" and name in _api_queue_table:
|
||||
return _api_queue_table[name][1]
|
||||
if mode == "history" and name in _api_history_table:
|
||||
return _api_history_table[name][1]
|
||||
if mode == "status" and name in _api_status_table:
|
||||
return _api_status_table[name][1]
|
||||
if mode == "config" and name in _api_config_table:
|
||||
@@ -1413,7 +1448,7 @@ def build_status(calculate_performance: bool = False, skip_dashboard: bool = Fal
|
||||
# build up header full of basic information
|
||||
info = build_header(trans_functions=False)
|
||||
|
||||
info["logfile"] = clip_path(sabnzbd.LOGFILE)
|
||||
info["logfile"] = clip_path(sabnzbd.LOGFILE) if sabnzbd.LOGFILE else ""
|
||||
info["weblogfile"] = clip_path(sabnzbd.WEBLOGFILE)
|
||||
info["webdir"] = clip_path(info["webdir"])
|
||||
info["loglevel"] = str(cfg.log_level())
|
||||
@@ -1610,6 +1645,9 @@ def build_queue(
|
||||
else:
|
||||
slot["avg_age"] = calc_age(nzo.avg_date)
|
||||
|
||||
# Add timestamp when the item was added to the queue
|
||||
slot["time_added"] = nzo.time_added
|
||||
|
||||
slotinfo.append(slot)
|
||||
n += 1
|
||||
|
||||
@@ -1895,7 +1933,13 @@ def build_history(
|
||||
|
||||
def add_active_history(postproc_queue: List[NzbObject], items: List[Dict[str, Any]]):
|
||||
"""Get the active history queue and add it to the existing items list"""
|
||||
nzo_ids = set([nzo["nzo_id"] for nzo in items])
|
||||
|
||||
for nzo in postproc_queue:
|
||||
# Skip already in history
|
||||
if nzo.nzo_id in nzo_ids:
|
||||
continue
|
||||
|
||||
# This output has to be the same as fetch_history!
|
||||
item = {
|
||||
"completed": int(time.time()),
|
||||
@@ -1929,6 +1973,7 @@ def add_active_history(postproc_queue: List[NzbObject], items: List[Dict[str, An
|
||||
"loaded": nzo.pp_active,
|
||||
"retry": False,
|
||||
"archive": False,
|
||||
"time_added": nzo.time_added,
|
||||
}
|
||||
# Add stage information, in the correct order
|
||||
for stage in STAGES:
|
||||
|
||||
@@ -26,9 +26,10 @@ import re
|
||||
from threading import Thread
|
||||
import ctypes
|
||||
from typing import Tuple, Optional, List
|
||||
import rarfile
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_all_passwords, match_str
|
||||
from sabnzbd.misc import get_all_passwords, match_str, SABRarFile
|
||||
from sabnzbd.filesystem import (
|
||||
set_permissions,
|
||||
clip_path,
|
||||
@@ -42,7 +43,6 @@ from sabnzbd.constants import Status, GIGI, MAX_ASSEMBLER_QUEUE
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.nzbstuff import NzbObject, NzbFile
|
||||
import sabnzbd.par2file as par2file
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
|
||||
|
||||
class Assembler(Thread):
|
||||
@@ -295,7 +295,7 @@ def check_encrypted_and_unwanted_files(nzo: NzbObject, filepath: str) -> Tuple[b
|
||||
# Is it even a rarfile?
|
||||
if rarfile.is_rarfile(filepath):
|
||||
# Open the rar
|
||||
zf = rarfile.RarFile(filepath, single_file_check=True)
|
||||
zf = SABRarFile(filepath, part_only=True)
|
||||
|
||||
# Check for encryption
|
||||
if (
|
||||
@@ -322,12 +322,7 @@ def check_encrypted_and_unwanted_files(nzo: NzbObject, filepath: str) -> Tuple[b
|
||||
logging.info('Trying password "%s" on job "%s"', password, nzo.final_name)
|
||||
try:
|
||||
zf.setpassword(password)
|
||||
except rarfile.Error:
|
||||
# On weird passwords the setpassword() will fail
|
||||
# but the actual testrar() will work
|
||||
pass
|
||||
try:
|
||||
zf.testrar()
|
||||
zf.trigger_parse()
|
||||
password_hit = password
|
||||
break
|
||||
except rarfile.RarWrongPassword:
|
||||
|
||||
@@ -122,6 +122,7 @@ class BPSMeter:
|
||||
"q_hour",
|
||||
"q_minute",
|
||||
"quota_enabled",
|
||||
"quota_notifications_sent",
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
@@ -161,6 +162,7 @@ class BPSMeter:
|
||||
self.q_hour = 0 # Quota reset hour
|
||||
self.q_minute = 0 # Quota reset minute
|
||||
self.quota_enabled: bool = True # Scheduled quota enable/disable
|
||||
self.quota_notifications_sent: int = 0 # Track highest quota threshold that has been notified
|
||||
|
||||
def save(self):
|
||||
"""Save admin to disk"""
|
||||
@@ -323,10 +325,7 @@ class BPSMeter:
|
||||
# Quota check
|
||||
if self.have_quota and self.quota_enabled:
|
||||
self.left -= self.sum_cached_amount
|
||||
if self.left <= 0.0:
|
||||
if not sabnzbd.Downloader.paused:
|
||||
sabnzbd.Downloader.pause()
|
||||
logging.warning(T("Quota spent, pausing downloading"))
|
||||
self.check_quota()
|
||||
|
||||
# Speedometer
|
||||
try:
|
||||
@@ -431,15 +430,47 @@ class BPSMeter:
|
||||
# We record every second, but display at the user's refresh-rate
|
||||
return self.bps_list[::refresh_rate]
|
||||
|
||||
def check_quota(self):
|
||||
"""Pause the queue when all quota is spent
|
||||
Notify at specific quota usages (75%, 90%, 100%)
|
||||
"""
|
||||
if self.left <= 0.0:
|
||||
if not sabnzbd.Downloader.paused:
|
||||
sabnzbd.Downloader.pause()
|
||||
logging.warning(T("Quota spent, pausing downloading"))
|
||||
|
||||
# Guard against zero division
|
||||
if self.quota:
|
||||
# Check for quota notifications (75%, 90%, 100%)
|
||||
# Only send notification for the highest applicable threshold that hasn't been notified yet
|
||||
used_percentage = ((self.quota - self.left) / self.quota) * 100
|
||||
if used_percentage >= 100 and self.quota_notifications_sent < 100:
|
||||
sabnzbd.notifier.send_notification(T("Quota"), T("Quota spent, pausing downloading"), "quota")
|
||||
elif used_percentage >= 90 and self.quota_notifications_sent < 90:
|
||||
sabnzbd.notifier.send_notification(
|
||||
T("Quota"),
|
||||
T("Quota limit warning (%d%%)") % used_percentage,
|
||||
"quota",
|
||||
)
|
||||
elif used_percentage >= 75 and self.quota_notifications_sent < 75:
|
||||
sabnzbd.notifier.send_notification(
|
||||
T("Quota"),
|
||||
T("Quota limit warning (%d%%)") % used_percentage,
|
||||
"quota",
|
||||
)
|
||||
self.quota_notifications_sent = used_percentage
|
||||
|
||||
def reset_quota(self, force: bool = False):
|
||||
"""Check if it's time to reset the quota, optionally resuming
|
||||
Return True, when still paused or should be paused
|
||||
"""
|
||||
if force or (self.have_quota and time.time() > (self.q_time - 50)):
|
||||
self.quota = self.left = cfg.quota_size.get_float()
|
||||
self.quota_notifications_sent = 0
|
||||
logging.info("Quota was reset to %s", self.quota)
|
||||
if cfg.quota_resume():
|
||||
logging.info("Auto-resume due to quota reset")
|
||||
sabnzbd.notifier.send_notification(T("Quota"), T("Downloading resumed after quota reset"), "quota")
|
||||
sabnzbd.Downloader.resume()
|
||||
self.next_reset()
|
||||
return False
|
||||
|
||||
@@ -531,6 +531,7 @@ switchinterval = OptionNumber("misc", "switchinterval", 0.005, minval=0.001)
|
||||
ssdp_broadcast_interval = OptionNumber("misc", "ssdp_broadcast_interval", 15, minval=1, maxval=600)
|
||||
ext_rename_ignore = OptionList("misc", "ext_rename_ignore", validation=lower_case_ext)
|
||||
unrar_parameters = OptionStr("misc", "unrar_parameters", validation=supported_unrar_parameters)
|
||||
outgoing_nntp_ip = OptionStr("misc", "outgoing_nntp_ip")
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -558,6 +559,7 @@ ncenter_prio_pp = OptionBool("ncenter", "ncenter_prio_pp", False)
|
||||
ncenter_prio_complete = OptionBool("ncenter", "ncenter_prio_complete", True)
|
||||
ncenter_prio_failed = OptionBool("ncenter", "ncenter_prio_failed", True)
|
||||
ncenter_prio_disk_full = OptionBool("ncenter", "ncenter_prio_disk_full", True)
|
||||
ncenter_prio_quota = OptionBool("ncenter", "ncenter_prio_quota", True)
|
||||
ncenter_prio_new_login = OptionBool("ncenter", "ncenter_prio_new_login", False)
|
||||
ncenter_prio_warning = OptionBool("ncenter", "ncenter_prio_warning", False)
|
||||
ncenter_prio_error = OptionBool("ncenter", "ncenter_prio_error", False)
|
||||
@@ -574,6 +576,7 @@ acenter_prio_pp = OptionBool("acenter", "acenter_prio_pp", False)
|
||||
acenter_prio_complete = OptionBool("acenter", "acenter_prio_complete", True)
|
||||
acenter_prio_failed = OptionBool("acenter", "acenter_prio_failed", True)
|
||||
acenter_prio_disk_full = OptionBool("acenter", "acenter_prio_disk_full", True)
|
||||
acenter_prio_quota = OptionBool("acenter", "acenter_prio_quota", True)
|
||||
acenter_prio_new_login = OptionBool("acenter", "acenter_prio_new_login", False)
|
||||
acenter_prio_warning = OptionBool("acenter", "acenter_prio_warning", False)
|
||||
acenter_prio_error = OptionBool("acenter", "acenter_prio_error", False)
|
||||
@@ -590,6 +593,7 @@ ntfosd_prio_pp = OptionBool("ntfosd", "ntfosd_prio_pp", False)
|
||||
ntfosd_prio_complete = OptionBool("ntfosd", "ntfosd_prio_complete", True)
|
||||
ntfosd_prio_failed = OptionBool("ntfosd", "ntfosd_prio_failed", True)
|
||||
ntfosd_prio_disk_full = OptionBool("ntfosd", "ntfosd_prio_disk_full", True)
|
||||
ntfosd_prio_quota = OptionBool("ntfosd", "ntfosd_prio_quota", True)
|
||||
ntfosd_prio_new_login = OptionBool("ntfosd", "ntfosd_prio_new_login", False)
|
||||
ntfosd_prio_warning = OptionBool("ntfosd", "ntfosd_prio_warning", False)
|
||||
ntfosd_prio_error = OptionBool("ntfosd", "ntfosd_prio_error", False)
|
||||
@@ -607,6 +611,7 @@ prowl_prio_pp = OptionNumber("prowl", "prowl_prio_pp", -3)
|
||||
prowl_prio_complete = OptionNumber("prowl", "prowl_prio_complete", 0)
|
||||
prowl_prio_failed = OptionNumber("prowl", "prowl_prio_failed", 1)
|
||||
prowl_prio_disk_full = OptionNumber("prowl", "prowl_prio_disk_full", 1)
|
||||
prowl_prio_quota = OptionNumber("prowl", "prowl_prio_quota", 0)
|
||||
prowl_prio_new_login = OptionNumber("prowl", "prowl_prio_new_login", -3)
|
||||
prowl_prio_warning = OptionNumber("prowl", "prowl_prio_warning", -3)
|
||||
prowl_prio_error = OptionNumber("prowl", "prowl_prio_error", -3)
|
||||
@@ -628,6 +633,7 @@ pushover_prio_pp = OptionNumber("pushover", "pushover_prio_pp", -3)
|
||||
pushover_prio_complete = OptionNumber("pushover", "pushover_prio_complete", -1)
|
||||
pushover_prio_failed = OptionNumber("pushover", "pushover_prio_failed", -1)
|
||||
pushover_prio_disk_full = OptionNumber("pushover", "pushover_prio_disk_full", 1)
|
||||
pushover_prio_quota = OptionNumber("pushover", "pushover_prio_quota", -1)
|
||||
pushover_prio_new_login = OptionNumber("pushover", "pushover_prio_new_login", -3)
|
||||
pushover_prio_warning = OptionNumber("pushover", "pushover_prio_warning", 1)
|
||||
pushover_prio_error = OptionNumber("pushover", "pushover_prio_error", 1)
|
||||
@@ -646,6 +652,7 @@ pushbullet_prio_pp = OptionBool("pushbullet", "pushbullet_prio_pp", False)
|
||||
pushbullet_prio_complete = OptionBool("pushbullet", "pushbullet_prio_complete", True)
|
||||
pushbullet_prio_failed = OptionBool("pushbullet", "pushbullet_prio_failed", True)
|
||||
pushbullet_prio_disk_full = OptionBool("pushbullet", "pushbullet_prio_disk_full", True)
|
||||
pushbullet_prio_quota = OptionBool("pushbullet", "pushbullet_prio_quota", True)
|
||||
pushbullet_prio_new_login = OptionBool("pushbullet", "pushbullet_prio_new_login", False)
|
||||
pushbullet_prio_warning = OptionBool("pushbullet", "pushbullet_prio_warning", False)
|
||||
pushbullet_prio_error = OptionBool("pushbullet", "pushbullet_prio_error", False)
|
||||
@@ -670,6 +677,8 @@ apprise_target_failed = OptionStr("apprise", "apprise_target_failed")
|
||||
apprise_target_failed_enable = OptionBool("apprise", "apprise_target_failed_enable", True)
|
||||
apprise_target_disk_full = OptionStr("apprise", "apprise_target_disk_full")
|
||||
apprise_target_disk_full_enable = OptionBool("apprise", "apprise_target_disk_full_enable", False)
|
||||
apprise_target_quota = OptionStr("apprise", "apprise_target_quota")
|
||||
apprise_target_quota_enable = OptionBool("apprise", "apprise_target_quota_enable", True)
|
||||
apprise_target_new_login = OptionStr("apprise", "apprise_target_new_login")
|
||||
apprise_target_new_login_enable = OptionBool("apprise", "apprise_target_new_login_enable", True)
|
||||
apprise_target_warning = OptionStr("apprise", "apprise_target_warning")
|
||||
@@ -693,6 +702,7 @@ nscript_prio_pp = OptionBool("nscript", "nscript_prio_pp", False)
|
||||
nscript_prio_complete = OptionBool("nscript", "nscript_prio_complete", True)
|
||||
nscript_prio_failed = OptionBool("nscript", "nscript_prio_failed", True)
|
||||
nscript_prio_disk_full = OptionBool("nscript", "nscript_prio_disk_full", True)
|
||||
nscript_prio_quota = OptionBool("nscript", "nscript_prio_quota", True)
|
||||
nscript_prio_new_login = OptionBool("nscript", "nscript_prio_new_login", False)
|
||||
nscript_prio_warning = OptionBool("nscript", "nscript_prio_warning", False)
|
||||
nscript_prio_error = OptionBool("nscript", "nscript_prio_error", False)
|
||||
|
||||
@@ -50,7 +50,7 @@ RENAMES_FILE = "__renames__"
|
||||
ATTRIB_FILE = "SABnzbd_attrib"
|
||||
REPAIR_REQUEST = "repair-all.sab"
|
||||
|
||||
SABCTOOLS_VERSION_REQUIRED = "8.2.5"
|
||||
SABCTOOLS_VERSION_REQUIRED = "8.2.6"
|
||||
|
||||
DB_HISTORY_VERSION = 1
|
||||
DB_HISTORY_NAME = "history%s.db" % DB_HISTORY_VERSION
|
||||
@@ -180,6 +180,7 @@ class DuplicateStatus:
|
||||
|
||||
class AddNzbFileResult:
|
||||
RETRY = "Retry" # File could not be read
|
||||
ERROR = "Error" # Rejected as duplicate, by pre-queue script or other failure to process file
|
||||
ERROR = "Error" # Rejected as duplicate or other failure to process file
|
||||
PREQUEUE_REJECTED = "Pre-queue rejected" # Rejected by pre-queue script
|
||||
OK = "OK" # Added to queue
|
||||
NO_FILES_FOUND = "No files found" # Malformed or might not be an NZB file
|
||||
|
||||
@@ -110,6 +110,10 @@ class HistoryDB:
|
||||
_ = self.execute("PRAGMA user_version = 4;") and self.execute(
|
||||
"ALTER TABLE history ADD COLUMN archive INTEGER;"
|
||||
)
|
||||
if version < 5:
|
||||
_ = self.execute("PRAGMA user_version = 5;") and self.execute(
|
||||
"ALTER TABLE history ADD COLUMN time_added INTEGER;"
|
||||
)
|
||||
|
||||
HistoryDB.startup_done = True
|
||||
|
||||
@@ -187,11 +191,12 @@ class HistoryDB:
|
||||
"md5sum" TEXT,
|
||||
"password" TEXT,
|
||||
"duplicate_key" TEXT,
|
||||
"archive" INTEGER
|
||||
"archive" INTEGER,
|
||||
"time_added" INTEGER
|
||||
)
|
||||
"""
|
||||
)
|
||||
self.execute("PRAGMA user_version = 4;")
|
||||
self.execute("PRAGMA user_version = 5;")
|
||||
|
||||
def close(self):
|
||||
"""Close database connection"""
|
||||
@@ -227,6 +232,11 @@ class HistoryDB:
|
||||
logging.info("Removing all jobs with status=%s", status)
|
||||
self.execute("""DELETE FROM history WHERE name LIKE ? AND status = ?""", (search, status))
|
||||
|
||||
def mark_as_completed(self, job: str):
|
||||
"""Mark a job as completed in the history"""
|
||||
self.execute("""UPDATE history SET status = ? WHERE nzo_id = ?""", (Status.COMPLETED, job))
|
||||
logging.info("[%s] Marked job %s as completed", caller_name(), job)
|
||||
|
||||
def get_failed_paths(self, search: Optional[str] = None) -> List[str]:
|
||||
"""Return list of all storage paths of failed jobs (may contain non-existing or empty paths)"""
|
||||
search = convert_search(search)
|
||||
@@ -293,8 +303,8 @@ class HistoryDB:
|
||||
self.execute(
|
||||
"""INSERT INTO history (completed, name, nzb_name, category, pp, script, report,
|
||||
url, status, nzo_id, storage, path, script_log, script_line, download_time, postproc_time, stage_log,
|
||||
downloaded, fail_message, url_info, bytes, duplicate_key, md5sum, password)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
downloaded, fail_message, url_info, bytes, duplicate_key, md5sum, password, time_added)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
||||
t,
|
||||
)
|
||||
logging.info("Added job %s to history", nzo.final_name)
|
||||
@@ -540,6 +550,7 @@ def build_history_info(nzo, workdir_complete: str, postproc_time: int, script_ou
|
||||
nzo.duplicate_key,
|
||||
nzo.md5sum,
|
||||
nzo.correct_password,
|
||||
nzo.time_added,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -64,22 +64,52 @@ def NzbQueueLocker(func: Callable):
|
||||
return call_func
|
||||
|
||||
|
||||
def cache_maintainer(clear_time: int):
|
||||
def conditional_cache(cache_time: int):
|
||||
"""
|
||||
A function decorator that clears functools.cache or functools.lru_cache clear_time seconds
|
||||
:param clear_time: In seconds, how often to clear cache (only checks when called)
|
||||
A decorator that caches function results for a specified time, but only if the result is not empty.
|
||||
Empty results (None, empty collections, empty strings, False, 0) are not cached.
|
||||
If a keyword argument of `force=True` is used, the cache is skipped.
|
||||
|
||||
Unhashable types (such as List) can not be used as an input to the wrapped function in the current implementation!
|
||||
|
||||
:param cache_time: Time in seconds to cache non-empty results
|
||||
"""
|
||||
|
||||
def inner(func):
|
||||
def decorator(func):
|
||||
cache = {}
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
if hasattr(func, "next_clear"):
|
||||
if time.time() > func.next_clear or kwargs.get("force"):
|
||||
func.cache_clear()
|
||||
func.next_clear = time.time() + clear_time
|
||||
else:
|
||||
func.next_clear = time.time() + clear_time
|
||||
return func(*args, **kwargs)
|
||||
current_time = time.time()
|
||||
|
||||
# Create cache key using functools._make_key
|
||||
try:
|
||||
key = functools._make_key(args, kwargs, typed=False)
|
||||
# Make sure it's a hashable to be used as key, this changed in Python 3.14
|
||||
hash(key)
|
||||
except TypeError:
|
||||
# If args/kwargs aren't hashable, skip caching entirely
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# Allow force kward to skip cache
|
||||
if not kwargs.get("force"):
|
||||
# Check if we have a valid cached result
|
||||
if key in cache:
|
||||
cached_result, timestamp = cache[key]
|
||||
if current_time - timestamp < cache_time:
|
||||
return cached_result
|
||||
# Cache entry expired, remove it
|
||||
del cache[key]
|
||||
|
||||
# Call the original function
|
||||
result = func(*args, **kwargs)
|
||||
|
||||
# Only cache non-empty results
|
||||
# This excludes None, [], {}, "", 0, False, etc.
|
||||
if result:
|
||||
cache[key] = (result, current_time)
|
||||
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return inner
|
||||
return decorator
|
||||
|
||||
@@ -30,13 +30,14 @@ from typing import Optional, Dict, List, Tuple
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import int_conv, format_time_string, build_and_run_command
|
||||
from sabnzbd.filesystem import long_path, remove_all, real_path, remove_file, get_basename
|
||||
from sabnzbd.filesystem import remove_all, real_path, remove_file, get_basename, clip_path
|
||||
from sabnzbd.nzbstuff import NzbObject, NzbFile
|
||||
from sabnzbd.encoding import platform_btou
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.newsunpack import RAR_EXTRACTFROM_RE, RAR_EXTRACTED_RE, rar_volumelist, add_time_left
|
||||
from sabnzbd.postproc import prepare_extraction_path
|
||||
from sabnzbd.utils.rarfile import RarFile
|
||||
from sabnzbd.misc import SABRarFile
|
||||
import rarfile
|
||||
from sabnzbd.utils.diskspeed import diskspeedmeasure
|
||||
|
||||
# Need a lock to make sure start and stop is handled correctly
|
||||
@@ -415,40 +416,24 @@ class DirectUnpacker(threading.Thread):
|
||||
|
||||
# Generate command
|
||||
rarfile_path = os.path.join(self.nzo.download_path, self.rarfile_nzf.filename)
|
||||
if sabnzbd.WINDOWS:
|
||||
# On Windows, UnRar uses a custom argument parser
|
||||
# See: https://github.com/sabnzbd/sabnzbd/issues/1043
|
||||
# The -scf forces the output to be UTF8
|
||||
command = [
|
||||
sabnzbd.newsunpack.RAR_COMMAND,
|
||||
action,
|
||||
"-vp",
|
||||
"-idp",
|
||||
"-scf",
|
||||
"-o+",
|
||||
"-ai",
|
||||
password_command,
|
||||
rarfile_path,
|
||||
"%s\\" % long_path(extraction_path),
|
||||
]
|
||||
else:
|
||||
# The -scf forces the output to be UTF8
|
||||
command = [
|
||||
sabnzbd.newsunpack.RAR_COMMAND,
|
||||
action,
|
||||
"-vp",
|
||||
"-idp",
|
||||
"-scf",
|
||||
"-o+",
|
||||
"-ai",
|
||||
password_command,
|
||||
rarfile_path,
|
||||
"%s/" % extraction_path,
|
||||
]
|
||||
|
||||
# The -scf forces the output to be UTF8
|
||||
command = [
|
||||
sabnzbd.newsunpack.RAR_COMMAND,
|
||||
action,
|
||||
"-vp",
|
||||
"-idp",
|
||||
"-scf",
|
||||
"-o+",
|
||||
"-ai",
|
||||
password_command,
|
||||
rarfile_path,
|
||||
clip_path(extraction_path),
|
||||
]
|
||||
|
||||
if cfg.ignore_unrar_dates():
|
||||
command.insert(3, "-tsm-")
|
||||
if unrar_parameters := cfg.unrar_parameters().strip().split():
|
||||
if unrar_parameters := cfg.unrar_parameters().split():
|
||||
for param in unrar_parameters:
|
||||
command.insert(-2, param)
|
||||
|
||||
@@ -456,6 +441,8 @@ class DirectUnpacker(threading.Thread):
|
||||
self.cur_volume = 1
|
||||
|
||||
# Need to disable buffer to have direct feedback
|
||||
# On Windows, UnRar uses a custom argument parser
|
||||
# See: https://github.com/sabnzbd/sabnzbd/issues/1043
|
||||
self.active_instance = build_and_run_command(
|
||||
command, windows_unrar_command=True, text_mode=False, stdin=subprocess.PIPE
|
||||
)
|
||||
@@ -508,8 +495,8 @@ class DirectUnpacker(threading.Thread):
|
||||
if one_folder:
|
||||
# RarFile can fail for mysterious reasons
|
||||
try:
|
||||
rar_contents = RarFile(
|
||||
os.path.join(self.nzo.download_path, rarfile_nzf.filename), single_file_check=True
|
||||
rar_contents = SABRarFile(
|
||||
os.path.join(self.nzo.download_path, rarfile_nzf.filename), part_only=True
|
||||
).filelist()
|
||||
for rm_file in rar_contents:
|
||||
# Flat-unpack, so remove foldername from RarFile output
|
||||
|
||||
@@ -35,7 +35,7 @@ from sabnzbd.newswrapper import NewsWrapper, NNTPPermanentError
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import from_units, helpful_warning, int_conv, MultiAddQueue
|
||||
from sabnzbd.happyeyeballs import happyeyeballs, AddrInfo
|
||||
from sabnzbd.get_addrinfo import get_fastest_addrinfo, AddrInfo
|
||||
from sabnzbd.constants import SOFT_QUEUE_LIMIT
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ class Server:
|
||||
self.host: str = host
|
||||
self.port: int = port
|
||||
self.timeout: int = timeout
|
||||
self.threads: int = threads
|
||||
self.threads: int = threads # Total number of configured connections, not dynamic
|
||||
self.priority: int = priority
|
||||
self.ssl: bool = use_ssl
|
||||
self.ssl_verify: int = ssl_verify
|
||||
@@ -206,7 +206,7 @@ class Server:
|
||||
self.article_queue = []
|
||||
|
||||
def request_addrinfo(self):
|
||||
"""Launch async request to resolve server address and perform Happy Eyeballs.
|
||||
"""Launch async request to resolve server address and select the fastest.
|
||||
In some situations this can be slow and result in delayed starts and timeouts on connections.
|
||||
Because of this, the results will be cached in the server object."""
|
||||
if not self.request:
|
||||
@@ -214,7 +214,7 @@ class Server:
|
||||
Thread(target=self.request_addrinfo_blocking).start()
|
||||
|
||||
def request_addrinfo_blocking(self):
|
||||
"""Blocking attempt to run getaddrinfo() and Happy Eyeballs for specified server"""
|
||||
"""Blocking attempt to run getaddrinfo() and address selection for specified server"""
|
||||
logging.debug("Retrieving server address information for %s", self)
|
||||
|
||||
# Disable IPV6 if desired
|
||||
@@ -222,7 +222,7 @@ class Server:
|
||||
if not cfg.ipv6_servers():
|
||||
family = socket.AF_INET
|
||||
|
||||
self.addrinfo = happyeyeballs(self.host, self.port, self.timeout, family)
|
||||
self.addrinfo = get_fastest_addrinfo(self.host, self.port, self.timeout, family)
|
||||
if not self.addrinfo:
|
||||
self.bad_cons += self.threads
|
||||
# Notify next call to maybe_block_server
|
||||
@@ -490,7 +490,7 @@ class Downloader(Thread):
|
||||
|
||||
# Optional and active server had too many problems.
|
||||
# Disable it now and send a re-enable plan to the scheduler
|
||||
if server.optional and server.active and (server.threads < 1 or (server.bad_cons / server.threads) > 3):
|
||||
if server.optional and server.active and (server.bad_cons / server.threads) > 0.3:
|
||||
# Deactivate server
|
||||
server.bad_cons = 0
|
||||
server.deactivate()
|
||||
@@ -870,7 +870,6 @@ class Downloader(Thread):
|
||||
# Don't count this for the tries (max_art_tries) on this server
|
||||
self.__reset_nw(nw)
|
||||
self.plan_server(server, _PENALTY_TOOMANY)
|
||||
server.threads -= 1
|
||||
elif error.code in (502, 481, 482) and clues_too_many_ip(error.msg):
|
||||
# Login from (too many) different IP addresses
|
||||
errormsg = T(
|
||||
@@ -1144,6 +1143,11 @@ def check_server_quota():
|
||||
if server.quota():
|
||||
if server.quota.get_int() + server.usage_at_start() < sabnzbd.BPSMeter.grand_total.get(srv, 0):
|
||||
logging.warning(T("Server %s has used the specified quota"), server.displayname())
|
||||
sabnzbd.notifier.send_notification(
|
||||
T("Quota"),
|
||||
T("Server %s has used the specified quota") % server.displayname(),
|
||||
"quota",
|
||||
)
|
||||
server.quota.set("")
|
||||
config.save_config()
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import fnmatch
|
||||
import stat
|
||||
import ctypes
|
||||
import random
|
||||
import functools
|
||||
from typing import Union, List, Tuple, Any, Dict, Optional, BinaryIO
|
||||
|
||||
try:
|
||||
@@ -44,7 +43,7 @@ except ImportError:
|
||||
pass
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized, cache_maintainer
|
||||
from sabnzbd.decorators import synchronized, conditional_cache
|
||||
from sabnzbd.constants import (
|
||||
FUTURE_Q_FOLDER,
|
||||
JOB_ADMIN,
|
||||
@@ -55,7 +54,7 @@ from sabnzbd.constants import (
|
||||
DEX_FILE_EXTENSION_MAX,
|
||||
)
|
||||
from sabnzbd.encoding import correct_unknown_encoding, utob, limit_encoded_length
|
||||
from sabnzbd.utils import rarfile
|
||||
import rarfile
|
||||
|
||||
|
||||
# For Windows: determine executable extensions
|
||||
@@ -991,50 +990,6 @@ def remove_all(path: str, pattern: str = "*", keep_folder: bool = False, recursi
|
||||
##############################################################################
|
||||
# Diskfree
|
||||
##############################################################################
|
||||
def disk_free_macos_clib_statfs64(directory: str) -> Tuple[int, int]:
|
||||
# MacOS only!
|
||||
# direct system call to c-lib's statfs(), not python's os.statvfs()
|
||||
# because statvfs() on MacOS has a rollover at 4TB (possibly a 32bit rollover with 10bit block size)
|
||||
# See https://bugs.python.org/issue43638
|
||||
# Based on code of pudquick and blackntan
|
||||
# Input: directory.
|
||||
# Output: disksize and available space, in bytes
|
||||
|
||||
# format & parameters: on MacOS, see "man statfs", lines starting at
|
||||
# "struct statfs { /* when _DARWIN_FEATURE_64_BIT_INODE is defined */"
|
||||
class statfs64(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("f_bsize", ctypes.c_uint32),
|
||||
("f_iosize", ctypes.c_int32),
|
||||
("f_blocks", ctypes.c_uint64),
|
||||
("f_bfree", ctypes.c_uint64),
|
||||
("f_bavail", ctypes.c_uint64),
|
||||
("f_files", ctypes.c_uint64),
|
||||
("f_ffree", ctypes.c_uint64),
|
||||
("f_fsid", ctypes.c_uint64),
|
||||
("f_owner", ctypes.c_uint32),
|
||||
("f_type", ctypes.c_uint32),
|
||||
("f_flags", ctypes.c_uint32),
|
||||
("f_fssubtype", ctypes.c_uint32),
|
||||
("f_fstypename", ctypes.c_char * 16),
|
||||
("f_mntonname", ctypes.c_char * 1024),
|
||||
("f_mntfromname", ctypes.c_char * 1024),
|
||||
("f_reserved", ctypes.c_uint32 * 8),
|
||||
]
|
||||
|
||||
fs_info64 = statfs64() # set up the parameters to be filled out
|
||||
result = sabnzbd.MACOSLIBC.statfs64(
|
||||
ctypes.create_string_buffer(utob(directory)), ctypes.byref(fs_info64)
|
||||
) # fs_info64 gets filled out via the byref()
|
||||
if result == 0:
|
||||
# result = 0: "Upon successful completion, a value of 0 is returned."
|
||||
return fs_info64.f_blocks * fs_info64.f_bsize, fs_info64.f_bavail * fs_info64.f_bsize
|
||||
else:
|
||||
# result = -1: "Otherwise, -1 is returned and the global variable errno is set to indicate the error."
|
||||
logging.debug("Call to MACOSLIBC.statfs64 not successful. Value of errno is %s", ctypes.get_errno())
|
||||
return 0, 0
|
||||
|
||||
|
||||
def diskspace_base(dir_to_check: str) -> Tuple[float, float]:
|
||||
"""Return amount of free and used diskspace in GBytes"""
|
||||
# Find first folder level that exists in the path
|
||||
@@ -1049,10 +1004,6 @@ def diskspace_base(dir_to_check: str) -> Tuple[float, float]:
|
||||
return disk_size / GIGI, available / GIGI
|
||||
except Exception:
|
||||
return 0.0, 0.0
|
||||
elif sabnzbd.MACOS:
|
||||
# MacOS diskfree ... via c-lib call statfs()
|
||||
disk_size, available = disk_free_macos_clib_statfs64(dir_to_check)
|
||||
return disk_size / GIGI, available / GIGI
|
||||
elif hasattr(os, "statvfs"):
|
||||
# posix diskfree
|
||||
try:
|
||||
@@ -1072,10 +1023,9 @@ def diskspace_base(dir_to_check: str) -> Tuple[float, float]:
|
||||
return 20.0, 10.0
|
||||
|
||||
|
||||
@cache_maintainer(clear_time=10)
|
||||
@functools.lru_cache(maxsize=None)
|
||||
@conditional_cache(cache_time=10)
|
||||
def diskspace(force: bool = False) -> Dict[str, Tuple[float, float]]:
|
||||
"""Wrapper to keep results cached by cache_maintainer
|
||||
"""Wrapper to keep results cached by conditional_cache
|
||||
If called with force=True, the wrapper will clear the results"""
|
||||
return {
|
||||
"download_dir": diskspace_base(sabnzbd.cfg.download_dir.get_path()),
|
||||
@@ -1291,6 +1241,10 @@ def check_filesystem_capabilities(test_dir: str) -> bool:
|
||||
|
||||
# if not on Windows, check special chars like \ and :
|
||||
if not sabnzbd.WINDOWS and not directory_is_writable_with_file(test_dir, "sab_test \\ bla :: , bla.txt"):
|
||||
# Always enable "Make Windows Compatible"
|
||||
sabnzbd.cfg.sanitize_safe.set(True)
|
||||
|
||||
# However, external programs like unrar can still try to write them so we still warn the user
|
||||
sabnzbd.misc.helpful_warning(
|
||||
T("%s is not writable with special character filenames. This can cause problems."), test_dir
|
||||
)
|
||||
|
||||
@@ -16,31 +16,24 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
sabnzbd.happyeyeballs - Python implementation of RFC 6555 / Happy Eyeballs: find the quickest IPv4/IPv6 connection
|
||||
sabnzbd.get_addrinfo - Concurrent IP address testing: find the fastest IPv4/IPv6 connection
|
||||
"""
|
||||
|
||||
# Python implementation of RFC 6555/8305 (Happy Eyeballs): find the quickest IPv4/IPv6 connection
|
||||
# See https://tools.ietf.org/html/rfc6555
|
||||
# See https://tools.ietf.org/html/rfc8305
|
||||
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
import queue
|
||||
import functools
|
||||
from dataclasses import dataclass
|
||||
from typing import Tuple, Union, Optional
|
||||
from more_itertools import roundrobin
|
||||
from typing import Tuple, Union, Optional
|
||||
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import DEF_NETWORKING_TIMEOUT
|
||||
from sabnzbd.decorators import cache_maintainer
|
||||
from sabnzbd.decorators import conditional_cache
|
||||
|
||||
# How long to delay between connection attempts? The RFC suggests 250ms, but this is
|
||||
# quite long and might give us a slow host that just happened to be on top of the list.
|
||||
# The absolute minimum specified in RFC 8305 is 10ms, so we use that.
|
||||
CONNECTION_ATTEMPT_DELAY = 0.01
|
||||
# How often to check for connection results
|
||||
CONNECTION_RESULT_CHECK = 0.1 # 100ms
|
||||
|
||||
# While providers are afraid to add IPv6 to their standard hostnames
|
||||
# we map a number of well known hostnames to their IPv6 alternatives.
|
||||
@@ -71,6 +64,7 @@ class AddrInfo:
|
||||
sockaddr: Union[Tuple[str, int], Tuple[str, int, int, int]]
|
||||
ipaddress: str = ""
|
||||
port: int = 0
|
||||
connection_time: float = 0.0
|
||||
|
||||
def __post_init__(self):
|
||||
# For easy access
|
||||
@@ -90,25 +84,26 @@ def family_type(family) -> str:
|
||||
|
||||
|
||||
# Called by each thread
|
||||
def do_socket_connect(result_queue: queue.Queue, addrinfo: AddrInfo, timeout: int):
|
||||
"""Connect to the ip, and put the result into the queue"""
|
||||
def do_socket_connect(results_list: list, addrinfo: AddrInfo, timeout: int):
|
||||
"""Connect to the ip, and add the result with timing info to the shared list"""
|
||||
try:
|
||||
start = time.time()
|
||||
s = socket.socket(addrinfo.family, addrinfo.type)
|
||||
s.settimeout(timeout)
|
||||
try:
|
||||
s.connect(addrinfo.sockaddr)
|
||||
result_queue.put(addrinfo)
|
||||
addrinfo.connection_time = time.time() - start
|
||||
results_list.append(addrinfo)
|
||||
logging.debug(
|
||||
"Happy Eyeballs connected to %s (%s, port=%d) in %dms",
|
||||
"Connected to %s (%s, port=%d) in %dms",
|
||||
addrinfo.ipaddress,
|
||||
addrinfo.canonname,
|
||||
addrinfo.port,
|
||||
1000 * (time.time() - start),
|
||||
1000 * addrinfo.connection_time,
|
||||
)
|
||||
except socket.error:
|
||||
logging.debug(
|
||||
"Happy Eyeballs failed to connect to %s (%s, port=%d) in %dms",
|
||||
"Failed to connect to %s (%s, port=%d) in %dms",
|
||||
addrinfo.ipaddress,
|
||||
addrinfo.canonname,
|
||||
addrinfo.port,
|
||||
@@ -120,28 +115,28 @@ def do_socket_connect(result_queue: queue.Queue, addrinfo: AddrInfo, timeout: in
|
||||
pass
|
||||
|
||||
|
||||
@cache_maintainer(clear_time=10)
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def happyeyeballs(
|
||||
@conditional_cache(cache_time=60)
|
||||
def get_fastest_addrinfo(
|
||||
host: str,
|
||||
port: int,
|
||||
timeout: int = DEF_NETWORKING_TIMEOUT,
|
||||
family=socket.AF_UNSPEC,
|
||||
) -> Optional[AddrInfo]:
|
||||
"""Return the fastest result of getaddrinfo() based on RFC 6555/8305 (Happy Eyeballs),
|
||||
including IPv6 addresses if desired. Returns None in case no addresses were returned
|
||||
by getaddrinfo or if no connection could be made to any of the addresses.
|
||||
If family is specified, only that family is tried"""
|
||||
"""Return the fastest result of getaddrinfo() by testing all IP addresses concurrently.
|
||||
Tests all available IP addresses simultaneously (alternating IPv4/6) in separate threads and returns the
|
||||
connection with the shortest response time after CONNECTION_CHECK interval.
|
||||
Returns None in case no addresses were returned by getaddrinfo or if no connection
|
||||
could be made to any of the addresses. If family is specified, only that family is tried"""
|
||||
try:
|
||||
# See if we can add a IPv6 alternative
|
||||
# See if we can add an IPv6 alternative
|
||||
check_hosts = [host]
|
||||
if cfg.ipv6_staging() and host in IPV6_MAPPING:
|
||||
check_hosts.append(IPV6_MAPPING[host])
|
||||
logging.info("Added IPv6 alternative %s for host %s", IPV6_MAPPING[host], host)
|
||||
|
||||
last_canonname = ""
|
||||
ipv4_addrinfo = []
|
||||
ipv6_addrinfo = []
|
||||
last_canonname = ""
|
||||
for check_host in check_hosts:
|
||||
try:
|
||||
for addrinfo in socket.getaddrinfo(
|
||||
@@ -178,39 +173,44 @@ def happyeyeballs(
|
||||
len(ipv6_addrinfo),
|
||||
)
|
||||
|
||||
# To optimize success, the RFC states to alternate between trying the
|
||||
# IPv6 and IPv4 results, starting with IPv6 since it is the preferred method.
|
||||
result_queue: queue.Queue[AddrInfo] = queue.Queue()
|
||||
addr_tried = 0
|
||||
result: Optional[AddrInfo] = None
|
||||
if not ipv4_addrinfo and not ipv6_addrinfo:
|
||||
raise ConnectionError("No usable IP addresses found for %s" % ", ".join(check_hosts))
|
||||
|
||||
# Try IPv6 and IPv4 alternating since there is delay in starting threads
|
||||
successful_connections = []
|
||||
threads = []
|
||||
for addrinfo in roundrobin(ipv6_addrinfo, ipv4_addrinfo):
|
||||
threading.Thread(target=do_socket_connect, args=(result_queue, addrinfo, timeout), daemon=True).start()
|
||||
addr_tried += 1
|
||||
try:
|
||||
result = result_queue.get(timeout=CONNECTION_ATTEMPT_DELAY)
|
||||
break
|
||||
except queue.Empty:
|
||||
# Start a thread for the next address in the list if the previous
|
||||
# connection attempt did not complete in time or if it wasn't a success
|
||||
continue
|
||||
thread = threading.Thread(
|
||||
target=do_socket_connect,
|
||||
args=(successful_connections, addrinfo, timeout),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
# If we had no results, we might just need to give it more time
|
||||
if not result:
|
||||
try:
|
||||
# Reduce waiting time by time already spent
|
||||
result = result_queue.get(timeout=timeout - addr_tried * CONNECTION_ATTEMPT_DELAY)
|
||||
except queue.Empty:
|
||||
raise ConnectionError("No usable IP addresses found for %s" % ", ".join(check_hosts))
|
||||
# Wait for the first successful connection
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < timeout:
|
||||
time.sleep(CONNECTION_RESULT_CHECK)
|
||||
# Check if we have any successful connections
|
||||
if successful_connections:
|
||||
# Return the fastest connection
|
||||
fastest_addrinfo = min(successful_connections, key=lambda result: result.connection_time)
|
||||
logging.info(
|
||||
"Fastest connection to %s (port=%d, %s): %s (%s) in %dms (out of %d results)",
|
||||
host,
|
||||
port,
|
||||
family_type(family),
|
||||
fastest_addrinfo.ipaddress,
|
||||
fastest_addrinfo.canonname,
|
||||
1000 * fastest_addrinfo.connection_time,
|
||||
len(successful_connections),
|
||||
)
|
||||
return fastest_addrinfo
|
||||
|
||||
# If no connections succeeded within timeout
|
||||
raise ConnectionError("No usable IP addresses found for %s" % ", ".join(check_hosts))
|
||||
|
||||
logging.info(
|
||||
"Quickest IP address for %s (port=%d, %s): %s (%s)",
|
||||
host,
|
||||
port,
|
||||
family_type(family),
|
||||
result.ipaddress,
|
||||
result.canonname,
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
logging.debug("Failed Happy Eyeballs lookup: %s", e)
|
||||
logging.debug("Failed IP address lookup: %s", e)
|
||||
return None
|
||||
@@ -32,7 +32,7 @@ import socks
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg
|
||||
from sabnzbd.encoding import ubtou
|
||||
from sabnzbd.happyeyeballs import happyeyeballs, family_type
|
||||
from sabnzbd.get_addrinfo import get_fastest_addrinfo, family_type
|
||||
from sabnzbd.constants import DEF_NETWORKING_SHORT_TIMEOUT
|
||||
|
||||
|
||||
@@ -73,9 +73,11 @@ def addresslookup6(myhost):
|
||||
|
||||
|
||||
def active_socks5_proxy() -> Optional[str]:
|
||||
"""Return the active proxy"""
|
||||
if socket.socket == socks.socksocket:
|
||||
return "%s:%s" % socks.socksocket.default_proxy[1:3]
|
||||
"""Return the active proxy. And None if no proxy is set"""
|
||||
if socks.socksocket.default_proxy:
|
||||
socks5host = socks.socksocket.default_proxy[1]
|
||||
socks5port = sabnzbd.misc.int_conv(socks.socksocket.default_proxy[2], default=1080)
|
||||
return f"{socks5host}:{socks5port}"
|
||||
return None
|
||||
|
||||
|
||||
@@ -92,11 +94,21 @@ def dnslookup() -> bool:
|
||||
|
||||
|
||||
def local_ipv4() -> Optional[str]:
|
||||
"""return IPv4 address of default local LAN interface"""
|
||||
try:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s_ipv4:
|
||||
# Option: use 100.64.1.1 (IANA-Reserved IPv4 Prefix for Shared Address Space)
|
||||
s_ipv4.connect(("10.255.255.255", 80))
|
||||
ipv4 = s_ipv4.getsockname()[0]
|
||||
if not socks.socksocket.default_proxy:
|
||||
# No socks5 proxy, so we can use UDP (SOCK_DGRAM) and a non-reachable host
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s_ipv4:
|
||||
s_ipv4.connect(("10.255.255.255", 80))
|
||||
ipv4 = s_ipv4.getsockname()[0]
|
||||
else:
|
||||
# socks5 proxy set, so we must use TCP (SOCK_STREAM) and a reachable host: the proxy server
|
||||
socks5host = socks.socksocket.default_proxy[1]
|
||||
socks5port = sabnzbd.misc.int_conv(socks.socksocket.default_proxy[2], default=1080)
|
||||
logging.debug(f"Using proxy {socks5host} on port {socks5port} to determine local IPv4 address")
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s_ipv4:
|
||||
s_ipv4.connect((socks5host, socks5port))
|
||||
ipv4 = s_ipv4.getsockname()[0]
|
||||
except socket.error:
|
||||
ipv4 = None
|
||||
|
||||
@@ -109,7 +121,7 @@ def public_ip(family: int = socket.AF_UNSPEC) -> Optional[str]:
|
||||
Reports the client's public IP address (IPv4 or IPv6, if specified by family), as reported by selftest host
|
||||
"""
|
||||
start = time.time()
|
||||
if resolvehostaddress := happyeyeballs(
|
||||
if resolvehostaddress := get_fastest_addrinfo(
|
||||
sabnzbd.cfg.selftest_host(),
|
||||
port=443,
|
||||
timeout=DEF_NETWORKING_SHORT_TIMEOUT,
|
||||
|
||||
@@ -55,7 +55,7 @@ from sabnzbd.misc import (
|
||||
get_cpu_name,
|
||||
clean_comma_separated_list,
|
||||
)
|
||||
from sabnzbd.happyeyeballs import happyeyeballs
|
||||
from sabnzbd.get_addrinfo import get_fastest_addrinfo
|
||||
from sabnzbd.filesystem import (
|
||||
real_path,
|
||||
globber,
|
||||
@@ -310,9 +310,6 @@ def set_login_cookie(remove=False, remember_me=False):
|
||||
if remove:
|
||||
cherrypy.response.cookie["login_cookie"]["expires"] = 0
|
||||
cherrypy.response.cookie["login_salt"]["expires"] = 0
|
||||
else:
|
||||
# Notify about new login
|
||||
notifier.send_notification(T("User logged in"), T("User logged in to the web interface"), "new_login")
|
||||
|
||||
|
||||
def check_login_cookie():
|
||||
@@ -679,6 +676,8 @@ class LoginPage:
|
||||
set_login_cookie(remember_me=kwargs.get("remember_me", False))
|
||||
# Log the success
|
||||
logging.info("Successful login from %s", cherrypy.request.remote_label)
|
||||
# Notify about new login
|
||||
notifier.send_notification(T("User logged in"), T("User logged in to the web interface"), "new_login")
|
||||
# Redirect
|
||||
raise Raiser("/")
|
||||
elif kwargs.get("username") or kwargs.get("password"):
|
||||
@@ -912,6 +911,7 @@ SPECIAL_VALUE_LIST = (
|
||||
"selftest_host",
|
||||
"ssdp_broadcast_interval",
|
||||
"unrar_parameters",
|
||||
"outgoing_nntp_ip",
|
||||
)
|
||||
SPECIAL_LIST_LIST = (
|
||||
"rss_odd_titles",
|
||||
@@ -1174,7 +1174,7 @@ def handle_server(kwargs, root=None, new_svr=False):
|
||||
kwargs["connections"] = "1"
|
||||
|
||||
if kwargs.get("enable") == "1":
|
||||
if not happyeyeballs(
|
||||
if not get_fastest_addrinfo(
|
||||
host, int_conv(port), int_conv(kwargs.get("timeout"), default=DEF_NETWORKING_TEST_TIMEOUT)
|
||||
):
|
||||
return badParameterResponse(T('Server address "%s:%s" is not valid.') % (host, port), ajax)
|
||||
@@ -2029,6 +2029,7 @@ NOTIFY_OPTIONS = {
|
||||
"ncenter_prio_complete",
|
||||
"ncenter_prio_failed",
|
||||
"ncenter_prio_disk_full",
|
||||
"ncenter_prio_quota",
|
||||
"ncenter_prio_warning",
|
||||
"ncenter_prio_error",
|
||||
"ncenter_prio_queue_done",
|
||||
@@ -2045,6 +2046,7 @@ NOTIFY_OPTIONS = {
|
||||
"acenter_prio_complete",
|
||||
"acenter_prio_failed",
|
||||
"acenter_prio_disk_full",
|
||||
"acenter_prio_quota",
|
||||
"acenter_prio_warning",
|
||||
"acenter_prio_error",
|
||||
"acenter_prio_queue_done",
|
||||
@@ -2061,6 +2063,7 @@ NOTIFY_OPTIONS = {
|
||||
"ntfosd_prio_complete",
|
||||
"ntfosd_prio_failed",
|
||||
"ntfosd_prio_disk_full",
|
||||
"ntfosd_prio_quota",
|
||||
"ntfosd_prio_warning",
|
||||
"ntfosd_prio_error",
|
||||
"ntfosd_prio_queue_done",
|
||||
@@ -2078,6 +2081,7 @@ NOTIFY_OPTIONS = {
|
||||
"prowl_prio_complete",
|
||||
"prowl_prio_failed",
|
||||
"prowl_prio_disk_full",
|
||||
"prowl_prio_quota",
|
||||
"prowl_prio_warning",
|
||||
"prowl_prio_error",
|
||||
"prowl_prio_queue_done",
|
||||
@@ -2097,6 +2101,7 @@ NOTIFY_OPTIONS = {
|
||||
"pushover_prio_complete",
|
||||
"pushover_prio_failed",
|
||||
"pushover_prio_disk_full",
|
||||
"pushover_prio_quota",
|
||||
"pushover_prio_warning",
|
||||
"pushover_prio_error",
|
||||
"pushover_prio_queue_done",
|
||||
@@ -2117,6 +2122,7 @@ NOTIFY_OPTIONS = {
|
||||
"pushbullet_prio_complete",
|
||||
"pushbullet_prio_failed",
|
||||
"pushbullet_prio_disk_full",
|
||||
"pushbullet_prio_quota",
|
||||
"pushbullet_prio_warning",
|
||||
"pushbullet_prio_error",
|
||||
"pushbullet_prio_queue_done",
|
||||
@@ -2141,6 +2147,8 @@ NOTIFY_OPTIONS = {
|
||||
"apprise_target_failed_enable",
|
||||
"apprise_target_disk_full",
|
||||
"apprise_target_disk_full_enable",
|
||||
"apprise_target_quota",
|
||||
"apprise_target_quota_enable",
|
||||
"apprise_target_warning",
|
||||
"apprise_target_warning_enable",
|
||||
"apprise_target_error",
|
||||
@@ -2164,6 +2172,7 @@ NOTIFY_OPTIONS = {
|
||||
"nscript_prio_complete",
|
||||
"nscript_prio_failed",
|
||||
"nscript_prio_disk_full",
|
||||
"nscript_prio_quota",
|
||||
"nscript_prio_warning",
|
||||
"nscript_prio_error",
|
||||
"nscript_prio_queue_done",
|
||||
|
||||
@@ -31,7 +31,7 @@ from typing import Dict
|
||||
import sabctools
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import DEF_NETWORKING_SHORT_TIMEOUT
|
||||
from sabnzbd.happyeyeballs import happyeyeballs, family_type
|
||||
from sabnzbd.get_addrinfo import get_fastest_addrinfo, family_type
|
||||
|
||||
TEST_HOSTNAME = "sabnzbd.org"
|
||||
TEST_PORT = 443
|
||||
@@ -88,8 +88,8 @@ def internetspeed_interal(family: int = socket.AF_UNSPEC) -> float:
|
||||
context.verify_flags &= ~ssl.VERIFY_X509_STRICT
|
||||
|
||||
try:
|
||||
if not (addrinfo := happyeyeballs(TEST_HOSTNAME, TEST_PORT, DEF_NETWORKING_SHORT_TIMEOUT, family)):
|
||||
# no addrinfo from happyeyeballs, so no connection was possible
|
||||
if not (addrinfo := get_fastest_addrinfo(TEST_HOSTNAME, TEST_PORT, DEF_NETWORKING_SHORT_TIMEOUT, family)):
|
||||
# no addrinfo from get_fastest_addrinfo, so no connection was possible
|
||||
return 0.0 # no speed at all
|
||||
|
||||
for _ in range(NR_CONNECTIONS):
|
||||
|
||||
@@ -24,7 +24,6 @@ import platform
|
||||
import ssl
|
||||
import sys
|
||||
import logging
|
||||
import functools
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import re
|
||||
@@ -39,6 +38,7 @@ import html
|
||||
import ipaddress
|
||||
import socks
|
||||
import math
|
||||
import rarfile
|
||||
from threading import Thread
|
||||
from collections.abc import Iterable
|
||||
from typing import Union, Tuple, Any, AnyStr, Optional, List, Dict, Collection
|
||||
@@ -55,7 +55,7 @@ from sabnzbd.constants import (
|
||||
)
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.decorators import cache_maintainer
|
||||
from sabnzbd.decorators import conditional_cache
|
||||
from sabnzbd.encoding import ubtou, platform_btou
|
||||
from sabnzbd.filesystem import userxbit, make_script_path, remove_file
|
||||
|
||||
@@ -774,8 +774,7 @@ def get_macos_memory():
|
||||
return float(system_output.split()[1])
|
||||
|
||||
|
||||
@cache_maintainer(clear_time=3600)
|
||||
@functools.lru_cache(maxsize=None)
|
||||
@conditional_cache(cache_time=3600)
|
||||
def get_cpu_name():
|
||||
"""Find the CPU name (which needs a different method per OS), and return it
|
||||
If none found, return platform.platform()"""
|
||||
@@ -1586,3 +1585,40 @@ def convert_history_retention():
|
||||
cfg.history_retention_number.set(to_keep)
|
||||
elif to_keep < 0:
|
||||
cfg.history_retention_option.set("all-delete")
|
||||
|
||||
|
||||
##
|
||||
## SABnzbd patched rarfile classes
|
||||
## Patch for https://github.com/markokr/rarfile/issues/56#issuecomment-711146569
|
||||
##
|
||||
|
||||
|
||||
class SABRarFile(rarfile.RarFile):
|
||||
"""SABnzbd patched RarFile class with info_callback fix for multi-volume archives"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Patch RarFile-call when using `part_only`
|
||||
to store filenames inside the RAR-files"""
|
||||
if kwargs.get("part_only"):
|
||||
kwargs["info_callback"] = self.info_callback
|
||||
|
||||
# Let RarFile handle the rest!
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def info_callback(self, rar_obj: rarfile.RarInfo):
|
||||
"""Called for every RarInfo-object found"""
|
||||
# We only care about files inside the Rar
|
||||
# For Rar5 there is a separate object, for Rar3 we need to check if a filename was parsed
|
||||
if isinstance(rar_obj, (rarfile.Rar5FileInfo, rarfile.Rar3Info)) and rar_obj.filename:
|
||||
# Avoid duplicates
|
||||
if rar_obj not in self._file_parser._info_list:
|
||||
self._file_parser._info_list.append(rar_obj)
|
||||
self._file_parser._info_map[rar_obj.filename.rstrip("/")] = rar_obj
|
||||
|
||||
def filelist(self):
|
||||
"""Return list of filenames in archive."""
|
||||
return [f.filename for f in self.infolist() if not f.isdir()]
|
||||
|
||||
def trigger_parse(self):
|
||||
"""Force re-parse, wich is needed to trigger password checking logic"""
|
||||
self._parse()
|
||||
|
||||
@@ -28,11 +28,11 @@ import time
|
||||
import io
|
||||
import shutil
|
||||
import functools
|
||||
import rarfile
|
||||
from typing import Tuple, List, BinaryIO, Optional, Dict, Any, Union, Set
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.encoding import correct_unknown_encoding, ubtou
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.misc import (
|
||||
format_time_string,
|
||||
find_on_path,
|
||||
@@ -44,6 +44,7 @@ from sabnzbd.misc import (
|
||||
build_and_run_command,
|
||||
format_time_left,
|
||||
is_none,
|
||||
SABRarFile,
|
||||
)
|
||||
from sabnzbd.filesystem import (
|
||||
make_script_path,
|
||||
@@ -66,7 +67,7 @@ from sabnzbd.filesystem import (
|
||||
)
|
||||
from sabnzbd.nzbstuff import NzbObject
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import Status, JOB_ADMIN
|
||||
from sabnzbd.constants import Status
|
||||
|
||||
|
||||
# Regex globals
|
||||
@@ -105,14 +106,14 @@ def find_programs(curdir: str):
|
||||
|
||||
if sabnzbd.MACOS:
|
||||
if sabnzbd.MACOSARM64:
|
||||
# M1 (ARM64) versions
|
||||
sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, "macos/par2/arm64/par2")
|
||||
# ARM64 version of unrar
|
||||
sabnzbd.newsunpack.RAR_COMMAND = check(curdir, "macos/unrar/arm64/unrar")
|
||||
else:
|
||||
# Regular x64 versions
|
||||
sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, "macos/par2/par2")
|
||||
# Regular x64 version
|
||||
sabnzbd.newsunpack.RAR_COMMAND = check(curdir, "macos/unrar/unrar")
|
||||
# The 7zip binary is universal2
|
||||
|
||||
# The par2 and 7zip binary are universal2
|
||||
sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, "macos/par2/par2")
|
||||
sabnzbd.newsunpack.SEVENZIP_COMMAND = check(curdir, "macos/7zip/7zz")
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
@@ -647,15 +648,14 @@ def rar_extract_core(
|
||||
"""
|
||||
start = time.time()
|
||||
|
||||
logging.debug("rar_extract(): Extractionpath: %s", extraction_path)
|
||||
logging.debug("Extraction path: %s", extraction_path)
|
||||
logging.debug("Found rar version: %s", rarfile.is_rarfile(rarfile_path))
|
||||
|
||||
if password:
|
||||
password_command = "-p%s" % password
|
||||
else:
|
||||
password_command = "-p-"
|
||||
|
||||
############################################################################
|
||||
|
||||
if one_folder or cfg.flat_unpack():
|
||||
action = "e"
|
||||
else:
|
||||
@@ -667,24 +667,7 @@ def rar_extract_core(
|
||||
overwrite = "-o-" # Disable overwrite
|
||||
rename = "-or" # Auto renaming
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
# On Windows, UnRar uses a custom argument parser
|
||||
# See: https://github.com/sabnzbd/sabnzbd/issues/1043
|
||||
# The -scf forces the output to be UTF8
|
||||
command = [
|
||||
RAR_COMMAND,
|
||||
action,
|
||||
"-idp",
|
||||
"-scf",
|
||||
overwrite,
|
||||
rename,
|
||||
"-ai",
|
||||
password_command,
|
||||
rarfile_path,
|
||||
"%s\\" % long_path(extraction_path),
|
||||
]
|
||||
|
||||
elif RAR_PROBLEM:
|
||||
if RAR_PROBLEM:
|
||||
# Use only oldest options, specifically no "-or" or "-scf"
|
||||
command = [
|
||||
RAR_COMMAND,
|
||||
@@ -693,10 +676,11 @@ def rar_extract_core(
|
||||
overwrite,
|
||||
password_command,
|
||||
rarfile_path,
|
||||
"%s/" % extraction_path,
|
||||
extraction_path,
|
||||
]
|
||||
else:
|
||||
# The -scf forces the output to be UTF8
|
||||
# On Windows, specifically remove long path from destination so Unrar handles it
|
||||
command = [
|
||||
RAR_COMMAND,
|
||||
action,
|
||||
@@ -707,17 +691,17 @@ def rar_extract_core(
|
||||
"-ai",
|
||||
password_command,
|
||||
rarfile_path,
|
||||
"%s/" % extraction_path,
|
||||
clip_path(extraction_path),
|
||||
]
|
||||
|
||||
if cfg.ignore_unrar_dates():
|
||||
command.insert(3, "-tsm-")
|
||||
if not RAR_PROBLEM and (unrar_parameters := cfg.unrar_parameters().strip().split()):
|
||||
for param in unrar_parameters:
|
||||
command.insert(-2, param)
|
||||
if cfg.ignore_unrar_dates():
|
||||
command.insert(3, "-tsm-")
|
||||
if unrar_parameters := cfg.unrar_parameters().split():
|
||||
for param in unrar_parameters:
|
||||
command.insert(-2, param)
|
||||
|
||||
# Get list of all the volumes part of this set
|
||||
logging.debug("Analyzing rar file ... %s found", rarfile.is_rarfile(rarfile_path))
|
||||
# On Windows, UnRar uses a custom argument parser
|
||||
# See: https://github.com/sabnzbd/sabnzbd/issues/1043
|
||||
p = build_and_run_command(command, windows_unrar_command=True)
|
||||
sabnzbd.PostProcessor.external_process = p
|
||||
|
||||
@@ -793,11 +777,21 @@ def rar_extract_core(
|
||||
requires_kill = True
|
||||
|
||||
elif line.startswith("Cannot create"):
|
||||
line2 = p.stdout.readline()
|
||||
if "must not exceed 260" in line2:
|
||||
msg = "%s: %s" % (T("Unpacking failed, path is too long"), line[13:])
|
||||
else:
|
||||
msg = "%s %s" % (T("Unpacking failed, write error or disk is full?"), line[13:])
|
||||
# Check if maybe it can be salvaged
|
||||
line = p.stdout.readline()
|
||||
lines.append(line.strip())
|
||||
# Error is different on Linux and Windows
|
||||
if line.startswith(("Invalid argument", "The filename, directory name, or volume label syntax")):
|
||||
# Read another line
|
||||
line = p.stdout.readline()
|
||||
lines.append(line.strip())
|
||||
# Will it try to correct?
|
||||
if line.startswith("WARNING: Attempting to correct"):
|
||||
# Great! Let it try
|
||||
logging.info("Unrar detected invalid filename and is attempting to correct")
|
||||
continue
|
||||
|
||||
msg = "%s %s" % (T("Unpacking failed, write error or disk is full?"), line)
|
||||
nzo.fail_msg = msg
|
||||
nzo.set_unpack_info("Unpack", msg, setname)
|
||||
fail = 1
|
||||
@@ -1474,7 +1468,7 @@ def rar_volumelist(rarfile_path: str, password: str, known_volumes: List[str]) -
|
||||
# UnRar is required to read some RAR files
|
||||
# RarFile can fail in special cases
|
||||
try:
|
||||
zf = rarfile.RarFile(rarfile_path)
|
||||
zf = SABRarFile(rarfile_path)
|
||||
|
||||
# setpassword can fail due to bugs in RarFile
|
||||
if password:
|
||||
|
||||
@@ -32,7 +32,7 @@ import sabnzbd
|
||||
import sabnzbd.cfg
|
||||
from sabnzbd.constants import DEF_NETWORKING_TIMEOUT, NNTP_BUFFER_SIZE, NTTP_MAX_BUFFER_SIZE
|
||||
from sabnzbd.encoding import utob, ubtou
|
||||
from sabnzbd.happyeyeballs import AddrInfo
|
||||
from sabnzbd.get_addrinfo import AddrInfo
|
||||
from sabnzbd.decorators import synchronized, DOWNLOADER_LOCK
|
||||
from sabnzbd.misc import int_conv
|
||||
|
||||
@@ -342,6 +342,20 @@ class NNTP:
|
||||
self.sock.settimeout(self.nw.server.timeout)
|
||||
|
||||
# Connect
|
||||
if outgoing_nntp_ip := sabnzbd.cfg.outgoing_nntp_ip():
|
||||
try:
|
||||
self.sock.bind((outgoing_nntp_ip, 0))
|
||||
socket_info = self.sock.getsockname()
|
||||
logging.debug(
|
||||
"%s@%s: Successfully bound to following ip address: %s at following port: %d",
|
||||
self.nw.thrdnum,
|
||||
self.nw.server.host,
|
||||
socket_info[0],
|
||||
socket_info[1],
|
||||
)
|
||||
except socket.error:
|
||||
raise ConnectionError(f"Could not bind to outgoing interface {outgoing_nntp_ip}")
|
||||
|
||||
self.sock.connect(self.addrinfo.sockaddr)
|
||||
|
||||
# Secured or unsecured?
|
||||
|
||||
@@ -41,12 +41,14 @@ from sabnzbd.misc import build_and_run_command, int_conv
|
||||
from sabnzbd.newsunpack import create_env
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
windows_major_version = int_conv(platform.version().split(".")[0])
|
||||
|
||||
try:
|
||||
from win32comext.shell import shell
|
||||
from windows_toasts import InteractableWindowsToaster, Toast, ToastActivatedEventArgs, ToastButton
|
||||
|
||||
# Only Windows 10 and above are supported
|
||||
if int_conv(platform.version().split(".")[0]) < 10:
|
||||
if windows_major_version < 10:
|
||||
raise OSError
|
||||
|
||||
# Set a custom AUMID to display the right icon, it is written to the registry by the installer
|
||||
@@ -54,7 +56,7 @@ if sabnzbd.WINDOWS:
|
||||
_HAVE_WINDOWS_TOASTER = True
|
||||
except Exception:
|
||||
# This needs to work on Windows releases
|
||||
if hasattr(sys, "frozen"):
|
||||
if windows_major_version >= 10 and hasattr(sys, "frozen"):
|
||||
raise
|
||||
|
||||
# Sending toasts on non-supported platforms results in segfaults
|
||||
@@ -87,6 +89,7 @@ NOTIFICATION_TYPES = {
|
||||
"warning": TT("Warning"), #: Notification
|
||||
"error": TT("Error"), #: Notification
|
||||
"disk_full": TT("Disk full"), #: Notification
|
||||
"quota": TT("Quota"), #: Notification
|
||||
"queue_done": TT("Queue finished"), #: Notification
|
||||
"new_login": TT("User logged in"), #: Notification
|
||||
"other": TT("Other Messages"), #: Notification
|
||||
@@ -321,6 +324,8 @@ def send_apprise(title, msg, notification_type, force=False, test=None):
|
||||
"error": apprise.common.NotifyType.FAILURE,
|
||||
# Disk full
|
||||
"disk_full": apprise.common.NotifyType.WARNING,
|
||||
# Quota
|
||||
"quota": apprise.common.NotifyType.WARNING,
|
||||
# Queue finished
|
||||
"queue_done": apprise.common.NotifyType.INFO,
|
||||
# User logged in
|
||||
|
||||
@@ -43,7 +43,8 @@ from sabnzbd.filesystem import (
|
||||
)
|
||||
from sabnzbd.misc import name_to_cat, cat_pp_script_sanitizer
|
||||
from sabnzbd.constants import DEFAULT_PRIORITY, VALID_ARCHIVES, AddNzbFileResult
|
||||
from sabnzbd.utils import rarfile
|
||||
from sabnzbd.misc import SABRarFile
|
||||
import rarfile
|
||||
|
||||
|
||||
def add_nzbfile(
|
||||
@@ -169,7 +170,7 @@ def process_nzb_archive_file(
|
||||
if zipfile.is_zipfile(path):
|
||||
zf = zipfile.ZipFile(path)
|
||||
elif rarfile.is_rarfile(path):
|
||||
zf = rarfile.RarFile(path)
|
||||
zf = SABRarFile(path)
|
||||
elif sabnzbd.newsunpack.is_sevenfile(path):
|
||||
zf = sabnzbd.newsunpack.SevenZip(path)
|
||||
else:
|
||||
@@ -218,8 +219,12 @@ def process_nzb_archive_file(
|
||||
nzo_id=nzo_id,
|
||||
dup_check=dup_check,
|
||||
)
|
||||
except (sabnzbd.nzbstuff.NzbEmpty, sabnzbd.nzbstuff.NzbRejected):
|
||||
# Empty or fully rejected
|
||||
except (
|
||||
sabnzbd.nzbstuff.NzbEmpty,
|
||||
sabnzbd.nzbstuff.NzbRejected,
|
||||
sabnzbd.nzbstuff.NzbPreQueueRejected,
|
||||
):
|
||||
# Empty or fully rejected (including pre-queue rejections)
|
||||
pass
|
||||
except sabnzbd.nzbstuff.NzbRejectToHistory as err:
|
||||
# Duplicate or unwanted extension directed to history
|
||||
@@ -329,8 +334,11 @@ def process_single_nzb(
|
||||
# Malformed or might not be an NZB file
|
||||
result = AddNzbFileResult.NO_FILES_FOUND
|
||||
except sabnzbd.nzbstuff.NzbRejected:
|
||||
# Rejected as duplicate or by pre-queue script
|
||||
# Rejected as duplicate
|
||||
result = AddNzbFileResult.ERROR
|
||||
except sabnzbd.nzbstuff.NzbPreQueueRejected:
|
||||
# Rejected by pre-queue script - should be silently ignored for URL fetches
|
||||
result = AddNzbFileResult.PREQUEUE_REJECTED
|
||||
except sabnzbd.nzbstuff.NzbRejectToHistory as err:
|
||||
# Duplicate or unwanted extension directed to history
|
||||
sabnzbd.NzbQueue.fail_to_history(err.nzo)
|
||||
|
||||
@@ -246,7 +246,9 @@ class NzbQueue:
|
||||
def set_top_only(self, value):
|
||||
self.__top_only = value
|
||||
|
||||
@NzbQueueLocker
|
||||
def change_opts(self, nzo_ids: List[str], pp: int) -> int:
|
||||
"""Locked so changes during URLGrabbing are correctly passed to new job"""
|
||||
result = 0
|
||||
for nzo_id in nzo_ids:
|
||||
if nzo_id in self.__nzo_table:
|
||||
@@ -254,7 +256,9 @@ class NzbQueue:
|
||||
result += 1
|
||||
return result
|
||||
|
||||
@NzbQueueLocker
|
||||
def change_script(self, nzo_ids: List[str], script: str) -> int:
|
||||
"""Locked so changes during URLGrabbing are correctly passed to new job"""
|
||||
result = 0
|
||||
if (script is None) or is_valid_script(script):
|
||||
for nzo_id in nzo_ids:
|
||||
@@ -264,7 +268,9 @@ class NzbQueue:
|
||||
result += 1
|
||||
return result
|
||||
|
||||
@NzbQueueLocker
|
||||
def change_cat(self, nzo_ids: List[str], cat: str) -> int:
|
||||
"""Locked so changes during URLGrabbing are correctly passed to new job"""
|
||||
result = 0
|
||||
for nzo_id in nzo_ids:
|
||||
if nzo_id in self.__nzo_table:
|
||||
@@ -278,7 +284,9 @@ class NzbQueue:
|
||||
result += 1
|
||||
return result
|
||||
|
||||
@NzbQueueLocker
|
||||
def change_name(self, nzo_id: str, name: str, password: str = None) -> bool:
|
||||
"""Locked so changes during URLGrabbing are correctly passed to new job"""
|
||||
if nzo_id in self.__nzo_table:
|
||||
nzo = self.__nzo_table[nzo_id]
|
||||
logging.info("Renaming %s to %s", nzo.final_name, name)
|
||||
@@ -440,7 +448,9 @@ class NzbQueue:
|
||||
handled.append(nzo_id)
|
||||
return handled
|
||||
|
||||
@NzbQueueLocker
|
||||
def pause_nzo(self, nzo_id: str) -> List[str]:
|
||||
"""Locked so changes during URLGrabbing are correctly passed to new job"""
|
||||
handled = []
|
||||
if nzo_id in self.__nzo_table:
|
||||
nzo = self.__nzo_table[nzo_id]
|
||||
|
||||
@@ -523,6 +523,10 @@ class NzbRejected(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NzbPreQueueRejected(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class NzbRejectToHistory(Exception):
|
||||
def __init__(self, nzo, fail_msg):
|
||||
self.nzo: NzbObject = nzo
|
||||
@@ -587,6 +591,7 @@ NzbObjectSaver = (
|
||||
"servercount",
|
||||
"unwanted_ext",
|
||||
"renames",
|
||||
"time_added",
|
||||
)
|
||||
|
||||
NzoAttributeSaver = ("cat", "pp", "script", "priority", "final_name", "password", "url")
|
||||
@@ -668,6 +673,7 @@ class NzbObject(TryList):
|
||||
self.avg_stamp = 0.0 # Avg age in seconds (calculated from avg_age)
|
||||
self.propagation_delay: Optional[float] = None # Set during parsing
|
||||
self.correct_password: Optional[str] = None
|
||||
self.time_added: int = int(time.time()) # When the NZB was added to the queue
|
||||
|
||||
# Bookkeeping values
|
||||
self.meta = {}
|
||||
@@ -868,7 +874,7 @@ class NzbObject(TryList):
|
||||
accept = int_conv(accept)
|
||||
if accept < 1:
|
||||
self.purge_data()
|
||||
raise NzbRejected
|
||||
raise NzbPreQueueRejected
|
||||
if accept == 2:
|
||||
raise NzbRejectToHistory(self, T("Pre-queue script marked job as failed"))
|
||||
|
||||
@@ -2090,6 +2096,9 @@ class NzbObject(TryList):
|
||||
if not isinstance(self.saved_articles, set):
|
||||
# Converted from list to set
|
||||
self.saved_articles = set(self.saved_articles)
|
||||
if self.time_added is None:
|
||||
# For backward compatibility with older saved NZOs
|
||||
self.time_added = 0
|
||||
|
||||
def __repr__(self):
|
||||
return "<NzbObject: filename=%s, bytes=%s, nzo_id=%s>" % (self.filename, self.bytes, self.nzo_id)
|
||||
|
||||
@@ -194,9 +194,10 @@ def parse_par2_file(fname: str, md5of16k: Dict[bytes, str]) -> Tuple[str, Dict[s
|
||||
for i in range(48, pack_len - 32, 20):
|
||||
filecrc32[fileid].append(struct.unpack("<I", data[i + 16 : i + 20])[0])
|
||||
|
||||
# On large files, we stop after seeing all the listings
|
||||
# On large files, we stop after seeing all the listings and have crc32 data for all listings
|
||||
# Our unit-tests do not include large par2 files, so we cannot verify cases like #3164!
|
||||
# On smaller files, we scan them fully to get the par2-creator
|
||||
if total_size > SCAN_LIMIT and len(filepar2info) == nr_files:
|
||||
if total_size > SCAN_LIMIT and len(filepar2info) == nr_files == len(filecrc32):
|
||||
break
|
||||
|
||||
# Process all the data
|
||||
|
||||
@@ -26,6 +26,7 @@ import time
|
||||
import re
|
||||
import gc
|
||||
import queue
|
||||
import rarfile
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
import sabnzbd
|
||||
@@ -46,6 +47,7 @@ from sabnzbd.misc import (
|
||||
change_queue_complete_action,
|
||||
run_script,
|
||||
is_none,
|
||||
SABRarFile,
|
||||
)
|
||||
from sabnzbd.filesystem import (
|
||||
real_path,
|
||||
@@ -89,7 +91,6 @@ import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
import sabnzbd.database as database
|
||||
import sabnzbd.notifier as notifier
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
import sabnzbd.utils.rarvolinfo as rarvolinfo
|
||||
import sabnzbd.utils.checkdir
|
||||
import sabnzbd.deobfuscate_filenames as deobfuscate
|
||||
@@ -892,7 +893,7 @@ def try_rar_check(nzo: NzbObject, rars: List[str]) -> bool:
|
||||
nzo.set_action_line(T("Trying RAR-based verification"), "...")
|
||||
try:
|
||||
# Requires de-unicode for RarFile to work!
|
||||
zf = rarfile.RarFile(rars[0])
|
||||
zf = SABRarFile(rars[0])
|
||||
|
||||
# Skip if it's encrypted
|
||||
if zf.needs_password():
|
||||
@@ -947,9 +948,13 @@ def rar_renamer(nzo: NzbObject) -> int:
|
||||
if not os.path.isfile(file_to_check):
|
||||
continue
|
||||
|
||||
# guard against cbr files due to pr#3114
|
||||
if get_ext(file_to_check) == ".cbr":
|
||||
continue
|
||||
|
||||
if rarfile.is_rarfile(file_to_check):
|
||||
# if a rar file is fully encrypted, rarfile.RarFile() will return an empty list:
|
||||
if not rarfile.RarFile(file_to_check, single_file_check=True).filelist():
|
||||
# if a rar file is fully encrypted, RarFile() will return an empty list:
|
||||
if not SABRarFile(file_to_check, part_only=True).filelist():
|
||||
logging.info(
|
||||
"Download %s contains a fully encrypted & obfuscated rar-file: %s.",
|
||||
nzo.final_name,
|
||||
@@ -965,9 +970,7 @@ def rar_renamer(nzo: NzbObject) -> int:
|
||||
logging.debug("Detected volume-number %s from RAR-header: %s ", rar_vol, file_to_check)
|
||||
volnrext[file_to_check] = (rar_vol, new_extension)
|
||||
# The files inside rar file
|
||||
rar_contents = rarfile.RarFile(
|
||||
os.path.join(nzo.download_path, file_to_check), single_file_check=True
|
||||
).filelist()
|
||||
rar_contents = SABRarFile(os.path.join(nzo.download_path, file_to_check), part_only=True).filelist()
|
||||
try:
|
||||
rarvolnr[rar_vol]
|
||||
except Exception:
|
||||
|
||||
@@ -40,6 +40,7 @@ class Scheduler:
|
||||
self.scheduler = kronos.ThreadedScheduler()
|
||||
self.pause_end: Optional[float] = None # Moment when pause will end
|
||||
self.resume_task: Optional[kronos.Task] = None
|
||||
self.rss_task: Optional[kronos.Task] = None # RSS interval task
|
||||
self.restart_scheduler = False
|
||||
self.pp_pause_event = False
|
||||
self.load_schedules()
|
||||
@@ -189,8 +190,7 @@ class Scheduler:
|
||||
delay = random.randint(0, interval - 1)
|
||||
logging.info("Scheduling RSS interval task every %s min (delay=%s)", interval, delay)
|
||||
sabnzbd.RSSReader.next_run = time.time() + delay * 60
|
||||
self.scheduler.add_interval_task(sabnzbd.RSSReader.run, "RSS", delay * 60, interval * 60)
|
||||
self.scheduler.add_single_task(sabnzbd.RSSReader.run, "RSS", 15)
|
||||
self.rss_task = self.scheduler.add_interval_task(sabnzbd.RSSReader.run, "RSS", delay * 60, interval * 60)
|
||||
|
||||
if cfg.version_check():
|
||||
# Check for new release daily at a random time
|
||||
@@ -449,8 +449,15 @@ class Scheduler:
|
||||
self.scheduler.add_single_task(action, "", interval * 60, args=parms)
|
||||
|
||||
def force_rss(self):
|
||||
"""Add a one-time RSS scan, one second from now"""
|
||||
self.scheduler.add_single_task(sabnzbd.RSSReader.run, "RSS", 1)
|
||||
"""Run RSS scan immediately and reschedule interval task"""
|
||||
# Cancel the current RSS interval task
|
||||
if self.rss_task:
|
||||
self.scheduler.cancel(self.rss_task)
|
||||
|
||||
# Schedule a new interval task and start one now
|
||||
interval = cfg.rss_rate() * 60 # Convert minutes to seconds
|
||||
sabnzbd.RSSReader.next_run = time.time() + interval
|
||||
self.rss_task = self.scheduler.add_interval_task(sabnzbd.RSSReader.run, "RSS", 0, interval)
|
||||
|
||||
|
||||
def sort_schedules(all_events, now=None):
|
||||
|
||||
@@ -171,6 +171,7 @@ SKIN_TEXT = {
|
||||
"mode": TT("Processing"), #: Queue page table column header
|
||||
"name": TT("Name"), #: Queue page table column header
|
||||
"button-retry": TT("Retry"), #: Queue page button
|
||||
"button-mark-completed": TT("Mark as Completed & Remove Temporary Files"), #: History page button
|
||||
"eoq-scripts": TT("Scripts"), #: Queue page table, script selection menu
|
||||
"purgeQueue": TT("Purge Queue"), #: Queue page button
|
||||
"purgeQueueConf": TT("Delete all items from the queue?"), #: Confirmation popup
|
||||
@@ -544,7 +545,8 @@ SKIN_TEXT = {
|
||||
"srv-expire_date": TT("Account expiration date"),
|
||||
"srv-explain-expire_date": TT("Warn 5 days in advance of account expiration date."),
|
||||
"srv-explain-quota": TT(
|
||||
"Quota for this account, counted from the time it is set. In bytes, optionally follow with K,M,G.<br />Warn when it reaches 0, checked every few minutes."
|
||||
"Quota for this server, counted from the time it is set. In bytes, optionally follow with K,M,G.<br />"
|
||||
"Checked every few minutes. Notification is sent when quota is spent."
|
||||
),
|
||||
"srv-retention": TT("Retention time"), #: Server's retention time in days
|
||||
"srv-ssl": TT("SSL"), #: Server SSL tickbox
|
||||
@@ -838,6 +840,9 @@ SKIN_TEXT = {
|
||||
"Glitter-backToQueue": TT("Send back to queue"),
|
||||
"Glitter-purgeOrphaned": TT("Delete All"),
|
||||
"Glitter-retryAllOrphaned": TT("Retry all"),
|
||||
"Glitter-clearOrphanWarning": TT(
|
||||
"Are you sure you want to delete all folders in your Temporary Download Folder? This cannot be undone!"
|
||||
),
|
||||
"Glitter-deleteJobAndFolders": TT("Remove NZB & Delete Files"),
|
||||
"Glitter-addFromURL": TT("Fetch NZB from URL"),
|
||||
"Glitter-addFromFile": TT("Upload NZB"),
|
||||
@@ -914,6 +919,7 @@ SKIN_TEXT = {
|
||||
"wizard-goto": TT("Go to SABnzbd"), #: Wizard step
|
||||
"wizard-exit": TT("Exit SABnzbd"), #: Wizard EXIT button on first page
|
||||
"wizard-start": TT("Start Wizard"), #: Wizard START button on first page
|
||||
"wizard-test-server-required": TT("Click on Test Server before continuing"), #: Tooltip for disabled Next button
|
||||
"restore-backup": TT("Restore backup"),
|
||||
# Special
|
||||
"yourRights": TT(
|
||||
|
||||
@@ -660,6 +660,16 @@ def guess_what(name: str) -> MatchesDict:
|
||||
# Unfix the title
|
||||
guess["title"] = guess.get("title", "")[len(digit_fix) :]
|
||||
|
||||
# Handle weird anime episode notation, that results in the episode number ending up as the episode title
|
||||
if (
|
||||
guess.get("type") == "episode"
|
||||
and not "episode" in guess
|
||||
and "season" in guess
|
||||
and guess.get("episode_title", "").isdigit()
|
||||
):
|
||||
guess.setdefault("episode", default=int(guess.get("episode_title")))
|
||||
guess.pop("episode_title")
|
||||
|
||||
# Force season to 1 for seasonless episodes with no date
|
||||
if guess.get("type") == "episode" and "date" not in guess:
|
||||
guess.setdefault("season", 1)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user