mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-31 19:50:12 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f7ff14981 |
@@ -1,43 +0,0 @@
|
||||
# `git blame` master ignore list.
|
||||
#
|
||||
# This file contains a list of git hashes of revisions to be ignored
|
||||
# by `git blame`. These revisions are considered "unimportant" in
|
||||
# that they are unlikely to be what you are interested in when blaming.
|
||||
# They are typically expected to be formatting-only changes.
|
||||
#
|
||||
# It can be used for `git blame` using `--ignore-revs-file` or by
|
||||
# setting `blame.ignoreRevsFile` in the `git config`[1].
|
||||
#
|
||||
# Ignore these commits when reporting with blame. Calling
|
||||
#
|
||||
# git blame --ignore-revs-file .git-blame-ignore-revs
|
||||
#
|
||||
# will tell `git blame` to ignore changes made by these revisions when
|
||||
# assigning blame, as if the change never happened.
|
||||
#
|
||||
# You can enable this as a default for your local repository by
|
||||
# running
|
||||
#
|
||||
# git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
#
|
||||
# This will probably be automatically picked by your IDE
|
||||
# (VSCode+GitLens and JetBrains products are confirmed to do this).
|
||||
#
|
||||
# Important: if you are switching to a branch without this file,
|
||||
# `git blame` will fail with an error.
|
||||
#
|
||||
# GitHub also excludes the commits listed below from its "Blame"
|
||||
# views[2][3].
|
||||
#
|
||||
# [1]: https://git-scm.com/docs/git-blame#Documentation/git-blame.txt-blameignoreRevsFile
|
||||
# [2]: https://github.blog/changelog/2022-03-24-ignore-commits-in-the-blame-view-beta/
|
||||
# [3]: https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view
|
||||
|
||||
|
||||
# Black changes
|
||||
465a88154152fb0607a63fa24c8446bff43ec886
|
||||
f06891926661986fff52d6eb4b4cb120c71972d1
|
||||
9bcbcaefdfecc85aedfd8e2f8aaa1ca7f959404e
|
||||
433dcab02b29f7bd3827e237434034deecc1b549
|
||||
9f6a9f991222efccc87b45a701086c95629c67b6
|
||||
f89114ca7e1b20bf8e645ecd0b52b707ec857aa9
|
||||
31
.github/ISSUE_TEMPLATE/bug.yml
vendored
31
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: Bug report
|
||||
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:
|
||||
- Support
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: SABnzbd version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating system
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Using Docker image
|
||||
options:
|
||||
- linuxserver
|
||||
- hotio
|
||||
- binhex
|
||||
- Other
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: Include error logs directly or link to extended logs on https://pastebin.com/
|
||||
validations:
|
||||
required: true
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
11
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,11 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Support forum
|
||||
url: https://forums.sabnzbd.org/
|
||||
about: Support questions can be asked on our forums, Reddit or Discord server.
|
||||
- name: Discord
|
||||
url: https://discord.sabnzbd.org
|
||||
about: Support questions can be asked on our forums, Reddit or Discord server.
|
||||
- name: Reddit - r/sabnzbd
|
||||
url: https://www.reddit.com/r/sabnzbd
|
||||
about: Support questions can be asked on our forums, Reddit or Discord server.
|
||||
10
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
10
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,10 +0,0 @@
|
||||
name: Feature request
|
||||
description: What new feature would you like to have added to SABnzbd?
|
||||
labels:
|
||||
- Support
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
validations:
|
||||
required: true
|
||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
21
.github/renovate.json
vendored
21
.github/renovate.json
vendored
@@ -7,30 +7,23 @@
|
||||
"schedule": [
|
||||
"before 8am on Monday"
|
||||
],
|
||||
"baseBranches": ["develop", "feature/uvicorn"],
|
||||
"ignorePaths": [
|
||||
"tests/**",
|
||||
".github/workflows/**"
|
||||
],
|
||||
"pip_requirements": {
|
||||
"fileMatch": [
|
||||
"requirements.txt",
|
||||
"tests/requirements.txt",
|
||||
"builder/requirements.txt",
|
||||
"builder/release-requirements.txt"
|
||||
"builder/release-requirements.txt",
|
||||
"builder/osx/requirements.txt"
|
||||
]
|
||||
},
|
||||
"ignorePaths": [],
|
||||
"ignoreDeps": [
|
||||
"jaraco.text",
|
||||
"jaraco.context",
|
||||
"jaraco.collections",
|
||||
"sabctools",
|
||||
"paho-mqtt",
|
||||
"werkzeug"
|
||||
"sabctools"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": ["github-actions"],
|
||||
"matchPackageNames": ["windows", "macos"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
|
||||
247
.github/workflows/build_release.yml
vendored
247
.github/workflows/build_release.yml
vendored
@@ -9,118 +9,125 @@ env:
|
||||
jobs:
|
||||
build_windows:
|
||||
name: Build Windows binary
|
||||
runs-on: windows-2022
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.11 (64bit)
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.14"
|
||||
python-version: "3.11"
|
||||
architecture: "x64"
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
- name: Install Python dependencies
|
||||
- name: Cache Python virtualenv (64bit)
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv-64bit
|
||||
with:
|
||||
custom_virtualenv_dir: "venv64"
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies (64bit)
|
||||
if: steps.cache-virtualenv-64bit.outputs.cache-hit != 'true'
|
||||
# Without dependencies to make sure everything is covered in the requirements.txt
|
||||
run: |
|
||||
python --version
|
||||
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
|
||||
id: windows_binary
|
||||
run: python builder/package.py binary
|
||||
- name: Upload Windows standalone binary (unsigned)
|
||||
uses: actions/upload-artifact@v4
|
||||
id: upload-unsigned-binary
|
||||
- name: Build Windows standalone binary and installer (64bit)
|
||||
run: python builder/package.py installer
|
||||
- name: Upload Windows standalone binary (64bit)
|
||||
uses: actions/upload-artifact@v3
|
||||
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
|
||||
name: Windows Windows standalone binary (64bit)
|
||||
- name: Upload Windows installer (64bit)
|
||||
uses: actions/upload-artifact@v3
|
||||
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/')
|
||||
- name: Set up Python 3.8 (32bit and legacy)
|
||||
uses: actions/setup-python@v4
|
||||
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
|
||||
python-version: "3.8"
|
||||
architecture: "x86"
|
||||
- name: Cache Python virtualenv (32bit and legacy)
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv-32bit
|
||||
with:
|
||||
name: Windows installer (signed)
|
||||
path: "signed/*-win-setup.exe"
|
||||
custom_virtualenv_dir: "venv32"
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies (32bit and legacy)
|
||||
if: steps.cache-virtualenv-32bit.outputs.cache-hit != 'true'
|
||||
# We do not care about the extra dependencies for the legacy build
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt
|
||||
pip install --upgrade -r builder/requirements.txt
|
||||
- name: Build Windows standalone binary (32bit and legacy)
|
||||
run: python builder/package.py binary
|
||||
- name: Upload Windows standalone binary (32bit and legacy)
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-win32-bin.zip"
|
||||
name: Windows Windows standalone binary (32bit and legacy)
|
||||
|
||||
build_macos:
|
||||
name: Build macOS binary
|
||||
runs-on: macos-14
|
||||
runs-on: macos-11
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
# 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.14.0"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.15"
|
||||
PYTHON_VERSION: "3.11.5"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.9"
|
||||
# We need to force compile for universal2 support
|
||||
CFLAGS: -arch x86_64 -arch arm64
|
||||
ARCHFLAGS: -arch x86_64 -arch arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python
|
||||
# Only use this for the caching of pip packages!
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.14"
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache Python download
|
||||
id: cache-python-download
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/python.pkg
|
||||
key: cache-macOS-Python-${{ env.PYTHON_VERSION }}
|
||||
- name: Get Python from python.org
|
||||
- name: Get Python
|
||||
if: steps.cache-python-download.outputs.cache-hit != 'true'
|
||||
run: curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o ~/python.pkg
|
||||
- name: Install Python
|
||||
run: sudo installer -pkg ~/python.pkg -target /
|
||||
run: |
|
||||
sudo installer -pkg ~/python.pkg -target /
|
||||
unlink /usr/local/bin/python
|
||||
ln -s /usr/local/bin/python3 /usr/local/bin/python
|
||||
- name: Cache Python virtualenv
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv
|
||||
with:
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies
|
||||
# We have to manually compile some modules as they don't automatically fetch universal2 binaries
|
||||
# We have to manually take a few steps:
|
||||
# 1. Because building cryptography is hard, and we cannot force pip to fetch universal2 version we
|
||||
# first install the x86 version (and it's dependencies) and then manually fetch the universal2 build
|
||||
# https://github.com/pyca/cryptography/issues/5918
|
||||
# 2. Due to PyObjC we cannot run pip on the main requirements without installing dependencies
|
||||
# 3. We need to build the PyInstaller bootloader:
|
||||
# https://github.com/pyinstaller/pyinstaller/issues/6235
|
||||
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
python3 --version
|
||||
pip3 install --upgrade pip wheel
|
||||
pip3 install --upgrade -r requirements.txt --no-binary cffi,CT3,PyYAML,charset_normalizer --no-dependencies
|
||||
pip3 install --upgrade -r builder/requirements.txt --no-dependencies
|
||||
|
||||
pip3 install --upgrade -r requirements.txt --no-binary cffi
|
||||
|
||||
pip3 uninstall cryptography -y
|
||||
pip3 download -r builder/osx/requirements.txt --platform macosx_10_12_universal2 --only-binary :all: --no-deps --dest .
|
||||
pip3 install -r builder/osx/requirements.txt --no-cache-dir --no-index --find-links .
|
||||
|
||||
PYINSTALLER_COMPILE_BOOTLOADER=1 pip3 install --upgrade -r builder/requirements.txt --no-binary pyinstaller --no-dependencies
|
||||
- name: Import macOS codesign certificates
|
||||
# Taken from https://github.com/Apple-Actions/import-codesign-certs/pull/27 (comments)
|
||||
env:
|
||||
@@ -130,8 +137,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
|
||||
@@ -140,7 +147,7 @@ jobs:
|
||||
# Run this on macOS so the line endings are correct by default
|
||||
run: python builder/package.py source
|
||||
- name: Upload source distribution
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-src.tar.gz"
|
||||
name: Source distribution
|
||||
@@ -153,88 +160,28 @@ jobs:
|
||||
python3 builder/package.py app
|
||||
python3 builder/make_dmg.py
|
||||
- name: Upload macOS binary
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
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
|
||||
path: "*-osx.dmg"
|
||||
name: macOS binary (not notarized)
|
||||
|
||||
release:
|
||||
name: Prepare Release
|
||||
name: Publish Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_windows, build_macos]
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.14"
|
||||
cache: pip
|
||||
cache-dependency-path: "builder/release-requirements.txt"
|
||||
- name: Download Source distribution artifact
|
||||
uses: actions/download-artifact@v5
|
||||
python-version: "3.x"
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
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
|
||||
path: dist
|
||||
- name: Move all artifacts to main folder
|
||||
run: find dist -type f -exec mv {} . \;
|
||||
- name: Prepare official release
|
||||
env:
|
||||
AUTOMATION_GITHUB_TOKEN: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
@@ -242,3 +189,9 @@ jobs:
|
||||
run: |
|
||||
pip3 install -r builder/release-requirements.txt
|
||||
python3 builder/release.py
|
||||
- name: Release latest available Snap
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_TOKEN }}
|
||||
run: |
|
||||
sudo snap install snapcraft --classic
|
||||
python3 snap/local/release_snap.py
|
||||
|
||||
41
.github/workflows/integration_testing.yml
vendored
41
.github/workflows/integration_testing.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Black Code Formatter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v3
|
||||
- name: Black Code Formatter
|
||||
uses: lgeiger/black-action@master
|
||||
with:
|
||||
@@ -31,33 +31,46 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||
python-architecture: ["x64"]
|
||||
name: ["Linux"]
|
||||
os: [ubuntu-latest]
|
||||
os: [ubuntu-20.04]
|
||||
include:
|
||||
- name: macOS
|
||||
os: macos-13
|
||||
python-version: "3.14"
|
||||
os: macos-latest
|
||||
python-version: "3.11"
|
||||
python-architecture: "x64"
|
||||
- name: Windows
|
||||
os: windows-2022
|
||||
python-version: "3.14"
|
||||
os: windows-latest
|
||||
python-version: "3.11"
|
||||
python-architecture: "x64"
|
||||
- name: Windows (32bit)
|
||||
os: windows-latest
|
||||
python-version: "3.8"
|
||||
python-architecture: "x86"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v6
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }} ${{ matrix.python-architecture }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: pip
|
||||
cache-dependency-path: "**/requirements.txt"
|
||||
architecture: ${{ matrix.python-architecture }}
|
||||
- name: Install system dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get install unrar 7zip par2
|
||||
run: sudo apt-get install unrar p7zip-full par2
|
||||
- name: Cache Python virtualenv
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv
|
||||
with:
|
||||
custom_cache_key_element: ci-${{ matrix.python-architecture }}
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies
|
||||
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt --no-dependencies
|
||||
pip install --upgrade -r requirements.txt
|
||||
pip install --upgrade -r tests/requirements.txt
|
||||
- name: Test SABnzbd
|
||||
run: pytest -s
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
if: github.repository_owner == 'sabnzbd'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
if: github.repository_owner == 'sabnzbd'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
log-output: true
|
||||
issue-inactive-days: 60
|
||||
|
||||
8
.github/workflows/translations.yml
vendored
8
.github/workflows/translations.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TX_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
- name: Generate translatable texts
|
||||
@@ -21,16 +21,16 @@ jobs:
|
||||
- name: Push/pull Transifex translations
|
||||
if: env.TX_TOKEN
|
||||
# Add --translation to the push command in order to update Transifex using local translation edits
|
||||
# However, this prevents modifying existing translations in Transifex as they will be overwritten by the push!
|
||||
# However, this prevents modifying existing translations in Transifex as they will be overwritten by the push!
|
||||
run: |
|
||||
curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash
|
||||
./tx push --source
|
||||
./tx push --source
|
||||
./tx pull --all --force
|
||||
- name: Compile translations to validate them
|
||||
run: |
|
||||
python3 tools/make_mo.py
|
||||
- name: Push translatable and translated texts back to repo
|
||||
uses: stefanzweifel/git-auto-commit-action@v7.0.0
|
||||
uses: stefanzweifel/git-auto-commit-action@v4.16.0
|
||||
if: env.TX_TOKEN
|
||||
with:
|
||||
commit_message: |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
(c) Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
(c) Copyright 2007-2023 by The SABnzbd-Team (sabnzbd.org)
|
||||
|
||||
The SABnzbd-Team is:
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
0) LICENSE
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
(c) Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
(c) Copyright 2007-2023 by The SABnzbd-Team (sabnzbd.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(c) Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
(c) Copyright 2007-2023 by The SABnzbd-Team (sabnzbd.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
|
||||
11
README.md
11
README.md
@@ -2,7 +2,7 @@ SABnzbd - The automated Usenet download tool
|
||||
============================================
|
||||
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](https://discord.sabnzbd.org)
|
||||
[](https://discord.gg/KQzDe7fvNU)
|
||||
|
||||
SABnzbd is an Open Source Binary Newsreader written in Python.
|
||||
|
||||
@@ -66,12 +66,3 @@ 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).
|
||||
|
||||
139
README.mkd
139
README.mkd
@@ -1,109 +1,46 @@
|
||||
Release Notes - SABnzbd 4.5.5
|
||||
Release Notes - SABnzbd 4.1.0
|
||||
=========================================================
|
||||
|
||||
## Bug fixes and changes in 4.5.5
|
||||
## Changes since 4.0.3
|
||||
- Added a dark mode for the Config, Login, and Wizard pages.
|
||||
- Added multi-select to the History.
|
||||
- Show the number of items in post-processing when in Tabbed mode.
|
||||
- Added option `verify_xff_header` to include `X-Forwarded-For` when
|
||||
validating if connections should be accepted when using a proxy.
|
||||
- Added option to purge log files from the Folders Config page.
|
||||
- Moved `Server IP address selection` and `On failure, try
|
||||
alternative NZB` to Special settings.
|
||||
- Special setting `ipv6_servers` changed to on/off.
|
||||
- Only use 7zip to unpack `.zip` files.
|
||||
- Windows: Added option `enable_multipar` to use par2cmdline-turbo
|
||||
instead of Multipar for verification and repair. It is faster,
|
||||
but on Windows it can fail on special (UTF8) filenames.
|
||||
- macOS: Switched to par2cmdline-turbo for verification and repair.
|
||||
- Linux: Detect more recent versions of 7zip.
|
||||
- Windows: Use `All Users` locations during installation of shortcuts.
|
||||
- Windows/macOS: Updated Python to 3.11.5, 7Zip to 23.01 and
|
||||
UnRar to 6.23. All these updates include security fixes.
|
||||
|
||||
* 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.
|
||||
* Invalid cookies of other services could result in errors.
|
||||
* Internet Bandwidth test could be stuck in infinite loop.
|
||||
* RSS readout did not ignore torrent alternatives.
|
||||
* Prowl and Pushover settings did not load correctly.
|
||||
* Renamed `osx` to `macos` internally.
|
||||
* API: Removed `B` post-fix from `quota` and `left_quota` fields in `queue`.
|
||||
* Windows: Support more languages in the installer.
|
||||
* Windows and macOS: Updated par2cmdline-turbo to 1.3.0 and Unrar to 7.12.
|
||||
|
||||
## Bug fixes and changes in 4.5.1
|
||||
|
||||
* Correct platform detection on Linux.
|
||||
* The `From SxxEyy` RSS filters did not always work.
|
||||
* Windows and macOS: Update Unrar to 7.11.
|
||||
|
||||
## New features in 4.5.0
|
||||
|
||||
* Improved failure detection by downloading additional par2 files right away.
|
||||
* Added more diagnostic information about the system.
|
||||
* Use XFF headers for login validation if `verify_xff_header` is enabled.
|
||||
* Added Turkish translation (by @cardpuncher).
|
||||
* Added `unrar_parameters` option to supply custom Unrar parameters.
|
||||
* Windows: Removed MultiPar support.
|
||||
* Windows and macOS: Updated Python to 3.13.2, 7zip to 24.09,
|
||||
Unrar to 7.10 and par2cmdline-turbo to 1.2.0.
|
||||
|
||||
## Bug fixes since 4.4.0
|
||||
|
||||
* Handle filenames that exceed maximum filesystem lengths.
|
||||
* Directly decompress gzip responses when retrieving NZB's.
|
||||
|
||||
## Upgrade notices
|
||||
|
||||
* Direct upgrade supported from version 3.0.0 and newer.
|
||||
* Older versions require performing a `Queue repair` after upgrading.
|
||||
## Bugfixes since 4.0.3
|
||||
- Series duplicate detection did not detect duplicates.
|
||||
- Sorting would append `.1` to some filenames.
|
||||
- If a paused queue contained items with `Force` priority,
|
||||
items with a lower priority would also be downloaded.
|
||||
- Not all API-keys were removed during log-sanitization.
|
||||
- In certain situations, not all data would be written to disk.
|
||||
- Folder names could be sanitized too eagerly.
|
||||
- Some articles would fail to decode.
|
||||
- QuickCheck could wrongly rename files with identical content.
|
||||
- Warning about `Scripts Folder` location was triggered incorrectly.
|
||||
|
||||
## 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.
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
## 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, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
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,
|
||||
extract and clean up posts downloaded from Usenet.
|
||||
|
||||
(c) Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
(c) Copyright 2007-2023 by The SABnzbd-Team (sabnzbd.org)
|
||||
|
||||
447
SABnzbd.py
447
SABnzbd.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -32,14 +32,13 @@ import traceback
|
||||
import getopt
|
||||
import signal
|
||||
import socket
|
||||
import platform
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
import ssl
|
||||
import time
|
||||
import re
|
||||
import gc
|
||||
import threading
|
||||
import http.cookies
|
||||
from typing import List, Dict, Any
|
||||
|
||||
try:
|
||||
@@ -48,7 +47,6 @@ try:
|
||||
import feedparser
|
||||
import configobj
|
||||
import cherrypy
|
||||
import cheroot.errors
|
||||
import portend
|
||||
import cryptography
|
||||
import chardet
|
||||
@@ -66,7 +64,7 @@ import sabnzbd
|
||||
import sabnzbd.lang
|
||||
import sabnzbd.interface
|
||||
from sabnzbd.constants import (
|
||||
DEF_NETWORKING_TIMEOUT,
|
||||
DEF_TIMEOUT,
|
||||
DEF_LOG_ERRFILE,
|
||||
DEF_MAIN_TMPL,
|
||||
DEF_STD_WEB_DIR,
|
||||
@@ -85,6 +83,7 @@ from sabnzbd.constants import (
|
||||
)
|
||||
import sabnzbd.newsunpack
|
||||
from sabnzbd.misc import (
|
||||
check_latest_version,
|
||||
exit_sab,
|
||||
split_host,
|
||||
create_https_certificates,
|
||||
@@ -94,6 +93,8 @@ from sabnzbd.misc import (
|
||||
get_from_url,
|
||||
upload_file_to_sabnzbd,
|
||||
is_localhost,
|
||||
is_lan_addr,
|
||||
ip_in_subnet,
|
||||
helpful_warning,
|
||||
set_https_verification,
|
||||
)
|
||||
@@ -103,6 +104,9 @@ import sabnzbd.config as config
|
||||
import sabnzbd.cfg
|
||||
import sabnzbd.notifier as notifier
|
||||
import sabnzbd.zconfig
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6, dnslookup
|
||||
from sabnzbd.utils.getperformance import getpystone, getcpu
|
||||
import sabnzbd.utils.ssdp as ssdp
|
||||
|
||||
try:
|
||||
import win32api
|
||||
@@ -119,7 +123,7 @@ try:
|
||||
|
||||
win32api.SetConsoleCtrlHandler(sabnzbd.sig_handler, True)
|
||||
except ImportError:
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
print("Sorry, requires Python module PyWin32.")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -167,8 +171,7 @@ class GUIHandler(logging.Handler):
|
||||
# This prevents endless looping if the notification service itself throws an error/warning
|
||||
# We don't check based on message content, because if it includes a timestamp it's not unique
|
||||
if not any(
|
||||
stored_warning["origin"] == warning["origin"]
|
||||
and stored_warning["time"] + DEF_NETWORKING_TIMEOUT > time.time()
|
||||
stored_warning["origin"] == warning["origin"] and stored_warning["time"] + DEF_TIMEOUT > time.time()
|
||||
for stored_warning in self.store
|
||||
):
|
||||
if record.levelno == logging.WARNING:
|
||||
@@ -207,7 +210,7 @@ def print_help():
|
||||
print(" -w --weblogging Enable cherrypy access logging")
|
||||
print()
|
||||
print(" -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]")
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
print(" -d --daemon Use when run as a service")
|
||||
else:
|
||||
print(" -d --daemon Fork daemon process")
|
||||
@@ -241,7 +244,7 @@ def print_version():
|
||||
"""
|
||||
%s-%s
|
||||
|
||||
(C) Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
Copyright (C) 2007-2023 The SABnzbd-Team (sabnzbd.org)
|
||||
SABnzbd comes with ABSOLUTELY NO WARRANTY.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions. It is licensed under the
|
||||
@@ -295,14 +298,14 @@ def daemonize():
|
||||
os.dup2(f.fileno(), sys.stderr.fileno())
|
||||
|
||||
|
||||
def abort_and_show_error(browserhost, web_port, err=""):
|
||||
def abort_and_show_error(browserhost, cherryport, err=""):
|
||||
"""Abort program because of CherryPy troubles"""
|
||||
logging.error(T("Failed to start web-interface") + " : " + str(err))
|
||||
if not sabnzbd.DAEMON:
|
||||
if "49" in err:
|
||||
panic_host(browserhost, web_port)
|
||||
panic_host(browserhost, cherryport)
|
||||
else:
|
||||
panic_port(browserhost, web_port)
|
||||
panic_port(browserhost, cherryport)
|
||||
sabnzbd.halt()
|
||||
exit_sab(2)
|
||||
|
||||
@@ -312,7 +315,7 @@ def identify_web_template(key, defweb, wdir):
|
||||
if wdir is None:
|
||||
try:
|
||||
wdir = fix_webname(key())
|
||||
except Exception:
|
||||
except:
|
||||
wdir = ""
|
||||
if not wdir:
|
||||
wdir = defweb
|
||||
@@ -372,25 +375,25 @@ def get_user_profile_paths():
|
||||
# just assume that everything defaults to the program dir
|
||||
sabnzbd.DIR_LCLDATA = sabnzbd.DIR_PROG
|
||||
sabnzbd.DIR_HOME = sabnzbd.DIR_PROG
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
# Ignore Win32 "logoff" signal
|
||||
# This should work, but it doesn't
|
||||
# Instead the signal_handler will ignore the "logoff" signal
|
||||
# signal.signal(5, signal.SIG_IGN)
|
||||
pass
|
||||
return
|
||||
elif sabnzbd.WINDOWS:
|
||||
elif sabnzbd.WIN32:
|
||||
try:
|
||||
path = shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, None, 0)
|
||||
sabnzbd.DIR_LCLDATA = os.path.join(path, DEF_WORKDIR)
|
||||
sabnzbd.DIR_HOME = os.environ["USERPROFILE"]
|
||||
except Exception:
|
||||
except:
|
||||
try:
|
||||
root = os.environ["AppData"]
|
||||
user = os.environ["USERPROFILE"]
|
||||
sabnzbd.DIR_LCLDATA = "%s\\%s" % (root.replace("\\Roaming", "\\Local"), DEF_WORKDIR)
|
||||
sabnzbd.DIR_HOME = user
|
||||
except Exception:
|
||||
except:
|
||||
pass
|
||||
|
||||
# Long-path everything
|
||||
@@ -426,7 +429,10 @@ 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):
|
||||
helpful_warning(T("Unable to link to OpenSSL, optimized SSL connection functions will not be used."))
|
||||
logging.warning(
|
||||
"Could not link to OpenSSL library, please report here: "
|
||||
"https://github.com/sabnzbd/sabnzbd/issues/2421"
|
||||
)
|
||||
else:
|
||||
# Wrong SABCTools version, if it was fully missing it would fail to start due to check at the very top
|
||||
logging.error(
|
||||
@@ -440,6 +446,9 @@ def print_modules():
|
||||
|
||||
logging.info("Cryptography module (v%s)... found!", cryptography.__version__)
|
||||
|
||||
if sabnzbd.WIN32 and sabnzbd.newsunpack.MULTIPAR_COMMAND:
|
||||
logging.info("MultiPar binary... found (%s)", sabnzbd.newsunpack.MULTIPAR_COMMAND)
|
||||
|
||||
if sabnzbd.newsunpack.PAR2_COMMAND:
|
||||
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
|
||||
else:
|
||||
@@ -455,7 +464,7 @@ def print_modules():
|
||||
have_str = "%.2f" % (float(sabnzbd.newsunpack.RAR_VERSION) / 100)
|
||||
want_str = "%.2f" % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
|
||||
helpful_warning(T("Your UNRAR version is %s, we recommend version %s or higher.<br />"), have_str, want_str)
|
||||
elif not (sabnzbd.WINDOWS or sabnzbd.MACOS):
|
||||
elif not (sabnzbd.WIN32 or sabnzbd.MACOS):
|
||||
logging.info("UNRAR binary version %.2f", (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
else:
|
||||
logging.error(T("unrar binary... NOT found"))
|
||||
@@ -464,12 +473,12 @@ def print_modules():
|
||||
|
||||
if sabnzbd.newsunpack.SEVENZIP_COMMAND:
|
||||
logging.info("7za binary... found (%s)", sabnzbd.newsunpack.SEVENZIP_COMMAND)
|
||||
if not (sabnzbd.WINDOWS or sabnzbd.MACOS):
|
||||
if not (sabnzbd.WIN32 or sabnzbd.MACOS):
|
||||
logging.info("7za binary version %s", sabnzbd.newsunpack.SEVENZIP_VERSION)
|
||||
else:
|
||||
logging.warning(T("7za binary... NOT found!"))
|
||||
|
||||
if not sabnzbd.WINDOWS:
|
||||
if not sabnzbd.WIN32:
|
||||
if sabnzbd.newsunpack.NICE_COMMAND:
|
||||
logging.info("nice binary... found (%s)", sabnzbd.newsunpack.NICE_COMMAND)
|
||||
else:
|
||||
@@ -522,19 +531,19 @@ def check_resolve(host):
|
||||
return True
|
||||
|
||||
|
||||
def get_webhost(web_host, web_port, https_port):
|
||||
def get_webhost(cherryhost, cherryport, https_port):
|
||||
"""Determine the webhost address and port,
|
||||
return (host, port, browserhost)
|
||||
"""
|
||||
if web_host == "0.0.0.0" and not check_resolve("127.0.0.1"):
|
||||
web_host = ""
|
||||
elif web_host == "::" and not check_resolve("::1"):
|
||||
web_host = ""
|
||||
if cherryhost == "0.0.0.0" and not check_resolve("127.0.0.1"):
|
||||
cherryhost = ""
|
||||
elif cherryhost == "::" and not check_resolve("::1"):
|
||||
cherryhost = ""
|
||||
|
||||
if web_host is None:
|
||||
web_host = sabnzbd.cfg.web_host()
|
||||
if cherryhost is None:
|
||||
cherryhost = sabnzbd.cfg.cherryhost()
|
||||
else:
|
||||
sabnzbd.cfg.web_host.set(web_host)
|
||||
sabnzbd.cfg.cherryhost.set(cherryhost)
|
||||
|
||||
# Get IP address, but discard APIPA/IPV6
|
||||
# If only APIPA's or IPV6 are found, fall back to localhost
|
||||
@@ -546,10 +555,10 @@ def get_webhost(web_host, web_port, https_port):
|
||||
# Hostname does not resolve
|
||||
try:
|
||||
# Valid user defined name?
|
||||
info = socket.getaddrinfo(web_host, None)
|
||||
info = socket.getaddrinfo(cherryhost, None)
|
||||
except socket.error:
|
||||
if not is_localhost(web_host):
|
||||
web_host = "0.0.0.0"
|
||||
if not is_localhost(cherryhost):
|
||||
cherryhost = "0.0.0.0"
|
||||
try:
|
||||
info = socket.getaddrinfo(localhost, None)
|
||||
except socket.error:
|
||||
@@ -566,75 +575,75 @@ def get_webhost(web_host, web_port, https_port):
|
||||
hostip = ip
|
||||
|
||||
# A blank host will use the local ip address
|
||||
if web_host == "":
|
||||
if cherryhost == "":
|
||||
if ipv6 and ipv4:
|
||||
# To protect Firefox users, use numeric IP
|
||||
web_host = hostip
|
||||
cherryhost = hostip
|
||||
browserhost = hostip
|
||||
else:
|
||||
web_host = socket.gethostname()
|
||||
browserhost = web_host
|
||||
cherryhost = socket.gethostname()
|
||||
browserhost = cherryhost
|
||||
|
||||
# 0.0.0.0 will listen on all ipv4 interfaces (no ipv6 addresses)
|
||||
elif web_host == "0.0.0.0":
|
||||
elif cherryhost == "0.0.0.0":
|
||||
# Just take the gamble for this
|
||||
web_host = "0.0.0.0"
|
||||
cherryhost = "0.0.0.0"
|
||||
browserhost = localhost
|
||||
|
||||
# :: will listen on all ipv6 interfaces (no ipv4 addresses)
|
||||
elif web_host in ("::", "[::]"):
|
||||
web_host = web_host.strip("[").strip("]")
|
||||
elif cherryhost in ("::", "[::]"):
|
||||
cherryhost = cherryhost.strip("[").strip("]")
|
||||
# Assume '::1' == 'localhost'
|
||||
browserhost = localhost
|
||||
|
||||
# IPV6 address
|
||||
elif "[" in web_host or ":" in web_host:
|
||||
browserhost = web_host
|
||||
elif "[" in cherryhost or ":" in cherryhost:
|
||||
browserhost = cherryhost
|
||||
|
||||
# IPV6 numeric address
|
||||
elif web_host.replace(".", "").isdigit():
|
||||
elif cherryhost.replace(".", "").isdigit():
|
||||
# IPV4 numerical
|
||||
browserhost = web_host
|
||||
browserhost = cherryhost
|
||||
|
||||
elif web_host == localhost:
|
||||
web_host = localhost
|
||||
elif cherryhost == localhost:
|
||||
cherryhost = localhost
|
||||
browserhost = localhost
|
||||
|
||||
else:
|
||||
# If on APIPA, use numerical IP, to help FireFoxers
|
||||
if ipv6 and ipv4:
|
||||
web_host = hostip
|
||||
browserhost = web_host
|
||||
cherryhost = hostip
|
||||
browserhost = cherryhost
|
||||
|
||||
# Some systems don't like brackets in numerical ipv6
|
||||
if sabnzbd.MACOS:
|
||||
web_host = web_host.strip("[]")
|
||||
cherryhost = cherryhost.strip("[]")
|
||||
else:
|
||||
try:
|
||||
socket.getaddrinfo(web_host, None)
|
||||
socket.getaddrinfo(cherryhost, None)
|
||||
except socket.error:
|
||||
web_host = web_host.strip("[]")
|
||||
cherryhost = cherryhost.strip("[]")
|
||||
|
||||
if ipv6 and ipv4 and web_host == "" and sabnzbd.WINDOWS:
|
||||
if ipv6 and ipv4 and cherryhost == "" and sabnzbd.WIN32:
|
||||
helpful_warning(T("Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access"))
|
||||
|
||||
if web_host == "localhost" and not sabnzbd.WINDOWS and not sabnzbd.MACOS:
|
||||
if cherryhost == "localhost" and not sabnzbd.WIN32 and not sabnzbd.MACOS:
|
||||
# On the Ubuntu family, localhost leads to problems for CherryPy
|
||||
ips = ip_extract()
|
||||
if "127.0.0.1" in ips and "::1" in ips:
|
||||
web_host = "127.0.0.1"
|
||||
cherryhost = "127.0.0.1"
|
||||
if ips[0] != "127.0.0.1":
|
||||
browserhost = "127.0.0.1"
|
||||
|
||||
# This is to please Chrome on macOS
|
||||
if web_host == "localhost" and sabnzbd.MACOS:
|
||||
web_host = "127.0.0.1"
|
||||
if cherryhost == "localhost" and sabnzbd.MACOS:
|
||||
cherryhost = "127.0.0.1"
|
||||
browserhost = "localhost"
|
||||
|
||||
if web_port is None:
|
||||
web_port = sabnzbd.cfg.web_port.get_int()
|
||||
if cherryport is None:
|
||||
cherryport = sabnzbd.cfg.cherryport.get_int()
|
||||
else:
|
||||
sabnzbd.cfg.web_port.set(str(web_port))
|
||||
sabnzbd.cfg.cherryport.set(str(cherryport))
|
||||
|
||||
if https_port is None:
|
||||
https_port = sabnzbd.cfg.https_port.get_int()
|
||||
@@ -643,12 +652,12 @@ def get_webhost(web_host, web_port, https_port):
|
||||
# if the https port was specified, assume they want HTTPS enabling also
|
||||
sabnzbd.cfg.enable_https.set(True)
|
||||
|
||||
if web_port == https_port and sabnzbd.cfg.enable_https():
|
||||
if cherryport == https_port and sabnzbd.cfg.enable_https():
|
||||
sabnzbd.cfg.enable_https.set(False)
|
||||
# Should have a translated message, but that's not available yet
|
||||
logging.error(T("HTTP and HTTPS ports cannot be the same"))
|
||||
|
||||
return web_host, web_port, browserhost, https_port
|
||||
return cherryhost, cherryport, browserhost, https_port
|
||||
|
||||
|
||||
def attach_server(host, port, cert=None, key=None, chain=None):
|
||||
@@ -673,7 +682,7 @@ def is_sabnzbd_running(url):
|
||||
ver = get_from_url(url)
|
||||
set_https_verification(prev)
|
||||
return ver and (re.search(r"\d+\.\d+\.", ver) or ver.strip() == sabnzbd.__version__)
|
||||
except Exception:
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
@@ -684,7 +693,7 @@ def find_free_port(host, currentport):
|
||||
try:
|
||||
portend.free(host, currentport, timeout=0.025)
|
||||
return currentport
|
||||
except Exception:
|
||||
except:
|
||||
currentport += 5
|
||||
n += 1
|
||||
return 0
|
||||
@@ -743,9 +752,26 @@ def commandline_handler():
|
||||
serv_opts = [os.path.normpath(os.path.abspath(sys.argv[0]))]
|
||||
upload_nzbs = []
|
||||
|
||||
# macOS binary: get rid of the weird -psn_0_123456 parameter
|
||||
for arg in sys.argv:
|
||||
if arg.startswith("-psn_"):
|
||||
sys.argv.remove(arg)
|
||||
break
|
||||
|
||||
# Ugly hack to remove the extra "SABnzbd*" parameter the Windows binary
|
||||
# gets when it's restarted
|
||||
if len(sys.argv) > 1 and "sabnzbd" in sys.argv[1].lower() and not sys.argv[1].startswith("-"):
|
||||
slice_start = 2
|
||||
else:
|
||||
slice_start = 1
|
||||
|
||||
# Prepend options from env-variable to options
|
||||
info = os.environ.get("SABnzbd", "").split()
|
||||
info.extend(sys.argv[slice_start:])
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
info,
|
||||
"phdvncwl:s:f:t:b:2:",
|
||||
[
|
||||
"pause",
|
||||
@@ -828,13 +854,13 @@ def main():
|
||||
|
||||
autobrowser = None
|
||||
autorestarted = False
|
||||
sabnzbd.MY_FULLNAME = __file__
|
||||
sabnzbd.MY_FULLNAME = sys.argv[0]
|
||||
sabnzbd.MY_NAME = os.path.basename(sabnzbd.MY_FULLNAME)
|
||||
fork = False
|
||||
pause = False
|
||||
inifile = None
|
||||
web_host = None
|
||||
web_port = None
|
||||
cherryhost = None
|
||||
cherryport = None
|
||||
https_port = None
|
||||
cherrypylogging = None
|
||||
clean_up = False
|
||||
@@ -857,7 +883,7 @@ def main():
|
||||
if opt == "--servicecall":
|
||||
sabnzbd.MY_FULLNAME = arg
|
||||
elif opt in ("-d", "--daemon"):
|
||||
if not sabnzbd.WINDOWS:
|
||||
if not sabnzbd.WIN32:
|
||||
fork = True
|
||||
autobrowser = False
|
||||
sabnzbd.DAEMON = True
|
||||
@@ -872,11 +898,14 @@ def main():
|
||||
elif opt in ("-t", "--templates"):
|
||||
web_dir = arg
|
||||
elif opt in ("-s", "--server"):
|
||||
(web_host, web_port) = split_host(arg)
|
||||
(cherryhost, cherryport) = split_host(arg)
|
||||
elif opt in ("-n", "--nobrowser"):
|
||||
autobrowser = False
|
||||
elif opt in ("-b", "--browser"):
|
||||
autobrowser = sabnzbd.misc.bool_conv(arg)
|
||||
try:
|
||||
autobrowser = bool(int(arg))
|
||||
except ValueError:
|
||||
autobrowser = True
|
||||
elif opt == "--autorestarted":
|
||||
autorestarted = True
|
||||
elif opt in ("-c", "--clean"):
|
||||
@@ -886,14 +915,13 @@ def main():
|
||||
elif opt in ("-l", "--logging"):
|
||||
try:
|
||||
logging_level = int(arg)
|
||||
except Exception:
|
||||
except:
|
||||
logging_level = -2
|
||||
if logging_level < -1 or logging_level > 2:
|
||||
print_help()
|
||||
exit_sab(1)
|
||||
elif opt == "--console":
|
||||
console_logging = True
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
elif opt in ("-v", "--version"):
|
||||
print_version()
|
||||
exit_sab(0)
|
||||
@@ -938,7 +966,7 @@ def main():
|
||||
org_dir = os.getcwd()
|
||||
|
||||
# Need console logging if requested, for SABnzbd.py and SABnzbd-console.exe
|
||||
console_logging = console_logging or sys.executable.endswith("console.exe") or not hasattr(sys, "frozen")
|
||||
console_logging = console_logging or sabnzbd.MY_NAME.lower().find("-console") > 0 or not hasattr(sys, "frozen")
|
||||
console_logging = console_logging and not sabnzbd.DAEMON
|
||||
|
||||
LOGLEVELS = (logging.FATAL, logging.WARNING, logging.INFO, logging.DEBUG)
|
||||
@@ -995,35 +1023,35 @@ def main():
|
||||
sabnzbd.cfg.ipv6_hosting.set(ipv6_hosting)
|
||||
|
||||
# Determine web host address
|
||||
web_host, web_port, browserhost, https_port = get_webhost(web_host, web_port, https_port)
|
||||
cherryhost, cherryport, browserhost, https_port = get_webhost(cherryhost, cherryport, https_port)
|
||||
enable_https = sabnzbd.cfg.enable_https()
|
||||
|
||||
# When this is a daemon, just check and bail out if port in use
|
||||
if sabnzbd.DAEMON:
|
||||
if enable_https and https_port:
|
||||
try:
|
||||
portend.free(web_host, https_port, timeout=0.05)
|
||||
portend.free(cherryhost, https_port, timeout=0.05)
|
||||
except IOError:
|
||||
abort_and_show_error(browserhost, web_port)
|
||||
except Exception:
|
||||
abort_and_show_error(browserhost, web_port, "49")
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
except:
|
||||
abort_and_show_error(browserhost, cherryport, "49")
|
||||
try:
|
||||
portend.free(web_host, web_port, timeout=0.05)
|
||||
portend.free(cherryhost, cherryport, timeout=0.05)
|
||||
except IOError:
|
||||
abort_and_show_error(browserhost, web_port)
|
||||
except Exception:
|
||||
abort_and_show_error(browserhost, web_port, "49")
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
except:
|
||||
abort_and_show_error(browserhost, cherryport, "49")
|
||||
|
||||
# Windows instance is reachable through registry
|
||||
url = None
|
||||
if sabnzbd.WINDOWS and not new_instance:
|
||||
if sabnzbd.WIN32 and not new_instance:
|
||||
url = get_connection_info()
|
||||
if url and check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
exit_sab(0)
|
||||
|
||||
# SSL
|
||||
if enable_https:
|
||||
port = https_port or web_port
|
||||
port = https_port or cherryport
|
||||
try:
|
||||
portend.free(browserhost, port, timeout=0.05)
|
||||
except IOError as error:
|
||||
@@ -1035,7 +1063,7 @@ def main():
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
# Bail out if we have fixed our ports after first start-up
|
||||
if sabnzbd.cfg.fixed_ports():
|
||||
abort_and_show_error(browserhost, web_port)
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
# Find free port to bind
|
||||
newport = find_free_port(browserhost, port)
|
||||
if newport > 0:
|
||||
@@ -1045,34 +1073,34 @@ def main():
|
||||
sabnzbd.cfg.https_port.set(newport)
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
web_port = newport
|
||||
sabnzbd.cfg.web_port.set(newport)
|
||||
except Exception:
|
||||
cherryport = newport
|
||||
sabnzbd.cfg.cherryport.set(newport)
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
abort_and_show_error(browserhost, web_port, "49")
|
||||
abort_and_show_error(browserhost, cherryport, "49")
|
||||
|
||||
# NonSSL check if there's no HTTPS or we only use 1 port
|
||||
if not (enable_https and not https_port):
|
||||
try:
|
||||
portend.free(browserhost, web_port, timeout=0.05)
|
||||
portend.free(browserhost, cherryport, timeout=0.05)
|
||||
except IOError as error:
|
||||
if str(error) == "Port not bound.":
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = "http://%s:%s%s/api?" % (browserhost, web_port, sabnzbd.cfg.url_base())
|
||||
url = "http://%s:%s%s/api?" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
# Bail out if we have fixed our ports after first start-up
|
||||
if sabnzbd.cfg.fixed_ports():
|
||||
abort_and_show_error(browserhost, web_port)
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
# Find free port to bind
|
||||
port = find_free_port(browserhost, web_port)
|
||||
port = find_free_port(browserhost, cherryport)
|
||||
if port > 0:
|
||||
sabnzbd.cfg.web_port.set(port)
|
||||
web_port = port
|
||||
except Exception:
|
||||
sabnzbd.cfg.cherryport.set(port)
|
||||
cherryport = port
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
abort_and_show_error(browserhost, web_port, "49")
|
||||
abort_and_show_error(browserhost, cherryport, "49")
|
||||
|
||||
# We found a port, now we never check again
|
||||
sabnzbd.cfg.fixed_ports.set(True)
|
||||
@@ -1084,11 +1112,12 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if clean_up:
|
||||
for x in globber_full(logdir):
|
||||
xlist = globber_full(logdir)
|
||||
for x in xlist:
|
||||
if RSS_FILE_NAME not in x:
|
||||
try:
|
||||
os.remove(x)
|
||||
except Exception:
|
||||
except:
|
||||
pass
|
||||
|
||||
# Prevent the logger from raising exceptions
|
||||
@@ -1100,13 +1129,12 @@ 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()
|
||||
)
|
||||
@@ -1119,7 +1147,7 @@ def main():
|
||||
exit_sab(2)
|
||||
|
||||
# Fork on non-Windows processes
|
||||
if fork and not sabnzbd.WINDOWS:
|
||||
if fork and not sabnzbd.WIN32:
|
||||
daemonize()
|
||||
else:
|
||||
if console_logging:
|
||||
@@ -1133,8 +1161,53 @@ def main():
|
||||
# Start SABnzbd
|
||||
logging.info("--------------------------------")
|
||||
logging.info("%s-%s", sabnzbd.MY_NAME, sabnzbd.__version__)
|
||||
|
||||
# See if we can get version from git when running an unknown revision
|
||||
if sabnzbd.__baseline__ == "unknown":
|
||||
try:
|
||||
sabnzbd.__baseline__ = sabnzbd.misc.run_command(
|
||||
["git", "rev-parse", "--short", "HEAD"], cwd=sabnzbd.DIR_PROG
|
||||
).strip()
|
||||
except:
|
||||
pass
|
||||
logging.info("Commit = %s", sabnzbd.__baseline__)
|
||||
|
||||
logging.info("Full executable path = %s", sabnzbd.MY_FULLNAME)
|
||||
logging.info("Arguments = %s", sabnzbd.CMDLINE)
|
||||
logging.info("Python-version = %s", sys.version)
|
||||
logging.info("Dockerized = %s", sabnzbd.DOCKER)
|
||||
logging.info("CPU architecture = %s", platform.uname().machine)
|
||||
|
||||
try:
|
||||
logging.info("Platform = %s - %s", os.name, platform.platform())
|
||||
except:
|
||||
# Can fail on special platforms (like Snapcraft or embedded)
|
||||
pass
|
||||
|
||||
# Find encoding; relevant for external processing activities
|
||||
logging.info("Preferred encoding = %s", sabnzbd.encoding.CODEPAGE)
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly advised:
|
||||
if not sabnzbd.WIN32 and not sabnzbd.MACOS and not ("utf-8" in sabnzbd.encoding.CODEPAGE.lower()):
|
||||
helpful_warning(
|
||||
T(
|
||||
"SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads."
|
||||
),
|
||||
sabnzbd.encoding.CODEPAGE,
|
||||
)
|
||||
|
||||
# Verify umask, we need at least 700
|
||||
if not sabnzbd.WIN32 and sabnzbd.ORG_UMASK > int("077", 8):
|
||||
sabnzbd.misc.helpful_warning(
|
||||
T("Current umask (%o) might deny SABnzbd access to the files and folders it creates."),
|
||||
sabnzbd.ORG_UMASK,
|
||||
)
|
||||
|
||||
# Log JSON module in case of problems
|
||||
logging.debug("JSON-module = %s %s", sabnzbd.api.json.__name__, sabnzbd.api.json.__version__)
|
||||
|
||||
# SSL Information
|
||||
logging.info("SSL version = %s", ssl.OPENSSL_VERSION)
|
||||
|
||||
# Load (extra) certificates if supplied by certifi
|
||||
# This is optional and provided in the binaries
|
||||
@@ -1145,14 +1218,28 @@ def main():
|
||||
os.environ["SSL_CERT_FILE"] = certifi.where()
|
||||
logging.info("Certifi version = %s", certifi.__version__)
|
||||
logging.info("Loaded additional certificates from %s", os.environ["SSL_CERT_FILE"])
|
||||
except Exception:
|
||||
except:
|
||||
# Sometimes the certificate file is blocked
|
||||
logging.warning(T("Could not load additional certificates from certifi package"))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
|
||||
# Extra startup info
|
||||
if sabnzbd.cfg.log_level() > 1:
|
||||
# List the number of certificates available (can take up to 1.5 seconds)
|
||||
logging.debug("Available certificates = %s", repr(ssl.create_default_context().cert_store_stats()))
|
||||
|
||||
# List networking
|
||||
localipv4()
|
||||
publicipv4()
|
||||
ipv6()
|
||||
dnslookup()
|
||||
|
||||
# Measure basic system performance measured by pystone and - if possible - CPU model
|
||||
getpystone()
|
||||
getcpu()
|
||||
|
||||
logging.info("Using INI file %s", inifile)
|
||||
|
||||
# Store auto-browser setting from command line
|
||||
if autobrowser is not None:
|
||||
sabnzbd.cfg.autobrowser.set(autobrowser)
|
||||
|
||||
@@ -1169,7 +1256,7 @@ def main():
|
||||
|
||||
# Handle the several tray icons
|
||||
if sabnzbd.cfg.tray_icon() and not sabnzbd.DAEMON and not sabnzbd.WIN_SERVICE:
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
sabnzbd.WINTRAY = sabnzbd.sabtray.SABTrayThread()
|
||||
elif sabnzbd.LINUX_POWER and os.environ.get("DISPLAY"):
|
||||
try:
|
||||
@@ -1180,7 +1267,7 @@ def main():
|
||||
import sabnzbd.sabtraylinux
|
||||
|
||||
sabnzbd.sabtraylinux.StatusIcon()
|
||||
except Exception:
|
||||
except:
|
||||
logging.info("python3-gi not found, no SysTray.")
|
||||
|
||||
# Find external programs
|
||||
@@ -1209,7 +1296,7 @@ def main():
|
||||
try:
|
||||
trialcontext.load_cert_chain(https_cert, https_key)
|
||||
logging.info("HTTPS keys are OK")
|
||||
except Exception:
|
||||
except:
|
||||
logging.warning(T("Disabled HTTPS because of invalid CERT and KEY files"))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
enable_https = False
|
||||
@@ -1218,29 +1305,29 @@ def main():
|
||||
# Starting of the webserver
|
||||
# Determine if this system has multiple definitions for 'localhost'
|
||||
hosts = all_localhosts()
|
||||
multilocal = len(hosts) > 1 and web_host in ("localhost", "0.0.0.0")
|
||||
multilocal = len(hosts) > 1 and cherryhost in ("localhost", "0.0.0.0")
|
||||
|
||||
# For 0.0.0.0 CherryPy will always pick IPv4, so make sure the secondary localhost is IPv6
|
||||
if multilocal and web_host == "0.0.0.0" and hosts[1] == "127.0.0.1":
|
||||
if multilocal and cherryhost == "0.0.0.0" and hosts[1] == "127.0.0.1":
|
||||
hosts[1] = "::1"
|
||||
|
||||
# The Windows binary requires numeric localhost as primary address
|
||||
if web_host == "localhost":
|
||||
web_host = hosts[0]
|
||||
if cherryhost == "localhost":
|
||||
cherryhost = hosts[0]
|
||||
|
||||
if enable_https:
|
||||
if https_port:
|
||||
# Extra HTTP port for primary localhost
|
||||
attach_server(web_host, web_port)
|
||||
attach_server(cherryhost, cherryport)
|
||||
if multilocal:
|
||||
# Extra HTTP port for secondary localhost
|
||||
attach_server(hosts[1], web_port)
|
||||
attach_server(hosts[1], cherryport)
|
||||
# Extra HTTPS port for secondary localhost
|
||||
attach_server(hosts[1], https_port, https_cert, https_key, https_chain)
|
||||
web_port = https_port
|
||||
cherryport = https_port
|
||||
elif multilocal:
|
||||
# Extra HTTPS port for secondary localhost
|
||||
attach_server(hosts[1], web_port, https_cert, https_key, https_chain)
|
||||
attach_server(hosts[1], cherryport, https_cert, https_key, https_chain)
|
||||
|
||||
cherrypy.config.update(
|
||||
{
|
||||
@@ -1252,7 +1339,7 @@ def main():
|
||||
)
|
||||
elif multilocal:
|
||||
# Extra HTTP port for secondary localhost
|
||||
attach_server(hosts[1], web_port)
|
||||
attach_server(hosts[1], cherryport)
|
||||
|
||||
if no_login:
|
||||
sabnzbd.cfg.username.set("")
|
||||
@@ -1275,9 +1362,10 @@ def main():
|
||||
cherrypy.config.update(
|
||||
{
|
||||
"server.environment": "production",
|
||||
"server.socket_host": web_host,
|
||||
"server.socket_port": web_port,
|
||||
"server.socket_host": cherryhost,
|
||||
"server.socket_port": cherryport,
|
||||
"server.shutdown_timeout": 0,
|
||||
"log.screen": False,
|
||||
"engine.autoreload.on": False,
|
||||
"tools.encode.on": True,
|
||||
"tools.gzip.on": True,
|
||||
@@ -1288,22 +1376,14 @@ def main():
|
||||
}
|
||||
)
|
||||
|
||||
# Monkey-patch key validation to prevent cherrypy from stumbling over invalid cookies
|
||||
http.cookies._is_legal_key = lambda _: True
|
||||
|
||||
# Catch shutdown errors that can break cherrypy/cheroot
|
||||
# See https://github.com/cherrypy/cheroot/issues/710
|
||||
try:
|
||||
cheroot.errors.acceptable_sock_shutdown_exceptions += (OSError,)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Do we want CherryPy Logging? Cannot be done via the config
|
||||
cherrypy.log.screen = False
|
||||
cherrypy.log.access_log.propagate = False
|
||||
if cherrypylogging:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
cherrypy.log.screen = True
|
||||
cherrypy.log.access_log.propagate = True
|
||||
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
|
||||
else:
|
||||
cherrypy.log.access_log.propagate = False
|
||||
|
||||
# Force mimetypes (OS might overwrite them)
|
||||
forced_mime_types = {"css": "text/css", "js": "application/javascript"}
|
||||
@@ -1347,17 +1427,17 @@ def main():
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
logging.info("Starting web-interface on %s:%s", web_host, web_port)
|
||||
logging.info("Starting web-interface on %s:%s", cherryhost, cherryport)
|
||||
|
||||
sabnzbd.cfg.log_level.callback(guard_loglevel)
|
||||
|
||||
try:
|
||||
cherrypy.engine.start()
|
||||
except Exception:
|
||||
except:
|
||||
# Since the webserver is started by cherrypy in a separate thread, we can't really catch any
|
||||
# start-up errors. This try/except only catches very few errors, the rest is only shown in the console.
|
||||
logging.error(T("Failed to start web-interface: "), exc_info=True)
|
||||
abort_and_show_error(browserhost, web_port)
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
|
||||
# Create a record of the active cert/key/chain files, for use with config.create_config_backup()
|
||||
if enable_https:
|
||||
@@ -1365,18 +1445,24 @@ def main():
|
||||
if full_path := getattr(sabnzbd.cfg, setting).get_path():
|
||||
sabnzbd.CONFIG_BACKUP_HTTPS_OK.append(full_path)
|
||||
|
||||
# Set URL for browser
|
||||
if enable_https:
|
||||
sabnzbd.BROWSER_URL = "https://%s:%s%s" % (browserhost, web_port, sabnzbd.cfg.url_base())
|
||||
else:
|
||||
sabnzbd.BROWSER_URL = "http://%s:%s%s" % (browserhost, web_port, sabnzbd.cfg.url_base())
|
||||
if sabnzbd.WIN32:
|
||||
if enable_https:
|
||||
mode = "s"
|
||||
else:
|
||||
mode = ""
|
||||
api_url = "http%s://%s:%s%s/api?apikey=%s" % (
|
||||
mode,
|
||||
browserhost,
|
||||
cherryport,
|
||||
sabnzbd.cfg.url_base(),
|
||||
sabnzbd.cfg.api_key(),
|
||||
)
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
# Write URL for uploads and version check directly to registry
|
||||
set_connection_info(f"{sabnzbd.BROWSER_URL}/api?apikey={sabnzbd.cfg.api_key()}")
|
||||
# Write URL directly to registry
|
||||
set_connection_info(api_url)
|
||||
|
||||
if pid_path or pid_file:
|
||||
sabnzbd.pid_file(pid_path, pid_file, web_port)
|
||||
sabnzbd.pid_file(pid_path, pid_file, cherryport)
|
||||
|
||||
# Stop here in case of fatal errors
|
||||
if sabnzbd.NO_DOWNLOADING:
|
||||
@@ -1389,7 +1475,7 @@ def main():
|
||||
logging.info("Starting %s-%s", sabnzbd.MY_NAME, sabnzbd.__version__)
|
||||
try:
|
||||
sabnzbd.start()
|
||||
except Exception:
|
||||
except:
|
||||
logging.exception("Failed to start %s-%s", sabnzbd.MY_NAME, sabnzbd.__version__)
|
||||
sabnzbd.halt()
|
||||
|
||||
@@ -1398,13 +1484,58 @@ def main():
|
||||
for upload_nzb in upload_nzbs:
|
||||
sabnzbd.nzbparser.add_nzbfile(upload_nzb)
|
||||
|
||||
# Set URL for browser
|
||||
if enable_https:
|
||||
browser_url = "https://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
else:
|
||||
browser_url = "http://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
sabnzbd.BROWSER_URL = browser_url
|
||||
|
||||
if not autorestarted:
|
||||
launch_a_browser(sabnzbd.BROWSER_URL)
|
||||
launch_a_browser(browser_url)
|
||||
notifier.send_notification("SABnzbd", T("SABnzbd %s started") % sabnzbd.__version__, "startup")
|
||||
# Now's the time to check for a new version
|
||||
check_latest_version()
|
||||
autorestarted = False
|
||||
|
||||
# Do checks and miscellaneous logging in separate thread for performance
|
||||
threading.Thread(target=sabnzbd.delayed_startup_actions).start()
|
||||
# Start SSDP and Bonjour if SABnzbd isn't listening on localhost only
|
||||
if sabnzbd.cfg.enable_broadcast() and not is_localhost(cherryhost):
|
||||
# Try to find a LAN IP address for SSDP/Bonjour
|
||||
if is_lan_addr(cherryhost):
|
||||
# A specific listening address was configured, use that
|
||||
external_host = cherryhost
|
||||
else:
|
||||
# Fall back to the IPv4 address of the LAN interface
|
||||
external_host = localipv4()
|
||||
logging.debug("Using %s as host address for Bonjour and SSDP", external_host)
|
||||
|
||||
# Only broadcast to local network addresses. If local ranges have been defined, further
|
||||
# restrict broadcasts to those specific ranges in order to avoid broadcasting to the "wrong"
|
||||
# private network when the system is connected to multiple such networks (e.g. a corporate
|
||||
# VPN in addition to a standard household LAN).
|
||||
if is_lan_addr(external_host) and (
|
||||
(not sabnzbd.cfg.local_ranges()) or any(ip_in_subnet(external_host, r) for r in sabnzbd.cfg.local_ranges())
|
||||
):
|
||||
# Start Bonjour and SSDP
|
||||
sabnzbd.zconfig.set_bonjour(external_host, cherryport)
|
||||
|
||||
# Set URL for browser for external hosts
|
||||
ssdp_url = "%s://%s:%s%s" % (
|
||||
("https" if enable_https else "http"),
|
||||
external_host,
|
||||
cherryport,
|
||||
sabnzbd.cfg.url_base(),
|
||||
)
|
||||
ssdp.start_ssdp(
|
||||
external_host,
|
||||
"SABnzbd",
|
||||
ssdp_url,
|
||||
"SABnzbd %s" % sabnzbd.__version__,
|
||||
"SABnzbd Team",
|
||||
"https://sabnzbd.org/",
|
||||
"SABnzbd %s" % sabnzbd.__version__,
|
||||
ssdp_broadcast_interval=sabnzbd.cfg.ssdp_broadcast_interval(),
|
||||
)
|
||||
|
||||
# Have to keep this running, otherwise logging will terminate
|
||||
timer = 0
|
||||
@@ -1465,15 +1596,16 @@ def main():
|
||||
if hasattr(sys, "frozen"):
|
||||
if sabnzbd.MACOS:
|
||||
# On macOS restart of app instead of embedded python
|
||||
my_name = sabnzbd.MY_FULLNAME.replace("/Contents/MacOS/SABnzbd", "")
|
||||
my_args = " ".join(sys.argv[1:])
|
||||
cmd = 'kill -9 %s && open "%s" --args %s' % (os.getpid(), sys.executable, my_args)
|
||||
cmd = 'kill -9 %s && open "%s" --args %s' % (os.getpid(), my_name, my_args)
|
||||
logging.info("Launching: %s", cmd)
|
||||
os.system(cmd)
|
||||
elif sabnzbd.WIN_SERVICE:
|
||||
# Use external service handler to do the restart
|
||||
# Wait 5 seconds to clean up
|
||||
subprocess.Popen("timeout 5 & sc start SABnzbd", shell=True)
|
||||
elif sabnzbd.WINDOWS:
|
||||
elif sabnzbd.WIN32:
|
||||
# Just a simple restart of the exe
|
||||
os.execv(sys.executable, ['"%s"' % arg for arg in sys.argv])
|
||||
else:
|
||||
@@ -1495,7 +1627,7 @@ def main():
|
||||
if hasattr(sys, "frozen") and sabnzbd.MACOS:
|
||||
try:
|
||||
AppHelper.stopEventLoop()
|
||||
except Exception:
|
||||
except:
|
||||
# Failing AppHelper library!
|
||||
os._exit(0)
|
||||
elif sabnzbd.WIN_SERVICE:
|
||||
@@ -1510,7 +1642,7 @@ def main():
|
||||
##############################################################################
|
||||
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
|
||||
class SABnzbd(win32serviceutil.ServiceFramework):
|
||||
"""Win32 Service Handler"""
|
||||
@@ -1626,20 +1758,21 @@ if __name__ == "__main__":
|
||||
signal.signal(signal.SIGINT, sabnzbd.sig_handler)
|
||||
signal.signal(signal.SIGTERM, sabnzbd.sig_handler)
|
||||
|
||||
if sabnzbd.WINDOWS:
|
||||
if sabnzbd.WIN32:
|
||||
if not handle_windows_service():
|
||||
main()
|
||||
|
||||
elif sabnzbd.MACOS and sabnzbd.FOUNDATION:
|
||||
# macOS binary runner
|
||||
from threading import Thread
|
||||
from PyObjCTools import AppHelper
|
||||
from AppKit import NSApplication
|
||||
from sabnzbd.macosmenu import SABnzbdDelegate
|
||||
from sabnzbd.osxmenu import SABnzbdDelegate
|
||||
|
||||
# Need to run the main application in separate thread because the eventLoop
|
||||
# has to be in the main thread. The eventLoop is required for the menu.
|
||||
# This code is made with trial-and-error, please feel free to improve!
|
||||
class startApp(threading.Thread):
|
||||
class startApp(Thread):
|
||||
def run(self):
|
||||
main()
|
||||
AppHelper.stopEventLoop()
|
||||
@@ -1649,10 +1782,10 @@ if __name__ == "__main__":
|
||||
|
||||
# Initialize the menu
|
||||
shared_app = NSApplication.sharedApplication()
|
||||
sabnzbd.MACOSTRAY = SABnzbdDelegate.alloc().init()
|
||||
shared_app.setDelegate_(sabnzbd.MACOSTRAY)
|
||||
sabnzbd_menu = SABnzbdDelegate.alloc().init()
|
||||
shared_app.setDelegate_(sabnzbd_menu)
|
||||
# Build the menu
|
||||
sabnzbd.MACOSTRAY.awakeFromNib()
|
||||
sabnzbd_menu.awakeFromNib()
|
||||
# Run the main eventloop
|
||||
AppHelper.runEventLoop()
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# -*- mode: python -*-
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from PyInstaller.building.api import EXE, COLLECT, PYZ
|
||||
@@ -7,14 +8,13 @@ from PyInstaller.building.build_main import Analysis
|
||||
from PyInstaller.building.osx import BUNDLE
|
||||
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||||
|
||||
from builder.constants import EXTRA_FILES, EXTRA_FOLDERS, RELEASE_VERSION, RELEASE_VERSION_TUPLE
|
||||
from builder.constants import EXTRA_FILES, EXTRA_FOLDERS, RELEASE_VERSION
|
||||
|
||||
# Add extra files in the PyInstaller-spec
|
||||
extra_pyinstaller_files = []
|
||||
|
||||
# Add hidden imports
|
||||
extra_hiddenimports = ["Cheetah.DummyTransaction", "cheroot.ssl.builtin", "certifi"]
|
||||
extra_hiddenimports.extend(collect_submodules("apprise"))
|
||||
extra_hiddenimports.extend(collect_submodules("babelfish.converters"))
|
||||
extra_hiddenimports.extend(collect_submodules("guessit.data"))
|
||||
|
||||
@@ -22,9 +22,9 @@ extra_hiddenimports.extend(collect_submodules("guessit.data"))
|
||||
if sys.platform == "darwin":
|
||||
extra_hiddenimports.extend(["objc", "PyObjCTools"])
|
||||
# macOS folders
|
||||
EXTRA_FOLDERS += ["macos/par2/", "macos/unrar/", "macos/7zip/"]
|
||||
EXTRA_FOLDERS += ["osx/par2/", "osx/unrar/", "osx/7zip/"]
|
||||
# Add NZB-icon file
|
||||
extra_pyinstaller_files.append(("builder/macos/image/nzbfile.icns", "."))
|
||||
extra_pyinstaller_files.append(("builder/osx/image/nzbfile.icns", "."))
|
||||
# Version information is set differently on macOS
|
||||
version_info = None
|
||||
else:
|
||||
@@ -40,16 +40,20 @@ else:
|
||||
)
|
||||
|
||||
# Windows
|
||||
extra_hiddenimports.extend(["win32timezone", "winrt.windows.foundation.collections"])
|
||||
EXTRA_FOLDERS += ["win/par2/", "win/unrar/", "win/7zip/"]
|
||||
extra_hiddenimports.append("win32timezone")
|
||||
EXTRA_FOLDERS += ["win/multipar/", "win/par2/", "win/unrar/", "win/7zip/"]
|
||||
EXTRA_FILES += ["portable.cmd"]
|
||||
|
||||
# Parse the version info
|
||||
version_regexed = re.search(r"(\d+)\.(\d+)\.(\d+)([a-zA-Z]*)(\d*)", RELEASE_VERSION)
|
||||
version_tuple = (int(version_regexed.group(1)), int(version_regexed.group(2)), int(version_regexed.group(3)), 0)
|
||||
|
||||
# Detailed instructions are in the PyInstaller documentation
|
||||
# We don't include the alpha/beta/rc in the counters
|
||||
version_info = VSVersionInfo(
|
||||
ffi=FixedFileInfo(
|
||||
filevers=RELEASE_VERSION_TUPLE,
|
||||
prodvers=RELEASE_VERSION_TUPLE,
|
||||
filevers=version_tuple,
|
||||
prodvers=version_tuple,
|
||||
mask=0x3F,
|
||||
flags=0x0,
|
||||
OS=0x40004,
|
||||
@@ -87,15 +91,12 @@ for folder_item in EXTRA_FOLDERS:
|
||||
# Add babelfish data files
|
||||
extra_pyinstaller_files.extend(collect_data_files("babelfish"))
|
||||
extra_pyinstaller_files.extend(collect_data_files("guessit"))
|
||||
extra_pyinstaller_files.extend(collect_data_files("apprise"))
|
||||
extra_pyinstaller_files.extend(collect_data_files("dateutil"))
|
||||
|
||||
pyi_analysis = Analysis(
|
||||
["SABnzbd.py"],
|
||||
datas=extra_pyinstaller_files,
|
||||
hiddenimports=extra_hiddenimports,
|
||||
excludes=["ujson", "FixTk", "tcl", "tk", "_tkinter", "tkinter", "Tkinter", "pydoc", "pydoc_data.topics"],
|
||||
module_collection_mode={"apprise.plugins": "py"},
|
||||
excludes=["ujson", "FixTk", "tcl", "tk", "_tkinter", "tkinter", "Tkinter"],
|
||||
)
|
||||
|
||||
pyz = PYZ(pyi_analysis.pure, pyi_analysis.zipped_data)
|
||||
@@ -112,13 +113,13 @@ exe = EXE(
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="SABnzbd",
|
||||
upx=True,
|
||||
console=False,
|
||||
append_pkg=False,
|
||||
icon="icons/sabnzbd.ico",
|
||||
contents_directory=".",
|
||||
version=version_info,
|
||||
target_arch="universal2",
|
||||
entitlements_file="builder/macos/entitlements.plist",
|
||||
entitlements_file="builder/osx/entitlements.plist",
|
||||
codesign_identity=codesign_identity,
|
||||
)
|
||||
|
||||
@@ -133,9 +134,9 @@ if sys.platform == "win32":
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name="SABnzbd-console",
|
||||
upx=True,
|
||||
append_pkg=False,
|
||||
icon="icons/sabnzbd.ico",
|
||||
contents_directory=".",
|
||||
version=version_info,
|
||||
)
|
||||
|
||||
@@ -144,6 +145,7 @@ if sys.platform == "win32":
|
||||
pyi_analysis.binaries,
|
||||
pyi_analysis.zipfiles,
|
||||
pyi_analysis.datas,
|
||||
upx=True,
|
||||
name="SABnzbd-console",
|
||||
)
|
||||
|
||||
@@ -166,14 +168,14 @@ if sys.platform == "darwin":
|
||||
"NSPersistentStoreTypeKey": "Binary",
|
||||
}
|
||||
],
|
||||
"LSMinimumSystemVersion": "10.13",
|
||||
"LSMinimumSystemVersion": "10.9",
|
||||
"LSEnvironment": {"LANG": "en_US.UTF-8", "LC_ALL": "en_US.UTF-8"},
|
||||
}
|
||||
|
||||
app = BUNDLE(
|
||||
coll,
|
||||
name="SABnzbd.app",
|
||||
icon="builder/macos/image/sabnzbdplus.icns",
|
||||
icon="builder/osx/image/sabnzbdplus.icns",
|
||||
bundle_identifier="org.sabnzbd.sabnzbd",
|
||||
info_plist=info_plist,
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2008-2017 The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -16,7 +16,6 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
# Constants
|
||||
VERSION_FILE = "sabnzbd/version.py"
|
||||
@@ -34,18 +33,14 @@ RELEASE_VERSION = __version__
|
||||
# Pre-releases are longer than 6 characters (e.g. 3.1.0Beta1 vs 3.1.0, but also 3.0.11)
|
||||
PRERELEASE = len(RELEASE_VERSION) > 5
|
||||
|
||||
# Parse the version info for Windows file properties information
|
||||
version_regexed = re.search(r"(\d+)\.(\d+)\.(\d+)([a-zA-Z]*)(\d*)", RELEASE_VERSION)
|
||||
RELEASE_VERSION_TUPLE = (int(version_regexed.group(1)), int(version_regexed.group(2)), int(version_regexed.group(3)), 0)
|
||||
RELEASE_VERSION_BASE = f"{RELEASE_VERSION_TUPLE[0]}.{RELEASE_VERSION_TUPLE[1]}.{RELEASE_VERSION_TUPLE[2]}"
|
||||
|
||||
# Define release name
|
||||
RELEASE_NAME = "SABnzbd-%s" % RELEASE_VERSION
|
||||
RELEASE_TITLE = "SABnzbd %s" % RELEASE_VERSION
|
||||
RELEASE_SRC = RELEASE_NAME + "-src.tar.gz"
|
||||
RELEASE_BINARY = RELEASE_NAME + "-win64-bin.zip"
|
||||
RELEASE_BINARY_32 = RELEASE_NAME + "-win32-bin.zip"
|
||||
RELEASE_BINARY_64 = RELEASE_NAME + "-win64-bin.zip"
|
||||
RELEASE_INSTALLER = RELEASE_NAME + "-win-setup.exe"
|
||||
RELEASE_MACOS = RELEASE_NAME + "-macos.dmg"
|
||||
RELEASE_MACOS = RELEASE_NAME + "-osx.dmg"
|
||||
RELEASE_README = "README.mkd"
|
||||
|
||||
# Used in package.py and SABnzbd.spec
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2008-2017 The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -24,7 +24,7 @@ if __name__ == "__main__":
|
||||
# Check for DMGBuild
|
||||
try:
|
||||
import dmgbuild
|
||||
except Exception:
|
||||
except:
|
||||
print("Requires dmgbuild-module, use pip install dmgbuild")
|
||||
exit()
|
||||
|
||||
@@ -39,7 +39,7 @@ if __name__ == "__main__":
|
||||
# Create sub-folder to upload later
|
||||
release = RELEASE_VERSION
|
||||
prod = "SABnzbd-" + release
|
||||
fileDmg = prod + "-macos.dmg"
|
||||
fileDmg = prod + "-osx.dmg"
|
||||
|
||||
# Path to app file
|
||||
apppath = "dist/SABnzbd.app"
|
||||
@@ -48,8 +48,8 @@ if __name__ == "__main__":
|
||||
readmepath = os.path.join(apppath, "Contents/Resources/README.txt")
|
||||
|
||||
# Path to background and the icon
|
||||
backgroundpath = "builder/macos/image/sabnzbd_new_bg.png"
|
||||
iconpath = "builder/macos/image/sabnzbdplus.icns"
|
||||
backgroundpath = "builder/osx/image/sabnzbd_new_bg.png"
|
||||
iconpath = "builder/osx/image/sabnzbdplus.icns"
|
||||
|
||||
# Make DMG
|
||||
print("Building DMG")
|
||||
|
||||
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
3
builder/osx/requirements.txt
Normal file
3
builder/osx/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# Special requirements for macOS universal2 binary release
|
||||
# This way dependabot can auto-update them
|
||||
cryptography==41.0.4
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2008-2017 The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -16,6 +16,7 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import glob
|
||||
import platform
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
@@ -27,16 +28,15 @@ import tarfile
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import configobj
|
||||
import packaging.version
|
||||
from typing import List
|
||||
|
||||
from constants import (
|
||||
RELEASE_VERSION,
|
||||
RELEASE_VERSION_TUPLE,
|
||||
VERSION_FILE,
|
||||
RELEASE_README,
|
||||
RELEASE_NAME,
|
||||
RELEASE_BINARY,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
ON_GITHUB_ACTIONS,
|
||||
RELEASE_THIS,
|
||||
@@ -58,21 +58,16 @@ def safe_remove(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def delete_files_glob(glob_pattern: str, allow_no_matches: bool = False):
|
||||
"""Delete one file or set of files from wild-card spec.
|
||||
We expect to match at least 1 file, to force expected behavior"""
|
||||
if files_to_remove := glob.glob(glob_pattern):
|
||||
for path in files_to_remove:
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
else:
|
||||
if not allow_no_matches:
|
||||
raise FileNotFoundError(f"No files found that match '{glob_pattern}'")
|
||||
def delete_files_glob(name):
|
||||
"""Delete one file or set of files from wild-card spec"""
|
||||
for f in glob.glob(name):
|
||||
if os.path.exists(f):
|
||||
os.remove(f)
|
||||
|
||||
|
||||
def run_external_command(command: List[str], print_output: bool = True, **kwargs):
|
||||
def run_external_command(command: List[str], print_output: bool = True):
|
||||
"""Wrapper to ease the use of calling external programs"""
|
||||
process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, **kwargs)
|
||||
process = subprocess.Popen(command, text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
output, _ = process.communicate()
|
||||
ret = process.wait()
|
||||
if (output and print_output) or ret != 0:
|
||||
@@ -109,52 +104,6 @@ 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:
|
||||
@@ -171,7 +120,7 @@ def test_sab_binary(binary_path: str):
|
||||
try:
|
||||
urllib.request.urlopen(base_url, timeout=1).read()
|
||||
break
|
||||
except Exception:
|
||||
except:
|
||||
time.sleep(1)
|
||||
else:
|
||||
# Print console output and give some time to print
|
||||
@@ -247,74 +196,78 @@ if __name__ == "__main__":
|
||||
if not os.path.exists("locale"):
|
||||
raise FileNotFoundError("Failed to compile language files")
|
||||
|
||||
if "binary" in sys.argv:
|
||||
# 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:
|
||||
# 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")
|
||||
# Check what architecture we are on
|
||||
RELEASE_BINARY = RELEASE_BINARY_32
|
||||
if platform.architecture()[0] == "64bit":
|
||||
RELEASE_BINARY = RELEASE_BINARY_64
|
||||
|
||||
# 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)
|
||||
safe_remove("dist/SABnzbd-console")
|
||||
|
||||
# Remove unwanted DLL's
|
||||
shutil.rmtree("dist/SABnzbd/Pythonwin")
|
||||
delete_files_glob("dist/SABnzbd/api-ms-win*.dll", allow_no_matches=True)
|
||||
delete_files_glob("dist/SABnzbd/ucrtbase.dll", allow_no_matches=True)
|
||||
delete_files_glob("dist/SABnzbd/api-ms-win*.dll")
|
||||
delete_files_glob("dist/SABnzbd/mfc140u.dll")
|
||||
delete_files_glob("dist/SABnzbd/ucrtbase.dll")
|
||||
|
||||
# Test the release
|
||||
test_sab_binary("dist/SABnzbd/SABnzbd.exe")
|
||||
# Remove other files we don't need
|
||||
delete_files_glob("dist/SABnzbd/win32ui.pyd")
|
||||
delete_files_glob("dist/SABnzbd/winxpgui.pyd")
|
||||
|
||||
if "installer" in sys.argv:
|
||||
# Needs to be run on 64 bit
|
||||
if RELEASE_BINARY != RELEASE_BINARY_64:
|
||||
raise RuntimeError("Installer should be created on 64bit Python")
|
||||
|
||||
# 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"])
|
||||
|
||||
# Remove 32bit external executables
|
||||
delete_files_glob("dist/SABnzbd/win/par2/par2.exe")
|
||||
delete_files_glob("dist/SABnzbd/win/multipar/par2j.exe")
|
||||
delete_files_glob("dist/SABnzbd/win/unrar/UnRAR.exe")
|
||||
|
||||
# Run NSIS to build installer
|
||||
run_external_command(
|
||||
[
|
||||
"makensis.exe",
|
||||
"/V3",
|
||||
"/DSAB_PRODUCT=%s" % RELEASE_NAME,
|
||||
"/DSAB_VERSION=%s" % RELEASE_VERSION,
|
||||
"/DSAB_FILE=%s" % RELEASE_INSTALLER,
|
||||
"NSIS_Installer.nsi.tmp",
|
||||
]
|
||||
)
|
||||
|
||||
# Rename the folder
|
||||
shutil.copytree("dist/SABnzbd", RELEASE_NAME)
|
||||
|
||||
# Create the archive
|
||||
run_external_command(["win/7zip/7za.exe", "a", RELEASE_BINARY, "SABnzbd"], cwd="dist")
|
||||
shutil.move(f"dist/{RELEASE_BINARY}", RELEASE_BINARY)
|
||||
run_external_command(["win/7zip/7za.exe", "a", RELEASE_BINARY, RELEASE_NAME])
|
||||
|
||||
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",
|
||||
]
|
||||
)
|
||||
# Test the release, as the very last step to not mess with any release code
|
||||
test_sab_binary("dist/SABnzbd/SABnzbd.exe")
|
||||
|
||||
if "app" in sys.argv:
|
||||
# Must be run on macOS
|
||||
@@ -330,17 +283,14 @@ if __name__ == "__main__":
|
||||
# Otherwise the signature of the main application becomes invalid
|
||||
if authority:
|
||||
files_to_sign = [
|
||||
"macos/par2/par2",
|
||||
"macos/unrar/unrar",
|
||||
"macos/unrar/arm64/unrar",
|
||||
"macos/7zip/7zz",
|
||||
"osx/par2/par2-turbo",
|
||||
"osx/par2/arm64/par2-turbo",
|
||||
"osx/unrar/unrar",
|
||||
"osx/unrar/arm64/unrar",
|
||||
"osx/7zip/7zz",
|
||||
]
|
||||
for file_to_sign in files_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)
|
||||
print("Signing %s with hardended runtime" % file_to_sign)
|
||||
run_external_command(
|
||||
[
|
||||
"codesign",
|
||||
@@ -350,7 +300,7 @@ if __name__ == "__main__":
|
||||
"--options",
|
||||
"runtime",
|
||||
"--entitlements",
|
||||
"builder/macos/entitlements.plist",
|
||||
"builder/osx/entitlements.plist",
|
||||
"-s",
|
||||
authority,
|
||||
file_to_sign,
|
||||
@@ -360,21 +310,17 @@ 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/**/*.so", recursive=True):
|
||||
for bin_to_check in glob.glob("dist/SABnzbd.app/Contents/MacOS/**/*.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
|
||||
@@ -404,8 +350,8 @@ if __name__ == "__main__":
|
||||
if authority not in sign_result or "adhoc" in sign_result or "invalid" in sign_result:
|
||||
raise RuntimeError("Signature of %s seems invalid!" % file_to_check)
|
||||
|
||||
# Always notarize, as newer macOS versions don't allow any code without it
|
||||
if notarization_user and notarization_pass:
|
||||
# Only notarize for real builds that we want to deploy
|
||||
if notarization_user and notarization_pass and RELEASE_THIS:
|
||||
# Prepare zip to upload to notarization service
|
||||
print("Creating zip to send to Apple notarization service")
|
||||
# We need to use ditto, otherwise the signature gets lost!
|
||||
@@ -439,6 +385,8 @@ if __name__ == "__main__":
|
||||
# Staple the notarization!
|
||||
print("Approved! Stapling the result to the app")
|
||||
run_external_command(["xcrun", "stapler", "staple", "dist/SABnzbd.app"])
|
||||
elif notarization_user and notarization_pass:
|
||||
print("Notarization skipped, tag commit to trigger notarization!")
|
||||
else:
|
||||
print("Notarization skipped, NOTARIZATION_USER or NOTARIZATION_PASS missing.")
|
||||
else:
|
||||
@@ -496,7 +444,7 @@ if __name__ == "__main__":
|
||||
tarinfo.uid = 0
|
||||
tarinfo.gid = 0
|
||||
if _file in ("SABnzbd.py", "Sample-PostProc.sh", "make_mo.py", "msgfmt.py"):
|
||||
# Force Linux/macOS scripts as executable
|
||||
# Force Linux/OSX scripts as executable
|
||||
tarinfo.mode = 0o755
|
||||
else:
|
||||
tarinfo.mode = 0o644
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
PyGithub==2.8.1
|
||||
praw==7.8.1
|
||||
PyGithub==1.59.1
|
||||
praw==7.7.1
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2008-2017 The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
@@ -19,6 +19,7 @@ import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import github
|
||||
@@ -26,23 +27,23 @@ import praw
|
||||
|
||||
from constants import (
|
||||
RELEASE_VERSION,
|
||||
RELEASE_VERSION_BASE,
|
||||
PRERELEASE,
|
||||
RELEASE_SRC,
|
||||
RELEASE_BINARY,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
RELEASE_MACOS,
|
||||
RELEASE_README,
|
||||
RELEASE_THIS,
|
||||
RELEASE_TITLE,
|
||||
APPDATA_FILE,
|
||||
ON_GITHUB_ACTIONS,
|
||||
)
|
||||
|
||||
# Verify we have all assets
|
||||
files_to_check = (
|
||||
RELEASE_SRC,
|
||||
RELEASE_BINARY,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
RELEASE_MACOS,
|
||||
RELEASE_README,
|
||||
@@ -53,14 +54,9 @@ for file_to_check in files_to_check:
|
||||
print("All release files are present")
|
||||
|
||||
# Verify that appdata file is updated
|
||||
if not isinstance(ET.parse(APPDATA_FILE).find(f"./releases/release[@version='{RELEASE_VERSION_BASE}']"), ET.Element):
|
||||
release_missing = f"Could not find {RELEASE_VERSION_BASE} in {APPDATA_FILE}"
|
||||
if RELEASE_THIS:
|
||||
raise RuntimeError(release_missing)
|
||||
elif ON_GITHUB_ACTIONS:
|
||||
print(f"::warning file={APPDATA_FILE},title=Missing release::{release_missing}")
|
||||
else:
|
||||
print(release_missing)
|
||||
if not PRERELEASE:
|
||||
if not isinstance(ET.parse(APPDATA_FILE).find(f"./releases/release[@version='{RELEASE_VERSION}']"), ET.Element):
|
||||
raise RuntimeError(f"Could not find {RELEASE_VERSION} in {APPDATA_FILE}")
|
||||
|
||||
# Calculate hashes for Synology release
|
||||
with open(RELEASE_SRC, "rb") as inp_file:
|
||||
@@ -75,7 +71,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(auth=github.Auth.Token(gh_token))
|
||||
gh_obj = github.Github(gh_token)
|
||||
gh_repo = gh_obj.get_repo("sabnzbd/sabnzbd")
|
||||
|
||||
# Read the release notes
|
||||
@@ -86,7 +82,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.name)
|
||||
print("Found existing release %s" % gh_release.title)
|
||||
break
|
||||
else:
|
||||
# Did not find it, so create the release, use the GitHub tag we got as input
|
||||
@@ -238,7 +234,7 @@ if RELEASE_THIS and gh_token:
|
||||
readme_lines = readme_file.readlines()
|
||||
|
||||
# Put the download link after the title
|
||||
readme_lines[2] = "## https://sabnzbd.org/downloads\n\n"
|
||||
readme_lines[2] = "## https://sabnzbd.org/downloads\n"
|
||||
|
||||
# Use the header in the readme as title
|
||||
title = readme_lines[0]
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
# Basic build requirements
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
pyinstaller==6.16.0
|
||||
packaging==25.0
|
||||
pyinstaller-hooks-contrib==2025.9
|
||||
altgraph==0.17.4
|
||||
wrapt==2.0.0
|
||||
setuptools==80.9.0
|
||||
pyinstaller==5.13.2
|
||||
pyinstaller-hooks-contrib==2023.8
|
||||
altgraph==0.17.3
|
||||
wrapt==1.15.0
|
||||
setuptools==68.2.2
|
||||
certifi
|
||||
|
||||
# orjson does not support 32bit Windows, exclude it based on Python-version
|
||||
# This way we also test ujson on Python 3.8 in the CI-tests
|
||||
orjson==3.9.7; python_version > '3.8'
|
||||
|
||||
# For the Windows build
|
||||
pefile==2024.8.26; sys_platform == 'win32'
|
||||
pywin32-ctypes==0.2.3; sys_platform == 'win32'
|
||||
pefile==2023.2.7; sys_platform == 'win32'
|
||||
pywin32-ctypes==0.2.2; sys_platform == 'win32'
|
||||
|
||||
# For the macOS build
|
||||
dmgbuild==1.6.5; sys_platform == 'darwin'
|
||||
dmgbuild==1.6.1; sys_platform == 'darwin'
|
||||
mac-alias==2.2.2; sys_platform == 'darwin'
|
||||
macholib==1.16.3; sys_platform == 'darwin'
|
||||
macholib==1.16.2; sys_platform == 'darwin'
|
||||
ds-store==1.3.1; sys_platform == 'darwin'
|
||||
PyNaCl==1.6.0; sys_platform == 'darwin'
|
||||
PyNaCl==1.5.0; sys_platform == 'darwin'
|
||||
|
||||
@@ -29,7 +29,6 @@ Unicode true
|
||||
!include "nsProcess.nsh"
|
||||
!include "x64.nsh"
|
||||
!include "servicelib.nsh"
|
||||
!include "StdUtils.nsh"
|
||||
|
||||
;------------------------------------------------------------------
|
||||
;
|
||||
@@ -43,47 +42,13 @@ Unicode true
|
||||
RMDir /r "${idir}"
|
||||
!macroend
|
||||
|
||||
!define RemovePrevShortcuts "!insertmacro RemovePrevShortcuts"
|
||||
!macro RemovePrevShortcuts
|
||||
; Remove shortcuts, starting with current user ones (from old installs)
|
||||
SetShellVarContext current
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - SafeMode.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - Documentation.url"
|
||||
RMDir "$SMPROGRAMS\$MUI_TEMP"
|
||||
Delete "$SMPROGRAMS\Startup\SABnzbd.lnk"
|
||||
Delete "$DESKTOP\SABnzbd.lnk"
|
||||
|
||||
SetShellVarContext all
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - SafeMode.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - Documentation.url"
|
||||
RMDir "$SMPROGRAMS\$MUI_TEMP"
|
||||
Delete "$SMPROGRAMS\Startup\SABnzbd.lnk"
|
||||
Delete "$DESKTOP\SABnzbd.lnk"
|
||||
!macroend
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Define names of the product
|
||||
Name "SABnzbd ${SAB_VERSION}"
|
||||
VIProductVersion "${SAB_VERSIONKEY}"
|
||||
VIFileVersion "${SAB_VERSIONKEY}"
|
||||
|
||||
VIAddVersionKey "Comments" "SABnzbd ${SAB_VERSION}"
|
||||
VIAddVersionKey "CompanyName" "The SABnzbd-Team"
|
||||
VIAddVersionKey "FileDescription" "SABnzbd ${SAB_VERSION}"
|
||||
VIAddVersionKey "FileVersion" "${SAB_VERSION}"
|
||||
VIAddVersionKey "LegalCopyright" "The SABnzbd-Team"
|
||||
VIAddVersionKey "ProductName" "SABnzbd ${SAB_VERSION}"
|
||||
VIAddVersionKey "ProductVersion" "${SAB_VERSION}"
|
||||
|
||||
Name "${SAB_PRODUCT}"
|
||||
OutFile "${SAB_FILE}"
|
||||
InstallDir "$PROGRAMFILES\SABnzbd"
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Some default compiler settings (uncomment and change at will):
|
||||
SetCompress auto ; (can be off or force)
|
||||
@@ -140,9 +105,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)
|
||||
@@ -155,21 +120,12 @@ Unicode true
|
||||
!insertmacro MUI_UNPAGE_COMPONENTS
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Run as user-level at end of install
|
||||
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
|
||||
; DOES NOT WORK
|
||||
; Function PageFinishRun
|
||||
; !insertmacro UAC_AsUser_ExecShell "" "$INSTDIR\SABnzbd.exe" "" "" ""
|
||||
; FunctionEnd
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
@@ -185,19 +141,18 @@ FunctionEnd
|
||||
!insertmacro MUI_LANGUAGE "Polish"
|
||||
!insertmacro MUI_LANGUAGE "Swedish"
|
||||
!insertmacro MUI_LANGUAGE "Danish"
|
||||
!insertmacro MUI_LANGUAGE "Italian"
|
||||
!insertmacro MUI_LANGUAGE "Norwegian"
|
||||
!insertmacro MUI_LANGUAGE "Romanian"
|
||||
!insertmacro MUI_LANGUAGE "Spanish"
|
||||
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||
!insertmacro MUI_LANGUAGE "Serbian"
|
||||
!insertmacro MUI_LANGUAGE "Turkish"
|
||||
!insertmacro MUI_LANGUAGE "Hebrew"
|
||||
!insertmacro MUI_LANGUAGE "Russian"
|
||||
!insertmacro MUI_LANGUAGE "Czech"
|
||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||
|
||||
|
||||
|
||||
;------------------------------------------------------------------
|
||||
;Reserve Files
|
||||
;If you are using solid compression, files that are required before
|
||||
@@ -215,24 +170,6 @@ Section "SABnzbd" SecDummy
|
||||
SetOutPath "$INSTDIR"
|
||||
SetShellVarContext all
|
||||
|
||||
DetailPrint $(MsgShutting)
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Shutdown any running service
|
||||
|
||||
!insertmacro SERVICE "stop" "SABnzbd" ""
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Terminate SABnzbd.exe
|
||||
loop:
|
||||
${nsProcess::FindProcess} "SABnzbd.exe" $R0
|
||||
StrCmp $R0 0 0 endcheck
|
||||
${nsProcess::CloseProcess} "SABnzbd.exe" $R0
|
||||
Sleep 500
|
||||
Goto loop
|
||||
endcheck:
|
||||
${nsProcess::Unload}
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Make sure old versions are gone (reg-key already read in onInt)
|
||||
StrCmp $PREV_INST_DIR "" noPrevInstallRemove
|
||||
@@ -263,14 +200,9 @@ Section "SABnzbd" SecDummy
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "URLUpdateInfo" 'https://sabnzbd.org/'
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "Comments" 'The automated Usenet download tool'
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "DisplayIcon" '$INSTDIR\icons\sabnzbd.ico'
|
||||
|
||||
WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "EstimatedSize" 40674
|
||||
WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "EstimatedSize" 25674
|
||||
WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "NoRepair" -1
|
||||
WriteRegDWORD HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "NoModify" -1
|
||||
|
||||
WriteRegStr HKEY_CURRENT_USER "Software\Classes\AppUserModelId\SABnzbd" "DisplayName" "SABnzbd"
|
||||
WriteRegStr HKEY_CURRENT_USER "Software\Classes\AppUserModelId\SABnzbd" "IconUri" '$INSTDIR\icons\sabnzbd16_32.ico'
|
||||
|
||||
; write out uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
@@ -303,13 +235,15 @@ Function .onInit
|
||||
${If} ${RunningX64}
|
||||
StrCpy $INSTDIR "$PROGRAMFILES64\SABnzbd"
|
||||
${Else}
|
||||
MessageBox MB_OK|MB_ICONSTOP $(MsgOnly64bit)
|
||||
MessageBox MB_OK $(MsgOnly64bit)
|
||||
ExecShell "open" "https://sabnzbd.org/downloads"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
; Python 3.9 no longer supports Windows 7
|
||||
${If} ${AtMostWin8}
|
||||
MessageBox MB_OK|MB_ICONSTOP $(MsgNoWin7)
|
||||
MessageBox MB_OK $(MsgNoWin7)
|
||||
ExecShell "open" "https://sabnzbd.org/downloads"
|
||||
Abort
|
||||
${EndIf}
|
||||
|
||||
@@ -327,9 +261,8 @@ Function .onInit
|
||||
;------------------------------------------------------------------
|
||||
; Check what the user has currently set for install options
|
||||
SetShellVarContext current
|
||||
IfFileExists "$SMPROGRAMS\Startup\SABnzbd.lnk" 0 endCheckStartupCurrent
|
||||
IfFileExists "$SMPROGRAMS\Startup\SABnzbd.lnk" 0 endCheckStartup
|
||||
SectionSetFlags ${startup} 1
|
||||
endCheckStartupCurrent:
|
||||
SetShellVarContext all
|
||||
IfFileExists "$SMPROGRAMS\Startup\SABnzbd.lnk" 0 endCheckStartup
|
||||
SectionSetFlags ${startup} 1
|
||||
@@ -337,12 +270,11 @@ Function .onInit
|
||||
|
||||
SetShellVarContext current
|
||||
IfFileExists "$DESKTOP\SABnzbd.lnk" endCheckDesktop 0
|
||||
; If not present for current user, first check all user folder
|
||||
SetShellVarContext all
|
||||
IfFileExists "$DESKTOP\SABnzbd.lnk" endCheckDesktop 0
|
||||
SectionSetFlags ${desktop} 0 ; SAB is installed but desktop-icon not, so uncheck it
|
||||
endCheckDesktop:
|
||||
SectionSetFlags ${desktop} 0 ; SAB is installed but desktop-icon not, so uncheck it
|
||||
SetShellVarContext all
|
||||
IfFileExists "$DESKTOP\SABnzbd.lnk" endCheckDesktop 0
|
||||
SectionSetFlags ${desktop} 0 ; SAB is installed but desktop-icon not, so uncheck it
|
||||
endCheckDesktop:
|
||||
|
||||
Push $1
|
||||
ReadRegStr $1 HKCR ".nzb" "" ; read current file association
|
||||
@@ -354,6 +286,31 @@ Function .onInit
|
||||
; Display language chooser
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; make sure user terminates sabnzbd.exe or else abort
|
||||
;
|
||||
loop:
|
||||
${nsProcess::FindProcess} "SABnzbd.exe" $R0
|
||||
StrCmp $R0 0 0 endcheck
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION $(MsgCloseSab) IDOK loop IDCANCEL exitinstall
|
||||
exitinstall:
|
||||
${nsProcess::Unload}
|
||||
Abort
|
||||
endcheck:
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; make sure both services aren't running
|
||||
;
|
||||
!insertmacro SERVICE "running" "SABnzbd" ""
|
||||
Pop $0 ;response
|
||||
!insertmacro SERVICE "running" "SABHelper" ""
|
||||
Pop $1
|
||||
${If} $0 == true
|
||||
${OrIf} $1 == true
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION $(MsgCloseSab) IDOK loop IDCANCEL exitinstall
|
||||
; exitinstall already defined above
|
||||
${EndIf}
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Tell users about the service change
|
||||
;
|
||||
@@ -361,8 +318,7 @@ Function .onInit
|
||||
Pop $0 ;response
|
||||
${If} $0 == true
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION $(MsgServChange) IDOK removeservices IDCANCEL exitinstall
|
||||
exitinstall:
|
||||
Abort
|
||||
; exitinstall already defined above
|
||||
removeservices:
|
||||
!insertmacro SERVICE "delete" "SABHelper" ""
|
||||
!insertmacro SERVICE "delete" "SABnzbd" ""
|
||||
@@ -370,30 +326,65 @@ 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)
|
||||
|
||||
Section "un.$(MsgDelProgram)" Uninstall
|
||||
;make sure sabnzbd.exe isn't running..if so shut it down
|
||||
DetailPrint $(MsgShutting)
|
||||
${nsProcess::KillProcess} "SABnzbd.exe" $R0
|
||||
${nsProcess::Unload}
|
||||
DetailPrint "Process Killed"
|
||||
|
||||
; add delete commands to delete whatever files/registry keys/etc you installed here.
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\SABnzbd"
|
||||
DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd"
|
||||
DeleteRegKey HKEY_CURRENT_USER "Software\Classes\AppUserModelId\SABnzbd"
|
||||
DeleteRegKey HKEY_CURRENT_USER "Software\SABnzbd"
|
||||
|
||||
${RemovePrev} "$INSTDIR"
|
||||
${RemovePrevShortcuts}
|
||||
|
||||
; Remove firewall entries
|
||||
liteFirewallW::RemoveRule "$INSTDIR\SABnzbd.exe" "SABnzbd"
|
||||
liteFirewallW::RemoveRule "$INSTDIR\SABnzbd-console.exe" "SABnzbd-console"
|
||||
|
||||
SetShellVarContext all
|
||||
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - SafeMode.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - Documentation.url"
|
||||
RMDir "$SMPROGRAMS\$MUI_TEMP"
|
||||
|
||||
Delete "$SMPROGRAMS\Startup\SABnzbd.lnk"
|
||||
|
||||
Delete "$DESKTOP\SABnzbd.lnk"
|
||||
|
||||
SetShellVarContext current
|
||||
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - SafeMode.lnk"
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd - Documentation.url"
|
||||
RMDir "$SMPROGRAMS\$MUI_TEMP"
|
||||
|
||||
Delete "$SMPROGRAMS\Startup\SABnzbd.lnk"
|
||||
|
||||
Delete "$DESKTOP\SABnzbd.lnk"
|
||||
|
||||
DeleteRegKey HKEY_CURRENT_USER "Software\SABnzbd"
|
||||
|
||||
${unregisterExtension} ".nzb" "NZB File"
|
||||
${RefreshShellIcons}
|
||||
|
||||
@@ -411,17 +402,15 @@ 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 MsgCloseSab ${LANG_ENGLISH} "Please close $\"SABnzbd.exe$\" first"
|
||||
|
||||
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."
|
||||
|
||||
LangString MsgOnly64bit ${LANG_ENGLISH} "SABnzbd only supports 64-bit Windows."
|
||||
LangString MsgOnly64bit ${LANG_ENGLISH} "The installer only supports 64-bit Windows, use the standalone version to run on 32-bit Windows."
|
||||
|
||||
LangString MsgNoWin7 ${LANG_ENGLISH} "SABnzbd only supports Windows 8.1 and above."
|
||||
|
||||
LangString MsgShutting ${LANG_ENGLISH} "Shutting down SABnzbd"
|
||||
LangString MsgNoWin7 ${LANG_ENGLISH} "The installer only supports Windows 8.1 and above, use the standalone legacy version to run on older Windows version."
|
||||
|
||||
LangString MsgUninstall ${LANG_ENGLISH} "This will uninstall SABnzbd from your system"
|
||||
|
||||
@@ -435,6 +424,10 @@ SectionEnd
|
||||
|
||||
LangString MsgDelSettings ${LANG_ENGLISH} "Delete Settings"
|
||||
|
||||
LangString MsgRemoveOld ${LANG_ENGLISH} "You cannot overwrite an existing installation. $\n$\nClick `OK` to remove the previous version or `Cancel` to cancel this upgrade."
|
||||
|
||||
LangString MsgRemoveOld2 ${LANG_ENGLISH} "Your settings and data will be preserved."
|
||||
|
||||
LangString MsgLangCode ${LANG_ENGLISH} "en"
|
||||
|
||||
Function un.onInit
|
||||
|
||||
@@ -1,501 +0,0 @@
|
||||
#################################################################################
|
||||
# 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___
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
##
|
||||
## Bad URL Fetch Email template for SABnzbd
|
||||
## This a Cheetah template
|
||||
## Documentation: https://sabnzbd.org/wiki/extra/email-templates
|
||||
## Documentation: http://sabnzbd.wikidot.com/email-templates
|
||||
##
|
||||
## Newlines and whitespace are significant!
|
||||
##
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
##
|
||||
## Default Email template for SABnzbd
|
||||
## This a Cheetah template
|
||||
## Documentation: https://sabnzbd.org/wiki/extra/email-templates
|
||||
## Documentation: http://sabnzbd.wikidot.com/email-templates
|
||||
##
|
||||
## Newlines and whitespace are significant!
|
||||
##
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
##
|
||||
## RSS Email template for SABnzbd
|
||||
## This a Cheetah template
|
||||
## Documentation: https://sabnzbd.org/wiki/extra/email-templates
|
||||
## Documentation: http://sabnzbd.wikidot.com/email-templates
|
||||
##
|
||||
## Newlines and whitespace are significant!
|
||||
##
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
<div class="modal-body">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
||||
<!--#if not $windows#-->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="show_hidden_folders"> <span>$T('hiddenFolders')</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!--#end if#-->
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span> $T('cancel')</button>
|
||||
<button type="button" class="btn btn-default" id="filebrowser_modal_accept"><span class="glyphicon glyphicon-ok"></span> $T('rss-accept')</button>
|
||||
</div>
|
||||
|
||||
@@ -70,7 +70,27 @@
|
||||
<script type="text/javascript" src="${root}staticcfg/js/script.js?v=$version"></script>
|
||||
<script type="text/javascript">
|
||||
// Set default functions for the autocomplete everywhere
|
||||
|
||||
jQuery.extend(jQuery.fn.typeahead.defaults, {
|
||||
source: function (query, process) {
|
||||
// If there's no separator, it must be a relative path
|
||||
if(query.split(folderSeperator).length < 2 && this.\$element.data('initialdir')) {
|
||||
query = this.\$element.data('initialdir') + folderSeperator + query;
|
||||
}
|
||||
// Get info from the API
|
||||
return jQuery.get(folderBrowseUrl + '&compact=1&term=' + query, function (data) {
|
||||
return process(data);
|
||||
});
|
||||
},
|
||||
updater: function(item) {
|
||||
// Is it a relative path?
|
||||
if(item.indexOf(this.\$element.data('initialdir')) === 0) {
|
||||
// Remove start
|
||||
return item.replace(this.\$element.data('initialdir')+folderSeperator, '');
|
||||
}
|
||||
// Full path
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
||||
// to top right away
|
||||
if(window.location.hash) {
|
||||
@@ -92,7 +112,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
|
||||
<a class="navbar-logo navbar-logo-small" href="${root}" title="$T('Home')" data-placement="bottom">
|
||||
<a class="navbar-logo navbar-logo-small" href="${root}" title="$T('Home')">
|
||||
#include $webdir + "/staticcfg/images/logo-small.svg"#
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -29,8 +29,18 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
<td>$ssl_version</td>
|
||||
<td>
|
||||
$ssl_version
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $certificate_validation#-->
|
||||
<tr>
|
||||
<th scope="row"></th>
|
||||
<td>
|
||||
<span class="label label-danger">$T('warning')</span> $T('explain-nosslcontext')
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $windows and not $macos#-->
|
||||
<tr>
|
||||
<th scope="row">Par2cmdline-turbo:</th>
|
||||
@@ -78,8 +88,8 @@
|
||||
<td><a href="https://github.com/sabnzbd/sabnzbd" target="_blank">https://github.com/sabnzbd/sabnzbd</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('menu-live-chat') </th>
|
||||
<td><a href="https://sabnzbd.org/live-chat/" target="_blank">https://sabnzbd.org/live-chat/</a> (IRC & Discord)</td>
|
||||
<th scope="row">$T('menu-irc') </th>
|
||||
<td><a href="irc://irc.synirc.net/#sabnzbd"><i>#sabnzbd</i> on <i>irc.synirc.net</i></a> $T('or') (<a href="http://sabnzbd.org/live-chat/" target="_blank">webchat</a>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('menu-issues') </th>
|
||||
@@ -96,7 +106,7 @@
|
||||
|
||||
<div class="colmask">
|
||||
<div class="padding">
|
||||
<h5 class="copyright">Copyright © 2007-2025 by The SABnzbd-Team (<a href="https://sabnzbd.org/" target="_blank">sabnzbd.org</a>)</h5>
|
||||
<h5 class="copyright">Copyright © 2007-2023 The SABnzbd-Team (<a href="https://sabnzbd.org/" target="_blank">sabnzbd.org</a>)</h5>
|
||||
<p class="copyright"><small>$T('yourRights')</small></p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
update: function(event, ui) {
|
||||
jQuery('.Categories form.sorting-row').each(function(index, elm) {
|
||||
// Update order of all elements
|
||||
if(index !== elm.order.value) {
|
||||
if(index != elm.order.value) {
|
||||
elm.order.value = index
|
||||
// Submit changed order
|
||||
var data = {}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="password_file">$T('opt-password_file')</label>
|
||||
<input type="text" name="password_file" id="password_file" value="$password_file" class="fileBrowserField" data-initialdir="$my_home" data-files="1" />
|
||||
<input type="text" name="password_file" id="password_file" value="$password_file" />
|
||||
<span class="desc">$T('explain-password_file')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -133,7 +133,7 @@
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function() {
|
||||
// Add autocomplete and file-browser
|
||||
jQuery('.col1 input[name$="_dir"], #password_file').typeahead().fileBrowser();
|
||||
jQuery('.col1 input[name$="_dir"]').typeahead().fileBrowser();
|
||||
|
||||
jQuery('#purge_log_files').click(function () {
|
||||
if ( confirm("$T('confirm')") ) {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="port">$T('opt-port')</label>
|
||||
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" min="0" max="65535" />
|
||||
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" />
|
||||
<span class="desc">$T('explain-port')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -35,7 +35,7 @@
|
||||
<span class="desc">$T('explain-enable_https')</span>
|
||||
<span class="desc"><span class="label label-warning">$T('warning').upper()</span> $T('explain-enable_https_warning')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="web_dir">$T('opt-web_dir')</label>
|
||||
<select name="web_dir" id="web_dir">
|
||||
<!--#for $webline in $web_list#-->
|
||||
@@ -69,12 +69,12 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_port">$T('opt-https_port')</label>
|
||||
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" min="0" max="65535" />
|
||||
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" />
|
||||
<span class="desc">$T('explain-https_port')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_cert">$T('opt-https_cert')</label>
|
||||
<input type="text" name="https_cert" id="https_cert" value="$https_cert" class="fileBrowserField" data-initialdir="$my_lcldata" data-files="1" />
|
||||
<input type="text" name="https_cert" id="https_cert" value="$https_cert" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')">
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
@@ -82,7 +82,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_key">$T('opt-https_key')</label>
|
||||
<input type="text" name="https_key" id="https_key" value="$https_key" class="fileBrowserField" data-initialdir="$my_lcldata" data-files="1" />
|
||||
<input type="text" name="https_key" id="https_key" value="$https_key" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')">
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
@@ -90,7 +90,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_chain">$T('opt-https_chain')</label>
|
||||
<input type="text" name="https_chain" id="https_chain" value="$https_chain" class="fileBrowserField" data-initialdir="$my_lcldata" data-files="1" />
|
||||
<input type="text" name="https_chain" id="https_chain" value="$https_chain" />
|
||||
<span class="desc">$T('explain-https_chain')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -136,14 +136,14 @@
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="apikey_display">$T('opt-apikey')</label>
|
||||
<input type="text" id="apikey_display" value="$apikey" readonly />
|
||||
<input type="text" id="apikey_display" class="fileBrowserField" value="$apikey" readonly />
|
||||
<button class="btn btn-default show_qrcode" title="$T('explain-qr-code')" rel="$apikey" ><span class="glyphicon glyphicon-qrcode"></span></button>
|
||||
<button class="btn btn-default generate_key" id="generate_new_apikey" title="$T('button-apikey')"><span class="glyphicon glyphicon-repeat"></span></button>
|
||||
<span class="desc">$T('explain-apikey')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nzbkey">$T('opt-nzbkey')</label>
|
||||
<input type="text" id="nzbkey" value="$nzb_key" readonly />
|
||||
<input type="text" id="nzbkey" class="fileBrowserField" value="$nzb_key" readonly />
|
||||
<button class="btn btn-default show_qrcode" title="$T('explain-qr-code')" rel="$nzb_key" ><span class="glyphicon glyphicon-qrcode"></span></button>
|
||||
<button class="btn btn-default generate_key" id="generate_new_nzbkey" title="$T('button-apikey')"><span class="glyphicon glyphicon-repeat"></span></button>
|
||||
<span class="desc">$T('explain-nzbkey')</span>
|
||||
@@ -172,10 +172,11 @@
|
||||
<option value="1" <!--#if $check_new_rel == 1 then 'selected="selected"' else ""#--> >$T('on')</option>
|
||||
<option value="2" <!--#if $check_new_rel == 2 then 'selected="selected"' else ""#--> >$T('also-test')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-check_new_rel')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<div class="field-pair advanced-settings <!--#if int($certificate_validation) == 0 then "disabled" else ""#-->">
|
||||
<label class="config" for="enable_https_verification">$T('opt-enable_https_verification')</label>
|
||||
<input type="checkbox" name="enable_https_verification" id="enable_https_verification" value="1" <!--#if int($enable_https_verification) > 0 then 'checked="checked"' else ""#--> />
|
||||
<input type="checkbox" name="enable_https_verification" id="enable_https_verification" value="1" <!--#if int($enable_https_verification) > 0 then 'checked="checked"' else ""#--> <!--#if int($certificate_validation) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<span class="desc">$T('explain-enable_https_verification')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
@@ -266,7 +267,7 @@
|
||||
jQuery(document).ready(function(){
|
||||
// Show the message about translating when it's non-English
|
||||
function hideOrShowTranslate() {
|
||||
if(jQuery('#language').val() === 'en') {
|
||||
if(jQuery('#language').val() == 'en') {
|
||||
jQuery('.alert-translate').hide()
|
||||
} else {
|
||||
jQuery('.alert-translate').show()
|
||||
@@ -289,7 +290,7 @@ jQuery(document).ready(function(){
|
||||
// So when exposed to internet and no password, no external limit or no username/password
|
||||
var safeCheck = jQuery('#host, #inet_exposure, #${pid}_wu, #${pid}_wp')
|
||||
function checkSafety() {
|
||||
if(jQuery('#host').val() !== 'localhost' && jQuery('#host').val() !== '127.0.0.1') {
|
||||
if(jQuery('#host').val() != 'localhost' && jQuery('#host').val() != '127.0.0.1') {
|
||||
// No limitation on local-network
|
||||
if(jQuery('#inet_exposure').val() > 3) {
|
||||
// And no username and password?
|
||||
@@ -341,9 +342,6 @@ jQuery(document).ready(function(){
|
||||
}
|
||||
});
|
||||
|
||||
// Add autocomplete and file-browser
|
||||
jQuery('.fileBrowserField').typeahead().fileBrowser();
|
||||
|
||||
jQuery('.show_qrcode').click(function (e) {
|
||||
// Show in modal
|
||||
jQuery('#modal_qr .modal-dialog').width(330)
|
||||
@@ -377,7 +375,7 @@ jQuery(document).ready(function(){
|
||||
})
|
||||
|
||||
// Only allow re-generate if default certs
|
||||
if(jQuery('#https_cert').val() !== '$def_https_cert_file') {
|
||||
if(jQuery('#https_cert').val() != '$def_https_cert_file') {
|
||||
jQuery('.generate_cert').attr('disabled', 'disabled')
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
<!--#set global $help_uri = $confighelpuri + "notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#import apprise#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
<!--#for $type in $notify_types#-->
|
||||
<div class="field-pair">
|
||||
<label class="config wide" for="${section_label}_prio_$type">
|
||||
$T($notify_types[$type]).replace('/', ' / ')
|
||||
</label>
|
||||
<input type="checkbox" name="${section_label}_prio_$type" id="${section_label}_prio_$type" value="1" <!--#if $getVar($section_label + '_prio_' + $type) then 'checked="checked"' else ""#--> />
|
||||
<input type="checkbox" name="${section_label}_prio_$type" id="${section_label}_prio_$type" value="1" <!--#if int($getVar($section_label + '_prio_' + $type)) > 0 then 'checked="checked"' else ""#--> />
|
||||
</div>
|
||||
<!--#end for#-->
|
||||
<!--#end def#-->
|
||||
|
||||
<!--#def show_cat_box($section_label)#-->
|
||||
<div class="col2-cats" <!--#if $getVar($section_label + '_enable') then '' else 'style="display:none"'#-->>
|
||||
<div class="col2-cats" <!--#if int($getVar($section_label + '_enable')) > 0 then '' else 'style="display:none"'#-->>
|
||||
<hr>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="${section_label}_cats" multiple="multiple" class="multiple_cats" size="$len($categories)">
|
||||
@@ -58,12 +56,12 @@
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="email_full">$T('opt-email_full')</label>
|
||||
<input type="checkbox" name="email_full" id="email_full" value="1" <!--#if $email_full then 'checked="checked"' else ""#--> />
|
||||
<input type="checkbox" name="email_full" id="email_full" value="1" <!--#if int($email_full) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-email_full')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="email_rss">$T('opt-email_rss')</label>
|
||||
<input type="checkbox" name="email_rss" id="email_rss" value="1" <!--#if $email_rss then 'checked="checked"' else ""#--> />
|
||||
<input type="checkbox" name="email_rss" id="email_rss" value="1" <!--#if int($email_rss) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-email_rss')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -107,12 +105,12 @@
|
||||
<h3>$T('section-NC')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="ncenter_enable" id="ncenter_enable" value="1" <!--#if $ncenter_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="ncenter_enable" id="ncenter_enable" value="1" <!--#if int($ncenter_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="ncenter_enable"> $T('opt-ncenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col1" <!--#if $ncenter_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($ncenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ncenter')
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
@@ -132,13 +130,13 @@
|
||||
<h3>$T('section-AC')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="acenter_enable" id="acenter_enable" value="1" <!--#if $acenter_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="acenter_enable" id="acenter_enable" value="1" <!--#if int($acenter_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="acenter_enable"> $T('opt-acenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
$show_cat_box('acenter')
|
||||
</div>
|
||||
<div class="col1" <!--#if $acenter_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($acenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('acenter')
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
@@ -158,13 +156,13 @@
|
||||
<h3>$T('section-OSD') <a href="$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="ntfosd_enable" id="ntfosd_enable" value="1" <!--#if $ntfosd_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="ntfosd_enable" id="ntfosd_enable" value="1" <!--#if int($ntfosd_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="ntfosd_enable"> $T('opt-ntfosd_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
$show_cat_box('ntfosd')
|
||||
</div>
|
||||
<div class="col1" <!--#if $ntfosd_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($ntfosd_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ntfosd')
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
@@ -178,64 +176,19 @@
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<div class="section" id="apprise">
|
||||
<div class="col2">
|
||||
<h3>Apprise <a href="$help_uri#apprise" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="apprise_enable" id="apprise_enable" value="1" <!--#if $apprise_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="apprise_enable"> $T('opt-apprise_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-apprise_enable')</em><br>
|
||||
<p>$T('version'): ${apprise.__version__}</p>
|
||||
|
||||
$show_cat_box('apprise')
|
||||
</div>
|
||||
<div class="col1" <!--#if $apprise_enable then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="apprise_urls">$T('opt-apprise_urls')</label>
|
||||
<input type="text" name="apprise_urls" id="apprise_urls" value="$apprise_urls" />
|
||||
<span class="desc">$T('explain-apprise_urls'). <br>$T('readwiki')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<span class="desc">$T('explain-apprise_extra_urls')</span>
|
||||
</div>
|
||||
<!--#set $section_label = 'apprise'#-->
|
||||
<!--#for $type in $notify_types#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="${section_label}_target_${type}">
|
||||
$T($notify_types[$type]).replace('/', ' / ')
|
||||
</label>
|
||||
<input type="checkbox" name="${section_label}_target_${type}_enable" id="${section_label}_target_${type}_enable" value="1" <!--#if $getVar($section_label + '_target_' + $type + '_enable') then 'checked="checked"' else ""#--> />
|
||||
<input type="text" name="${section_label}_target_${type}" id="${section_label}_target_${type}" value="$getVar($section_label + '_target_' + $type)" placeholder="$T('opt-apprise_urls')" />
|
||||
</div>
|
||||
<!--#end for#-->
|
||||
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default" type="button" id="test_apprise"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
|
||||
</div>
|
||||
<div class="field-pair result-box">
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="nscript">
|
||||
<div class="col2">
|
||||
<h3>$T('section-NScript') <a href="$help_uri#nscript" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if $nscript_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-nscript_enable')</em><br><a href="$help_uri#nscript" target="_blank">$T('readwiki')</a>
|
||||
$show_cat_box('nscript')
|
||||
</div>
|
||||
<div class="col1" <!--#if $nscript_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
|
||||
@@ -249,7 +202,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_parameters">$T('opt-nscript_parameters')</label>
|
||||
<input type="text" name="nscript_parameters" id="nscript_parameters" value="$nscript_parameters" />
|
||||
<span class="desc">$T('Optional') - $T('readwiki')</span>
|
||||
<span class="desc">$T('Optional') - $T('explain-nscript_parameters')</span>
|
||||
</div>
|
||||
$show_notify_checkboxes('nscript')
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
@@ -267,14 +220,14 @@
|
||||
<h3>$T('section-Prowl')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="prowl_enable" id="prowl_enable" value="1" <!--#if $prowl_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="prowl_enable" id="prowl_enable" value="1" <!--#if int($prowl_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="prowl_enable"> $T('opt-prowl_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-prowl_enable')</em>
|
||||
$show_cat_box('prowl')
|
||||
</div>
|
||||
<div class="col1" <!--#if $prowl_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($prowl_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="prowl_apikey">$T('opt-prowl_apikey')</label>
|
||||
@@ -288,12 +241,12 @@
|
||||
$T($notify_types[$type]).replace('/', ' / ')
|
||||
</label>
|
||||
<select name="${section_label}_prio_$type" id="${section_label}_prio_$type">
|
||||
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == -3 then 'selected="selected"' else ""#--> >$T('prowl-off')</option>
|
||||
<option value="-2" <!--#if $getVar($section_label + '_prio_' + $type) == -2 then 'selected="selected"' else ""#--> >$T('prowl-very-low')</option>
|
||||
<option value="-1" <!--#if $getVar($section_label + '_prio_' + $type) == -1 then 'selected="selected"' else ""#--> >$T('prowl-moderate')</option>
|
||||
<option value="0" <!--#if $getVar($section_label + '_prio_' + $type) == 0 then 'selected="selected"' else ""#--> >$T('prowl-normal')</option>
|
||||
<option value="1" <!--#if $getVar($section_label + '_prio_' + $type) == 1 then 'selected="selected"' else ""#--> >$T('prowl-high')</option>
|
||||
<option value="2" <!--#if $getVar($section_label + '_prio_' + $type) == 2 then 'selected="selected"' else ""#--> >$T('prowl-emergency')</option>
|
||||
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == "-3" then 'selected="selected"' else ""#--> >$T('prowl-off')</option>
|
||||
<option value="-2" <!--#if $getVar($section_label + '_prio_' + $type) == "-2" then 'selected="selected"' else ""#--> >$T('prowl-very-low')</option>
|
||||
<option value="-1" <!--#if $getVar($section_label + '_prio_' + $type) == "-1" then 'selected="selected"' else ""#--> >$T('prowl-moderate')</option>
|
||||
<option value="0" <!--#if $getVar($section_label + '_prio_' + $type) == "0" then 'selected="selected"' else ""#--> >$T('prowl-normal')</option>
|
||||
<option value="1" <!--#if $getVar($section_label + '_prio_' + $type) == "1" then 'selected="selected"' else ""#--> >$T('prowl-high')</option>
|
||||
<option value="2" <!--#if $getVar($section_label + '_prio_' + $type) == "2" then 'selected="selected"' else ""#--> >$T('prowl-emergency')</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--#end for#-->
|
||||
@@ -313,14 +266,14 @@
|
||||
<h3>$T('section-Pushover')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="pushover_enable" id="pushover_enable" value="1" <!--#if $pushover_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="pushover_enable" id="pushover_enable" value="1" <!--#if int($pushover_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="pushover_enable"> $T('opt-pushover_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushover_enable')</em>
|
||||
$show_cat_box('pushover')
|
||||
</div>
|
||||
<div class="col1" <!--#if $pushover_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($pushover_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pushover_token">$T('opt-pushover_token')</label>
|
||||
@@ -354,12 +307,12 @@
|
||||
$T($notify_types[$type]).replace('/', ' / ')
|
||||
</label>
|
||||
<select name="${section_label}_prio_$type" id="${section_label}_prio_$type">
|
||||
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == -3 then 'selected="selected"' else ""#--> >$T('pushover-off')</option>
|
||||
<option value="-2" <!--#if $getVar($section_label + '_prio_' + $type) == -2 then 'selected="selected"' else ""#--> >$T('prowl-very-low')</option>
|
||||
<option value="-1" <!--#if $getVar($section_label + '_prio_' + $type) == -1 then 'selected="selected"' else ""#--> >$T('pushover-low')</option>
|
||||
<option value="0" <!--#if $getVar($section_label + '_prio_' + $type) == 0 then 'selected="selected"' else ""#--> >$T('prowl-normal')</option>
|
||||
<option value="1" <!--#if $getVar($section_label + '_prio_' + $type) == 1 then 'selected="selected"' else ""#--> >$T('pushover-high')</option>
|
||||
<option value="2" <!--#if $getVar($section_label + '_prio_' + $type) == 2 then 'selected="selected"' else ""#--> >$T('prowl-emergency')</option>
|
||||
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == "-3" then 'selected="selected"' else ""#--> >$T('pushover-off')</option>
|
||||
<option value="-2" <!--#if $getVar($section_label + '_prio_' + $type) == "-2" then 'selected="selected"' else ""#--> >$T('prowl-very-low')</option>
|
||||
<option value="-1" <!--#if $getVar($section_label + '_prio_' + $type) == "-1" then 'selected="selected"' else ""#--> >$T('pushover-low')</option>
|
||||
<option value="0" <!--#if $getVar($section_label + '_prio_' + $type) == "0" then 'selected="selected"' else ""#--> >$T('prowl-normal')</option>
|
||||
<option value="1" <!--#if $getVar($section_label + '_prio_' + $type) == "1" then 'selected="selected"' else ""#--> >$T('pushover-high')</option>
|
||||
<option value="2" <!--#if $getVar($section_label + '_prio_' + $type) == "2" then 'selected="selected"' else ""#--> >$T('prowl-emergency')</option>
|
||||
</select>
|
||||
</div>
|
||||
<!--#end for#-->
|
||||
@@ -378,14 +331,14 @@
|
||||
<h3>$T('section-Pushbullet')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="pushbullet_enable" id="pushbullet_enable" value="1" <!--#if $pushbullet_enable then 'checked="checked"' else ""#--> /></td>
|
||||
<td><input type="checkbox" name="pushbullet_enable" id="pushbullet_enable" value="1" <!--#if int($pushbullet_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="pushbullet_enable"> $T('opt-pushbullet_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushbullet_enable')</em>
|
||||
$show_cat_box('pushbullet')
|
||||
</div>
|
||||
<div class="col1" <!--#if $pushbullet_enable then '' else 'style="display:none"'#-->>
|
||||
<div class="col1" <!--#if int($pushbullet_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pushbullet_apikey">$T('opt-pushbullet_apikey')</label>
|
||||
@@ -473,7 +426,7 @@ jQuery(document).ready(function(){
|
||||
}
|
||||
})
|
||||
}
|
||||
jQuery('#test_email, #test_notif, #test_windows, #test_apprise, #test_pushbullet, #test_pushover, #test_prowl, #test_osd, #test_nscript').click(function () {
|
||||
jQuery('#test_email, #test_notif, #test_windows, #test_pushbullet, #test_pushover, #test_prowl, #test_osd, #test_nscript').click(function () {
|
||||
testNotification(this)
|
||||
})
|
||||
});
|
||||
|
||||
@@ -616,7 +616,7 @@ jQuery(document).ready(function(){
|
||||
|
||||
// Only the Accept filter needs all the options
|
||||
jQuery('form[action="upd_rss_filter"]').find('select[name="filter_type"]').change(function() {
|
||||
jQuery(this).parent().parent().find('select:not([name="filter_type"])').attr('disabled', jQuery(this).val() !== "A" && jQuery(this).val() !== "S")
|
||||
jQuery(this).parent().parent().find('select:not([name="filter_type"])').attr('disabled', jQuery(this).val() != "A" && jQuery(this).val() != "S")
|
||||
})
|
||||
// Trigger on-load for all
|
||||
jQuery('.disabled_options_rule').find('td select:not([name="filter_type"])').attr('disabled', true)
|
||||
|
||||
@@ -117,7 +117,7 @@ else:
|
||||
jQuery('#arguments').val((jQuery(this).find('option:selected').data('action')))
|
||||
|
||||
// Is it speedlimit?
|
||||
if(jQuery(this).find('option:selected').val() === 'speedlimit') {
|
||||
if(jQuery(this).find('option:selected').val() == 'speedlimit') {
|
||||
jQuery('#hidden_arguments').show()
|
||||
jQuery('#hidden_arguments input').attr('placeholder', 'Bytes/s, "1M" = 1 MB/s, "500K" = 500 KB/s')
|
||||
} else {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</label>
|
||||
|
||||
<div class="advanced-buttonSeperator"></div>
|
||||
<div class="chart-selector-container" title="$T('selectedDates')" data-placement="bottom">
|
||||
<div class="chart-selector-container" title="$T('selectedDates')">
|
||||
<span class="glyphicon glyphicon-signal"></span>
|
||||
<!--#set today = datetime.date.today()#-->
|
||||
<input type="date" name="chart-start" id="chart-start" value="<!--#echo (today-datetime.timedelta(days=30)).strftime('%Y-%m-%d')#-->"> -
|
||||
@@ -59,7 +59,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="port">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port" size="8" value="563" min="0" max="65535" />
|
||||
<input type="number" name="port" id="port" size="8" value="563" min="0" />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl">$T('srv-ssl')</label>
|
||||
@@ -91,15 +91,14 @@
|
||||
<label class="config" for="timeout">$T('srv-timeout')</label>
|
||||
<input type="number" name="timeout" id="timeout" min="20" max="240" /> <i>$T('seconds')</i>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<div class="field-pair <!--#if int($certificate_validation) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify">
|
||||
<option value="3" selected>$T('ssl_verify-strict')</option>
|
||||
<option value="2">$T('ssl_verify-medium')</option>
|
||||
<option value="1">$T('ssl_verify-minimal')</option>
|
||||
<select name="ssl_verify" id="ssl_verify" <!--#if int($certificate_validation) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="2" selected>$T('ssl_verify-strict')</option>
|
||||
<option value="1">$T('ssl_verify-normal')</option>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('-', '<br/>-')</span>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
@@ -107,6 +106,11 @@
|
||||
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
|
||||
<a href="https://sabnzbd.org/wiki/advanced/ssl-ciphers" target="_blank">https://sabnzbd.org/wiki/advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="send_group">$T('srv-send_group')</label>
|
||||
<input type="checkbox" name="send_group" id="send_group" value="1" />
|
||||
<span class="desc">$T('srv-explain-send_group')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="required">$T('srv-required')</label>
|
||||
<input type="checkbox" name="required" id="required" value="1" />
|
||||
@@ -132,7 +136,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 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"><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">
|
||||
@@ -143,7 +147,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--#set $prio_colors = ["#59cc33", "#26a69a", "#3366cc", "#7f33cc", "#cc33a6", "#f39c12", "#cc3333", "#8d6e63"] #-->
|
||||
<!--#set $prio_colors = ["#59cc33", "#3366cc","#7f33cc", "#cc33a6", "#cc3333"] #-->
|
||||
<!--#set $cur_prio_color = -1 #-->
|
||||
<!--#set $last_prio = -1 #-->
|
||||
<!--#for $cur, $server in enumerate($servers) #-->
|
||||
@@ -172,9 +176,6 @@
|
||||
</table>
|
||||
<button type="button" class="btn btn-default showserver"><span class="glyphicon glyphicon-pencil"></span> $T('showDetails')</button>
|
||||
<button type="button" class="btn btn-default clrServer"><span class="glyphicon glyphicon-remove"></span> $T('button-clrServer')</button>
|
||||
<!--#if $server['notes'] #-->
|
||||
<p>$server['notes'].replace('\n', '<br>')</p>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
<div class="col1" style="display:none;">
|
||||
<input type="hidden" name="enable" id="enable$cur" value="$int($server['enable'])" />
|
||||
@@ -189,7 +190,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="port$cur">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" min="0" max="65535" required />
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" min="0" required />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
|
||||
@@ -222,15 +223,14 @@
|
||||
<input type="number" name="timeout" id="timeout$cur" value="$server['timeout']" min="20" max="240" required /> <i>$T('seconds')</i>
|
||||
</div>
|
||||
|
||||
<div class="field-pair advanced-settings">
|
||||
<div class="field-pair <!--#if int($certificate_validation) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify$cur">
|
||||
<option value="3" <!--#if $server['ssl_verify'] == 3 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-medium')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-minimal')</option>
|
||||
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($certificate_validation) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('-', '<br/>-')</span>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="ssl_ciphers$cur">$T('opt-ssl_ciphers')</label>
|
||||
@@ -248,6 +248,11 @@
|
||||
<input type="checkbox" name="optional" id="optional$cur" value="1" <!--#if int($server['optional']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-optional')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="send_group$cur">$T('srv-send_group')</label>
|
||||
<input type="checkbox" name="send_group" id="send_group$cur" value="1" <!--#if int($server['send_group']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('srv-explain-send_group')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="expire_date$cur">$T('srv-expire_date')</label>
|
||||
<input type="date" name="expire_date" id="expire_date$cur" value="$server['expire_date']" />
|
||||
@@ -293,7 +298,7 @@
|
||||
<p><b>$T('srv-expire_date'):</b> $(server['expire_date'])</p>
|
||||
<!--#end if#-->
|
||||
<!--#if $server['quota']#-->
|
||||
<p><b>$T('quota-left'):</b> $(server['quota_left'])</p>
|
||||
<p><b>$T('quota-left'):</b> $(server['quota_left'])B</p>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
<div class="server-chart" data-serverid="${cur}">
|
||||
@@ -464,14 +469,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(event) {
|
||||
var parentForm = jQuery(event.target).parents("form")
|
||||
parentForm.unbind("submit")
|
||||
parentForm.find('input[name="ajax"]').val('')
|
||||
// Exception when change of priority, reload
|
||||
jQuery('input[name="priority"], input[name="displayname"]').on('change', function() {
|
||||
jQuery('.fullform').submit(function() {
|
||||
// No ajax this time
|
||||
jQuery('input[name="ajax"]').val('')
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
})
|
||||
|
||||
/**
|
||||
@@ -520,13 +525,13 @@
|
||||
var portBox = jQuery(this).parent().parent().find('[name="port"]')
|
||||
if(this.checked) {
|
||||
// Enabled SSL change port when not already a custom port
|
||||
if(portBox.val() === '119') {
|
||||
if(portBox.val() == '119') {
|
||||
portBox.val('563')
|
||||
portBox.addClass('port-highlight')
|
||||
}
|
||||
} else {
|
||||
// Remove SSL port
|
||||
if(portBox.val() === '563') {
|
||||
if(portBox.val() == '563') {
|
||||
portBox.val('119')
|
||||
portBox.addClass('port-highlight')
|
||||
}
|
||||
@@ -559,26 +564,9 @@
|
||||
if(data.value.result) {
|
||||
resultBox.addClass('alert-success')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
|
||||
|
||||
// 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')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -627,15 +615,15 @@
|
||||
function receiveMessage(event) {
|
||||
// Check origin of message for security reasons
|
||||
if(event.origin === 'https://sabnzbd.org') {
|
||||
if(event.data === 'show_server') {
|
||||
if(event.data == 'show_server') {
|
||||
jQuery('.Servers .server-frame').show()
|
||||
jQuery('.Servers .server-frame a').click(function () {
|
||||
localStorage.setItem("server-frame-hide-$version", "hide")
|
||||
jQuery('.Servers .server-frame').hide()
|
||||
})
|
||||
}
|
||||
if(event.data === 'hide_server') {
|
||||
// Hide and don't load anymore until the next release
|
||||
if(event.data == 'hide_server') {
|
||||
// Hide and don't load anymore untill the next release
|
||||
jQuery('.Servers .server-frame').hide()
|
||||
localStorage.setItem("server-frame-hide-$version", "hide")
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
<!--#set global $pane="Sorting"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="padTable section explain-sorting">
|
||||
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
$T('explain-sorting')
|
||||
</div>
|
||||
<div class="colmask">
|
||||
<div class="padTable section">
|
||||
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
$T('explain-sorting')
|
||||
</div>
|
||||
<div class="padding alt section">
|
||||
<button type="button" class="btn btn-default addSorter"><span class="glyphicon glyphicon-plus"></span> $T('add-sorter')</button>
|
||||
<label for="advanced-settings-button" class="form-control advanced-button ">
|
||||
@@ -71,7 +72,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="field_sort_string_$cur">$T('sortString')</label>
|
||||
<input type="text" name="sort_string" id="field_sort_string_$cur" value="$slot.sort_string" required="required" />
|
||||
<button type="button" class="btn btn-default patternKey" onclick="jQuery('#pattern_explainer_$cur').toggle(); window.scrollBy(0, 500);">
|
||||
<button type="button" title="$T('sort-legenda')" class="btn btn-default patternKey" onclick="jQuery('#pattern_explainer_$cur').toggle(); window.scrollBy(0, 500);">
|
||||
<span class="glyphicon glyphicon-list-alt" aria-hidden="true"></span> $T('sort-legenda')
|
||||
</button>
|
||||
</div>
|
||||
@@ -344,7 +345,7 @@
|
||||
</div>
|
||||
|
||||
<!--#if len($slotinfo) == 1 and ("tv" in $categories or "movies" in $categories)#-->
|
||||
<div class="section align-center sorting-quick-setup">
|
||||
<div class="section align-center alt sorting-quick-setup">
|
||||
<h3>$T('sort-quick-add'):</h3>
|
||||
<!--#if "tv" in $categories#-->
|
||||
<form action="save_sorter" method="post" autocomplete="off">
|
||||
@@ -492,7 +493,7 @@
|
||||
update: function(event, ui) {
|
||||
jQuery('.Sorting form.sorting-row').each(function(index, elm) {
|
||||
// Update order of all elements
|
||||
if(index !== elm.order.value) {
|
||||
if(index != elm.order.value) {
|
||||
elm.order.value = index
|
||||
// Submit changed order
|
||||
var data = {}
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<div class="field-pair advanced-settings">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pre_script">$T('opt-pre_script')</label>
|
||||
<select name="pre_script" id="pre_script">
|
||||
<!--#for $sc in $scripts#-->
|
||||
@@ -54,22 +54,9 @@
|
||||
</select>
|
||||
<span class="desc">$T('explain-pre_script')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="end_queue_script">$T('opt-end_queue_script')</label>
|
||||
<select name="end_queue_script" id="end_queue_script">
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc.lower() == $end_queue_script.lower()#-->
|
||||
<option value="$sc" selected="selected">$Tspec($sc)</option>
|
||||
<!--#else#-->
|
||||
<option value="$sc">$Tspec($sc)</option>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-end_queue_script')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="propagation_delay">$T('opt-propagation_delay')</label>
|
||||
<input type="number" name="propagation_delay" id="propagation_delay" value="$propagation_delay" min="0" /> <i>$T('minutes')</i>
|
||||
<input type="number" name="propagation_delay" id="propagation_delay" value="$propagation_delay" /> <i>$T('minutes')</i>
|
||||
<span class="desc">$T('explain-propagation_delay')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
@@ -93,39 +80,33 @@
|
||||
<option value="0" <!--#if int($no_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="4" <!--#if int($no_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
|
||||
<option value="2" <!--#if int($no_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="3" <!--#if int($no_dupes) == 3 then 'selected="selected"' else ""#--> >$T('fail-to-history')</option>
|
||||
<option value="3" <!--#if int($no_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
|
||||
<option value="1" <!--#if int($no_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
|
||||
</select>
|
||||
<span class="desc">
|
||||
$T('explain-no_dupes')<br>
|
||||
<a href="https://sabnzbd.org/wiki/duplicate-detection" target="_blank">https://sabnzbd.org/wiki/duplicate-detection</a>
|
||||
</span>
|
||||
<span class="desc">$T('explain-no_dupes')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="no_smart_dupes">$T('opt-no_smart_dupes')</label>
|
||||
<select name="no_smart_dupes" id="no_smart_dupes">
|
||||
<option value="0" <!--#if int($no_smart_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="4" <!--#if int($no_smart_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
|
||||
<option value="2" <!--#if int($no_smart_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="3" <!--#if int($no_smart_dupes) == 3 then 'selected="selected"' else ""#--> >$T('fail-to-history')</option>
|
||||
<option value="1" <!--#if int($no_smart_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
|
||||
<label class="config" for="no_series_dupes">$T('opt-no_series_dupes')</label>
|
||||
<select name="no_series_dupes" id="no_series_dupes">
|
||||
<option value="0" <!--#if int($no_series_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="4" <!--#if int($no_series_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
|
||||
<option value="2" <!--#if int($no_series_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="3" <!--#if int($no_series_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
|
||||
<option value="1" <!--#if int($no_series_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
|
||||
</select>
|
||||
<span class="desc">
|
||||
$T('explain-no_smart_dupes')<br>
|
||||
<a href="https://sabnzbd.org/wiki/duplicate-detection" target="_blank">https://sabnzbd.org/wiki/duplicate-detection</a>
|
||||
</span>
|
||||
<span class="desc">$T('explain-no_series_dupes')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="dupes_propercheck">$T('opt-dupes_propercheck')</label>
|
||||
<input type="checkbox" name="dupes_propercheck" id="dupes_propercheck" value="1" <!--#if int($dupes_propercheck) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-dupes_propercheck')</span>
|
||||
<label class="config" for="series_propercheck">$T('opt-series_propercheck')</label>
|
||||
<input type="checkbox" name="series_propercheck" id="series_propercheck" value="1" <!--#if int($series_propercheck) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-series_propercheck')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pause_on_pwrar">$T('opt-pause_on_pwrar')</label>
|
||||
<select name="pause_on_pwrar" id="pause_on_pwrar">
|
||||
<option value="0" <!--#if int($pause_on_pwrar) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="1" <!--#if int($pause_on_pwrar) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="2" <!--#if int($pause_on_pwrar) == 2 then 'selected="selected"' else ""#--> >$T('fail-to-history')</option>
|
||||
<option value="2" <!--#if int($pause_on_pwrar) == 2 then 'selected="selected"' else ""#--> >$T('abort')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-pause_on_pwrar')</span>
|
||||
</div>
|
||||
@@ -143,7 +124,7 @@
|
||||
<select name="action_on_unwanted_extensions" id="action_on_unwanted_extensions">
|
||||
<option value="0" <!--#if int($action_on_unwanted_extensions) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="1" <!--#if int($action_on_unwanted_extensions) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="2" <!--#if int($action_on_unwanted_extensions) == 2 then 'selected="selected"' else ""#--> >$T('fail-to-history')</option>
|
||||
<option value="2" <!--#if int($action_on_unwanted_extensions) == 2 then 'selected="selected"' else ""#--> >$T('abort')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-action_on_unwanted_extensions')</span>
|
||||
</div>
|
||||
@@ -193,12 +174,12 @@
|
||||
<div class="field-pair advanced-settings <!--#if not $have_nice then "disabled" else "" #-->">
|
||||
<label class="config" for="nice">$T('opt-nice')</label>
|
||||
<input type="text" name="nice" id="nice" value="$nice" <!--#if not $have_nice then 'readonly="readonly" disabled="disabled"' else "" #--> />
|
||||
<span class="desc">$T('readwiki')</span>
|
||||
<span class="desc">$T('explain-nice')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings <!--#if not $have_ionice then "disabled" else "" #-->">
|
||||
<label class="config" for="ionice">$T('opt-ionice')</label>
|
||||
<input type="text" name="ionice" id="ionice" value="$ionice" <!--#if not $have_ionice then 'readonly="readonly" disabled="disabled"' else "" #--> />
|
||||
<span class="desc">$T('readwiki')</span>
|
||||
<span class="desc">$T('explain-ionice')</span>
|
||||
</div>
|
||||
<!--#else#-->
|
||||
<div class="field-pair advanced-settings">
|
||||
@@ -210,13 +191,13 @@
|
||||
<option value="2" <!--#if int($win_process_prio) == 2 then 'selected="selected"' else ""#-->>$T('win_process_prio-low')</option>
|
||||
<option value="1" <!--#if int($win_process_prio) == 1 then 'selected="selected"' else ""#-->>$T('win_process_prio-idle')</option>
|
||||
</select>
|
||||
<span class="desc">$T('readwiki')</span>
|
||||
<span class="desc">$T('explain-win_process_prio')</span>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="par_option">$T('opt-par_option')</label>
|
||||
<input type="text" name="par_option" id="par_option" value="$par_option" />
|
||||
<span class="desc">$T('readwiki')</span>
|
||||
<span class="desc">$T('explain-par_option')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="sfv_check">$T('opt-sfv_check')</label>
|
||||
@@ -259,17 +240,16 @@
|
||||
<span class="desc">$T('explain-cleanup_list')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="history_retention_option">$T('opt-history_retention')</label>
|
||||
<select name="history_retention_option" id="history_retention_option">
|
||||
<option value="all" <!--#if $auto_sort == "all" then 'selected="selected"' else ""#-->>$T('history_retention-all')</option>
|
||||
<option value="number-archive" <!--#if $history_retention_option == "number-archive" then 'selected="selected"' else ""#-->>$T('history_retention-number-archive')</option>
|
||||
<option value="number-delete" <!--#if $history_retention_option == "number-delete" then 'selected="selected"' else ""#-->>$T('history_retention-number-delete')</option>
|
||||
<option value="days-archive" <!--#if $history_retention_option == "days-archive" then 'selected="selected"' else ""#-->>$T('history_retention-days-archive')</option>
|
||||
<option value="days-delete" <!--#if $history_retention_option == "days-delete" then 'selected="selected"' else ""#-->>$T('history_retention-days-delete')</option>
|
||||
<option value="all-archive" <!--#if $history_retention_option == "all-archive" then 'selected="selected"' else ""#-->>$T('history_retention-archive')</option>
|
||||
<option value="all-delete" <!--#if $history_retention_option == "all-delete" then 'selected="selected"' else ""#-->>$T('history_retention-none')</option>
|
||||
<label class="config" for="history_retention_select">$T('opt-history_retention')</label>
|
||||
<input type="hidden" name="history_retention" id="history_retention" value="$history_retention">
|
||||
<select name="history_retention_select" id="history_retention_select">
|
||||
<option value="0">$T('history_retention-all')</option>
|
||||
<option value="n">$T('history_retention-number')</option>
|
||||
<option value="d">$T('history_retention-days')</option>
|
||||
<option value="-1">$T('history_retention-none')</option>
|
||||
</select>
|
||||
<input type="number" id="history_retention_number" name="history_retention_number" min="1" value="$history_retention_number">
|
||||
<input type="number" id="history_retention_number" name="history_retention_number" min="1">
|
||||
<span class="desc">$T('explain-history_retention').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
@@ -360,24 +340,52 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
jQuery(document).ready(function() {
|
||||
jQuery('#history_retention_option').on('change', updateHistoryRetention)
|
||||
jQuery('#history_retention_select, #history_retention_number').on('change', updateHistoryRetention)
|
||||
function updateHistoryRetention() {
|
||||
var retention_option = jQuery('#history_retention_option').val()
|
||||
var retention_setting = jQuery('#history_retention')
|
||||
var retention_select = jQuery('#history_retention_select').val()
|
||||
var retention_number = jQuery('#history_retention_number')
|
||||
|
||||
if(retention_option === "number-archive" || retention_option === "number-delete") {
|
||||
retention_number.show()
|
||||
retention_number.attr('placeholder', '$T('history_retention-limit')')
|
||||
} else if(retention_option === "days-archive" || retention_option === "days-delete") {
|
||||
retention_number.show()
|
||||
retention_number.attr('placeholder', '$T('days').capitalize()')
|
||||
} else {
|
||||
// Keep all or keep none
|
||||
if(retention_select == "0" || retention_select == "-1") {
|
||||
retention_number.hide()
|
||||
retention_number.val('')
|
||||
retention_number.attr('placeholder', '')
|
||||
retention_setting.val(retention_select)
|
||||
} else {
|
||||
retention_number.show()
|
||||
// Days or number?
|
||||
if(retention_select.indexOf("d") !== -1) {
|
||||
retention_number.attr('placeholder', '$T('days').capitalize()')
|
||||
if(retention_number.val()) {
|
||||
retention_setting.val(retention_number.val() + 'd')
|
||||
} else if(parseInt(retention_setting.val()) > 0) {
|
||||
retention_number.val(parseInt(retention_setting.val()))
|
||||
}
|
||||
} else {
|
||||
retention_number.attr('placeholder', '$T('history_retention-limit')')
|
||||
if(retention_number.val()) {
|
||||
retention_setting.val(retention_number.val())
|
||||
} else if(parseInt(retention_setting.val()) > 0) {
|
||||
retention_number.val(parseInt(retention_setting.val()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updateHistoryRetention()
|
||||
// Set the history-retention settig
|
||||
var retention_setting_value = jQuery('#history_retention').val()
|
||||
if(parseInt(retention_setting_value) > 0) {
|
||||
// Days or number?
|
||||
if(retention_setting_value.indexOf("d") !== -1) {
|
||||
jQuery('#history_retention_select').val("d")
|
||||
} else {
|
||||
jQuery('#history_retention_select').val("n")
|
||||
}
|
||||
jQuery('#history_retention_number').val(parseInt(retention_setting_value))
|
||||
} else {
|
||||
// Keep all or keep none
|
||||
jQuery('#history_retention_select').val(retention_setting_value)
|
||||
jQuery('#history_retention_number').hide()
|
||||
}
|
||||
|
||||
jQuery('.restoreDefaults').click(function(e) {
|
||||
// Get section name
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html lang="$active_lang">
|
||||
<head>
|
||||
<title>SABnzbd - $T('login')</title>
|
||||
@@ -37,8 +36,8 @@
|
||||
<div class="alert alert-danger" role="alert">$error</div>
|
||||
<!--#end if#-->
|
||||
|
||||
<input type="text" class="form-control" name="username" placeholder="$T('srv-username')" autocomplete="username" required autofocus>
|
||||
<input type="password" class="form-control" name="password" placeholder="$T('srv-password')" autocomplete="current-password" required>
|
||||
<input type="text" class="form-control" name="username" placeholder="$T('srv-username')" required autofocus>
|
||||
<input type="password" class="form-control" name="password" placeholder="$T('srv-password')" required>
|
||||
|
||||
<button class="btn btn-default"><span class="glyphicon glyphicon-circle-arrow-right"></span> $T('login') </button>
|
||||
|
||||
@@ -63,4 +62,4 @@
|
||||
} catch(err) { }
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -47,9 +47,9 @@ textarea,
|
||||
.navbar-default .navbar-nav>.open>a:hover,
|
||||
.navbar-default .navbar-nav>li>a.active,
|
||||
.navbar-default .navbar-nav>li>a:hover,
|
||||
.navbar-default .navbar-nav>li>a:focus,
|
||||
.navbar-logo:hover,
|
||||
.quoteBlock,
|
||||
.selected,
|
||||
.server-disabled,
|
||||
#serverResponse,
|
||||
.table>tbody>tr:nth-child(odd),
|
||||
@@ -61,10 +61,30 @@ select:hover {
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
|
||||
.failed {
|
||||
.correct {
|
||||
border: 2px solid #00cc22 !important;
|
||||
}
|
||||
|
||||
.failed,
|
||||
.required-star,
|
||||
.error-text {
|
||||
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;
|
||||
@@ -87,6 +107,7 @@ select.form-control,
|
||||
.navbar-default,
|
||||
.search-box input,
|
||||
.select,
|
||||
.Sorting .explain-sorting,
|
||||
.table-striped>tbody>tr:nth-child(even),
|
||||
.table>tbody>tr:nth-child(even),
|
||||
.tab-pane tr:nth-child(odd),
|
||||
@@ -187,7 +208,7 @@ ul.tabs a,
|
||||
#subscriptions,
|
||||
.RSS form[action="add_rss_feed"] tr:nth-child(even),
|
||||
.Config .table {
|
||||
border: 1px solid #555555;
|
||||
border: 1px solid #555555 !important;
|
||||
}
|
||||
|
||||
.Categories form:first-of-type tr:last-of-type,
|
||||
@@ -267,9 +288,18 @@ col2 h3 a,
|
||||
fill: #555555;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
/* Placeholders - Will not work if grouped! */
|
||||
::-webkit-input-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
@@ -285,14 +315,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ body {
|
||||
float: left;
|
||||
overflow: visible;
|
||||
border: 1px solid #dfdede;
|
||||
border-bottom: none !important;
|
||||
background-color: #FFF;
|
||||
width: 100%
|
||||
}
|
||||
@@ -165,9 +164,6 @@ input[type="checkbox"]+.desc {
|
||||
color: #666;
|
||||
margin: 1em 0;
|
||||
}
|
||||
.col2 p {
|
||||
margin-left: 3px;
|
||||
}
|
||||
.field-pair {
|
||||
padding: 6px;
|
||||
clear: both;
|
||||
@@ -363,6 +359,10 @@ tr.separator {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.Sorting .explain-sorting {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.Sorting .explain-pattern {
|
||||
border: none;
|
||||
width: 100%;
|
||||
@@ -1226,6 +1226,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
.value-and-select select {
|
||||
min-width: 30px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.dotOne, .dotTwo, .dotThree {
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
// Initialize
|
||||
this.element = $(element);
|
||||
this.initialDir = null;
|
||||
this.showFiles = false;
|
||||
this.currentBrowserPath = null;
|
||||
this.currentRequest = null;
|
||||
this.fileBrowserDialog = $('#filebrowser_modal .modal-body');
|
||||
@@ -100,11 +99,6 @@
|
||||
this.initialDir = this.element.data('initialdir') + folderSeperator + this.element.val();
|
||||
}
|
||||
|
||||
// Are we selecting files or folders
|
||||
if(this.element.data('files')) {
|
||||
this.showFiles = true
|
||||
}
|
||||
|
||||
// Browse
|
||||
this.browse(this.initialDir , folderBrowseUrl);
|
||||
|
||||
@@ -115,13 +109,13 @@
|
||||
// Remove start
|
||||
self.currentBrowserPath = self.currentBrowserPath.replace(self.element.data('initialdir')+folderSeperator, '');
|
||||
// If it's identical to the initial dir the replacement won't work
|
||||
if(self.currentBrowserPath === self.element.data('initialdir')) {
|
||||
if(self.currentBrowserPath == self.element.data('initialdir')) {
|
||||
self.currentBrowserPath = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Changed?
|
||||
if(self.element.val() !== self.currentBrowserPath) {
|
||||
if(self.element.val() != self.currentBrowserPath) {
|
||||
self.element.val(self.currentBrowserPath);
|
||||
formHasChanged = true;
|
||||
}
|
||||
@@ -150,20 +144,12 @@
|
||||
// Still loading
|
||||
if (this.currentRequest) this.currentRequest.abort();
|
||||
|
||||
// Show hidden folders
|
||||
var params = { name: path}
|
||||
if($('#show_hidden_folders').is(':checked')) {
|
||||
params['show_hidden_folders'] = "1"
|
||||
}
|
||||
|
||||
// Show files?
|
||||
if(this.showFiles) {
|
||||
params['show_files'] = "1"
|
||||
}
|
||||
// Show hidden folders on Linux?
|
||||
var extraHidden = $('#show_hidden_folders').is(':checked') ? '&show_hidden_folders=1' : '';
|
||||
|
||||
// Get current folders
|
||||
this.currentBrowserPath = path;
|
||||
this.currentRequest = $.getJSON(endpoint, params, function (data) {
|
||||
this.currentRequest = $.getJSON(endpoint + extraHidden, { name: path }, function (data) {
|
||||
// Clean
|
||||
self.fileBrowserDialog.empty();
|
||||
|
||||
@@ -171,27 +157,17 @@
|
||||
var list = $('<div class="list-group">').appendTo(self.fileBrowserDialog);
|
||||
$.each(data.paths, function (i, entry) {
|
||||
// Title for first one
|
||||
if(i === 0) {
|
||||
if(i == 0) {
|
||||
self.fileBrowserDialog.prepend($('<h4>').text(entry.current_path))
|
||||
return
|
||||
}
|
||||
// Regular link
|
||||
link = $('<a class="list-group-item" href="javascript:void(0)" />').click(function () {
|
||||
// Are we looking for files and did we select a file?
|
||||
if(self.showFiles && !entry.dir) {
|
||||
// Trigger selection
|
||||
self.currentBrowserPath = entry.path
|
||||
$('#filebrowser_modal_accept').click()
|
||||
} else {
|
||||
self.browse(entry.path, endpoint);
|
||||
}
|
||||
}).text(entry.name);
|
||||
|
||||
self.browse(entry.path, endpoint); }
|
||||
).text(entry.name);
|
||||
// Back image
|
||||
if(entry.name === '..') {
|
||||
if(entry.name == '..') {
|
||||
$('<span class="glyphicon glyphicon-arrow-left"></span> ').prependTo(link);
|
||||
} else if(!entry.dir) {
|
||||
$('<span class="glyphicon glyphicon-file"></span> ').prependTo(link);
|
||||
} else {
|
||||
$('<span class="glyphicon glyphicon-folder-open"></span> ').prependTo(link);
|
||||
}
|
||||
@@ -262,9 +238,8 @@ function do_restart() {
|
||||
// Show overlay
|
||||
$('.main-restarting').show()
|
||||
|
||||
// Check if we need redirect
|
||||
// Uses == on purpose, because val() returns string and data() returns int!
|
||||
var switchedHTTPS = ($('#enable_https').is(':checked') === ($('#enable_https').data('original') === undefined))
|
||||
// What template
|
||||
var switchedHTTPS = ($('#enable_https').is(':checked') == ($('#enable_https').data('original') === undefined))
|
||||
var portsUnchanged = ($('#port').val() == $('#port').data('original')) && ($('#https_port').val() == $('#https_port').data('original'))
|
||||
|
||||
// Are we on settings page or did nothing change?
|
||||
@@ -273,7 +248,7 @@ function do_restart() {
|
||||
var urlTotal = window.location.origin + urlBase
|
||||
} else {
|
||||
// Protocol and port depend on http(s) setting
|
||||
if($('#enable_https').is(':checked') && (window.location.protocol === 'https:' || !$('#https_port').val())) {
|
||||
if($('#enable_https').is(':checked') && (window.location.protocol == 'https:' || !$('#https_port').val())) {
|
||||
// Https on and we visited this page from HTTPS
|
||||
var urlProtocol = 'https:';
|
||||
var urlPort = $('#https_port').val() ? $('#https_port').val() : $('#port').val();
|
||||
@@ -322,7 +297,7 @@ function do_restart() {
|
||||
|
||||
// Exception if we go from HTTPS to HTTP
|
||||
// (this is not allowed by browsers and all of the above will be ignored)
|
||||
if(window.location.protocol !== urlProtocol) {
|
||||
if(window.location.protocol != urlProtocol) {
|
||||
// Saftey redirect after 20 sec
|
||||
setTimeout(function() {
|
||||
location.href = urlTotal;
|
||||
@@ -332,7 +307,7 @@ function do_restart() {
|
||||
});
|
||||
}
|
||||
|
||||
// Remove obfuscation
|
||||
// Remove obfusication
|
||||
function removeObfuscation() {
|
||||
$('input[data-hide]').each(function(index, objInput) {
|
||||
$(objInput).attr('name', $(objInput).data('hide'))
|
||||
@@ -348,36 +323,6 @@ function addRowColor() {
|
||||
})
|
||||
}
|
||||
|
||||
// Set default functions for the autocomplete everywhere
|
||||
jQuery.extend(jQuery.fn.typeahead.defaults, {
|
||||
source: function (query, process) {
|
||||
// If there's no separator, it must be a relative path
|
||||
if(query.split(folderSeperator).length < 2 && this.$element.data('initialdir')) {
|
||||
query = this.$element.data('initialdir') + folderSeperator + query;
|
||||
}
|
||||
var params = { compact: "1", name: query }
|
||||
if($('#show_hidden_folders').is(':checked')) {
|
||||
params['show_hidden_folders'] = "1"
|
||||
}
|
||||
if(this.$element.data('files')) {
|
||||
params['show_files'] = "1"
|
||||
}
|
||||
// Get info from the API
|
||||
return jQuery.get(folderBrowseUrl, params, function (data) {
|
||||
return process(data["paths"]);
|
||||
});
|
||||
},
|
||||
updater: function(item) {
|
||||
// Is it a relative path?
|
||||
if(item.indexOf(this.$element.data('initialdir')) === 0) {
|
||||
// Remove start
|
||||
return item.replace(this.$element.data('initialdir') + folderSeperator, '');
|
||||
}
|
||||
// Full path
|
||||
return item
|
||||
}
|
||||
})
|
||||
|
||||
$(document).ready(function () {
|
||||
/**
|
||||
Restart function
|
||||
@@ -484,15 +429,12 @@ $(document).ready(function () {
|
||||
$('.advanced-settings').toggle()
|
||||
addRowColor()
|
||||
})
|
||||
if(localStorage.getItem('advanced-settings') === 'true') {
|
||||
if(localStorage.getItem('advanced-settings') == 'true') {
|
||||
$('.advanced-settings').show()
|
||||
$('#advanced-settings-button').prop('checked', true)
|
||||
addRowColor()
|
||||
}
|
||||
addRowColor()
|
||||
|
||||
// Add tooltips
|
||||
jQuery('[title]').tooltip()
|
||||
});
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class="history" id="history-tab">
|
||||
<div class="history" id="history-tab" data-bind="visible: hasHistory() || displayTabbed()" style="display: none">
|
||||
<div class="history-header">
|
||||
<h2>$T('menu-history') <small data-bind="visible: history.showArchive()">($T('archive'))</small></h2>
|
||||
<a href="#" data-bind="click: history.showMultiEdit, visible: hasHistory()">
|
||||
<h2>$T('menu-history')</h2>
|
||||
<a href="#" data-bind="click: history.showMultiEdit">
|
||||
<span class="glyphicon glyphicon-tasks" data-tooltip="true" data-placement="left" title="$T('Glitter-multiOperations')"></span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -18,16 +18,7 @@
|
||||
<th style="width: 60px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!-- ko if: !hasHistory() -->
|
||||
<tbody class="no-downloads">
|
||||
<tr>
|
||||
<td colspan="6" data-bind="attr: { 'colspan': 5 + extraHistoryColumns().length }">
|
||||
<span>$T('empty')</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<!-- /ko -->
|
||||
<tbody data-bind="foreach: history.historyItems, visible: hasHistory()" style="display: none;">
|
||||
<tbody data-bind="foreach: history.historyItems">
|
||||
<tr class="history-item" data-bind="css: {'history-failed-download':failed()}">
|
||||
<td>
|
||||
<div data-bind="visible: processingWaiting()">
|
||||
@@ -70,7 +61,7 @@
|
||||
<td class="history-completedon row-wrap-text" data-bind="text: completedOn(), attr: { 'data-timestamp': completed }" onclick="showDetails(this)"></td>
|
||||
<td class="delete">
|
||||
<label data-bind="visible: parent.isMultiEditing()">
|
||||
<input type="checkbox" name="multiedit" title="$T('Glitter-multiSelect')" data-bind="click: parent.addMultiEdit, attr: { 'id': 'multiedit_' + id } " />
|
||||
<input type="checkbox" name="multiedit" title="$T('Glitter-multiSelect')" data-bind="click: parent.parent.addMultiEdit, attr: { 'id': 'multiedit_' + id } " />
|
||||
</label>
|
||||
<div class="dropdown" data-bind="visible: !parent.isMultiEditing()">
|
||||
<a href="#" data-toggle="dropdown" data-bind="click: updateAllHistoryInfo">
|
||||
@@ -83,20 +74,13 @@
|
||||
<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">
|
||||
<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 class="col-sm-10" data-bind="text: glitterTranslate.status[historyStatus.status()] ? glitterTranslate.status[historyStatus.status()] : statusText()"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">$T('size')</div>
|
||||
@@ -124,7 +108,7 @@
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<a href="#" data-bind="click: parent.triggerRemoveDownload">
|
||||
<a href="#" data-bind="click: deleteSlot">
|
||||
<span class="hover-button glyphicon glyphicon-trash" data-bind="css: { 'glyphicon-stop' : processingDownload() == 2, disabled : processingDownload() == 1 }, attr: { title: processingDownload() == 2 ? '$T('abort')' : '$T('nzo-delete')' }"></span>
|
||||
</a>
|
||||
</td>
|
||||
@@ -146,22 +130,20 @@
|
||||
</ul>
|
||||
|
||||
<div class="multioperations-selector" id="history-options">
|
||||
<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>
|
||||
<a href="#" class="hover-button" title="$T('link-retryAll')" data-tooltip="true" data-placement="left" data-bind="click: history.retryAllFailed"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="hover-button" title="$T('showAllHis') / $T('showFailedHis')" data-tooltip="true" data-placement="left" data-bind="click: history.toggleShowFailed, css: { 'history-options-show-failed': history.showFailed }"><span class="glyphicon glyphicon-exclamation-sign"></span></a>
|
||||
|
||||
<div data-bind="visible: (history.isMultiEditing() && hasHistory())">
|
||||
<div data-bind="visible: history.isMultiEditing()">
|
||||
<span class="label label-default" data-bind="text: history.multiEditItems().length">0</span>
|
||||
<label for="multiedit-checkall-history">
|
||||
<input type="checkbox" name="multieditCheckAll" id="multiedit-checkall-history" title="$T('Glitter-checkAll')" data-bind="click: history.checkAllJobs" data-tooltip="true" data-placement="top" />
|
||||
<input type="checkbox" name="multieditCheckAll" id="multiedit-checkall-history" title="$T('Glitter-checkAll')" data-bind="click: checkAllJobs" data-tooltip="true" data-placement="top" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<a href="#" class="hover-button" title="$T('nzo-delete')" data-bind="visible: (history.isMultiEditing() && hasHistory()), click: history.doMultiDelete" data-tooltip="true" data-placement="top">
|
||||
<a href="#" class="hover-button" data-bind="visible: history.isMultiEditing(), click: doMultiDelete">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
<a href="#modal-purge-history" class="hover-button" title="$T('purgeHist')" data-bind="visible: !(history.isMultiEditing() && hasHistory())" data-toggle="modal" data-tooltip="true" data-placement="top">
|
||||
<a href="#modal-purge-history" class="hover-button" title="$T('purgeHist')" data-bind="visible: !history.isMultiEditing()" data-toggle="modal" data-tooltip="true" data-placement="left">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -173,3 +155,9 @@
|
||||
<span data-bind="text: history.downloadedTotal"></span>B $T('Glitter-total')
|
||||
</div>
|
||||
</div>
|
||||
<div class="info-container history-info" data-bind="visible: !hasHistory() && !displayTabbed()" style="display: none">
|
||||
<span class="glyphicon glyphicon-save"></span>
|
||||
<span data-bind="text: history.downloadedToday"></span>B $T('Glitter-today')
|
||||
<span data-bind="text: history.downloadedMonth"></span>B $T('Glitter-thisMonth')
|
||||
<span data-bind="text: history.downloadedTotal"></span>B $T('Glitter-total')
|
||||
</div>
|
||||
@@ -102,15 +102,26 @@
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header"><span class="glyphicon glyphicon-off"></span> $T('Glitter-onFinish'):</li>
|
||||
<li>
|
||||
<!-- ko if: queue.scriptsListLoaded -->
|
||||
<select data-bind="value: finishaction, event: { change: setOnQueueFinish }" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="shutdown_program">$T('shutdownSab')</option>
|
||||
<!--#if $power_options#-->
|
||||
<option value="shutdown_pc">$T('shutdownPc')</option>
|
||||
<option value="standby_pc">$T('standbyPc')</option>
|
||||
<option value="hibernate_pc">$T('hibernatePc')</option>
|
||||
<!--#end if#-->
|
||||
<optgroup label="$T('eoq-actions')">
|
||||
<option value="shutdown_program">$T('shutdownSab')</option>
|
||||
<!--#if $power_options#-->
|
||||
<option value="shutdown_pc">$T('shutdownPc')</option>
|
||||
<option value="standby_pc">$T('standbyPc')</option>
|
||||
<option value="hibernate_pc">$T('hibernatePc')</option>
|
||||
<!--#end if#-->
|
||||
</optgroup>
|
||||
<optgroup label="$T('eoq-scripts')" data-bind="visible: queue.scriptsList().length > 1">
|
||||
<!-- ko foreach: queue.scriptsList -->
|
||||
<!-- ko if: \$data.scriptValue != 'None' -->
|
||||
<option data-bind="text: \$data.scriptText, attr: { value: 'script_'+\$data.scriptValue } " ></option>
|
||||
<!-- /ko -->
|
||||
<!-- /ko -->
|
||||
</optgroup>
|
||||
</select>
|
||||
<!-- /ko -->
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -134,7 +134,7 @@
|
||||
<div class="col-sm-6">$T('dashboard-systemPerformance') </div>
|
||||
<div class="col-sm-6 col-dot-overflow" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.pystone"></span>
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest') (~10 $T('seconds'))"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small title="$cpumodel $cpusimd" data-tooltip="true">$cpumodel $cpusimd</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
@@ -143,7 +143,7 @@
|
||||
<div class="col-sm-6">$T('dashboard-downloadDirSpeed') </div>
|
||||
<div class="col-sm-6 col-dot-overflow" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.downloaddirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest') (~10 $T('seconds'))"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="text: statusInfo.downloaddir, attr: { 'data-original-title': statusInfo.downloaddir }" data-tooltip="true"></small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
@@ -152,7 +152,7 @@
|
||||
<div class="col-sm-6">$T('dashboard-completeDirSpeed') </div>
|
||||
<div class="col-sm-6 col-dot-overflow" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.completedirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest') (~10 $T('seconds'))"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="text: statusInfo.completedir, attr: { 'data-original-title': statusInfo.completedir }" data-tooltip="true"></small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
@@ -161,17 +161,11 @@
|
||||
<div class="col-sm-6">$T('dashboard-internetBandwidth') </div>
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.internetbandwidth()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest') (~10 $T('seconds'))"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="diskspeed-button" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small><span data-bind="text: statusInfo.internetbandwidth()*8"></span> Mbps</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('platform') </div>
|
||||
<div class="col-sm-6">
|
||||
$platform
|
||||
</div>
|
||||
</div>
|
||||
<div class="row test-download">
|
||||
<div class="col-sm-6">$T('dashboard-testDownload') </div>
|
||||
<div class="col-sm-6">
|
||||
@@ -231,20 +225,15 @@
|
||||
</div>
|
||||
<div class="row" data-bind="visible: serverssl">
|
||||
<div class="col-sm-6">$T('srv-ssl')</div>
|
||||
<div class="col-sm-6 col-dot-overflow">
|
||||
<span class="glyphicon glyphicon-ok"></span>
|
||||
<span data-bind="text: serversslinfo"></span>
|
||||
<div class="col-sm-6">
|
||||
<span class="glyphicon glyphicon-ok"></span> <span data-bind="text: serversslinfo"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6"># $T('connections')</div>
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: serverconnections().length"></span> /
|
||||
<span data-bind="text: servertotalconn"></span><br>
|
||||
<!-- ko if: serveripaddress() -->
|
||||
<span data-bind="text: servercanonname"></span><br>
|
||||
<span data-bind="text: serveripaddress"></span>
|
||||
<!-- /ko -->
|
||||
<span data-bind="text: servertotalconn"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -260,11 +249,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-bind="visible: serverwarning()">
|
||||
<div class="row" data-bind="visible: !isFinite(serveractiveconn())">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-warning">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
<span data-bind="text: serverwarning()"></span>
|
||||
<span data-bind="text: serveractiveconn()"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -534,7 +523,7 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('category')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText', optionsCaption: ''"></select>
|
||||
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText'"></select>
|
||||
<span class="glyphicon glyphicon-tag"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -543,7 +532,7 @@
|
||||
<div class="col-sm-6">
|
||||
<!-- This list is different from the one during download! -->
|
||||
<select name="Priority" class="form-control">
|
||||
<option value=""></option>
|
||||
<option value="-100">$T('default')</option>
|
||||
<option value="2">$T('pr-force')</option>
|
||||
<option value="1">$T('pr-high')</option>
|
||||
<option value="0">$T('pr-normal')</option>
|
||||
@@ -556,14 +545,14 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('swtag-pp')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Processing" class="form-control" data-bind="options: queue.processingOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: ''"></select>
|
||||
<select name="Processing" class="form-control" data-bind="options: queue.processingOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
|
||||
<span class="glyphicon glyphicon-check"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('eoq-scripts')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '', optionsValue: 'scriptValue', optionsText: 'scriptText', enable: (queue.scriptsList().length > 1)"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '$T('default')', optionsValue: 'scriptValue', optionsText: 'scriptText', enable: (queue.scriptsList().length > 1)"></select>
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -641,59 +630,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-delete-queue-job" class="modal modal-delete-job fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title row-wrap-text">$T('removeNZB-Files')</h4>
|
||||
</div>
|
||||
<form data-bind="submit: queue.removeDownloads">
|
||||
<div class="modal-body">
|
||||
$T('confirm-delete')
|
||||
|
||||
<ul data-bind="foreach: queue.deleteItems">
|
||||
<li data-bind="text: name"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">$T('cancel')</button>
|
||||
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> $T('nzo-delete')</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-delete-history-job" class="modal modal-delete-job fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h4 class="modal-title row-wrap-text">$T('nzo-delete')</h4>
|
||||
</div>
|
||||
<form data-bind="submit: history.removeDownloads">
|
||||
<div class="modal-body">
|
||||
$T('confirm-delete')
|
||||
|
||||
<ul data-bind="foreach: history.deleteItems">
|
||||
<li data-bind="text: historyStatus.name"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="checkbox" data-bind="visible: !history.showArchive()">
|
||||
<label>
|
||||
<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>
|
||||
<button type="submit" class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span> $T('nzo-delete')</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-retry-job" class="modal modal-small fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
@@ -724,9 +660,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
$T('Glitter-retryNoChecks')
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default"><span class="glyphicon glyphicon-repeat"></span> $T('button-retry')</button>
|
||||
@@ -774,13 +707,13 @@
|
||||
<td><a href="https://github.com/sabnzbd/sabnzbd" target="_blank">https://github.com/sabnzbd/sabnzbd/</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>$T('menu-live-chat'):</strong></td>
|
||||
<td><strong>$T('menu-irc'):</strong></td>
|
||||
<td><a href="https://sabnzbd.org/live-chat" target="_blank">https://sabnzbd.org/live-chat</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr/>
|
||||
<p><small>Copyright © 2007-2025 by The SABnzbd-Team (<a href="https://sabnzbd.org/" target="_blank">sabnzbd.org</a>)<br/>$T('yourRights') </small></p>
|
||||
<p><small>Copyright (C) 2007-2021 The SABnzbd-Team (<a href="https://sabnzbd.org/" target="_blank">sabnzbd.org</a>)<br/>$T('yourRights') </small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -799,18 +732,11 @@
|
||||
<button type="button" class="btn btn-danger" data-bind="click: history.emptyHistory" data-action="history-purge-completed"><span class="glyphicon glyphicon-floppy-saved"></span> $T('purgeCompl')</button><hr />
|
||||
<button type="button" class="btn btn-danger" data-bind="click: history.emptyHistory" data-action="history-purge-page"><span class="glyphicon glyphicon-check"></span> $T('purgePage') <span class="label label-default" data-bind="text: history.historyItems().length"></span></button>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-bind="checked: history.permanentlyDelete"> <span>$T('permanently-delete')</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-custom-pause" class="modal modal-small fade" tabindex="-1">
|
||||
<div id="modal_custom_pause" class="modal modal-small fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
<span data-bind="text: queueDataLeft"></span> $T('Glitter-left')
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<!-- ko if: (parseInt(quotaLimit()) != 0) -->
|
||||
<!-- ko if: (quotaLimit() != 0) -->
|
||||
<div class="info-container-box">
|
||||
<span data-bind="css: { 'queue-error-info' : (parseInt(quotaLimitLeft())<=0) }">
|
||||
<span class="glyphicon glyphicon-log-in"></span> <span data-bind="text: quotaLimitLeft"></span> / <span data-bind="text: quotaLimit"></span> $T('quota-left')
|
||||
<span class="glyphicon glyphicon-log-in"></span> <span data-bind="text: quotaLimitLeft"></span>B / <span data-bind="text: quotaLimit"></span>B $T('quota-left')
|
||||
</span>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
@@ -132,7 +132,7 @@
|
||||
<td class="timeleft row-wrap-text" data-bind="text: statusText"></td>
|
||||
<td class="delete">
|
||||
<label data-bind="visible: parent.isMultiEditing()">
|
||||
<input type="checkbox" name="multiedit" title="$T('Glitter-multiSelect')" data-bind="click: parent.addMultiEdit, attr: { 'id': 'multiedit_' + id } " />
|
||||
<input type="checkbox" name="multiedit" title="$T('Glitter-multiSelect')" data-bind="click: parent.parent.addMultiEdit, attr: { 'id': 'multiedit_' + id } " />
|
||||
</label>
|
||||
<!-- ko if: !isGrabbing() -->
|
||||
<div class="dropdown" data-bind="visible: !parent.isMultiEditing()">
|
||||
@@ -163,7 +163,7 @@
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<a href="#" class="hover-button" title="$T('removeNZB-Files')" data-bind="click: parent.triggerRemoveDownload"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
<a href="#" class="hover-button" title="$T('removeNZB-Files')" data-bind="click: removeDownload"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -172,27 +172,21 @@
|
||||
<form class="multioperations-selector" data-bind="visible: (hasQueue() && queue.isMultiEditing())" style="display: none;">
|
||||
<div class="add-nzb-inputbox add-nzb-inputbox-small add-nzb-inputbox-options">
|
||||
<label for="multiedit-checkall-queue">
|
||||
<input type="checkbox" name="multieditCheckAll" id="multiedit-checkall-queue" title="$T('Glitter-checkAll')" data-bind="click: queue.checkAllJobs" data-tooltip="true" data-placement="top" />
|
||||
<input type="checkbox" name="multieditCheckAll" id="multiedit-checkall-queue" title="$T('Glitter-checkAll')" data-bind="click: checkAllJobs" data-tooltip="true" data-placement="top" />
|
||||
</label>
|
||||
<a href="#" class="hover-button" title="$T('removeNZB-Files')" data-bind="click: queue.doMultiDelete" data-tooltip="true" data-placement="top">
|
||||
<a href="#" class="hover-button" data-bind="click: doMultiDelete">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="add-nzb-inputbox add-nzb-inputbox-small">
|
||||
<label for="multiedit-play" data-bind="event: { mousedown: queue.handleMultiEditStatusMouseDown }">
|
||||
<label for="multiedit-play">
|
||||
<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" data-bind="event: { mousedown: queue.handleMultiEditStatusMouseDown }">
|
||||
<label for="multiedit-pause">
|
||||
<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>
|
||||
@@ -227,4 +221,4 @@
|
||||
<span data-bind="text: page"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -52,21 +52,21 @@
|
||||
var glitterTranslate = new Object();
|
||||
glitterTranslate.paused = "$T('post-Paused')";
|
||||
glitterTranslate.left = "$T('Glitter-left')";
|
||||
glitterTranslate.clearOrphanWarning = "$T('Glitter-clearOrphanWarning')";
|
||||
glitterTranslate.clearWarn = "$T('confirm')";
|
||||
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.confirm = "$T('confirm')";
|
||||
glitterTranslate.markComplete = "$T('button-mark-completed')";
|
||||
glitterTranslate.deleteMsg = "$T('nzo-delete')";
|
||||
glitterTranslate.removeDown = "$T('confirm')";
|
||||
glitterTranslate.removeDow1 = "$T('confirm')";
|
||||
glitterTranslate.renameAbort = "$T('Glitter-confirmAbortDirectUnpack')\n$T('confirm')";
|
||||
glitterTranslate.retryAll = "$T('link-retryAll')?";
|
||||
glitterTranslate.fetch = "$T('Glitter-fetch')";
|
||||
glitterTranslate.checking = "$T('post-Checking')";
|
||||
glitterTranslate.misingArt = "$T('missingArt')";
|
||||
glitterTranslate.fetchingURL = "$T('Glitter-addFromURL')"
|
||||
glitterTranslate.chooseFile = "$T('Glitter-chooseFile')";
|
||||
glitterTranslate.orphanedJobsMsg = "$T('explain-orphans')";
|
||||
glitterTranslate.useCache = "$T('explain-cache_limitstr').replace("64M", "256M").replace("128M", "512M")";
|
||||
@@ -96,7 +96,6 @@
|
||||
glitterTranslate.status['Unpack'] = "$T('stage-unpack')";
|
||||
glitterTranslate.status['Deobfuscate'] = "$T('stage-deobfuscate')";
|
||||
glitterTranslate.status['Script'] = "$T('stage-script')";
|
||||
glitterTranslate.status['RSS'] = "$T('stage-rss')";
|
||||
glitterTranslate.status['Source'] = "$T('stage-source')";
|
||||
glitterTranslate.status['Servers'] = "$T('stage-servers')";
|
||||
glitterTranslate.status['INFO'] = "$T('log-info')".replace('+', '').toUpperCase();
|
||||
|
||||
@@ -58,11 +58,11 @@ function convertHTMLtoText(htmltxt) {
|
||||
// Function to re-write 0:09:21=>9:21, 0:10:10=>10:10, 0:00:30=>0:30
|
||||
function rewriteTime(timeString) {
|
||||
// Remove "0:0" from start
|
||||
if(timeString.substring(0,3) === '0:0') {
|
||||
if(timeString.substring(0,3) == '0:0') {
|
||||
timeString = timeString.substring(3)
|
||||
}
|
||||
// Remove "0:" from start
|
||||
else if(timeString.substring(0,2) === '0:') {
|
||||
else if(timeString.substring(0,2) == '0:') {
|
||||
timeString = timeString.substring(2)
|
||||
}
|
||||
return timeString
|
||||
@@ -71,13 +71,13 @@ function rewriteTime(timeString) {
|
||||
// How to display the date-time?
|
||||
function displayDateTime(inDate, outFormat, inFormat) {
|
||||
// What input?
|
||||
if(inDate === '') {
|
||||
if(inDate == '') {
|
||||
var theMoment = moment()
|
||||
} else {
|
||||
var theMoment = moment.utc(inDate, inFormat)
|
||||
}
|
||||
// Special format or regular format?
|
||||
if(outFormat === 'fromNow') {
|
||||
if(outFormat == 'fromNow') {
|
||||
return theMoment.fromNow()
|
||||
} else {
|
||||
return theMoment.local().format(outFormat)
|
||||
@@ -155,7 +155,7 @@ function setCheckAllState(checkSelector, rangeSelector) {
|
||||
var nrChecks = allChecks.filter(":checked");
|
||||
if(nrChecks.length === 0) {
|
||||
$(checkSelector).prop({'checked': false, 'indeterminate': false})
|
||||
} else if(nrChecks.length === allChecks.length) {
|
||||
} else if(nrChecks.length == allChecks.length) {
|
||||
$(checkSelector).prop({'checked': true, 'indeterminate': false})
|
||||
} else {
|
||||
$(checkSelector).prop({'checked': false, 'indeterminate': true})
|
||||
|
||||
@@ -57,7 +57,7 @@ function Fileslisting(parent) {
|
||||
$.each(response.files, function(index, slot) {
|
||||
// Existing or updating?
|
||||
var existingItem = ko.utils.arrayFirst(self.fileItems(), function(i) {
|
||||
return i.nzf_id() === slot.nzf_id;
|
||||
return i.nzf_id() == slot.nzf_id;
|
||||
});
|
||||
|
||||
if(existingItem) {
|
||||
@@ -76,7 +76,7 @@ function Fileslisting(parent) {
|
||||
}
|
||||
|
||||
// Check if we show/hide completed
|
||||
if(localStorageGetItem('showCompletedFiles') === 'No') {
|
||||
if(localStorageGetItem('showCompletedFiles') == 'No') {
|
||||
$('.item-files-table tr.files-done').hide();
|
||||
$('#filelist-showcompleted').removeClass('hover-button')
|
||||
}
|
||||
@@ -217,8 +217,8 @@ function FileslistingModel(parent, data) {
|
||||
self.nzf_id = ko.observable(data.nzf_id);
|
||||
self.file_age = ko.observable(data.age);
|
||||
self.mb = ko.observable(data.mb);
|
||||
self.canselect = ko.observable(data.status !== "finished" && data.status !== "queued");
|
||||
self.isdone = ko.observable(data.status === "finished");
|
||||
self.canselect = ko.observable(data.status != "finished" && data.status != "queued");
|
||||
self.isdone = ko.observable(data.status == "finished");
|
||||
self.percentage = ko.observable(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
|
||||
|
||||
// Update internally
|
||||
@@ -227,8 +227,8 @@ function FileslistingModel(parent, data) {
|
||||
self.nzf_id(data.nzf_id)
|
||||
self.file_age(data.age)
|
||||
self.mb(data.mb)
|
||||
self.canselect(data.status !== "finished" && data.status !== "queued")
|
||||
self.isdone(data.status === "finished")
|
||||
self.canselect(data.status != "finished" && data.status != "queued")
|
||||
self.isdone(data.status == "finished")
|
||||
// Data is given in MB, would always show 0% for small files even if completed
|
||||
self.percentage(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)))
|
||||
}
|
||||
@@ -266,7 +266,7 @@ function paginationModel(parent) {
|
||||
// Return object for adding
|
||||
return {
|
||||
page: pageNr,
|
||||
isCurrent: pageNr === self.currentPage(),
|
||||
isCurrent: pageNr == self.currentPage(),
|
||||
isDots: false,
|
||||
onclick: function(data) {
|
||||
self.moveToPage(data.page);
|
||||
@@ -356,7 +356,7 @@ function paginationModel(parent) {
|
||||
}
|
||||
|
||||
// Change of number of pages?
|
||||
if(newNrPages !== self.nrPages()) {
|
||||
if(newNrPages != self.nrPages()) {
|
||||
// Update
|
||||
self.nrPages(newNrPages);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,10 @@ function HistoryListModel(parent) {
|
||||
self.lastUpdate = 0;
|
||||
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' });
|
||||
self.totalItems = ko.observable(0);
|
||||
self.deleteItems = ko.observableArray([]);
|
||||
self.ppItems = ko.observable(0);
|
||||
self.pagination = new paginationModel(self);
|
||||
self.isMultiEditing = ko.observable(false).extend({ persist: 'historyIsMultiEditing' });
|
||||
@@ -46,7 +43,7 @@ function HistoryListModel(parent) {
|
||||
var newItems = [];
|
||||
$.each(data.slots, function(index, slot) {
|
||||
var existingItem = ko.utils.arrayFirst(self.historyItems(), function(i) {
|
||||
return i.historyStatus.nzo_id() === slot.nzo_id;
|
||||
return i.historyStatus.nzo_id() == slot.nzo_id;
|
||||
});
|
||||
// Set index in the results
|
||||
slot.index = index
|
||||
@@ -62,7 +59,7 @@ function HistoryListModel(parent) {
|
||||
});
|
||||
|
||||
// Remove all items
|
||||
if(itemIds.length === self.paginationLimit()) {
|
||||
if(itemIds.length == self.paginationLimit()) {
|
||||
// Replace it, so only 1 Knockout DOM-update!
|
||||
self.historyItems(newItems);
|
||||
newItems = [];
|
||||
@@ -71,7 +68,7 @@ function HistoryListModel(parent) {
|
||||
$.each(itemIds, function() {
|
||||
var id = this.toString();
|
||||
self.historyItems.remove(ko.utils.arrayFirst(self.historyItems(), function(i) {
|
||||
return i.historyStatus.nzo_id() === id;
|
||||
return i.historyStatus.nzo_id() == id;
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -85,7 +82,7 @@ function HistoryListModel(parent) {
|
||||
if(self.parent.queue.multiEditItems().length > 0) {
|
||||
$.each(newItems, function() {
|
||||
var currentItem = this;
|
||||
self.parent.queue.multiEditItems.remove(function(inList) { return inList.id === currentItem.id; })
|
||||
self.parent.queue.multiEditItems.remove(function(inList) { return inList.id == currentItem.id; })
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -121,27 +118,6 @@ function HistoryListModel(parent) {
|
||||
self.parent.refresh(true)
|
||||
});
|
||||
|
||||
self.triggerRemoveDownload = function(items) {
|
||||
// Show and fill modal
|
||||
self.deleteItems.removeAll()
|
||||
|
||||
// Single or multiple items?
|
||||
if(items.length) {
|
||||
ko.utils.arrayPushAll(self.deleteItems, items)
|
||||
} else {
|
||||
self.deleteItems.push(items)
|
||||
}
|
||||
|
||||
// Show modal or delete right away
|
||||
if(self.parent.confirmDeleteHistory()) {
|
||||
// Open modal if desired
|
||||
$('#modal-delete-history-job').modal("show")
|
||||
} else {
|
||||
// Otherwise just submit right away
|
||||
$('#modal-delete-history-job form').submit()
|
||||
}
|
||||
}
|
||||
|
||||
// Retry a job
|
||||
self.retryJob = function(form) {
|
||||
// Adding a extra retry file happens through this special function
|
||||
@@ -172,7 +148,7 @@ function HistoryListModel(parent) {
|
||||
// Searching in history (rate-limited in declaration)
|
||||
self.searchTerm.subscribe(function() {
|
||||
// Go back to page 1
|
||||
if(self.pagination.currentPage() !== 1) {
|
||||
if(self.pagination.currentPage() != 1) {
|
||||
// This forces a refresh
|
||||
self.pagination.moveToPage(1);
|
||||
} else {
|
||||
@@ -184,13 +160,13 @@ function HistoryListModel(parent) {
|
||||
// Clear searchterm
|
||||
self.clearSearchTerm = function(data, event) {
|
||||
// Was it escape key or click?
|
||||
if(event.type === 'mousedown' || (event.keyCode && event.keyCode === 27)) {
|
||||
if(event.type == 'mousedown' || (event.keyCode && event.keyCode == 27)) {
|
||||
// Set the loader so it doesn't flicker and then switch
|
||||
self.isLoading(true)
|
||||
self.searchTerm('');
|
||||
}
|
||||
// Was it click and the field is empty? Then we focus on the field
|
||||
if(event.type === 'mousedown' && self.searchTerm() === '') {
|
||||
if(event.type == 'mousedown' && self.searchTerm() == '') {
|
||||
$(event.target).parents('.search-box').find('input[type="text"]').focus()
|
||||
return;
|
||||
}
|
||||
@@ -200,17 +176,10 @@ function HistoryListModel(parent) {
|
||||
|
||||
// Toggle showing failed
|
||||
self.toggleShowFailed = function(data, event) {
|
||||
// Set the loader so it doesn't flicker and then switch
|
||||
self.isLoading(true)
|
||||
self.showFailed(!self.showFailed())
|
||||
// Force hide tooltip so it doesn't linger
|
||||
$('#history-options a').tooltip('hide')
|
||||
// Force refresh
|
||||
self.parent.refresh(true)
|
||||
}
|
||||
|
||||
// Toggle showing archive
|
||||
self.toggleShowArchive = function(data, event) {
|
||||
self.showArchive(!self.showArchive())
|
||||
// Force hide tooltip so it doesn't linger
|
||||
// Forde hide tooltip so it doesn't linger
|
||||
$('#history-options a').tooltip('hide')
|
||||
// Force refresh
|
||||
self.parent.refresh(true)
|
||||
@@ -232,28 +201,30 @@ function HistoryListModel(parent) {
|
||||
|
||||
// Empty history options
|
||||
self.emptyHistory = function(data, event) {
|
||||
// Make sure no flickering
|
||||
self.isLoading(true)
|
||||
|
||||
// What event?
|
||||
var whatToRemove = $(event.target).data('action');
|
||||
var skipArchive = $('#modal-purge-history input[type="checkbox"]').prop("checked")
|
||||
var del_files, value;
|
||||
|
||||
// Purge failed
|
||||
if(whatToRemove === 'history-purge-failed') {
|
||||
if(whatToRemove == 'history-purge-failed') {
|
||||
del_files = 0;
|
||||
value = 'failed';
|
||||
}
|
||||
// Also remove files
|
||||
if(whatToRemove === 'history-purgeremove-failed') {
|
||||
if(whatToRemove == 'history-purgeremove-failed') {
|
||||
del_files = 1;
|
||||
value = 'failed';
|
||||
}
|
||||
// Remove completed
|
||||
if(whatToRemove === 'history-purge-completed') {
|
||||
if(whatToRemove == 'history-purge-completed') {
|
||||
del_files = 0;
|
||||
value = 'completed';
|
||||
}
|
||||
// Remove the ones on this page
|
||||
if(whatToRemove === 'history-purge-page') {
|
||||
if(whatToRemove == 'history-purge-page') {
|
||||
// List all the ID's
|
||||
var strIDs = '';
|
||||
$.each(self.historyItems(), function(index) {
|
||||
@@ -267,7 +238,6 @@ function HistoryListModel(parent) {
|
||||
mode: 'history',
|
||||
name: 'delete',
|
||||
del_files: 1,
|
||||
archive: (!skipArchive) * 1,
|
||||
value: strIDs
|
||||
}).then(function() {
|
||||
// Clear search, refresh and hide
|
||||
@@ -282,9 +252,8 @@ function HistoryListModel(parent) {
|
||||
callAPI({
|
||||
mode: 'history',
|
||||
name: 'delete',
|
||||
del_files: del_files,
|
||||
archive: (!skipArchive) * 1,
|
||||
value: value
|
||||
value: value,
|
||||
del_files: del_files
|
||||
}).then(function() {
|
||||
self.parent.refresh();
|
||||
$("#modal-purge-history").modal('hide');
|
||||
@@ -295,184 +264,8 @@ function HistoryListModel(parent) {
|
||||
self.showMultiEdit = function() {
|
||||
self.isMultiEditing(!self.isMultiEditing())
|
||||
self.multiEditItems.removeAll();
|
||||
$('.history-table input[name="multiedit"], #multiedit-checkall-history').prop({'checked': false, 'indeterminate': false})
|
||||
$('.history-table input[name="multiedit"], #history-options #multiedit-checkall').prop({'checked': false, 'indeterminate': false})
|
||||
}
|
||||
|
||||
// Add to the list
|
||||
self.addMultiEdit = function(item, event) {
|
||||
// Is it a shift-click?
|
||||
if(event.shiftKey) {
|
||||
checkShiftRange('.history-table input[name="multiedit"]');
|
||||
}
|
||||
|
||||
// Add or remove from the list?
|
||||
if(event.currentTarget.checked) {
|
||||
// Add item
|
||||
self.multiEditItems.push(item);
|
||||
} else {
|
||||
// Go over them all to know which one to remove
|
||||
self.multiEditItems.remove(function(inList) { return inList.id == item.id; })
|
||||
}
|
||||
|
||||
// Update check-all buton state
|
||||
setCheckAllState('#multiedit-checkall-history', '.history-table input[name="multiedit"]')
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check all
|
||||
self.checkAllJobs = function(item, event) {
|
||||
// Get which ones we care about
|
||||
var allChecks = $('.history-table input[name="multiedit"]').filter(':not(:disabled):visible');
|
||||
|
||||
// We need to re-evaltuate the state of this check-all
|
||||
// Otherwise the 'inderterminate' will be overwritten by the click event!
|
||||
setCheckAllState('#multiedit-checkall-history', '.history-table input[name="multiedit"]')
|
||||
|
||||
// Now we can check what happend
|
||||
// For when some are checked, or all are checked (but not partly)
|
||||
if(event.target.indeterminate || (event.target.checked && !event.target.indeterminate)) {
|
||||
var allActive = allChecks.filter(":checked")
|
||||
// First remove the from the list
|
||||
if(allActive.length == self.multiEditItems().length) {
|
||||
// Just remove all
|
||||
self.multiEditItems.removeAll();
|
||||
// Remove the check
|
||||
allActive.prop('checked', false)
|
||||
} else {
|
||||
// Remove them seperate
|
||||
allActive.each(function() {
|
||||
// Go over them all to know which one to remove
|
||||
var item = ko.dataFor(this)
|
||||
self.multiEditItems.remove(function(inList) { return inList.id == item.id; })
|
||||
// Remove the check of this one
|
||||
this.checked = false;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// None are checked, so check and add them all
|
||||
allChecks.prop('checked', true)
|
||||
allChecks.each(function() { self.multiEditItems.push(ko.dataFor(this)) })
|
||||
event.target.checked = true
|
||||
}
|
||||
// Set state of all the check-all's
|
||||
setCheckAllState('#multiedit-checkall-history', '.history-table input[name="multiedit"]')
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove downloads from history
|
||||
self.removeDownloads = function(form) {
|
||||
// Hide modal and show notification
|
||||
$('#modal-delete-history-job').modal("hide")
|
||||
showNotification('.main-notification-box-removing')
|
||||
|
||||
var strIDsPP = '';
|
||||
var strIDsHistory = '';
|
||||
$.each(self.deleteItems(), function(index) {
|
||||
// Split in jobs that need post-processing aborted, and jobs that need to be deleted
|
||||
if(this.processingDownload() === 2) {
|
||||
strIDsPP = strIDsPP + this.id + ',';
|
||||
// These items should not be listed in the deletedItems later on
|
||||
// as active post-processing aren't removed from the history output
|
||||
self.deleteItems.remove(this)
|
||||
} else {
|
||||
strIDsHistory = strIDsHistory + this.id + ',';
|
||||
}
|
||||
})
|
||||
|
||||
// Trigger post-processing aborting
|
||||
if(strIDsPP !== "") {
|
||||
callAPI({
|
||||
mode: 'cancel_pp',
|
||||
value: strIDsPP
|
||||
}).then(function(response) {
|
||||
// Only hide and refresh
|
||||
self.parent.refresh();
|
||||
hideNotification()
|
||||
});
|
||||
}
|
||||
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',
|
||||
del_files: 1,
|
||||
archive: (!skipArchive) * 1,
|
||||
value: strIDsHistory
|
||||
}).then(function(response) {
|
||||
self.historyItems.removeAll(self.deleteItems());
|
||||
self.multiEditItems.removeAll(self.deleteItems())
|
||||
self.parent.refresh();
|
||||
hideNotification()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete all selected
|
||||
self.doMultiDelete = function() {
|
||||
// Anything selected?
|
||||
if(self.multiEditItems().length < 1) return;
|
||||
|
||||
// Trigger modal
|
||||
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()
|
||||
})
|
||||
|
||||
// On change of page we need to check all those that were in the list!
|
||||
self.historyItems.subscribe(function() {
|
||||
// We need to wait until the unit is actually finished rendering
|
||||
setTimeout(function() {
|
||||
$.each(self.multiEditItems(), function(index) {
|
||||
$('#multiedit_' + this.id).prop('checked', true);
|
||||
})
|
||||
|
||||
// Update check-all buton state
|
||||
setCheckAllState('#multiedit-checkall-history', '.history-table input[name="multiedit"]')
|
||||
}, 100)
|
||||
}, null, "arrayChange")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -523,14 +316,14 @@ function HistoryModel(parent, data) {
|
||||
|
||||
// Waiting?
|
||||
self.processingWaiting = ko.pureComputed(function() {
|
||||
return(self.status() === 'Queued')
|
||||
return(self.status() == 'Queued')
|
||||
})
|
||||
|
||||
// Processing or done?
|
||||
self.processingDownload = ko.pureComputed(function() {
|
||||
var status = self.status();
|
||||
// When we can cancel
|
||||
if (status === 'Extracting' || status === 'Verifying' || status === 'Repairing' || status === 'Running') {
|
||||
if (status === 'Extracting' || status === 'Verifying' || status == 'Repairing' || status === 'Running') {
|
||||
return 2
|
||||
}
|
||||
// These cannot be cancelled
|
||||
@@ -564,7 +357,7 @@ function HistoryModel(parent, data) {
|
||||
try {
|
||||
// Extract the Download section
|
||||
var downloadLog = ko.utils.arrayFirst(self.historyStatus.stage_log(), function(item) {
|
||||
return item.name() === 'Download'
|
||||
return item.name() == 'Download'
|
||||
});
|
||||
// Extract the speed
|
||||
return downloadLog.actions()[0].match(/(\S*\s\S+)(?=<br\/>)/)[0]
|
||||
@@ -573,7 +366,7 @@ function HistoryModel(parent, data) {
|
||||
return;
|
||||
case 'category':
|
||||
// Exception for *
|
||||
if(self.historyStatus.category() === "*")
|
||||
if(self.historyStatus.category() == "*")
|
||||
return glitterTranslate.defaultText
|
||||
return self.historyStatus.category();
|
||||
case 'size':
|
||||
@@ -587,11 +380,6 @@ 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;
|
||||
@@ -607,11 +395,6 @@ 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
|
||||
@@ -648,4 +431,35 @@ function HistoryModel(parent, data) {
|
||||
return false;
|
||||
})
|
||||
}
|
||||
|
||||
// Delete button
|
||||
self.deleteSlot = function(item, event) {
|
||||
// Confirm?
|
||||
if(!self.parent.parent.confirmDeleteHistory() || confirm(glitterTranslate.deleteMsg + ":\n" + item.historyStatus.name() + "\n\n" + glitterTranslate.removeDow1)) {
|
||||
// Are we still processing and it can be stopped?
|
||||
if(item.processingDownload() == 2) {
|
||||
callAPI({
|
||||
mode: 'cancel_pp',
|
||||
value: self.id
|
||||
})
|
||||
// All we can do is wait
|
||||
} else {
|
||||
// Delete the item
|
||||
callAPI({
|
||||
mode: 'history',
|
||||
name: 'delete',
|
||||
del_files: 1,
|
||||
value: self.id
|
||||
}).then(function(response) {
|
||||
if(response.status) {
|
||||
// Make sure no flickering (if there are more items left) and then remove
|
||||
self.parent.isLoading(self.parent.totalItems() > 1)
|
||||
self.parent.historyItems.remove(self);
|
||||
self.parent.parent.refresh();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -13,7 +13,7 @@ function ViewModel() {
|
||||
self.useGlobalOptions = ko.observable(true).extend({ persist: 'useGlobalOptions' });
|
||||
self.refreshRate = ko.observable(1).extend({ persist: 'pageRefreshRate' });
|
||||
self.dateFormat = ko.observable('fromNow').extend({ persist: 'pageDateFormat' });
|
||||
self.displayTabbed = ko.observable(false).extend({ persist: 'displayTabbed' });
|
||||
self.displayTabbed = ko.observable().extend({ persist: 'displayTabbed' });
|
||||
self.displayCompact = ko.observable(false).extend({ persist: 'displayCompact' });
|
||||
self.displayFullWidth = ko.observable(false).extend({ persist: 'displayFullWidth' });
|
||||
self.confirmDeleteQueue = ko.observable(true).extend({ persist: 'confirmDeleteQueue' });
|
||||
@@ -90,7 +90,7 @@ function ViewModel() {
|
||||
var speedLimitNumber = Math.round(speedLimitNumberFull * 10) / 10;
|
||||
|
||||
// Fix it for lower than 1MB/s
|
||||
if (bandwithLimitText === 'M' && speedLimitNumber < 1) {
|
||||
if (bandwithLimitText == 'M' && speedLimitNumber < 1) {
|
||||
bandwithLimitText = 'K';
|
||||
speedLimitNumber = Math.round(speedLimitNumberFull * 1024);
|
||||
}
|
||||
@@ -120,7 +120,8 @@ function ViewModel() {
|
||||
|
||||
// Dynamic history length check
|
||||
self.hasHistory = ko.pureComputed(function() {
|
||||
return (self.history.historyItems().length > 0 || self.history.searchTerm() || self.history.isLoading())
|
||||
// We also 'have history' if we can't find any results of the search or there are no failed ones
|
||||
return (self.history.historyItems().length > 0 || self.history.searchTerm() || self.history.showFailed() || self.history.isLoading())
|
||||
})
|
||||
|
||||
self.hasWarnings = ko.pureComputed(function() {
|
||||
@@ -132,6 +133,100 @@ function ViewModel() {
|
||||
return parseInt(self.nrWarnings()) + self.allMessages().length;
|
||||
})
|
||||
|
||||
self.updateCheckAllButtonState = function(section) {
|
||||
setCheckAllState(`#multiedit-checkall-${section}`, `.${section}-table input[name="multiedit"]`)
|
||||
}
|
||||
|
||||
// Add queue or history item to multi-edit list
|
||||
self.addMultiEdit = function(item, event) {
|
||||
// The parent model is either the queue or history
|
||||
const model = this.parent;
|
||||
const section = model.queueItems ? 'queue' : 'history';
|
||||
|
||||
if(event.shiftKey) {
|
||||
checkShiftRange(`.${section}-table input[name="multiedit"]`);
|
||||
}
|
||||
|
||||
if(event.currentTarget.checked) {
|
||||
model.multiEditItems.push(item);
|
||||
|
||||
// History is not editable
|
||||
// Only the queue will fire the multi-edit update
|
||||
model.doMultiEditUpdate?.();
|
||||
} else {
|
||||
model.multiEditItems.remove(function(inList) { return inList.id == item.id; })
|
||||
}
|
||||
|
||||
self.updateCheckAllButtonState(section);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check all queue or history items
|
||||
self.checkAllJobs = function(item, event) {
|
||||
const section = event.currentTarget.closest('.multioperations-selector').id === 'history-options' ? 'history' : 'queue';
|
||||
const model = section === 'history' ? self.history : self.queue;
|
||||
|
||||
const allChecks = $(`.${section}-table input[name="multiedit"]`).filter(':not(:disabled):visible');
|
||||
|
||||
self.updateCheckAllButtonState(section);
|
||||
|
||||
if(event.target.indeterminate || (event.target.checked && !event.target.indeterminate)) {
|
||||
const allActive = allChecks.filter(":checked")
|
||||
if(allActive.length === model.multiEditItems().length) {
|
||||
model.multiEditItems.removeAll();
|
||||
allActive.prop('checked', false)
|
||||
} else {
|
||||
allActive.each(function() {
|
||||
var item = ko.dataFor(this)
|
||||
model.multiEditItems.remove(function(inList) { return inList.id === item.id; })
|
||||
this.checked = false;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
allChecks.prop('checked', true)
|
||||
allChecks.each(function() { model.multiEditItems.push(ko.dataFor(this)) })
|
||||
event.target.checked = true
|
||||
|
||||
model.multiEditUpdate?.();
|
||||
}
|
||||
|
||||
self.updateCheckAllButtonState(section);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Delete all selected queue or history items
|
||||
self.doMultiDelete = function(item, event) {
|
||||
const section = event.currentTarget.closest('.multioperations-selector').id === 'history-options' ? 'history' : 'queue';
|
||||
const model = section === 'history' ? self.history : self.queue;
|
||||
|
||||
// Anything selected?
|
||||
if(model.multiEditItems().length < 1) return;
|
||||
|
||||
if(!self.confirmDeleteHistory() || confirm(glitterTranslate.removeDown)) {
|
||||
let strIDs = '';
|
||||
$.each(model.multiEditItems(), function() {
|
||||
strIDs = strIDs + this.id + ',';
|
||||
})
|
||||
|
||||
showNotification('.main-notification-box-removing-multiple', 0, model.multiEditItems().length)
|
||||
|
||||
callAPI({
|
||||
mode: section,
|
||||
name: 'delete',
|
||||
del_files: 1,
|
||||
value: strIDs
|
||||
}).then(function(response) {
|
||||
if(response.status) {
|
||||
// Make sure the history doesnt flicker and then fade-out
|
||||
model.isLoading(true)
|
||||
self.refresh()
|
||||
model.multiEditItems.removeAll();
|
||||
hideNotification()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Update main queue
|
||||
self.updateQueue = function(response) {
|
||||
// Block in case off dragging
|
||||
@@ -149,7 +244,7 @@ function ViewModel() {
|
||||
/***
|
||||
Possible login failure?
|
||||
***/
|
||||
if (response.hasOwnProperty('error') && response.error === 'Missing authentication') {
|
||||
if (response.hasOwnProperty('error') && response.error == 'Missing authentication') {
|
||||
// Restart
|
||||
document.location = document.location;
|
||||
}
|
||||
@@ -170,7 +265,7 @@ function ViewModel() {
|
||||
self.diskSpaceLeft1(response.queue.diskspace1_norm)
|
||||
|
||||
// Same sizes? Then it's all 1 disk!
|
||||
if (response.queue.diskspace1 !== response.queue.diskspace2) {
|
||||
if (response.queue.diskspace1 != response.queue.diskspace2) {
|
||||
self.diskSpaceLeft2(response.queue.diskspace2_norm)
|
||||
} else {
|
||||
self.diskSpaceLeft2('')
|
||||
@@ -195,7 +290,7 @@ function ViewModel() {
|
||||
Spark line
|
||||
***/
|
||||
// Break the speed if empty queue
|
||||
if (response.queue.sizeleft === '0 B') {
|
||||
if (response.queue.sizeleft == '0 B') {
|
||||
response.queue.kbpersec = 0;
|
||||
response.queue.speed = '0';
|
||||
}
|
||||
@@ -214,9 +309,9 @@ function ViewModel() {
|
||||
self.speedHistory.push(parseInt(response.queue.kbpersec));
|
||||
|
||||
// Is sparkline visible? Not on small mobile devices..
|
||||
if ($('.sparkline-container').css('display') !== 'none') {
|
||||
if ($('.sparkline-container').css('display') != 'none') {
|
||||
// Make sparkline
|
||||
if (self.speedHistory.length === 1) {
|
||||
if (self.speedHistory.length == 1) {
|
||||
// We only use speedhistory from SAB if we use global settings
|
||||
// Otherwise SAB doesn't know the refresh rate
|
||||
if (!self.useGlobalOptions()) {
|
||||
@@ -251,7 +346,7 @@ function ViewModel() {
|
||||
Speedlimit
|
||||
***/
|
||||
// Nothing or 0 means 100%
|
||||
if(response.queue.speedlimit === '' || response.queue.speedlimit === '0') {
|
||||
if(response.queue.speedlimit == '' || response.queue.speedlimit == '0') {
|
||||
self.speedLimitInt(100)
|
||||
} else {
|
||||
self.speedLimitInt(parseInt(response.queue.speedlimit));
|
||||
@@ -274,7 +369,7 @@ function ViewModel() {
|
||||
|
||||
// Paused main queue
|
||||
if (self.downloadsPaused()) {
|
||||
if (response.queue.pause_int === '0') {
|
||||
if (response.queue.pause_int == '0') {
|
||||
timeString = glitterTranslate.paused;
|
||||
} else {
|
||||
var pauseSplit = response.queue.pause_int.split(/:/);
|
||||
@@ -337,13 +432,13 @@ function ViewModel() {
|
||||
limit: parseInt(self.queue.paginationLimit())
|
||||
}
|
||||
if (self.queue.searchTerm()) {
|
||||
parseSearchQuery(api_call, self.queue.searchTerm(), ["cat", "category", "priority", "status"])
|
||||
parseSearchQuery(api_call, self.queue.searchTerm(), ["cat", "category", "priority"])
|
||||
}
|
||||
var queueApi = callAPI(api_call)
|
||||
.done(self.updateQueue)
|
||||
.fail(function(response) {
|
||||
// Catch the failure of authorization error
|
||||
if (response.status === 401) {
|
||||
if (response.status == 401) {
|
||||
// Stop refresh and reload
|
||||
clearInterval(self.interval)
|
||||
location.reload();
|
||||
@@ -363,11 +458,10 @@ function ViewModel() {
|
||||
failed_only: self.history.showFailed() * 1,
|
||||
start: self.history.pagination.currentStart(),
|
||||
limit: parseInt(self.history.paginationLimit()),
|
||||
archive: self.history.showArchive() * 1,
|
||||
last_history_update: self.history.lastUpdate
|
||||
}
|
||||
if (self.history.searchTerm()) {
|
||||
parseSearchQuery(history_call, self.history.searchTerm(), ["cat", "category", "status"])
|
||||
parseSearchQuery(history_call, self.history.searchTerm(), ["cat", "category"])
|
||||
}
|
||||
|
||||
// History
|
||||
@@ -394,9 +488,10 @@ function ViewModel() {
|
||||
api_request[keyword] = parsed_query[keyword]
|
||||
}
|
||||
// Special case for priority, dirty replace of string by numeric value
|
||||
if (keyword === "priority" && api_request["priority"]) {
|
||||
if (keyword == "priority" && api_request["priority"]) {
|
||||
for (const prio_name in self.queue.priorityName) {
|
||||
api_request["priority"] = api_request["priority"].replace(prio_name, self.queue.priorityName[prio_name])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -431,16 +526,18 @@ function ViewModel() {
|
||||
return;
|
||||
}
|
||||
// Show modal
|
||||
$('#modal-custom-pause').modal('show')
|
||||
}
|
||||
$('#modal_custom_pause').modal('show')
|
||||
|
||||
// Focus on the input field
|
||||
$('#modal_custom_pause').on('shown.bs.modal', function() {
|
||||
$('#customPauseInput').focus()
|
||||
})
|
||||
|
||||
$('#modal-custom-pause').on('shown.bs.modal', function() {
|
||||
// Focus on the input field when opening the modal
|
||||
$('#customPauseInput').focus()
|
||||
}).on('hide.bs.modal', function() {
|
||||
// Reset on modal close
|
||||
self.pauseCustom('');
|
||||
})
|
||||
$('#modal_custom_pause').on('hide.bs.modal', function() {
|
||||
self.pauseCustom('');
|
||||
})
|
||||
}
|
||||
|
||||
// Update on changes
|
||||
self.pauseCustom.subscribe(function(newValue) {
|
||||
@@ -453,7 +550,7 @@ function ViewModel() {
|
||||
// At least 3 charaters
|
||||
if (newValue.length < 3) {
|
||||
$('#customPauseOutput').text('').data('time', 0)
|
||||
$('#modal-custom-pause .btn-default').addClass('disabled')
|
||||
$('#modal_custom_pause .btn-default').addClass('disabled')
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -478,11 +575,11 @@ function ViewModel() {
|
||||
var pauseDuration = Math.round((pauseParsed - Date.parse('now')) / 1000 / 60);
|
||||
$('#customPauseOutput').html('<span class="glyphicon glyphicon-pause"></span> ' + glitterTranslate.pauseFor + ' ' + pauseDuration + ' ' + glitterTranslate.minutes)
|
||||
$('#customPauseOutput').data('time', pauseDuration)
|
||||
$('#modal-custom-pause .btn-default').removeClass('disabled')
|
||||
$('#modal_custom_pause .btn-default').removeClass('disabled')
|
||||
} else if (newValue) {
|
||||
// No..
|
||||
$('#customPauseOutput').text(glitterTranslate.pausePromptFail)
|
||||
$('#modal-custom-pause .btn-default').addClass('disabled')
|
||||
$('#modal_custom_pause .btn-default').addClass('disabled')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -501,7 +598,7 @@ function ViewModel() {
|
||||
// Refresh and close the modal
|
||||
self.refresh()
|
||||
self.downloadsPaused(true);
|
||||
$('#modal-custom-pause').modal('hide')
|
||||
$('#modal_custom_pause').modal('hide')
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -509,7 +606,7 @@ function ViewModel() {
|
||||
// Update the warnings
|
||||
self.nrWarnings.subscribe(function(newValue) {
|
||||
// Really any change?
|
||||
if (newValue === self.allWarnings().length) return;
|
||||
if (newValue == self.allWarnings().length) return;
|
||||
|
||||
// Get all warnings
|
||||
callAPI({
|
||||
@@ -531,7 +628,7 @@ function ViewModel() {
|
||||
type: glitterTranslate.status[warning.type].slice(0, -1),
|
||||
text: convertHTMLtoText(warning.text).replace(/ /g, '\u00A0').replace(/(?:\r\n|\r|\n)/g, '<br />'),
|
||||
timestamp: warning.time,
|
||||
css: (warning.type === "ERROR" ? "danger" : warning.type === "WARNING" ? "warning" : "info"),
|
||||
css: (warning.type == "ERROR" ? "danger" : warning.type == "WARNING" ? "warning" : "info"),
|
||||
clear: self.clearWarnings
|
||||
};
|
||||
self.allWarnings.push(warningData)
|
||||
@@ -551,7 +648,7 @@ function ViewModel() {
|
||||
// Clear messages
|
||||
self.clearMessages = function(whatToRemove) {
|
||||
// Remove specifc type of messages
|
||||
self.allMessages.remove(function(item) { return item.index === whatToRemove });
|
||||
self.allMessages.remove(function(item) { return item.index == whatToRemove });
|
||||
// Now so we don't show again today
|
||||
localStorageSetItem(whatToRemove, Date.now())
|
||||
}
|
||||
@@ -562,7 +659,7 @@ function ViewModel() {
|
||||
if (!self.speedLimitInt()) return;
|
||||
|
||||
// Update
|
||||
if (self.speedLimitInt() !== newValue) {
|
||||
if (self.speedLimitInt() != newValue) {
|
||||
callAPI({
|
||||
mode: "config",
|
||||
name: "speedlimit",
|
||||
@@ -635,17 +732,11 @@ function ViewModel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable the buttons to prevent multiple uploads
|
||||
let submit_buttons = $(form).find("input[type='submit']")
|
||||
submit_buttons.attr("disabled", true)
|
||||
|
||||
// Upload file using the method we also use for drag-and-drop
|
||||
if ($(form.nzbFile)[0].files[0]) {
|
||||
self.addNZBFromFile($(form.nzbFile)[0].files);
|
||||
// Hide modal, upload will reset the form
|
||||
$("#modal-add-nzb").modal("hide");
|
||||
// Re-enable the buttons
|
||||
submit_buttons.attr("disabled", false)
|
||||
} else if ($(form.nzbURL).val()) {
|
||||
// Or add URL
|
||||
var theCall = {
|
||||
@@ -653,12 +744,15 @@ function ViewModel() {
|
||||
name: $(form.nzbURL).val(),
|
||||
nzbname: $('#nzbname').val(),
|
||||
password: $('#password').val(),
|
||||
cat: $('#modal-add-nzb select[name="Category"]').val(),
|
||||
priority: $('#modal-add-nzb select[name="Priority"]').val(),
|
||||
pp: $('#modal-add-nzb select[name="Processing"]').val(),
|
||||
script: $('#modal-add-nzb select[name="Post-processing"]').val(),
|
||||
priority: $('#modal-add-nzb select[name="Priority"]').val(),
|
||||
pp: $('#modal-add-nzb select[name="Processing"]').val()
|
||||
}
|
||||
|
||||
// Optional, otherwise they get mis-labeled if left empty
|
||||
if ($('#modal-add-nzb select[name="Category"]').val() != '*') theCall.cat = $('#modal-add-nzb select[name="Category"]').val()
|
||||
if ($('#modal-add-nzb select[name="Processing"]').val()) theCall.pp = $('#modal-add-nzb select[name="Category"]').val()
|
||||
|
||||
// Add
|
||||
callAPI(theCall).then(function(r) {
|
||||
// Hide and reset/refresh
|
||||
@@ -666,7 +760,6 @@ function ViewModel() {
|
||||
$("#modal-add-nzb").modal("hide");
|
||||
form.reset()
|
||||
$('#nzbname').val('')
|
||||
submit_buttons.attr("disabled", false)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -686,7 +779,7 @@ function ViewModel() {
|
||||
fileindex++
|
||||
|
||||
// Check if it's maybe a folder, we can't handle those
|
||||
if (!file.type && file.size % 4096 === 0) return;
|
||||
if (!file.type && file.size % 4096 == 0) return;
|
||||
|
||||
// Add notification
|
||||
showNotification('.main-notification-box-uploading', 0, fileindex)
|
||||
@@ -697,12 +790,14 @@ function ViewModel() {
|
||||
data.append("mode", "addfile");
|
||||
data.append("nzbname", $('#nzbname').val());
|
||||
data.append("password", $('#password').val());
|
||||
data.append("cat", $('#modal-add-nzb select[name="Category"]').val())
|
||||
data.append("priority", $('#modal-add-nzb select[name="Priority"]').val())
|
||||
data.append("pp", $('#modal-add-nzb select[name="Processing"]').val())
|
||||
data.append("script", $('#modal-add-nzb select[name="Post-processing"]').val())
|
||||
data.append("priority", $('#modal-add-nzb select[name="Priority"]').val())
|
||||
data.append("apikey", apiKey);
|
||||
|
||||
// Optional, otherwise they get mis-labeled if left empty
|
||||
if ($('#modal-add-nzb select[name="Category"]').val() != '*') data.append("cat", $('#modal-add-nzb select[name="Category"]').val());
|
||||
if ($('#modal-add-nzb select[name="Processing"]').val()) data.append("pp", $('#modal-add-nzb select[name="Processing"]').val());
|
||||
|
||||
// Add this one
|
||||
$.ajax({
|
||||
url: "./api",
|
||||
@@ -732,10 +827,10 @@ function ViewModel() {
|
||||
// Load status info
|
||||
self.loadStatusInfo = function(item, event) {
|
||||
// Full refresh? Only on click and for the status-screen
|
||||
var statusFullRefresh = (event !== undefined) && $('#options-status').hasClass('active');
|
||||
var statusFullRefresh = (event != undefined) && $('#options-status').hasClass('active');
|
||||
|
||||
// Measure performance? Takes a while
|
||||
var statusPerformance = (event !== undefined) && $(event.currentTarget).hasClass('diskspeed-button');
|
||||
var statusPerformance = (event != undefined) && $(event.currentTarget).hasClass('diskspeed-button');
|
||||
|
||||
// Make it spin if the user requested it otherwise we don't,
|
||||
// because browsers use a lot of CPU for the animation
|
||||
@@ -775,7 +870,43 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Update the servers
|
||||
ko.mapping.fromJS(data.status.servers, {}, self.statusInfo.servers)
|
||||
if (self.statusInfo.servers().length != data.status.servers.length) {
|
||||
// Empty them, in case of update
|
||||
self.statusInfo.servers([])
|
||||
|
||||
// Initial add
|
||||
$.each(data.status.servers, function() {
|
||||
self.statusInfo.servers.push({
|
||||
'servername': ko.observable(this.servername),
|
||||
'serveroptional': ko.observable(this.serveroptional),
|
||||
'serverpriority': ko.observable(this.serverpriority),
|
||||
'servertotalconn': ko.observable(this.servertotalconn),
|
||||
'serverssl': ko.observable(this.serverssl),
|
||||
'serversslinfo': ko.observable(this.serversslinfo),
|
||||
'serveractiveconn': ko.observable(this.serveractiveconn),
|
||||
'servererror': ko.observable(this.servererror),
|
||||
'serveractive': ko.observable(this.serveractive),
|
||||
'serverconnections': ko.observableArray(this.serverconnections),
|
||||
'serverbps': ko.observable(this.serverbps)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
// Update
|
||||
$.each(data.status.servers, function(index) {
|
||||
var activeServer = self.statusInfo.servers()[index];
|
||||
activeServer.servername(this.servername),
|
||||
activeServer.serveroptional(this.serveroptional),
|
||||
activeServer.serverpriority(this.serverpriority),
|
||||
activeServer.servertotalconn(this.servertotalconn),
|
||||
activeServer.serverssl(this.serverssl),
|
||||
activeServer.serversslinfo(this.serversslinfo),
|
||||
activeServer.serveractiveconn(this.serveractiveconn),
|
||||
activeServer.servererror(this.servererror),
|
||||
activeServer.serveractive(this.serveractive),
|
||||
activeServer.serverconnections(this.serverconnections),
|
||||
activeServer.serverbps(this.serverbps)
|
||||
})
|
||||
}
|
||||
|
||||
// Add tooltips to possible new items
|
||||
if (!isMobile) $('#modal-options [data-tooltip="true"]').tooltip({ trigger: 'hover', container: 'body' })
|
||||
@@ -791,7 +922,7 @@ function ViewModel() {
|
||||
var nzbSize = $(event.target).data('size')
|
||||
|
||||
// Maybe it was a click on the icon?
|
||||
if (nzbSize === undefined) {
|
||||
if (nzbSize == undefined) {
|
||||
nzbSize = $(event.target.parentElement).data('size')
|
||||
}
|
||||
|
||||
@@ -873,7 +1004,7 @@ function ViewModel() {
|
||||
$('#options-orphans [data-tooltip="true"]').tooltip('hide')
|
||||
|
||||
// Show notification on delete
|
||||
if ($(htmlElement.currentTarget).data('action') === 'delete_orphan') {
|
||||
if ($(htmlElement.currentTarget).data('action') == 'delete_orphan') {
|
||||
showNotification('.main-notification-box-removing', 1000)
|
||||
} else {
|
||||
// Adding back to queue
|
||||
@@ -895,7 +1026,7 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder deletion of all
|
||||
self.removeAllOrphaned = function() {
|
||||
if (confirm(glitterTranslate.clearOrphanWarning)) {
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-removing-multiple', 0, self.statusInfo.folders().length)
|
||||
// Delete them all
|
||||
@@ -912,7 +1043,7 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder adding of all
|
||||
self.addAllOrphaned = function() {
|
||||
if (confirm(glitterTranslate.confirm)) {
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-sendback')
|
||||
// Delete them all
|
||||
@@ -1087,7 +1218,7 @@ function ViewModel() {
|
||||
// Reformat and set categories
|
||||
self.queue.categoriesList($.map(response.config.categories, function(cat) {
|
||||
// Default?
|
||||
if(cat.name === '*') return { catValue: '*', catText: glitterTranslate.defaultText };
|
||||
if(cat.name == '*') return { catValue: '*', catText: glitterTranslate.defaultText };
|
||||
return { catValue: cat.name, catText: cat.name };
|
||||
}))
|
||||
|
||||
@@ -1099,10 +1230,14 @@ function ViewModel() {
|
||||
// Reformat script-list
|
||||
self.queue.scriptsList($.map(script_response.scripts, function(script) {
|
||||
// None?
|
||||
if(script === 'None') return { scriptValue: 'None', scriptText: glitterTranslate.noneText };
|
||||
if(script == 'None') return { scriptValue: 'None', scriptText: glitterTranslate.noneText };
|
||||
return { scriptValue: script, scriptText: script };
|
||||
}))
|
||||
self.queue.scriptsListLoaded(true)
|
||||
})
|
||||
} else {
|
||||
// We can already continue
|
||||
self.queue.scriptsListLoaded(true)
|
||||
}
|
||||
|
||||
|
||||
@@ -1179,7 +1314,7 @@ function ViewModel() {
|
||||
// Orphaned folders? If user clicked away we check again in 5 days
|
||||
if (self.statusInfo.folders().length >= 3 && orphanMsg) {
|
||||
// Check if not already there
|
||||
if (!ko.utils.arrayFirst(self.allMessages(), function(item) { return item.index === 'OrphanedMsg' })) {
|
||||
if (!ko.utils.arrayFirst(self.allMessages(), function(item) { return item.index == 'OrphanedMsg' })) {
|
||||
self.allMessages.push({
|
||||
index: 'OrphanedMsg',
|
||||
type: glitterTranslate.status['INFO'],
|
||||
@@ -1191,7 +1326,7 @@ function ViewModel() {
|
||||
} else {
|
||||
// Remove any message, if it was there
|
||||
self.allMessages.remove(function(item) {
|
||||
return item.index === 'OrphanedMsg';
|
||||
return item.index == 'OrphanedMsg';
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -32,12 +32,12 @@ function QueueListModel(parent) {
|
||||
// External var's
|
||||
self.queueItems = ko.observableArray([]);
|
||||
self.totalItems = ko.observable(0);
|
||||
self.deleteItems = ko.observableArray([]);
|
||||
self.isMultiEditing = ko.observable(false).extend({ persist: 'queueIsMultiEditing' });
|
||||
self.isLoading = ko.observable(false).extend({ rateLimit: 100 });
|
||||
self.multiEditItems = ko.observableArray([]);
|
||||
self.categoriesList = ko.observableArray([]);
|
||||
self.scriptsList = ko.observableArray([]);
|
||||
self.scriptsListLoaded = ko.observable(false);
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 400, method: "notifyWhenChangesStop" } });
|
||||
self.paginationLimit = ko.observable(20).extend({ persist: 'queuePaginationLimit' });
|
||||
self.pagination = new paginationModel(self);
|
||||
@@ -75,7 +75,7 @@ function QueueListModel(parent) {
|
||||
$.each(data.slots, function() {
|
||||
var item = this;
|
||||
var existingItem = ko.utils.arrayFirst(self.queueItems(), function(i) {
|
||||
return i.id === item.nzo_id;
|
||||
return i.id == item.nzo_id;
|
||||
});
|
||||
|
||||
if(existingItem) {
|
||||
@@ -88,7 +88,7 @@ function QueueListModel(parent) {
|
||||
});
|
||||
|
||||
// Remove all items if there's any
|
||||
if(itemIds.length === self.paginationLimit()) {
|
||||
if(itemIds.length == self.paginationLimit()) {
|
||||
// Replace it, so only 1 Knockout DOM-update!
|
||||
self.queueItems(newItems);
|
||||
newItems = [];
|
||||
@@ -97,7 +97,7 @@ function QueueListModel(parent) {
|
||||
$.each(itemIds, function() {
|
||||
var id = this.toString();
|
||||
self.queueItems.remove(ko.utils.arrayFirst(self.queueItems(), function(i) {
|
||||
return i.id === id;
|
||||
return i.id == id;
|
||||
}));
|
||||
});
|
||||
}
|
||||
@@ -148,27 +148,6 @@ function QueueListModel(parent) {
|
||||
|
||||
}
|
||||
|
||||
self.triggerRemoveDownload = function(items) {
|
||||
// Show and fill modal
|
||||
self.deleteItems.removeAll()
|
||||
|
||||
// Single or multiple items?
|
||||
if(items.length) {
|
||||
ko.utils.arrayPushAll(self.deleteItems, items)
|
||||
} else {
|
||||
self.deleteItems.push(items)
|
||||
}
|
||||
|
||||
// Show modal or delete right away
|
||||
if(self.parent.confirmDeleteQueue()) {
|
||||
// Open modal if desired
|
||||
$('#modal-delete-queue-job').modal("show")
|
||||
} else {
|
||||
// Otherwise just submit right away
|
||||
$('#modal-delete-queue-job form').submit()
|
||||
}
|
||||
}
|
||||
|
||||
// Save pagination state
|
||||
self.paginationLimit.subscribe(function(newValue) {
|
||||
// Save in config if global
|
||||
@@ -192,7 +171,7 @@ function QueueListModel(parent) {
|
||||
// Searching in queue (rate-limited in decleration)
|
||||
self.searchTerm.subscribe(function() {
|
||||
// Go back to page 1
|
||||
if(self.pagination.currentPage() !== 1) {
|
||||
if(self.pagination.currentPage() != 1) {
|
||||
// This forces a refresh
|
||||
self.pagination.moveToPage(1);
|
||||
} else {
|
||||
@@ -204,12 +183,12 @@ function QueueListModel(parent) {
|
||||
// Clear searchterm
|
||||
self.clearSearchTerm = function(data, event) {
|
||||
// Was it escape key or click?
|
||||
if(event.type === 'mousedown' || (event.keyCode && event.keyCode === 27)) {
|
||||
if(event.type == 'mousedown' || (event.keyCode && event.keyCode == 27)) {
|
||||
self.isLoading(true)
|
||||
self.searchTerm('');
|
||||
}
|
||||
// Was it click and the field is empty? Then we focus on the field
|
||||
if(event.type === 'mousedown' && self.searchTerm() === '') {
|
||||
if(event.type == 'mousedown' && self.searchTerm() == '') {
|
||||
$(event.target).parents('.search-box').find('input[type="text"]').focus()
|
||||
return;
|
||||
}
|
||||
@@ -276,7 +255,7 @@ function QueueListModel(parent) {
|
||||
// Reset form and remove all checked ones
|
||||
$form[0].reset();
|
||||
self.multiEditItems.removeAll();
|
||||
$('.queue-table input[name="multiedit"], #multiedit-checkall-queue').prop({'checked': false, 'indeterminate': false})
|
||||
$('.queue-table input[name="multiedit"], .queue #multiedit-checkall').prop({'checked': false, 'indeterminate': false})
|
||||
|
||||
// Is the multi-edit in view?
|
||||
if(($form.offset().top + $form.outerHeight(true)) > ($(window).scrollTop()+$(window).height())) {
|
||||
@@ -287,72 +266,6 @@ function QueueListModel(parent) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add to the list
|
||||
self.addMultiEdit = function(item, event) {
|
||||
// Is it a shift-click?
|
||||
if(event.shiftKey) {
|
||||
checkShiftRange('.queue-table input[name="multiedit"]');
|
||||
}
|
||||
|
||||
// Add or remove from the list?
|
||||
if(event.currentTarget.checked) {
|
||||
// Add item
|
||||
self.multiEditItems.push(item);
|
||||
// Update them all
|
||||
self.doMultiEditUpdate();
|
||||
} else {
|
||||
// Go over them all to know which one to remove
|
||||
self.multiEditItems.remove(function(inList) { return inList.id == item.id; })
|
||||
}
|
||||
|
||||
// Update check-all buton state
|
||||
setCheckAllState('#multiedit-checkall-queue', '.queue-table input[name="multiedit"]')
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check all
|
||||
self.checkAllJobs = function(item, event) {
|
||||
// Get which ones we care about
|
||||
var allChecks = $('.queue-table input[name="multiedit"]').filter(':not(:disabled):visible');
|
||||
|
||||
// We need to re-evaltuate the state of this check-all
|
||||
// Otherwise the 'inderterminate' will be overwritten by the click event!
|
||||
setCheckAllState('#multiedit-checkall-queue', '.queue-table input[name="multiedit"]')
|
||||
|
||||
// Now we can check what happend
|
||||
// For when some are checked, or all are checked (but not partly)
|
||||
if(event.target.indeterminate || (event.target.checked && !event.target.indeterminate)) {
|
||||
var allActive = allChecks.filter(":checked")
|
||||
// First remove the from the list
|
||||
if(allActive.length == self.multiEditItems().length) {
|
||||
// Just remove all
|
||||
self.multiEditItems.removeAll();
|
||||
// Remove the check
|
||||
allActive.prop('checked', false)
|
||||
} else {
|
||||
// Remove them seperate
|
||||
allActive.each(function() {
|
||||
// Go over them all to know which one to remove
|
||||
var item = ko.dataFor(this)
|
||||
self.multiEditItems.remove(function(inList) { return inList.id == item.id; })
|
||||
// Remove the check of this one
|
||||
this.checked = false;
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// None are checked, so check and add them all
|
||||
allChecks.prop('checked', true)
|
||||
allChecks.each(function() { self.multiEditItems.push(ko.dataFor(this)) })
|
||||
event.target.checked = true
|
||||
|
||||
// Now we fire the update
|
||||
self.doMultiEditUpdate()
|
||||
}
|
||||
// Set state of all the check-all's
|
||||
setCheckAllState('#multiedit-checkall-queue', '.queue-table input[name="multiedit"]')
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do the actual multi-update immediatly
|
||||
self.doMultiEditUpdate = function() {
|
||||
// Anything selected?
|
||||
@@ -373,14 +286,14 @@ function QueueListModel(parent) {
|
||||
|
||||
// All non-category updates need to only happen after a category update
|
||||
function nonCatUpdates() {
|
||||
if(newScript !== '') {
|
||||
if(newScript != '') {
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: strIDs,
|
||||
value2: newScript
|
||||
})
|
||||
}
|
||||
if(newPrior !== '') {
|
||||
if(newPrior != '') {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'priority',
|
||||
@@ -388,7 +301,7 @@ function QueueListModel(parent) {
|
||||
value2: newPrior
|
||||
})
|
||||
}
|
||||
if(newProc !== '') {
|
||||
if(newProc != '') {
|
||||
callAPI({
|
||||
mode: 'change_opts',
|
||||
value: strIDs,
|
||||
@@ -405,13 +318,13 @@ function QueueListModel(parent) {
|
||||
|
||||
// Wat a little and do the refresh
|
||||
// Only if anything changed!
|
||||
if(newStatus || newProc !== '' || newPrior !== '' || newScript !== '' || newCat !== '') {
|
||||
if(newStatus || newProc != '' || newPrior != '' || newScript != '' || newCat != '') {
|
||||
setTimeout(parent.refresh, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// What is changed?
|
||||
if(newCat !== '') {
|
||||
if(newCat != '') {
|
||||
callAPI({
|
||||
mode: 'change_cat',
|
||||
value: strIDs,
|
||||
@@ -423,103 +336,6 @@ 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
|
||||
$('#modal-delete-queue-job').modal("hide")
|
||||
showNotification('.main-notification-box-removing')
|
||||
|
||||
var strIDs = '';
|
||||
$.each(self.deleteItems(), function(index) {
|
||||
strIDs = strIDs + this.id + ',';
|
||||
})
|
||||
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'delete',
|
||||
del_files: 1,
|
||||
value: strIDs
|
||||
}).then(function(response) {
|
||||
self.queueItems.removeAll(self.deleteItems());
|
||||
self.multiEditItems.removeAll(self.deleteItems())
|
||||
self.parent.refresh();
|
||||
hideNotification()
|
||||
});
|
||||
};
|
||||
|
||||
// Delete all selected
|
||||
self.doMultiDelete = function() {
|
||||
// Anything selected?
|
||||
if(self.multiEditItems().length < 1) return;
|
||||
|
||||
// Trigger modal
|
||||
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()
|
||||
})
|
||||
|
||||
// On change of page we need to check all those that were in the list!
|
||||
self.queueItems.subscribe(function() {
|
||||
// We need to wait until the unit is actually finished rendering
|
||||
@@ -529,7 +345,7 @@ function QueueListModel(parent) {
|
||||
})
|
||||
|
||||
// Update check-all buton state
|
||||
setCheckAllState('#multiedit-checkall-queue', '.queue-table input[name="multiedit"]')
|
||||
setCheckAllState('.queue #multiedit-checkall', '.queue-table input[name="multiedit"]')
|
||||
}, 100)
|
||||
}, null, "arrayChange")
|
||||
}
|
||||
@@ -549,8 +365,8 @@ function QueueModel(parent, data) {
|
||||
self.index = ko.observable(data.index);
|
||||
self.status = ko.observable(data.status);
|
||||
self.labels = ko.observableArray(data.labels);
|
||||
self.isGrabbing = ko.observable(data.status === 'Grabbing' || data.avg_age === '-')
|
||||
self.isFetchingBlocks = data.status === 'Fetching' || data.priority === 'Repair' // No need to update
|
||||
self.isGrabbing = ko.observable(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
self.isFetchingBlocks = data.status == 'Fetching' || data.priority == 'Repair' // No need to update
|
||||
self.totalMB = ko.observable(parseFloat(data.mb));
|
||||
self.remainingMB = ko.observable(parseFloat(data.mbleft))
|
||||
self.missingMB = ko.observable(parseFloat(data.mbmissing))
|
||||
@@ -561,7 +377,7 @@ function QueueModel(parent, data) {
|
||||
self.priority = ko.observable(parent.priorityName[data.priority]);
|
||||
self.script = ko.observable(data.script);
|
||||
self.unpackopts = ko.observable(parseInt(data.unpackopts)) // UnpackOpts fails if not parseInt'd!
|
||||
self.pausedStatus = ko.observable(data.status === 'Paused');
|
||||
self.pausedStatus = ko.observable(data.status == 'Paused');
|
||||
self.timeLeft = ko.observable(data.timeleft);
|
||||
|
||||
// Initially empty
|
||||
@@ -572,7 +388,7 @@ function QueueModel(parent, data) {
|
||||
// Color of the progress bar
|
||||
self.progressColor = ko.computed(function() {
|
||||
// Checking
|
||||
if(self.status() === 'Checking') {
|
||||
if(self.status() == 'Checking') {
|
||||
return '#58A9FA'
|
||||
}
|
||||
// Check for missing data, the value is arbitrary! (2%)
|
||||
@@ -580,7 +396,7 @@ function QueueModel(parent, data) {
|
||||
return '#F8A34E'
|
||||
}
|
||||
// Set to grey, only when not Force download
|
||||
if((self.parent.parent.downloadsPaused() && self.priority() !== 2) || self.pausedStatus()) {
|
||||
if((self.parent.parent.downloadsPaused() && self.priority() != 2) || self.pausedStatus()) {
|
||||
return '#B7B7B7'
|
||||
}
|
||||
// Nothing
|
||||
@@ -589,9 +405,6 @@ function QueueModel(parent, data) {
|
||||
|
||||
// MB's
|
||||
self.progressText = ko.pureComputed(function() {
|
||||
if(self.isGrabbing()) {
|
||||
return glitterTranslate.fetchingURL
|
||||
}
|
||||
return (self.totalMB() - self.remainingMB()).toFixed(0) + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
|
||||
})
|
||||
|
||||
@@ -611,15 +424,15 @@ function QueueModel(parent, data) {
|
||||
})
|
||||
self.statusText = ko.computed(function() {
|
||||
// Checking
|
||||
if(self.status() === 'Checking') {
|
||||
if(self.status() == 'Checking') {
|
||||
return glitterTranslate.checking
|
||||
}
|
||||
// Grabbing
|
||||
if(self.status() === 'Grabbing') {
|
||||
if(self.status() == 'Grabbing') {
|
||||
return glitterTranslate.fetch
|
||||
}
|
||||
// Pausing status
|
||||
if((self.parent.parent.downloadsPaused() && self.priority() !== 2) || self.pausedStatus()) {
|
||||
if((self.parent.parent.downloadsPaused() && self.priority() != 2) || self.pausedStatus()) {
|
||||
return glitterTranslate.paused;
|
||||
}
|
||||
// Just the time
|
||||
@@ -629,7 +442,7 @@ function QueueModel(parent, data) {
|
||||
// Icon to better show force-priority
|
||||
self.queueIcon = ko.computed(function() {
|
||||
// Force comes first
|
||||
if(self.priority() === 2) {
|
||||
if(self.priority() == 2) {
|
||||
return 'glyphicon-forward'
|
||||
}
|
||||
if(self.pausedStatus()) {
|
||||
@@ -643,17 +456,17 @@ function QueueModel(parent, data) {
|
||||
switch(param) {
|
||||
case 'category':
|
||||
// Exception for *
|
||||
if(self.category() === "*")
|
||||
if(self.category() == "*")
|
||||
return glitterTranslate.defaultText
|
||||
return self.category();
|
||||
case 'priority':
|
||||
// Onload-exception
|
||||
if(self.priority() === undefined) return;
|
||||
return ko.utils.arrayFirst(self.parent.priorityOptions(), function(item) { return item.value === self.priority()}).name;
|
||||
if(self.priority() == undefined) return;
|
||||
return ko.utils.arrayFirst(self.parent.priorityOptions(), function(item) { return item.value == self.priority()}).name;
|
||||
case 'processing':
|
||||
// Onload-exception
|
||||
if(self.unpackopts() === undefined) return;
|
||||
return ko.utils.arrayFirst(self.parent.processingOptions(), function(item) { return item.value === self.unpackopts()}).name;
|
||||
if(self.unpackopts() == undefined) return;
|
||||
return ko.utils.arrayFirst(self.parent.processingOptions(), function(item) { return item.value == self.unpackopts()}).name;
|
||||
case 'scripts':
|
||||
return self.script();
|
||||
case 'age':
|
||||
@@ -669,7 +482,7 @@ function QueueModel(parent, data) {
|
||||
self.password(data.password);
|
||||
self.index(data.index);
|
||||
self.status(data.status)
|
||||
self.isGrabbing(data.status === 'Grabbing' || data.avg_age === '-')
|
||||
self.isGrabbing(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
self.totalMB(parseFloat(data.mb));
|
||||
self.remainingMB(parseFloat(data.mbleft));
|
||||
self.missingMB(parseFloat(data.mbmissing))
|
||||
@@ -680,12 +493,12 @@ function QueueModel(parent, data) {
|
||||
self.priority(parent.priorityName[data.priority]);
|
||||
self.script(data.script);
|
||||
self.unpackopts(parseInt(data.unpackopts)) // UnpackOpts fails if not parseInt'd!
|
||||
self.pausedStatus(data.status === 'Paused');
|
||||
self.pausedStatus(data.status == 'Paused');
|
||||
self.timeLeft(data.timeleft);
|
||||
|
||||
// Did the label-list change?
|
||||
// Otherwise KO will send updates to all texts during refresh()
|
||||
if(self.rawLabels !== data.labels.toString()) {
|
||||
if(self.rawLabels != data.labels.toString()) {
|
||||
// Update
|
||||
self.labels(data.labels);
|
||||
self.rawLabels = data.labels.toString();
|
||||
@@ -710,37 +523,9 @@ function QueueModel(parent, data) {
|
||||
self.editingName(true)
|
||||
self.nameForEdit(self.name())
|
||||
|
||||
// Select the input
|
||||
const $input = $(event.target).parents('.name').find('input');
|
||||
$input.select();
|
||||
|
||||
// Add Tab/Shift+Tab navigation
|
||||
$input.off('keydown.tabnav').on('keydown.tabnav', function (e) {
|
||||
if (e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
|
||||
// Find all rename inputs that are currently visible
|
||||
const inputs = $('.queue-table input[type="text"]');
|
||||
const currentIndex = inputs.index(this);
|
||||
let nextIndex = e.shiftKey ? currentIndex - 1 : currentIndex + 1;
|
||||
|
||||
// Wrap around
|
||||
if (nextIndex >= inputs.length) nextIndex = 0;
|
||||
if (nextIndex < 0) nextIndex = inputs.length - 1;
|
||||
|
||||
// Simulate clicking Rename on the next row
|
||||
const $nextRow = inputs.eq(nextIndex).closest('tr');
|
||||
$nextRow.find('.hover-button[title="Rename"]').click();
|
||||
|
||||
// Delay focusing to wait for new input to appear
|
||||
setTimeout(() => {
|
||||
const $nextInput = $('.queue-table input[type="text"]').eq(nextIndex);
|
||||
$nextInput.focus().select();
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Select
|
||||
$(event.target).parents('.name').find('input').select()
|
||||
}
|
||||
|
||||
// Catch the submit action
|
||||
self.editingNameSubmit = function() {
|
||||
@@ -750,7 +535,7 @@ function QueueModel(parent, data) {
|
||||
// Do on change
|
||||
self.nameForEdit.subscribe(function(newName) {
|
||||
// Anything change or empty?
|
||||
if(!newName || self.name() === newName) return;
|
||||
if(!newName || self.name() == newName) return;
|
||||
|
||||
// Rename would abort Direct Unpack, so ask if user is sure
|
||||
if(self.direct_unpack() && !confirm(glitterTranslate.renameAbort)) return;
|
||||
@@ -822,5 +607,29 @@ function QueueModel(parent, data) {
|
||||
})
|
||||
}
|
||||
|
||||
// Remove 1 download from queue
|
||||
self.removeDownload = function(item, event) {
|
||||
// Confirm and remove
|
||||
if(!self.parent.parent.confirmDeleteQueue() || confirm(glitterTranslate.deleteMsg + ":\n" + item.name() + "\n\n" + glitterTranslate.removeDow1)) {
|
||||
var itemToDelete = this;
|
||||
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-removing')
|
||||
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'delete',
|
||||
del_files: 1,
|
||||
value: item.id
|
||||
}).then(function(response) {
|
||||
// Make sure no flickering (if there are more items left) and then remove
|
||||
self.parent.isLoading(self.parent.totalItems() > 1)
|
||||
parent.queueItems.remove(itemToDelete);
|
||||
parent.multiEditItems.remove(function(inList) { return inList.id == itemToDelete.id; })
|
||||
self.parent.parent.refresh();
|
||||
// Hide notifcation
|
||||
hideNotification()
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -140,12 +140,6 @@ select.form-control,
|
||||
color: #EBEBEB;
|
||||
}
|
||||
|
||||
.btn-default:not(.navbar-btn):hover,
|
||||
select:hover,
|
||||
input:hover {
|
||||
background-color: #666666;
|
||||
}
|
||||
|
||||
/* Needed to force the text-color */
|
||||
.table-striped>tbody>tr:nth-child(odd)>td,
|
||||
.table>tbody>tr:nth-child(odd)>td,
|
||||
@@ -206,8 +200,7 @@ tbody .caret {
|
||||
.info-container,
|
||||
#modal-options .options-status-box small,
|
||||
#modal-options #options-status small,
|
||||
#modal-options .tab-content h4,
|
||||
h2 small {
|
||||
#modal-options .tab-content h4 {
|
||||
color: #D6D6D6;
|
||||
}
|
||||
|
||||
@@ -273,11 +266,16 @@ button:focus {
|
||||
outline: initial;
|
||||
}
|
||||
|
||||
.archive-icon {
|
||||
fill: #EBEBEB;
|
||||
/* Placeholders - Will not work if grouped! */
|
||||
::-webkit-input-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #EBEBEB !important;
|
||||
opacity: 0.5 !important;
|
||||
::-moz-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
}
|
||||
@@ -690,10 +690,6 @@ tbody.no-downloads tr td {
|
||||
border-bottom: 1px solid #F0F0F0 !important;
|
||||
}
|
||||
|
||||
tbody.no-downloads tr td>span {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
tbody.no-downloads tr td a {
|
||||
line-height: 2em;
|
||||
font-size: 1.5em;
|
||||
@@ -860,7 +856,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox {
|
||||
width: 19%;
|
||||
width: 20%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
@@ -871,7 +867,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-small {
|
||||
width: 115px;
|
||||
width: 80px;
|
||||
float: right;
|
||||
padding-left: 0;
|
||||
padding-top: 12px;
|
||||
@@ -1097,13 +1093,6 @@ 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;
|
||||
}
|
||||
@@ -1114,26 +1103,20 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
#history-options .hover-button {
|
||||
padding: 7px;
|
||||
padding: 7px 8px 7px 8px;
|
||||
line-height: 1.428571429;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#history-options .hover-button.history-archive {
|
||||
line-height: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#history-options div {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#history-options input[name="multieditCheckAll"] {
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
top: -1px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
@@ -1203,10 +1186,6 @@ tr.queue-item>td:first-child>a {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.history-options-show-failed .archive-icon {
|
||||
fill: #2bbd43;
|
||||
}
|
||||
|
||||
.processing-download {
|
||||
width: 16px;
|
||||
height: 18px;
|
||||
@@ -1542,34 +1521,6 @@ input[name="nzbURL"] {
|
||||
transition : border 500ms ease-out;
|
||||
}
|
||||
|
||||
/* DELETE MODAL */
|
||||
.modal-delete-job ul {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.modal-delete-job li {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.modal-delete-job .checkbox {
|
||||
float: left;
|
||||
margin: 8px 5px 0px;
|
||||
}
|
||||
|
||||
#modal-purge-history .checkbox {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modal-delete-job .checkbox input,
|
||||
#modal-purge-history .checkbox input {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.modal-delete-job .checkbox input+span,
|
||||
#modal-purge-history .checkbox input+span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* HELP MODAL */
|
||||
|
||||
#modal-help .modal-body {
|
||||
|
||||
@@ -163,7 +163,7 @@ tr.queue-item>td:first-child>a {
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-small {
|
||||
width: 115px;
|
||||
width: 72px;
|
||||
}
|
||||
|
||||
.multioperations-selector .add-nzb-inputbox-clear {
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
<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>
|
||||
@@ -24,7 +22,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" required />
|
||||
<input type="text" class="form-control" name="host" id="host" value="$host" placeholder="$T('wizard-example') news.newshosting.com" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -59,31 +57,29 @@
|
||||
<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" required />
|
||||
<input type="number" class="form-control" name="port" id="port" value="<!--#if $port then $port else '563' #-->" />
|
||||
</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')" required />
|
||||
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ssl_verify" class="col-sm-4 control-label">$T('opt-ssl_verify')</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="ssl_verify" id="ssl_verify" class="form-control">
|
||||
<option value="3" <!--#if $ssl_verify == 3 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="2" <!--#if $ssl_verify == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-medium')</option>
|
||||
<option value="1" <!--#if $ssl_verify == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-minimal')</option>
|
||||
<select name="ssl_verify" id="ssl_verify" class="form-control" <!--#if int($certificate_validation) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="2" <!--#if $ssl_verify == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="1" <!--#if $ssl_verify == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="0" <!--#if $ssl_verify == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('-', '<br/>-')</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button id="serverTest" class="btn btn-default" data-toggle="tooltip" data-placement="left"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
|
||||
<button id="serverTest" class="btn btn-default"><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,31 +1,9 @@
|
||||
// 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() {
|
||||
// Check if form is valid using HTML5 validation and if server test passed
|
||||
if ($("form").get(0).checkValidity() && serverTestSuccessful) {
|
||||
if ($("#host").val() && $("#connections").val()) {
|
||||
$("#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;
|
||||
}
|
||||
}
|
||||
@@ -34,13 +12,8 @@ $(document).ready(function() {
|
||||
// Add tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
|
||||
// On server test button click
|
||||
// On form-submit
|
||||
$("#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",
|
||||
@@ -48,10 +21,8 @@ $(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);
|
||||
@@ -60,31 +31,47 @@ $(document).ready(function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
// Reset test result when any form field changes
|
||||
$("#host, #username, #password, #port, #connections, #ssl_verify").bind('input change', function() {
|
||||
resetTestResult();
|
||||
$("#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();
|
||||
});
|
||||
|
||||
$('#ssl').click(function() {
|
||||
if(this.checked) {
|
||||
// Enabled SSL change port when not already a custom port
|
||||
if($('#port').val() === '119') {
|
||||
if($('#port').val() == '119') {
|
||||
$('#port').val('563')
|
||||
}
|
||||
} else {
|
||||
// Remove SSL port
|
||||
if($('#port').val() === '563') {
|
||||
if($('#port').val() == '563') {
|
||||
$('#port').val('119')
|
||||
}
|
||||
}
|
||||
resetTestResult();
|
||||
})
|
||||
|
||||
checkRequired()
|
||||
|
||||
$('form').submit(function(event) {
|
||||
// Check if server test passed (HTML5 validation is automatic)
|
||||
if(!serverTestSuccessful) {
|
||||
// Double check
|
||||
if(!checkRequired()) {
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -88,25 +88,49 @@ label {
|
||||
float: right;
|
||||
margin: 0;
|
||||
}
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
@@ -122,12 +146,20 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2022-2025 by The SABnzbd-Team (sabnzbd.org) -->
|
||||
<!-- Copyright 2022-2023 The SABnzbd-Team (sabnzbd.org) -->
|
||||
<component type="desktop-application">
|
||||
<id>org.sabnzbd.sabnzbd</id>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
@@ -30,21 +30,6 @@
|
||||
<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"/>
|
||||
<release version="4.4.1" date="2024-12-23" type="stable"/>
|
||||
<release version="4.4.0" date="2024-12-09" type="stable"/>
|
||||
<release version="4.3.3" date="2024-08-01" type="stable"/>
|
||||
<release version="4.3.2" date="2024-05-30" type="stable"/>
|
||||
<release version="4.3.1" date="2024-05-03" type="stable"/>
|
||||
<release version="4.3.0" date="2024-05-01" type="stable"/>
|
||||
<release version="4.2.2" date="2024-02-01" type="stable"/>
|
||||
<release version="4.2.1" date="2024-01-05" type="stable"/>
|
||||
<release version="4.2.0" date="2024-01-03" type="stable"/>
|
||||
<release version="4.1.0" date="2023-09-26" type="stable"/>
|
||||
<release version="4.0.3" date="2023-06-16" type="stable"/>
|
||||
<release version="4.0.2" date="2023-06-09" type="stable"/>
|
||||
@@ -63,13 +48,11 @@
|
||||
<control>touch</control>
|
||||
</supports>
|
||||
<recommends>
|
||||
<display_length compare="ge">640</display_length>
|
||||
<display_length compare="ge">small</display_length>
|
||||
<internet>always</internet>
|
||||
</recommends>
|
||||
<project_license>GPL-2.0-or-later</project_license>
|
||||
<developer id="org.sabnzbd">
|
||||
<name>The SABnzbd-Team</name>
|
||||
</developer>
|
||||
<developer_name>The SABnzbd-Team</developer_name>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://sabnzbd.org/images/landing/screenshots/interface.png</image>
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Name=SABnzbd
|
||||
GenericName=Binary newsreader
|
||||
GenericName[fr]=Lecteur de newsgroups binaires
|
||||
GenericName[tr]=İkili haber grupları okuyucusu
|
||||
Comment=Download from Usenet
|
||||
Comment[fr]=Télécharger depuis Usenet
|
||||
Comment[tr]=Usenet ağından dosya indir
|
||||
Exec=/opt/sabnzbd/SABnzbd.py --browser 1 %F
|
||||
Icon=sabnzbd
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Network;FileTransfer;
|
||||
Keywords=usenet;binaries;download;nzb;nntp;newsreader;
|
||||
Keywords[fr]=usenet;binaires;télécharger;nzb;nntp;newsreader;
|
||||
Keywords[tr]=usenet;ikililer;indir;nzb;nntp;okuyucu;
|
||||
MimeType=application/x-nzb;application/x-compressed-nzb;
|
||||
|
||||
@@ -22,11 +22,6 @@ ExecStart=/opt/sabnzbd/SABnzbd.py --disable-file-log --logging 1 --browser 0
|
||||
User=%I
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
ProtectSystem=full
|
||||
DeviceAllow=/dev/null rw
|
||||
DeviceAllow=/dev/urandom r
|
||||
DevicePolicy=strict
|
||||
NoNewPrivileges=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
BIN
macos/7zip/7zz
BIN
macos/7zip/7zz
Binary file not shown.
@@ -1,144 +0,0 @@
|
||||
7-Zip for Linux and macOS
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
License for use and distribution
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
7-Zip Copyright (C) 1999-2024 Igor Pavlov.
|
||||
|
||||
The licenses for 7zz and 7zzs files are:
|
||||
|
||||
- The "GNU LGPL" as main license for most of the code
|
||||
- The "GNU LGPL" with "unRAR license restriction" for some code
|
||||
- The "BSD 3-clause License" for some code
|
||||
- The "BSD 2-clause License" for some code
|
||||
|
||||
Redistributions in binary form must reproduce related license information from this file.
|
||||
|
||||
Note:
|
||||
You can use 7-Zip on any computer, including a computer in a commercial
|
||||
organization. You don't need to register or pay for 7-Zip.
|
||||
|
||||
|
||||
GNU LGPL information
|
||||
--------------------
|
||||
|
||||
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 can receive a copy of the GNU Lesser General Public License from
|
||||
http://www.gnu.org/
|
||||
|
||||
|
||||
|
||||
|
||||
BSD 3-clause License in 7-Zip code
|
||||
----------------------------------
|
||||
|
||||
The "BSD 3-clause License" is used for the following code in 7z.dll
|
||||
1) LZFSE data decompression.
|
||||
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
|
||||
that also uses the "BSD 3-clause License".
|
||||
2) ZSTD data decompression.
|
||||
that code was developed using original zstd decoder code as reference code.
|
||||
The original zstd decoder code was developed by Facebook Inc,
|
||||
that also uses the "BSD 3-clause License".
|
||||
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
Copyright (c) Facebook, Inc. All rights reserved.
|
||||
Copyright (c) 2023-2024 Igor Pavlov.
|
||||
|
||||
Text of the "BSD 3-clause License"
|
||||
----------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
BSD 2-clause License in 7-Zip code
|
||||
----------------------------------
|
||||
|
||||
The "BSD 2-clause License" is used for the XXH64 code in 7-Zip.
|
||||
|
||||
XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet.
|
||||
|
||||
Copyright (c) 2012-2021 Yann Collet.
|
||||
Copyright (c) 2023-2024 Igor Pavlov.
|
||||
|
||||
Text of the "BSD 2-clause License"
|
||||
----------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
unRAR license restriction
|
||||
-------------------------
|
||||
|
||||
The decompression engine for RAR archives was developed using source
|
||||
code of unRAR program.
|
||||
All copyrights to original unRAR code are owned by Alexander Roshal.
|
||||
|
||||
The license for original unRAR code has the following restriction:
|
||||
|
||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
||||
or as a part of other software is permitted, provided that it is clearly
|
||||
stated in the documentation and source comments that the code may
|
||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
||||
|
||||
--
|
||||
BIN
macos/par2/par2
BIN
macos/par2/par2
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
osx/7zip/7zz
Executable file
BIN
osx/7zip/7zz
Executable file
Binary file not shown.
88
osx/7zip/License.txt
Normal file
88
osx/7zip/License.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
7-Zip
|
||||
~~~~~
|
||||
License for use and distribution
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
7-Zip Copyright (C) 1999-2021 Igor Pavlov.
|
||||
|
||||
The licenses for 7zz file are:
|
||||
|
||||
- The "GNU LGPL" as main license for most of the code
|
||||
- The "GNU LGPL" with "unRAR license restriction" for some code
|
||||
- The "BSD 3-clause License" for some code
|
||||
|
||||
Redistributions in binary form must reproduce related license information from this file.
|
||||
|
||||
Note:
|
||||
You can use 7-Zip on any computer, including a computer in a commercial
|
||||
organization. You don't need to register or pay for 7-Zip.
|
||||
|
||||
|
||||
GNU LGPL information
|
||||
--------------------
|
||||
|
||||
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 can receive a copy of the GNU Lesser General Public License from
|
||||
http://www.gnu.org/
|
||||
|
||||
|
||||
|
||||
|
||||
BSD 3-clause License
|
||||
--------------------
|
||||
|
||||
The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression.
|
||||
That code was derived from the code in the "LZFSE compression library" developed by Apple Inc,
|
||||
that also uses the "BSD 3-clause License":
|
||||
|
||||
----
|
||||
Copyright (c) 2015-2016, Apple Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
unRAR license restriction
|
||||
-------------------------
|
||||
|
||||
The decompression engine for RAR archives was developed using source
|
||||
code of unRAR program.
|
||||
All copyrights to original unRAR code are owned by Alexander Roshal.
|
||||
|
||||
The license for original unRAR code has the following restriction:
|
||||
|
||||
The unRAR sources cannot be used to re-create the RAR compression algorithm,
|
||||
which is proprietary. Distribution of modified unRAR sources in separate form
|
||||
or as a part of other software is permitted, provided that it is clearly
|
||||
stated in the documentation and source comments that the code may
|
||||
not be used to develop a RAR (WinRAR) compatible archiver.
|
||||
|
||||
|
||||
--
|
||||
Igor Pavlov
|
||||
BIN
osx/par2/arm64/par2-turbo
Executable file
BIN
osx/par2/arm64/par2-turbo
Executable file
Binary file not shown.
BIN
osx/par2/par2-turbo
Executable file
BIN
osx/par2/par2-turbo
Executable file
Binary file not shown.
BIN
osx/unrar/arm64/unrar
Executable file
BIN
osx/unrar/arm64/unrar
Executable file
Binary file not shown.
BIN
osx/unrar/unrar
Executable file
BIN
osx/unrar/unrar
Executable file
Binary file not shown.
@@ -1,10 +1,10 @@
|
||||
#
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0RC2\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: team@sabnzbd.org\n"
|
||||
"Language-Team: SABnzbd <team@sabnzbd.org>\n"
|
||||
@@ -16,7 +16,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -61,7 +61,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -90,7 +90,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -17,7 +17,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -62,7 +62,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -91,7 +91,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
|
||||
102
po/email/da.po
102
po/email/da.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Standard E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Dette er e-mail-headerne \n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har <!--#if $status then \"hentet\" else \"fejlet\" #--> job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd har hentet \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd kunne ikke hente \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Færdig kl. $end_time\n"
|
||||
"Hentet $size\n"
|
||||
"\n"
|
||||
"Resultat af job:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Etape $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Output fra brugerscriptet \"$script\" (Afslutningskode = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Hav det godt!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Beklager!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## RSS E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Dette er e-mai-headere\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har tilføjet $antal jobs til køen\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"\n"
|
||||
"SABnzbd har tilføjet $antal job(s) til køen.\n"
|
||||
"De er fra RSS-feedet \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Farvel\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Dårlig URL-hentning af E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Dette er e-mail-headere\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd kunne ikke hente en NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"\n"
|
||||
"SABnzbd kunne ikke hente NZB fra $url.\n"
|
||||
"Fejlmeddelelsen er: $msg\n"
|
||||
"\n"
|
||||
"Farvel\n"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -21,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -109,7 +109,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -161,7 +161,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
|
||||
102
po/email/es.po
102
po/email/es.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Plantilla de correo predeterminada para SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentación: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## !Los saltos de línea y espacios en blanco son significativos¡\n"
|
||||
"##\n"
|
||||
"## Cabeceras de correo electrónico\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd <!--#if $status then \"he bajado\" else \"fallo en bajar\" #--> job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## !Después de esto viene el cuerpo del mensaje, la línea en blanco es necesaria!\n"
|
||||
"\n"
|
||||
"Hola,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd he bajado \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd fallo en bajar \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Terminado a las $end_time\n"
|
||||
"$size bajado\n"
|
||||
"\n"
|
||||
"Resultado de la transferencia:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Etapa $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Producción desde el script de usuario \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Que lo disfrutes!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Perdon!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Plantilla de correo RSS para SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## !Los saltos de línea y espacios en blanco son significativos¡\n"
|
||||
"##\n"
|
||||
"## Cabeceras de correo electrónico\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd he añadido $amount transferencia(s) a la cola\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## !Después de esto viene el cuerpo del mensaje, la línea en blanco es necesaria!\n"
|
||||
"\n"
|
||||
"Hola,\n"
|
||||
"\n"
|
||||
"SABnzbd he añadido $amount transferencia(s) a la cola.\n"
|
||||
"Originaron desde el RSS \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
"$job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Adios\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Plantilla de correo para URLs incorrectas de SABnzbd\n"
|
||||
"## Esta es una plantilla Cheetah\n"
|
||||
"## Documentación: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Líneas nuevas y espacios en blanco IMPORTAN!\n"
|
||||
"##\n"
|
||||
"## Estas son las cabeceras del email\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd ha encontrado un error al recuperar un NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hola,\n"
|
||||
"\n"
|
||||
"SABnzbd ha encontrado un error al descargar un NZB desde $url.\n"
|
||||
"El error ha sido: $msg\n"
|
||||
"\n"
|
||||
"Un saludo\n"
|
||||
|
||||
100
po/email/fi.po
100
po/email/fi.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Oletus sähköpostipohja SABnzbd:lle\n"
|
||||
"## Tämä on Cheetah pohja\n"
|
||||
"## Dokumentaatio: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Rivinvaihdot ja välilyönnit ovat merkitseviä!\n"
|
||||
"##\n"
|
||||
"## Nämä ovat otsaketiedot. Rivien ensimmäisiä sanoja ei saa vaihtaa!\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd on <!--#if $status then \"valmistunut\" else \"epäonnistunut\" #--> työssä $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Tämän jälkeen tulee viestin runko, ensimmäinen rivinvaihto on pakollinen!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd on ladannut \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd on epäonnistunut \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#--> latauksessa\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Valmistui $end_time\n"
|
||||
"Ladattu $size\n"
|
||||
"\n"
|
||||
"Työn lopputulos:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Tila $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Käyttäjän skriptin tuloste \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Nauti!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Pahoittelut!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## RSS sähköpostipohja SABnzbd:lle\n"
|
||||
"## Tämä on Cheetah pohja\n"
|
||||
"## Dokumentaatio: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Rivinvaihdot ja välilyönnit ovat merkitseviä!\n"
|
||||
"##\n"
|
||||
"## Nämä ovat otsaketiedot. Rivien ensimmäisiä sanoja ei saa vaihtaa!\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd on lisännyt $amount työtä jonoon\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Tämän jälkeen tulee viestin runko, ensimmäinen rivinvaihto on pakollinen!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"\n"
|
||||
"SABnzbd on lisännyt $amount työtä jonoon.\n"
|
||||
"Ne ovat RSS syötteestä \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Heippa\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,23 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Virheellisen URL-noudon sähköpostin pohja SABnzbd ohjelmalle\n"
|
||||
"## Tämä on Cheetah pohja\n"
|
||||
"## Dokumentaatio: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Rivinvaihdot ja välilyönnit ovat merkitseviä!\n"
|
||||
"##\n"
|
||||
"## Tässä on sähköpostin otsikkotiedot\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd ei voinut hakea NZB tiedostoa\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Tämän jälkeen tulee viestin sisältö, tyhjä rivi on pakollinen!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"\n"
|
||||
"SABnzbd ei voinut hakea NZB tiedostoa osoitteesta $url.\n"
|
||||
"Virheviesti: $msg\n"
|
||||
|
||||
109
po/email/fr.po
109
po/email/fr.po
@@ -1,14 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Fred L <88com88@gmail.com>, 2025
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Fred L <88com88@gmail.com>, 2025\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -60,44 +60,45 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"#encoding UTF-8\n"
|
||||
"##\n"
|
||||
"## Modèle d'e-mail par défaut pour SABnzbd\n"
|
||||
"## Ceci est un modèle Cheetah\n"
|
||||
"## Documentation : https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Template Email pour SABnzbd\n"
|
||||
"## Ceci est un template Cheetah\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Les retours à la ligne et les espaces sont importants !\n"
|
||||
"##\n"
|
||||
"## Voici les en-têtes de l'email\n"
|
||||
"À : $to\n"
|
||||
"De : $from\n"
|
||||
"Date : $date\n"
|
||||
"Objet : SABnzbd a <!--#if $status then \"terminé\" else \"n'a pu terminer\" #--> la tâche $name\n"
|
||||
"X-priorité: 5\n"
|
||||
"X-MS-priorité: 5\n"
|
||||
"## Après cela vient le corps du message, la ligne vide est nécessaire !\n"
|
||||
"## Entêtes de l'email\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd <!--#if $status#-->Succès<!--#else#-->Echec<!--#end if#--> du téléchargement $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Après cela vient le contenu, la ligne vide est nécessaire! \n"
|
||||
"\n"
|
||||
"Bonjour,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd a téléchargé \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"SABnzbd a téléchargé avec succès \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd n'a pu télécharger \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"SABnzbd a téléchargé sans succès \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Terminé à $end_time\n"
|
||||
"$size téléchargés\n"
|
||||
"Téléchargé $size\n"
|
||||
"\n"
|
||||
"Résultats de la tâche :\n"
|
||||
"Résultat du téléchargement :\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Étape $stage <!--#slurp#-->\n"
|
||||
"Etape $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Résultats du script \"$script\" (Code de sortie = $script_ret):\n"
|
||||
"Sortie du script utilisateur \"$script\" (Code Retour = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Amusez-vous bien !\n"
|
||||
"A bientôt !\n"
|
||||
"<!--#else#-->\n"
|
||||
"Désolé !\n"
|
||||
"<!--#end if#-->\n"
|
||||
@@ -107,7 +108,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -131,37 +132,37 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Modèle d'e-mail RSS pour SABnzbd\n"
|
||||
"## Ceci est un modèle Cheetah\n"
|
||||
"## Documentation : https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Template Email pour SABnzbd\n"
|
||||
"## Ceci est un template Cheetah\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Les retours à la ligne et espaces sont importants !\n"
|
||||
"## Les retours à la ligne et les espaces sont importants !\n"
|
||||
"##\n"
|
||||
"## Voici les en-têtes de l'email\n"
|
||||
"À : $to\n"
|
||||
"De : $from\n"
|
||||
"Date : $date\n"
|
||||
"Objet : SABnzbd a ajouté $amount tâches dans la file d'attente\n"
|
||||
"X-priorité: 5\n"
|
||||
"X-MS-priorité: 5\n"
|
||||
"## Après cela vient le corps du message, la ligne vide est nécessaire !\n"
|
||||
"## Entêtes de l'email\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd a ajouté $amount fichier(s) à la file d'attente\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Après cela vient le contenu, la ligne vide est nécessaire!\n"
|
||||
"\n"
|
||||
"Bonjour,\n"
|
||||
"\n"
|
||||
"SABnzbd a ajouté $amount tâche(s) à la file d'attente.\n"
|
||||
"Elles viennent du flux RSS \"$feed\".\n"
|
||||
"SABnzbd a ajouté $amount fichier(s) à la file d'attente.\n"
|
||||
"Ils proviennent du Flux RSS \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
"Au Revoir\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -182,24 +183,24 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Modèle d'email d'URL incorrecte pour SABnzbd\n"
|
||||
"## Ceci est un modèle Cheetah\n"
|
||||
"## Documentation : https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Les retours à la ligne et les espaces sont importants !\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## Voici les en-têtes de l'email\n"
|
||||
"À : $to\n"
|
||||
"De : $from\n"
|
||||
"Date : $date\n"
|
||||
"Objet : SABnzbd n'a pas réussi à récupérer un NZB\n"
|
||||
"X-priorité: 5\n"
|
||||
"X-MS-priorité: 5\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd failed to fetch an NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Bonjour,\n"
|
||||
"Hi,\n"
|
||||
"\n"
|
||||
"SABnzbd n'a pas réussi à récupérer le NZB depuis $url.\n"
|
||||
"Le message d'erreur était : $msg\n"
|
||||
"SABnzbd has failed to retrieve the NZB from $url.\n"
|
||||
"The error message was: $msg\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
# ION, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Last-Translator: ION, 2020\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"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: he\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -105,7 +105,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -157,7 +157,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
|
||||
113
po/email/it.po
113
po/email/it.po
@@ -1,113 +0,0 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Language-Team: Italian (https://app.transifex.com/sabnzbd/teams/111101/it/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: it\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd has downloaded \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd has failed to download \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Finished at $end_time\n"
|
||||
"Downloaded $size\n"
|
||||
"\n"
|
||||
"Results of the job:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Stage $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Output from user script \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Enjoy!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd has added $amount jobs to the queue\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"\n"
|
||||
"SABnzbd has added $amount job(s) to the queue.\n"
|
||||
"They are from RSS feed \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd failed to fetch an NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hi,\n"
|
||||
"\n"
|
||||
"SABnzbd has failed to retrieve the NZB from $url.\n"
|
||||
"The error message was: $msg\n"
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
104
po/email/nb.po
104
po/email/nb.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,56 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"#encoding UTF-8\n"
|
||||
"## Translation by ProtX\n"
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har <!--#if $status then \"completed\" else \"failed\" #--> jobb $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd har lastet ned \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd mislyktes med å laste ned \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Ferdig $end_time\n"
|
||||
"Nedlastet $size\n"
|
||||
"\n"
|
||||
"Resultat av jobben:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Steg $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Utskrift fra brukerskript \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Gratulerer!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Synd!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +132,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har lagt $amount jobber til køen\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Etter dette kommer meldingen, den tomme linjen er nødvendig!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"\n"
|
||||
"SABnzbd har lagt $amount jobb(er) til køen.\n"
|
||||
"Disse er fra RSS feeden \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Hade\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +183,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd ikke klarte å hente en NZB fil\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Etter dette kommer meldingen, den tomme linjen er nødvendig!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"\n"
|
||||
"SABnzbd klarte ikke å hente NZB fra $url.\n"
|
||||
"Feilmeldingen var: $msg\n"
|
||||
"\n"
|
||||
"Hade\n"
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2025
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2025\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -21,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -107,7 +107,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -159,7 +159,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
|
||||
102
po/email/pl.po
102
po/email/pl.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Domyślny szablon maila w SABnzbd\n"
|
||||
"## To jest szablon Cheetah\n"
|
||||
"## Dokumentacja: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Znak nowego wiersza i spacji ma znaczenie!\n"
|
||||
"##\n"
|
||||
"## To są nagłowki maila\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd <!--#if $status then \"zakończył\" else \"zakończył z błędem\" #--> zadanie $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Następnie treść maila, wymagana jest pusta linia!\n"
|
||||
"\n"
|
||||
"Cześć,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd pobrał \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd nie pobrał \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Zakończono o $end_time\n"
|
||||
"Pobrano $size\n"
|
||||
"\n"
|
||||
"Rezultat zadania:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Etap $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
"$result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Odpowiedź od skryptu \"$script\" (kod wyjścia = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Baw się dobrze!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Przykro mi!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Szablon wiadomości RSS dla SABnzbd\n"
|
||||
"## To jest szablon Cheetah\n"
|
||||
"## Dokumentacja: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Znak nowego wiersza i spacji ma znaczenie!\n"
|
||||
"##\n"
|
||||
"## To są nagłowki maila\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd dodał $amount zadań/zadania do kolejki\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Następnie treść maila, wymagana jest pusta linia!\n"
|
||||
"\n"
|
||||
"Cześć,\n"
|
||||
"\n"
|
||||
"SABnzbd dodał $amount zadanie/zadań do kolejki.\n"
|
||||
"Pochodzą one z wiadomości RSS \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
"$job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Nara\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Szablon wiadomości błędnego pobierania URL SABnzbd\n"
|
||||
"## To jest szablon Cheetah\n"
|
||||
"## Dokumentacja: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Znaki nowego wiersza i białe znaki mają znaczenie!\n"
|
||||
"##\n"
|
||||
"## To są nagłówki wiadomości\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd nie udało się pobrać pliku NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Po tym następuje treść. Pusty wiersz jest wymagany!\n"
|
||||
"\n"
|
||||
"Cześć,\n"
|
||||
"\n"
|
||||
"SABnzbd nie udało się pobrać pliku NZB z $url.\n"
|
||||
"Komunikat błędu: $msg\n"
|
||||
"\n"
|
||||
"Do usłyszenia.\n"
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Template padrão de e-mail para SABnzbd\n"
|
||||
"## Este é um template Cheetah\n"
|
||||
"## Documentação: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Novas linhas e espaços em branco são significativos!\n"
|
||||
"##\n"
|
||||
"## Estes são os cabeçalhos de e-mail\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd <!--#if $status then \"completou \" else \"falhou n\" #-->a tarefa $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Depois daqui vem o corpo. A linha vazia é necessária!\n"
|
||||
"\n"
|
||||
"Olá,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd baixou \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd falhou no download de \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Completado em $end_time\n"
|
||||
"Baixados $size\n"
|
||||
"\n"
|
||||
"Resultados da tarefa:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Etapa $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Retorno do script de usuário \"$script\" (Código de retorno = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Aproveite!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Lamento!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Template de e-mail RSS para SABnzbd\n"
|
||||
"## Este é um template Cheetah\n"
|
||||
"## Documentação: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Novas linhas e espaços em branco são significativos!\n"
|
||||
"##\n"
|
||||
"## Estes são os cabeçalhos de e-mail\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd adicionou $amount <!--#if $amount == \"1\" then \"tarefa\" else \"tarefas\" #--> à fila\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Depois daqui vem o corpo. A linha vazia é necessária!\n"
|
||||
"\n"
|
||||
"Olá,\n"
|
||||
"\n"
|
||||
"SABnzbd adicionou $amount <!--#if $amount == \"1\" then \"tarefa\" else \"tarefas\" #--> à fila.\n"
|
||||
"Elas são do feed RSS \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"Tchau!\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Template de e-mail de busca em URL ruim para SABnzbd\n"
|
||||
"## Este é um template Cheetah\n"
|
||||
"## Documentação: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Novas linhas e espaços em branco são significativos!\n"
|
||||
"##\n"
|
||||
"## Estes são os cabeçalhos de e-mail\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd falhou ao buscar um NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Depois daqui vem o corpo. A linha vazia é necessária!\n"
|
||||
"\n"
|
||||
"Olá,\n"
|
||||
"\n"
|
||||
"SABnzbd não conseguiu obter o NZB de $url.\n"
|
||||
"A mensagem de erro foi: $msg\n"
|
||||
"\n"
|
||||
"Tchau!\n"
|
||||
|
||||
101
po/email/ro.po
101
po/email/ro.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Șablon Email Original pentru SABnzbd\n"
|
||||
"## Acesta este un Șablon Cheetah\n"
|
||||
"## Documentație: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"##Rândurile noi și caracterele spațiu sunt importante!\n"
|
||||
"##\n"
|
||||
"## Acestea sunt antetele email\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd <!--#if $status then \"a terminat\" else \"nu a reuşit\" #--> sarcina $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## După acesta urmează conţinutul, este necesar o linie goală!\n"
|
||||
"\n"
|
||||
"Salut,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd a descărcat \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd nu a reuşit să descarce \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Terminat la $end_time\n"
|
||||
"Mărime $size\n"
|
||||
"\n"
|
||||
"Rezultatele sarcinii:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Stagiu $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Rezultatul script-ului utilizatorului \"$script\" (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Bucuraţi-vă!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Ne pare rau!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,37 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"## Şablon Email RSS pentru SABnzbd\n"
|
||||
"## Acesta este un şablon Cheetah \n"
|
||||
"## Documentaţie: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Rândurile noi și caracterele spațiu sunt importante!\n"
|
||||
"##\n"
|
||||
"## Acestea sunt antetele email\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd a adăugat $amount sarcini în coadă\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## După acesta urmează conţinutul, este necesar o linie goală!\n"
|
||||
"\n"
|
||||
"Salut,\n"
|
||||
"\n"
|
||||
"SABnzbd a adăugat $amount sarcină(e) în coadă.\n"
|
||||
"Ele sunt din fluxuri RSS \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"La revedere !\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +180,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Adresă URL Greşită şablon Email pentru SABnybd \n"
|
||||
"## Acesta este un şablon Cheetah\n"
|
||||
"## Documentaţie : http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Liniile noi şi spaţiile sunt importante!\n"
|
||||
"##\n"
|
||||
"## Acestea sunt headerele email\n"
|
||||
"Către: $to\n"
|
||||
"De la: $from\n"
|
||||
"Dată: $date\n"
|
||||
"Subiect: SABnzbd nu a reuşit să descarce un NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## După aceasta urmează corpul email, linia goală e necesară !\n"
|
||||
"\n"
|
||||
"Salut,\n"
|
||||
"\n"
|
||||
"SABnzbd nu a putut descărca NZB-ul de la adresa $url.\n"
|
||||
"Mesajul de eroare a fost: $msg\n"
|
||||
"\n"
|
||||
"La revedere!\n"
|
||||
|
||||
102
po/email/ru.po
102
po/email/ru.po
@@ -1,10 +1,14 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2025 by The SABnzbd-Team (sabnzbd.org)
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
#
|
||||
# Translators:
|
||||
# Safihre <safihre@sabnzbd.org>, 2020
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-4.6.0\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0Beta1\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\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"
|
||||
@@ -17,7 +21,7 @@ msgid ""
|
||||
"##\n"
|
||||
"## Default Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -56,13 +60,54 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Стандартный шаблон сообщения электронной почты\n"
|
||||
"## Это шаблон Cheetah\n"
|
||||
"## Документация: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Новые строки и пробелы имеют значение!\n"
|
||||
"##\n"
|
||||
"## Это заголовки электронной почты\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd: задание $name <!--#if $status then \"успешно выполнено\" else \"не удалось выполнить\" #-->\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Теперь следует тело сообщения. Пустая строка является обязательной!\n"
|
||||
"\n"
|
||||
"Привет.\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Системой SABnzbd загружено задание «$name» <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"Системе SABnzbd не удалось загрузить «$name» <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"Время окончания загрузки: $end_time\n"
|
||||
"Загруженный размер: $size\n"
|
||||
"\n"
|
||||
"Результаты задания:\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"Stage $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Результат выполнения сценария «$script» (Exit code = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Удачи!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Сожалеем.\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -85,13 +130,38 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Шаблон RSS для электронной почты\n"
|
||||
"## Это шаблон Cheetah\n"
|
||||
"## Документация: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Новые строки и пробелы имеют значение!\n"
|
||||
"##\n"
|
||||
"## Это заголовки электронной почты\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: В очередь загрузки SABnzbd добавлены задания: $amount \n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Теперь следует тело сообщения. Пустая строка является обязательной!\n"
|
||||
"\n"
|
||||
"Привет.\n"
|
||||
"\n"
|
||||
"В очередь загрузки SABnzbd были добавлены задания: $amount.\n"
|
||||
"Они были получены из RSS-ленты «$feed».\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"До свидания\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: https://sabnzbd.org/wiki/extra/email-templates\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
@@ -111,3 +181,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Шаблон электронной почты для излечения неверного URL-адреса\n"
|
||||
"## Это шаблон Cheetah\n"
|
||||
"## Документация: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Новые строки и пробелы имеют значение!\n"
|
||||
"##\n"
|
||||
"## Это заголовки электронной почты\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: Службе SABnzbd не удалось загрузить NZB-файл\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Теперь следует тело сообщения. Пустая строка является обязательной!\n"
|
||||
"\n"
|
||||
"Привет.\n"
|
||||
"\n"
|
||||
"Службе SABnzbd не удалось загрузить NZB-файл по адресу $url.\n"
|
||||
"Сообщение об ошибке: $msg\n"
|
||||
"\n"
|
||||
"Конец сообщения\n"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user