mirror of
https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer.git
synced 2026-03-01 11:21:12 +03:00
Compare commits
292 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7792b3c276 | ||
|
|
abf2dfda8f | ||
|
|
39fc2179d0 | ||
|
|
4321f08f0f | ||
|
|
0f9bd70a3d | ||
|
|
7ab26caa6a | ||
|
|
d65c1371bf | ||
|
|
2fc0739be9 | ||
|
|
ac4a8f6910 | ||
|
|
92e505c135 | ||
|
|
1850c487f0 | ||
|
|
89ac524f06 | ||
|
|
75cafea579 | ||
|
|
f4d61e36f8 | ||
|
|
ddfe2c4983 | ||
|
|
224c9391aa | ||
|
|
3c6cec56c9 | ||
|
|
6747fdfc35 | ||
|
|
8e678367f5 | ||
|
|
3223d73592 | ||
|
|
955418340d | ||
|
|
8a9ed0eb20 | ||
|
|
9f4c9a7907 | ||
|
|
9f90810b94 | ||
|
|
ef23c823df | ||
|
|
cbe0ce8b21 | ||
|
|
c381ecccd1 | ||
|
|
d1fa426815 | ||
|
|
377dca8528 | ||
|
|
932f0fa862 | ||
|
|
fc62f2c034 | ||
|
|
8c997ce7fd | ||
|
|
598ded836a | ||
|
|
9c34f8408e | ||
|
|
2a2ee67d52 | ||
|
|
2e5ee8d428 | ||
|
|
1ef3a31f13 | ||
|
|
8757689b91 | ||
|
|
37a7c414bd | ||
|
|
c3659394d5 | ||
|
|
3381869811 | ||
|
|
32265caf39 | ||
|
|
bfc759bfd8 | ||
|
|
245d47a9e0 | ||
|
|
095d19e8f9 | ||
|
|
527d5ca08a | ||
|
|
2da3ee4df2 | ||
|
|
1fe99bd974 | ||
|
|
d58ae3ded3 | ||
|
|
c002f88a7b | ||
|
|
29e746dd09 | ||
|
|
94307d58db | ||
|
|
b667132efe | ||
|
|
8ff66448ad | ||
|
|
6085e11046 | ||
|
|
97e684ea30 | ||
|
|
c19d411677 | ||
|
|
1786591380 | ||
|
|
2d1eb06048 | ||
|
|
ca18e54097 | ||
|
|
6ac842b2d0 | ||
|
|
23d7213bc2 | ||
|
|
fcdea48ba8 | ||
|
|
9f75dbfefe | ||
|
|
3ac9aa3bdf | ||
|
|
75ac9d6e2e | ||
|
|
a128e1be6b | ||
|
|
f3c81a1400 | ||
|
|
74dbde4276 | ||
|
|
912bc84687 | ||
|
|
598274ec08 | ||
|
|
3db936c767 | ||
|
|
ffc314e6c4 | ||
|
|
2d6f5492f8 | ||
|
|
bafcc8721b | ||
|
|
d7424138c6 | ||
|
|
fa19289e76 | ||
|
|
3f45b55f02 | ||
|
|
3916205d56 | ||
|
|
a5e7c0e9ca | ||
|
|
d822bd2e9f | ||
|
|
717674ed2f | ||
|
|
32b95a72cf | ||
|
|
404496e818 | ||
|
|
11ed846c1a | ||
|
|
d02526c1d6 | ||
|
|
56486c1ec8 | ||
|
|
a46a352359 | ||
|
|
2d0a6525da | ||
|
|
753fda3407 | ||
|
|
387c2b501f | ||
|
|
016f17ab04 | ||
|
|
8e971fc6aa | ||
|
|
ac423c7c04 | ||
|
|
ec2b9293ee | ||
|
|
8cee7d4b97 | ||
|
|
8ecf4a9b8a | ||
|
|
2e20e6f7b7 | ||
|
|
eb4ca257f1 | ||
|
|
e6845eebdd | ||
|
|
ebb4995748 | ||
|
|
505a2d3be4 | ||
|
|
e31beed93f | ||
|
|
dc682cfc5f | ||
|
|
56f77379c7 | ||
|
|
e09155e9d2 | ||
|
|
641c2d0f5f | ||
|
|
3a98bc6cf9 | ||
|
|
e8491798a5 | ||
|
|
bb1bda5051 | ||
|
|
d6a8d28fbb | ||
|
|
79b1d68643 | ||
|
|
e852cbc4f1 | ||
|
|
3dcf9a2878 | ||
|
|
6012413efb | ||
|
|
2aeb46c718 | ||
|
|
fe0b0b7bcb | ||
|
|
2232312f83 | ||
|
|
f66bfc69cb | ||
|
|
0ef964e535 | ||
|
|
6a3e93d4d6 | ||
|
|
84a4e964a3 | ||
|
|
7bdbc061cc | ||
|
|
f6c6ff074c | ||
|
|
ee1a9140cd | ||
|
|
4e27791dfe | ||
|
|
5518f7f20e | ||
|
|
cea2bda15b | ||
|
|
da91012e06 | ||
|
|
1e33f057bf | ||
|
|
eab08e271d | ||
|
|
b20803cc5b | ||
|
|
b3eb8d007b | ||
|
|
35990672d2 | ||
|
|
cc666e4b68 | ||
|
|
70bdf89b4a | ||
|
|
a6263a2468 | ||
|
|
eead46aeb6 | ||
|
|
b7d1c921d3 | ||
|
|
8dbb0de4a2 | ||
|
|
868df40a6b | ||
|
|
0b77c32b5e | ||
|
|
c0e1f0333f | ||
|
|
78d0ede171 | ||
|
|
03647d6360 | ||
|
|
f17a8404f3 | ||
|
|
3d0f89e6a8 | ||
|
|
116351376a | ||
|
|
7dc7ecc6e3 | ||
|
|
f663eae6d9 | ||
|
|
bfed3237e7 | ||
|
|
fbec97cfad | ||
|
|
a48b49e9fc | ||
|
|
3b4e31e09e | ||
|
|
9159ee38ac | ||
|
|
0bdd8cd6ce | ||
|
|
de7edefc9c | ||
|
|
35cb7cf2e6 | ||
|
|
ef7b2abb31 | ||
|
|
5ed25031ca | ||
|
|
37d61eed78 | ||
|
|
9aa4073995 | ||
|
|
ecf6937f55 | ||
|
|
23ffe2d046 | ||
|
|
1c6ac7a282 | ||
|
|
5f3f8a6895 | ||
|
|
4e9ab2dcb8 | ||
|
|
c87b7bef65 | ||
|
|
60d30c3f05 | ||
|
|
0cab66098e | ||
|
|
030cf24786 | ||
|
|
1c12ad6af7 | ||
|
|
3e5d3bfe74 | ||
|
|
d802bddf52 | ||
|
|
3f28090e9b | ||
|
|
faab2a68df | ||
|
|
6f37097e97 | ||
|
|
9d4f6b5630 | ||
|
|
2c2bb5de80 | ||
|
|
cd7d17e2b0 | ||
|
|
29381bca2e | ||
|
|
4f0232ab6a | ||
|
|
28b39c39f5 | ||
|
|
f278f938c0 | ||
|
|
7d4c94bd62 | ||
|
|
e36755de2f | ||
|
|
c15405cebb | ||
|
|
17153786c5 | ||
|
|
e5ab14c48e | ||
|
|
1643ea5758 | ||
|
|
7906bca1b2 | ||
|
|
9cf3de81ae | ||
|
|
eb10033d19 | ||
|
|
91c3aa9549 | ||
|
|
c63adfa618 | ||
|
|
4471e27855 | ||
|
|
7ef1c516e1 | ||
|
|
2daac548cf | ||
|
|
e377ddfd49 | ||
|
|
ae4f2ccb2f | ||
|
|
64d610d6b2 | ||
|
|
88f4755c3d | ||
|
|
d401e750d5 | ||
|
|
cd55172fb0 | ||
|
|
2e495db51d | ||
|
|
cdbc9959c5 | ||
|
|
60e0148ee2 | ||
|
|
b68114e6db | ||
|
|
51af88217e | ||
|
|
c4a4e947ba | ||
|
|
fd7058ca57 | ||
|
|
a41a36aad3 | ||
|
|
1c8bbb7251 | ||
|
|
7a1729530a | ||
|
|
efdb3406fa | ||
|
|
821fac94f8 | ||
|
|
3069290214 | ||
|
|
9065cf2249 | ||
|
|
e58a198711 | ||
|
|
4a3e54bd47 | ||
|
|
9e6eaa2316 | ||
|
|
a1cac4ae17 | ||
|
|
a48e3b5d24 | ||
|
|
cc231e39fd | ||
|
|
9879412518 | ||
|
|
0af0f2d1aa | ||
|
|
e91a0f23d6 | ||
|
|
d4fc9b6071 | ||
|
|
0979d05234 | ||
|
|
d1078087f5 | ||
|
|
63f37a5cfa | ||
|
|
867c7c2c6e | ||
|
|
c572e2b8ed | ||
|
|
0400671fc4 | ||
|
|
d58baa4012 | ||
|
|
2ca2f4c7f0 | ||
|
|
8d7ab3429a | ||
|
|
096d7a3e57 | ||
|
|
276be8cabb | ||
|
|
8eef524927 | ||
|
|
754a47b4dd | ||
|
|
581f1bb238 | ||
|
|
669d3896a0 | ||
|
|
a8def26087 | ||
|
|
3ea51370cd | ||
|
|
f280c76a27 | ||
|
|
d59c5e42c5 | ||
|
|
04d6f5c228 | ||
|
|
9cd6006841 | ||
|
|
14adc6e806 | ||
|
|
e1b9d5b03d | ||
|
|
71cf3d62a8 | ||
|
|
fed80d8f55 | ||
|
|
b36a83038f | ||
|
|
f17efcbefc | ||
|
|
fe16834604 | ||
|
|
ac5fb567d0 | ||
|
|
f38c345cc6 | ||
|
|
b6ac5b600c | ||
|
|
36a9a267bc | ||
|
|
7350a0c92d | ||
|
|
a575a4353b | ||
|
|
2adebebb74 | ||
|
|
9c8ae5c572 | ||
|
|
7af3f099cb | ||
|
|
ad0c3ff186 | ||
|
|
405573bc36 | ||
|
|
224fb37350 | ||
|
|
62fc93f10f | ||
|
|
dea4f4e6c7 | ||
|
|
6a0a6df965 | ||
|
|
9c38699702 | ||
|
|
36b112cd96 | ||
|
|
0f68d108b1 | ||
|
|
a97d4dccf3 | ||
|
|
2a054c761f | ||
|
|
faa67aa904 | ||
|
|
432e50abdf | ||
|
|
de86a50773 | ||
|
|
2e9f1fd350 | ||
|
|
a76b2cc3da | ||
|
|
b7e2f85251 | ||
|
|
95f166124c | ||
|
|
073cd3ebd2 | ||
|
|
81ed6dcb81 | ||
|
|
750f571cf4 | ||
|
|
66d15cc6a8 | ||
|
|
1fab1ae861 | ||
|
|
196fe4e89b | ||
|
|
9c4c802d6d | ||
|
|
0432935f34 | ||
|
|
8ee69ef322 |
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@@ -84,7 +84,7 @@ We welcome pull requests! Please:
|
||||
```bash
|
||||
git remote add upstream https://github.com/PatrickSt1991/Samsung-Jellyfin-Installer.git
|
||||
git fetch upstream
|
||||
git rebase upstream/master
|
||||
git rebase upstream/beta
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@@ -1,5 +1,8 @@
|
||||
# Pull Request Template
|
||||
|
||||
## Branch
|
||||
- [ ] I branched off beta (not master) to develop this feature/fix
|
||||
|
||||
## Description
|
||||
|
||||
Please include a summary of the change and which issue is fixed. Also include relevant motivation and context.
|
||||
|
||||
304
.github/workflows/beta-prerelease.yml
vendored
Normal file
304
.github/workflows/beta-prerelease.yml
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
name: Beta Pre-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [beta]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
PRODUCT_NAME: Jellyfin2Samsung
|
||||
PROJECT_DIR: Jellyfin2Samsung-CrossOS
|
||||
CONFIGURATION: Release
|
||||
|
||||
jobs:
|
||||
# ======================================================
|
||||
# RELEASE + WINDOWS + LINUX
|
||||
# ======================================================
|
||||
beta-release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
VERSION_TAG: ${{ env.VERSION_TAG }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# ---------------- VERSION ----------------
|
||||
- name: Extract version
|
||||
shell: bash
|
||||
run: |
|
||||
VERSION=$(grep -oE 'v[0-9]+(\.[0-9]+){1,3}' \
|
||||
Jellyfin2Samsung-CrossOS/Helpers/AppSettings.cs)
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "❌ Version not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=${VERSION#v}" >> $GITHUB_ENV
|
||||
echo "VERSION_TAG=${VERSION}-beta" >> $GITHUB_ENV
|
||||
|
||||
# ---------------- RELEASE NOTES ----------------
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
LAST_TAG=$(git tag --sort=-creatordate | grep beta | head -n 1)
|
||||
RANGE="${LAST_TAG:+$LAST_TAG..HEAD}"
|
||||
|
||||
{
|
||||
echo "## ✅ What's New & Improved"
|
||||
git log ${RANGE:-HEAD} --pretty=format:"- %s"
|
||||
} > CHANGELOG.md
|
||||
|
||||
- name: Prepare release notes
|
||||
run: |
|
||||
DATE=$(date +'%Y-%m-%d')
|
||||
{
|
||||
echo "## 📦 [${VERSION_TAG}] – ${DATE}"
|
||||
echo ""
|
||||
cat CHANGELOG.md
|
||||
echo ""
|
||||
echo ""
|
||||
echo "| Platform | Status | Notes |"
|
||||
echo "|----------|--------|-------|"
|
||||
echo "| 🍎 macOS (.app + dmg) | ⚠️ Beta | ARM64 + Intel |"
|
||||
echo "| 🍎 macOS (CLI) | ⚠️ Beta | Per-arch tar.gz |"
|
||||
echo "| 🐧 Linux | ⚠️ Beta | tar.gz / .deb |"
|
||||
echo "| 🪟 Windows | ⚠️ Beta | CI-built |"
|
||||
} > RELEASE_NOTES.md
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# ---------------- WINDOWS ----------------
|
||||
- name: Build Windows
|
||||
run: |
|
||||
dotnet publish Jellyfin2Samsung-CrossOS/Jellyfin2Samsung.csproj \
|
||||
-c Release -r win-x64 -p:SelfContained=true \
|
||||
-o publish/win-x64
|
||||
|
||||
mkdir -p dist
|
||||
(cd publish/win-x64 && zip -r "../../dist/${PRODUCT_NAME}-${VERSION_TAG}-win-x64.zip" .)
|
||||
|
||||
# ---------------- LINUX ----------------
|
||||
- name: Build Linux
|
||||
run: |
|
||||
dotnet publish Jellyfin2Samsung-CrossOS/Jellyfin2Samsung.csproj \
|
||||
-c Release -r linux-x64 -p:SelfContained=true \
|
||||
-o publish/linux-x64
|
||||
|
||||
mkdir -p dist
|
||||
tar -czf "dist/${PRODUCT_NAME}-${VERSION_TAG}-linux-x64.tar.gz" \
|
||||
-C publish/linux-x64 .
|
||||
|
||||
# ---------------- LINUX ICON ----------------
|
||||
- name: Linux icon
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y librsvg2-bin
|
||||
rsvg-convert -w 256 -h 256 \
|
||||
jellyfin-tizen-logo.svg -o jellyfin.png
|
||||
|
||||
# ---------------- DEB ----------------
|
||||
- name: Build .deb
|
||||
run: |
|
||||
sudo apt-get install -y dpkg-dev
|
||||
|
||||
PKG=deb
|
||||
mkdir -p $PKG/opt/jellyfin2samsung \
|
||||
$PKG/usr/share/{applications,icons/hicolor/256x256/apps}
|
||||
|
||||
cp -R publish/linux-x64/. $PKG/opt/jellyfin2samsung/
|
||||
chmod +x $PKG/opt/jellyfin2samsung/Jellyfin2Samsung
|
||||
|
||||
# 🔥 quick+dirty: allow writes where your app writes
|
||||
mkdir -p \
|
||||
$PKG/opt/jellyfin2samsung/Logs \
|
||||
$PKG/opt/jellyfin2samsung/Downloads \
|
||||
$PKG/opt/jellyfin2samsung/Assets/TizenSDB \
|
||||
$PKG/opt/jellyfin2samsung/Assets/Certificate
|
||||
chmod -R 777 \
|
||||
$PKG/opt/jellyfin2samsung/Logs \
|
||||
$PKG/opt/jellyfin2samsung/Downloads \
|
||||
$PKG/opt/jellyfin2samsung/Assets/TizenSDB \
|
||||
$PKG/opt/jellyfin2samsung/Assets/Certificate
|
||||
|
||||
cp jellyfin.png $PKG/usr/share/icons/hicolor/256x256/apps/jellyfin2samsung.png
|
||||
|
||||
cat > $PKG/usr/share/applications/jellyfin2samsung.desktop <<EOF
|
||||
[Desktop Entry]
|
||||
Name=Jellyfin2Samsung
|
||||
Exec=/opt/jellyfin2samsung/Jellyfin2Samsung
|
||||
Icon=jellyfin2samsung
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
mkdir -p $PKG/DEBIAN
|
||||
cat > $PKG/DEBIAN/control <<EOF
|
||||
Package: jellyfin2samsung
|
||||
Version: ${VERSION}
|
||||
Architecture: amd64
|
||||
Maintainer: MadeByPatrick
|
||||
Description: Jellyfin2Samsung
|
||||
EOF
|
||||
|
||||
dpkg-deb --build $PKG
|
||||
mv $PKG.deb dist/${PRODUCT_NAME}-${VERSION_TAG}-linux-x64.deb
|
||||
|
||||
# ---------------- RELEASE (UPDATE SAME TAG EVERY COMMIT) ----------------
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ env.VERSION_TAG }}
|
||||
name: ${{ env.VERSION_TAG }}
|
||||
prerelease: true
|
||||
overwrite_files: true
|
||||
files: dist/*
|
||||
body_path: RELEASE_NOTES.md
|
||||
|
||||
# ======================================================
|
||||
# MACOS (PER-ARCH CLI + APP + DMG)
|
||||
# ======================================================
|
||||
macos-app:
|
||||
runs-on: macos-latest
|
||||
needs: beta-release
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# ---------------- ICON ----------------
|
||||
# ---------------- ICON ----------------
|
||||
- name: macOS icon (SVG -> ICNS)
|
||||
run: |
|
||||
# Reliable SVG -> PNG conversion on GitHub macOS runners
|
||||
brew install librsvg
|
||||
|
||||
# If the SVG is already in your repo, keep this as-is.
|
||||
# If not, uncomment the curl line below and remove the local reference.
|
||||
# curl -fsSL \
|
||||
# https://raw.githubusercontent.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer/refs/heads/master/jellyfin-tizen-logo.svg \
|
||||
# -o jellyfin-tizen-logo.svg
|
||||
|
||||
rsvg-convert -w 1024 -h 1024 jellyfin-tizen-logo.svg -o jellyfin.png
|
||||
|
||||
rm -rf icon.iconset
|
||||
mkdir icon.iconset
|
||||
for s in 16 32 128 256 512; do
|
||||
sips -z $s $s jellyfin.png --out icon.iconset/icon_${s}x${s}.png
|
||||
sips -z $((s*2)) $((s*2)) jellyfin.png --out icon.iconset/icon_${s}x${s}@2x.png
|
||||
done
|
||||
|
||||
iconutil -c icns icon.iconset -o jellyfin.icns
|
||||
|
||||
# ---------------- ARM64 ----------------
|
||||
- name: macOS ARM64 publish
|
||||
run: |
|
||||
dotnet publish Jellyfin2Samsung-CrossOS/Jellyfin2Samsung.csproj \
|
||||
-c Release -r osx-arm64 -p:SelfContained=true \
|
||||
-o publish/osx-arm64
|
||||
|
||||
mkdir -p dist
|
||||
tar -czf \
|
||||
dist/${PRODUCT_NAME}-${{ needs.beta-release.outputs.VERSION_TAG }}-macos-arm64.tar.gz \
|
||||
-C publish/osx-arm64 .
|
||||
|
||||
APP=Jellyfin2Samsung-arm64.app/Contents
|
||||
mkdir -p $APP/MacOS $APP/Resources
|
||||
cp -R publish/osx-arm64/* $APP/MacOS/
|
||||
chmod +x $APP/MacOS/Jellyfin2Samsung
|
||||
cp jellyfin.icns $APP/Resources/
|
||||
|
||||
cat > $APP/Info.plist <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleExecutable</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleIdentifier</key><string>org.madebypatrick.jellyfin2samsung</string>
|
||||
<key>CFBundleVersion</key><string>${{ needs.beta-release.outputs.VERSION }}</string>
|
||||
<key>CFBundleShortVersionString</key><string>${{ needs.beta-release.outputs.VERSION }}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
||||
|
||||
<key>CFBundleIconFile</key><string>jellyfin.icns</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
codesign --deep --force --sign - Jellyfin2Samsung-arm64.app
|
||||
|
||||
rm -rf dmg-arm64
|
||||
mkdir -p dmg-arm64
|
||||
cp -R Jellyfin2Samsung-arm64.app dmg-arm64/Jellyfin2Samsung.app
|
||||
ln -s /Applications dmg-arm64/Applications
|
||||
hdiutil create \
|
||||
-volname Jellyfin2Samsung \
|
||||
-srcfolder dmg-arm64 \
|
||||
-format UDZO \
|
||||
dist/${PRODUCT_NAME}-${{ needs.beta-release.outputs.VERSION_TAG }}-macos-arm64.dmg
|
||||
|
||||
# ---------------- INTEL ----------------
|
||||
- name: macOS Intel publish
|
||||
run: |
|
||||
dotnet publish Jellyfin2Samsung-CrossOS/Jellyfin2Samsung.csproj \
|
||||
-c Release -r osx-x64 -p:SelfContained=true \
|
||||
-o publish/osx-x64
|
||||
|
||||
tar -czf \
|
||||
dist/${PRODUCT_NAME}-${{ needs.beta-release.outputs.VERSION_TAG }}-macos-x64.tar.gz \
|
||||
-C publish/osx-x64 .
|
||||
|
||||
APP=Jellyfin2Samsung-x64.app/Contents
|
||||
mkdir -p $APP/MacOS $APP/Resources
|
||||
cp -R publish/osx-x64/* $APP/MacOS/
|
||||
chmod +x $APP/MacOS/Jellyfin2Samsung
|
||||
cp jellyfin.icns $APP/Resources/
|
||||
|
||||
cat > $APP/Info.plist <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleExecutable</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleIdentifier</key><string>org.madebypatrick.jellyfin2samsung</string>
|
||||
<key>CFBundleVersion</key><string>${{ needs.beta-release.outputs.VERSION }}</string>
|
||||
<key>CFBundleShortVersionString</key><string>${{ needs.beta-release.outputs.VERSION }}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
||||
|
||||
<key>CFBundleIconFile</key><string>jellyfin.icns</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
codesign --deep --force --sign - Jellyfin2Samsung-x64.app
|
||||
|
||||
rm -rf dmg-x64
|
||||
mkdir -p dmg-x64
|
||||
cp -R Jellyfin2Samsung-x64.app dmg-x64/Jellyfin2Samsung.app
|
||||
ln -s /Applications dmg-x64/Applications
|
||||
hdiutil create \
|
||||
-volname Jellyfin2Samsung \
|
||||
-srcfolder dmg-x64 \
|
||||
-format UDZO \
|
||||
dist/${PRODUCT_NAME}-${{ needs.beta-release.outputs.VERSION_TAG }}-macos-x64.dmg
|
||||
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.beta-release.outputs.VERSION_TAG }}
|
||||
name: ${{ needs.beta-release.outputs.VERSION_TAG }}
|
||||
prerelease: true
|
||||
overwrite_files: true
|
||||
files: dist/*
|
||||
373
.github/workflows/stable-release.yml
vendored
Normal file
373
.github/workflows/stable-release.yml
vendored
Normal file
@@ -0,0 +1,373 @@
|
||||
name: Stable Release
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
types: [closed]
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: "Git ref to build (branch/tag/SHA). Default: master"
|
||||
required: false
|
||||
default: "master"
|
||||
allow_existing_release:
|
||||
description: "If true, update assets even when the stable tag already exists"
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
PRODUCT_NAME: Jellyfin2Samsung
|
||||
PROJECT_DIR: Jellyfin2Samsung-CrossOS
|
||||
CONFIGURATION: Release
|
||||
|
||||
jobs:
|
||||
# ======================================================
|
||||
# STABLE RELEASE + WINDOWS + LINUX (tar.gz + deb)
|
||||
# ======================================================
|
||||
stable-release:
|
||||
if: |
|
||||
(github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.merged == true &&
|
||||
github.event.pull_request.base.ref == 'master' &&
|
||||
github.event.pull_request.head.ref == 'beta') ||
|
||||
(github.event_name == 'workflow_dispatch')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
VERSION: ${{ steps.version.outputs.VERSION }}
|
||||
VERSION_TAG: ${{ steps.version.outputs.VERSION_TAG }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
|
||||
|
||||
# ---------------- VERSION (NO -beta allowed) ----------------
|
||||
- name: Validate AppVersion (must be stable)
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
FILE="${PROJECT_DIR}/Helpers/AppSettings.cs"
|
||||
|
||||
VERSION_LINE=$(grep 'AppVersion' "$FILE" || true)
|
||||
|
||||
if echo "$VERSION_LINE" | grep -q '\-beta"'; then
|
||||
echo "❌ AppVersion still contains '-beta'"
|
||||
echo " Stable releases must use a clean version like v2.0.0.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$(echo "$VERSION_LINE" | grep -oE 'v[0-9]+(\.[0-9]+){1,3}')
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "❌ Failed to extract stable version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=${VERSION#v}" >> "$GITHUB_ENV"
|
||||
echo "VERSION_TAG=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
echo "VERSION=${VERSION#v}" >> "$GITHUB_OUTPUT"
|
||||
echo "VERSION_TAG=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# --------------------------------------------------
|
||||
# Guard (log-only): create or update assets
|
||||
# --------------------------------------------------
|
||||
- name: Stable release exists? (allow updating assets)
|
||||
shell: bash
|
||||
run: |
|
||||
if git tag --list | grep -qx "$VERSION_TAG"; then
|
||||
echo "ℹ️ Stable release $VERSION_TAG already exists — will update assets"
|
||||
else
|
||||
echo "ℹ️ Stable release $VERSION_TAG does not exist — will create it"
|
||||
fi
|
||||
|
||||
# --------------------------------------------------
|
||||
# Optional: in manual runs, allow user to DISABLE updating an existing release
|
||||
# --------------------------------------------------
|
||||
- name: Stop if release exists and manual updating is disabled
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.allow_existing_release == false
|
||||
shell: bash
|
||||
run: |
|
||||
if git tag --list | grep -qx "$VERSION_TAG"; then
|
||||
echo "ℹ️ Release $VERSION_TAG already exists and allow_existing_release=false — exiting"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---------------- CHANGELOG (from last stable tag) ----------------
|
||||
- name: Generate changelog
|
||||
shell: bash
|
||||
run: |
|
||||
LAST_STABLE=$(git tag \
|
||||
| grep -E '^v[0-9]+(\.[0-9]+){1,3}$' \
|
||||
| sort -V \
|
||||
| tail -n 1)
|
||||
|
||||
if [ -z "$LAST_STABLE" ]; then
|
||||
RANGE="HEAD"
|
||||
else
|
||||
RANGE="$LAST_STABLE..HEAD"
|
||||
fi
|
||||
|
||||
{
|
||||
echo "## ✅ What's New & Improved"
|
||||
git log $RANGE --pretty=format:"- %s"
|
||||
} > CHANGELOG.md
|
||||
|
||||
# ---------------- RELEASE NOTES ----------------
|
||||
- name: Prepare release notes
|
||||
shell: bash
|
||||
run: |
|
||||
DATE=$(date +'%Y-%m-%d')
|
||||
{
|
||||
echo "## 📦 [${VERSION_TAG}] – ${DATE}"
|
||||
echo ""
|
||||
cat CHANGELOG.md
|
||||
echo ""
|
||||
echo ""
|
||||
echo "| Platform | Status | Notes |"
|
||||
echo "|----------|--------|-------|"
|
||||
echo "| 🍎 macOS (.app + dmg) | ✅ Stable | ARM64 + Intel |"
|
||||
echo "| 🍎 macOS (CLI) | ✅ Stable | Per-arch tar.gz |"
|
||||
echo "| 🐧 Linux | ✅ Stable | tar.gz / .deb |"
|
||||
echo "| 🪟 Windows | ✅ Stable | CI-built |"
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
echo "### 🛡️ Security Notice"
|
||||
echo "Antivirus warnings may occur and are likely **false positives**."
|
||||
} > RELEASE_NOTES.md
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# ---------------- WINDOWS ----------------
|
||||
- name: Build Windows
|
||||
run: |
|
||||
dotnet publish ${PROJECT_DIR}/Jellyfin2Samsung.csproj \
|
||||
-c $CONFIGURATION -r win-x64 -p:SelfContained=true \
|
||||
-o publish/win-x64
|
||||
|
||||
mkdir -p dist
|
||||
(cd publish/win-x64 && zip -r "../../dist/${PRODUCT_NAME}-${VERSION_TAG}-win-x64.zip" .)
|
||||
|
||||
# ---------------- LINUX ----------------
|
||||
- name: Build Linux
|
||||
run: |
|
||||
dotnet publish ${PROJECT_DIR}/Jellyfin2Samsung.csproj \
|
||||
-c $CONFIGURATION -r linux-x64 -p:SelfContained=true \
|
||||
-o publish/linux-x64
|
||||
|
||||
tar -czf "dist/${PRODUCT_NAME}-${VERSION_TAG}-linux-x64.tar.gz" \
|
||||
-C publish/linux-x64 .
|
||||
|
||||
# ---------------- LINUX ICON ----------------
|
||||
- name: Linux icon
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y librsvg2-bin
|
||||
rsvg-convert -w 256 -h 256 \
|
||||
jellyfin-tizen-logo.svg -o jellyfin.png
|
||||
|
||||
# ---------------- DEB ----------------
|
||||
- name: Build .deb
|
||||
run: |
|
||||
sudo apt-get install -y dpkg-dev
|
||||
|
||||
PKG=deb
|
||||
mkdir -p $PKG/opt/jellyfin2samsung \
|
||||
$PKG/usr/share/{applications,icons/hicolor/256x256/apps}
|
||||
|
||||
cp -R publish/linux-x64/. $PKG/opt/jellyfin2samsung/
|
||||
chmod +x $PKG/opt/jellyfin2samsung/Jellyfin2Samsung
|
||||
|
||||
# 🔥 quick+dirty: allow writes where your app writes
|
||||
mkdir -p \
|
||||
$PKG/opt/jellyfin2samsung/Logs \
|
||||
$PKG/opt/jellyfin2samsung/Downloads \
|
||||
$PKG/opt/jellyfin2samsung/Assets/TizenSDB \
|
||||
$PKG/opt/jellyfin2samsung/Assets/Certificate
|
||||
chmod -R 777 \
|
||||
$PKG/opt/jellyfin2samsung/Logs \
|
||||
$PKG/opt/jellyfin2samsung/Downloads \
|
||||
$PKG/opt/jellyfin2samsung/Assets/TizenSDB \
|
||||
$PKG/opt/jellyfin2samsung/Assets/Certificate
|
||||
|
||||
cp jellyfin.png $PKG/usr/share/icons/hicolor/256x256/apps/jellyfin2samsung.png
|
||||
|
||||
cat > $PKG/usr/share/applications/jellyfin2samsung.desktop <<EOF
|
||||
[Desktop Entry]
|
||||
Name=Jellyfin2Samsung
|
||||
Exec=/opt/jellyfin2samsung/Jellyfin2Samsung
|
||||
Icon=jellyfin2samsung
|
||||
Type=Application
|
||||
Categories=Utility;
|
||||
EOF
|
||||
|
||||
mkdir -p $PKG/DEBIAN
|
||||
cat > $PKG/DEBIAN/control <<EOF
|
||||
Package: jellyfin2samsung
|
||||
Version: ${VERSION}
|
||||
Architecture: amd64
|
||||
Maintainer: MadeByPatrick
|
||||
Description: Jellyfin2Samsung
|
||||
EOF
|
||||
|
||||
dpkg-deb --build $PKG
|
||||
mv $PKG.deb dist/${PRODUCT_NAME}-${VERSION_TAG}-linux-x64.deb
|
||||
|
||||
# ---------------- CREATE/UPDATE STABLE RELEASE ----------------
|
||||
- name: Create/Update GitHub Release (stable)
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ env.VERSION_TAG }}
|
||||
name: ${{ env.VERSION_TAG }}
|
||||
prerelease: false
|
||||
overwrite_files: true
|
||||
files: dist/*
|
||||
body_path: RELEASE_NOTES.md
|
||||
|
||||
# ======================================================
|
||||
# MACOS (PER-ARCH CLI + APP + DMG)
|
||||
# ======================================================
|
||||
macos-app:
|
||||
runs-on: macos-latest
|
||||
needs: stable-release
|
||||
if: needs.stable-release.result == 'success'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }}
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
# ---------------- ICON ----------------
|
||||
- name: macOS icon
|
||||
run: |
|
||||
sips -s format png jellyfin-tizen-logo.svg --out jellyfin.png
|
||||
sips -z 1024 1024 jellyfin.png --out jellyfin.png
|
||||
|
||||
mkdir icon.iconset
|
||||
for s in 16 32 128 256 512; do
|
||||
sips -z $s $s jellyfin.png --out icon.iconset/icon_${s}x${s}.png
|
||||
sips -z $((s*2)) $((s*2)) jellyfin.png --out icon.iconset/icon_${s}x${s}@2x.png
|
||||
done
|
||||
iconutil -c icns icon.iconset -o jellyfin.icns
|
||||
|
||||
# ---------------- ARM64 ----------------
|
||||
- name: macOS ARM64 publish
|
||||
run: |
|
||||
dotnet publish ${PROJECT_DIR}/Jellyfin2Samsung.csproj \
|
||||
-c $CONFIGURATION -r osx-arm64 -p:SelfContained=true \
|
||||
-o publish/osx-arm64
|
||||
|
||||
mkdir -p dist
|
||||
tar -czf \
|
||||
dist/${PRODUCT_NAME}-${{ needs.stable-release.outputs.VERSION_TAG }}-macos-arm64.tar.gz \
|
||||
-C publish/osx-arm64 .
|
||||
|
||||
APP=Jellyfin2Samsung-arm64.app/Contents
|
||||
mkdir -p $APP/MacOS $APP/Resources
|
||||
cp -R publish/osx-arm64/* $APP/MacOS/
|
||||
chmod +x $APP/MacOS/Jellyfin2Samsung
|
||||
cp jellyfin.icns $APP/Resources/
|
||||
|
||||
cat > $APP/Info.plist <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleExecutable</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleIdentifier</key><string>org.madebypatrick.jellyfin2samsung</string>
|
||||
<key>CFBundleVersion</key><string>${{ needs.stable-release.outputs.VERSION }}</string>
|
||||
<key>CFBundleShortVersionString</key><string>${{ needs.stable-release.outputs.VERSION }}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
||||
|
||||
<key>CFBundleIconFile</key><string>jellyfin.icns</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
codesign --deep --force --sign - Jellyfin2Samsung-arm64.app
|
||||
|
||||
rm -rf dmg-arm64
|
||||
mkdir -p dmg-arm64
|
||||
cp -R Jellyfin2Samsung-arm64.app dmg-arm64/Jellyfin2Samsung.app
|
||||
ln -s /Applications dmg-arm64/Applications
|
||||
hdiutil create \
|
||||
-volname Jellyfin2Samsung \
|
||||
-srcfolder dmg-arm64 \
|
||||
-format UDZO \
|
||||
dist/${PRODUCT_NAME}-${{ needs.stable-release.outputs.VERSION_TAG }}-macos-arm64.dmg
|
||||
|
||||
# ---------------- INTEL ----------------
|
||||
- name: macOS Intel publish
|
||||
run: |
|
||||
dotnet publish ${PROJECT_DIR}/Jellyfin2Samsung.csproj \
|
||||
-c $CONFIGURATION -r osx-x64 -p:SelfContained=true \
|
||||
-o publish/osx-x64
|
||||
|
||||
tar -czf \
|
||||
dist/${PRODUCT_NAME}-${{ needs.stable-release.outputs.VERSION_TAG }}-macos-x64.tar.gz \
|
||||
-C publish/osx-x64 .
|
||||
|
||||
APP=Jellyfin2Samsung-x64.app/Contents
|
||||
mkdir -p $APP/MacOS $APP/Resources
|
||||
cp -R publish/osx-x64/* $APP/MacOS/
|
||||
chmod +x $APP/MacOS/Jellyfin2Samsung
|
||||
cp jellyfin.icns $APP/Resources/
|
||||
|
||||
cat > $APP/Info.plist <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
||||
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleExecutable</key><string>Jellyfin2Samsung</string>
|
||||
<key>CFBundleIdentifier</key><string>org.madebypatrick.jellyfin2samsung</string>
|
||||
<key>CFBundleVersion</key><string>${{ needs.stable-release.outputs.VERSION }}</string>
|
||||
<key>CFBundleShortVersionString</key><string>${{ needs.stable-release.outputs.VERSION }}</string>
|
||||
<key>CFBundlePackageType</key><string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key><string>11.0</string>
|
||||
|
||||
<key>CFBundleIconFile</key><string>jellyfin.icns</string>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
||||
codesign --deep --force --sign - Jellyfin2Samsung-x64.app
|
||||
|
||||
rm -rf dmg-x64
|
||||
mkdir -p dmg-x64
|
||||
cp -R Jellyfin2Samsung-x64.app dmg-x64/Jellyfin2Samsung.app
|
||||
ln -s /Applications dmg-x64/Applications
|
||||
hdiutil create \
|
||||
-volname Jellyfin2Samsung \
|
||||
-srcfolder dmg-x64 \
|
||||
-format UDZO \
|
||||
dist/${PRODUCT_NAME}-${{ needs.stable-release.outputs.VERSION_TAG }}-macos-x64.dmg
|
||||
|
||||
# ---------------- UPLOAD MACOS ASSETS TO SAME STABLE RELEASE ----------------
|
||||
- name: Upload macOS assets to GitHub Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.stable-release.outputs.VERSION_TAG }}
|
||||
name: ${{ needs.stable-release.outputs.VERSION_TAG }}
|
||||
prerelease: false
|
||||
overwrite_files: true
|
||||
files: dist/*
|
||||
11
.github/workflows/update-version-table.yml
vendored
11
.github/workflows/update-version-table.yml
vendored
@@ -3,8 +3,18 @@ on:
|
||||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["Beta Pre-Release"]
|
||||
types:
|
||||
- completed
|
||||
jobs:
|
||||
update-readme:
|
||||
if: |
|
||||
github.event_name != 'workflow_run' ||
|
||||
(
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
github.event.workflow_run.head_branch == 'beta'
|
||||
)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -12,6 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
ref: beta
|
||||
- name: Fetch latest releases
|
||||
id: releases
|
||||
uses: actions/github-script@v7
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -178,7 +178,6 @@ DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
esbuild/
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
|
||||
@@ -72,11 +72,17 @@ namespace Jellyfin2Samsung
|
||||
services.AddSingleton<ITizenCertificateService, TizenCertificateService>();
|
||||
services.AddSingleton<ITizenInstallerService, TizenInstallerService>();
|
||||
services.AddSingleton<IThemeService, ThemeService>();
|
||||
services.AddSingleton<IUpdaterService, UpdaterService>();
|
||||
services.AddSingleton<IUpdateDialogService, UpdateDialogService>();
|
||||
|
||||
// HttpClient (configured ONCE)
|
||||
// HttpClient (configured ONCE, with GitHub auth if available)
|
||||
services.AddSingleton(sp =>
|
||||
{
|
||||
var client = new HttpClient
|
||||
var appSettings = sp.GetRequiredService<AppSettings>();
|
||||
var token = Helpers.Core.GitHubAuthHandler.ResolveToken(appSettings);
|
||||
var handler = new Helpers.Core.GitHubAuthHandler(token);
|
||||
|
||||
var client = new HttpClient(handler)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(30)
|
||||
};
|
||||
@@ -84,7 +90,6 @@ namespace Jellyfin2Samsung
|
||||
client.DefaultRequestHeaders.UserAgent.ParseAdd("SamsungJellyfinInstaller/1.1");
|
||||
client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json");
|
||||
|
||||
|
||||
return client;
|
||||
});
|
||||
|
||||
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/af.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/af.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ar.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ar.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ca.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ca.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/cs.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/cs.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Tving Samsung-certifikat",
|
||||
"DeveloperModeRequired": "Samsung TV er ikke i developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP matcher ikke denne enheds lokale IP, vil du fortsætte?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Omvendt IP (for arabisk/hebraisk)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Tema",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "Server-URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Avancerede indstillinger",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Stil",
|
||||
"lblCssSettings": "Brugerdefinerede CSS-indstillinger",
|
||||
"lblCustomCss": "Brugerdefineret CSS-kode",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Programindstillinger",
|
||||
"lblRefreshUsers": "Opdater brugere",
|
||||
"lblUserSelectionHint": "Valgte brugere konfigureres under installationen",
|
||||
"lblYtDlpServer": "Lokal streamingserver",
|
||||
"lblValidateStream": "Valider server"
|
||||
"subnetMismatch": "Enhederne er i forskellige undernetværk (netværk)",
|
||||
"UpdateAvailable": "Opdatering tilgængelig",
|
||||
"UpdateCurrentVersion": "Nuværende version:",
|
||||
"UpdateLatestVersion": "Nyeste version:",
|
||||
"UpdateReleaseNotes": "Udgivelsesnoter:",
|
||||
"UpdateManual": "Åbn udgivelser",
|
||||
"UpdateAutomatic": "Opdater nu",
|
||||
"UpdateSkip": "Spring denne version over",
|
||||
"UpdateDownloading": "Downloader opdatering...",
|
||||
"UpdateApplying": "Anvender opdatering",
|
||||
"UpdateApplyingMessage": "Applikationen genstarter for at fuldføre opdateringen.",
|
||||
"UpdateError": "Opdatering mislykkedes",
|
||||
"UpdateCheckFailed": "Kunne ikke tjekke for opdateringer",
|
||||
"IncompatiblePackage": "Pakkeversionen er ikke kompatibel med enheden.",
|
||||
"IncompatiblePackageDetailed": "Pakkeversion {0} er ikke kompatibel med Tizen OS {1}",
|
||||
"lblMdnsWarning": "Advarsel: Du bruger et mDNS-hostnavn (.local). Samsung TV'er kan ikke pålideligt opløse .local-adresser, hvilket kan få serveren til at vises som \"undefined\" på dit TV — især efter netværksafbrydelser. Brug en direkte IP-adresse (f.eks. 192.168.1.100) i stedet for en stabil forbindelse."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Samsung-Zertifikat erzwingen",
|
||||
"DeveloperModeRequired": "Samsung TV ist nicht im Entwicklermodus...",
|
||||
"DeveloperIPMismatch": "Samsung Developer Mode IP stimmt nicht mit der lokalen IP dieses Geräts überein, möchten Sie fortfahren?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (für Arabisch/Hebräisch)",
|
||||
"ServerIP": "Server-IP",
|
||||
"Theme": "Theme",
|
||||
@@ -91,7 +92,7 @@
|
||||
"lblEnableDevLogs": "TV-Debugging aktivieren",
|
||||
"lblOpenDebugWindow": "TV-Log öffnen",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stoppen",
|
||||
"lblStopLog": "Stopp",
|
||||
"lblSaveLogs": "Speichern",
|
||||
"lblLaunchOnInstall": "Nach der Installation öffnen",
|
||||
"insufficientSpace": "Auf Ihrem Gerät ist nicht genügend Speicherplatz vorhanden. Bitte entfernen Sie einige Apps und versuchen Sie es erneut ...",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "Server-URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Erweiterte Einstellungen",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Benutzerdefinierte CSS-Einstellungen",
|
||||
"lblCustomCss": "Benutzerdefinierter CSS-Code",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Anwendungseinstellungen",
|
||||
"lblRefreshUsers": "Benutzer aktualisieren",
|
||||
"lblUserSelectionHint": "Ausgewählte Benutzer werden bei der Installation konfiguriert",
|
||||
"lblYtDlpServer": "Lokaler Streaming-Server",
|
||||
"lblValidateStream": "Server validieren"
|
||||
"subnetMismatch": "Die Geräte befinden sich in unterschiedlichen Subnetzen (Netzwerken).",
|
||||
"UpdateAvailable": "Update verfügbar",
|
||||
"UpdateCurrentVersion": "Aktuelle Version:",
|
||||
"UpdateLatestVersion": "Neueste Version:",
|
||||
"UpdateReleaseNotes": "Versionshinweise:",
|
||||
"UpdateManual": "Releases öffnen",
|
||||
"UpdateAutomatic": "Jetzt aktualisieren",
|
||||
"UpdateSkip": "Diese Version überspringen",
|
||||
"UpdateDownloading": "Update wird heruntergeladen...",
|
||||
"UpdateApplying": "Update wird angewendet",
|
||||
"UpdateApplyingMessage": "Die Anwendung wird neu gestartet, um das Update abzuschließen.",
|
||||
"UpdateError": "Update fehlgeschlagen",
|
||||
"UpdateCheckFailed": "Update-Überprüfung fehlgeschlagen",
|
||||
"IncompatiblePackage": "Paketversion ist nicht mit dem Gerät kompatibel.",
|
||||
"IncompatiblePackageDetailed": "Paketversion {0} nicht kompatibel mit Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warnung: Du verwendest einen mDNS-Hostnamen (.local). Samsung TVs können .local-Adressen nicht zuverlässig auflösen, was dazu führen kann, dass der Server auf deinem TV als \"undefined\" angezeigt wird — besonders nach Netzwerkunterbrechungen. Verwende stattdessen eine direkte IP-Adresse (z.B. 192.168.1.100) für eine stabile Verbindung."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/el.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/el.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"lblYtDlpServer": "Local streaming server",
|
||||
"lblValidateStream": "Validate Server"
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/es.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/es.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/fi.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/fi.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Certificat Samsung obligatoire",
|
||||
"DeveloperModeRequired": "Le téléviseur Samsung n'est pas en mode développeur...",
|
||||
"DeveloperIPMismatch": "L'adresse IP du mode développeur Samsung ne correspond pas à l'adresse IP locale de cet appareil. Souhaitez-vous continuer ?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Adresse IP inversée (pour l'arabe/l'hébreu)",
|
||||
"ServerIP": "IP du serveur",
|
||||
"Theme": "Thème",
|
||||
@@ -91,7 +92,7 @@
|
||||
"lblEnableDevLogs": "Activer le débogage TV",
|
||||
"lblOpenDebugWindow": "Ouvrir le journal TV",
|
||||
"lblStartLog": "Commencer",
|
||||
"lblStopLog": "Arrêt",
|
||||
"lblStopLog": "Arrêter",
|
||||
"lblSaveLogs": "Sauvegarder",
|
||||
"lblLaunchOnInstall": "Ouvrir après l'installation",
|
||||
"insufficientSpace": "Votre appareil ne dispose pas d'espace suffisant, veuillez supprimer certaines applications et réessayer...",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "URL du serveur",
|
||||
"lblConnectionStatus": "Statut serveur",
|
||||
"lblAdvancedSettings": "Paramètres avancés",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "Style CSS",
|
||||
"lblCssSettings": "Paramètres CSS personnalisés",
|
||||
"lblCustomCss": "Code CSS personnalisé",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Paramètres de l'application",
|
||||
"lblRefreshUsers": "Actualiser utilisateurs",
|
||||
"lblUserSelectionHint": "Les utilisateurs sélectionnés seront configurés lors de l'installation",
|
||||
"lblYtDlpServer": "Serveur de streaming local",
|
||||
"lblValidateStream": "Valider le serveur"
|
||||
"subnetMismatch": "Les appareils se trouvent dans différents sous-réseaux (réseau).",
|
||||
"UpdateAvailable": "Mise à jour disponible",
|
||||
"UpdateCurrentVersion": "Version actuelle:",
|
||||
"UpdateLatestVersion": "Dernière version:",
|
||||
"UpdateReleaseNotes": "Notes de version:",
|
||||
"UpdateManual": "Ouvrir les versions",
|
||||
"UpdateAutomatic": "Mettre à jour",
|
||||
"UpdateSkip": "Ignorer cette version",
|
||||
"UpdateDownloading": "Téléchargement...",
|
||||
"UpdateApplying": "Application de la mise à jour",
|
||||
"UpdateApplyingMessage": "L'application va redémarrer pour terminer la mise à jour.",
|
||||
"UpdateError": "Échec de la mise à jour",
|
||||
"UpdateCheckFailed": "Échec de la vérification des mises à jour",
|
||||
"IncompatiblePackage": "Version du package non compatible avec l'appareil.",
|
||||
"IncompatiblePackageDetailed": "La version du package {0} n'est pas compatible avec Tizen OS {1}",
|
||||
"lblMdnsWarning": "Attention : Vous utilisez un nom d'hôte mDNS (.local). Les TV Samsung ne peuvent pas résoudre de manière fiable les adresses .local, ce qui peut faire apparaître le serveur comme \"undefined\" sur votre TV — surtout après des interruptions réseau. Utilisez plutôt une adresse IP directe (ex. 192.168.1.100) pour une connexion stable."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/he.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/he.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/hu.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/hu.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/it.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/it.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ja.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ja.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ko.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ko.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Forceer Samsung-certificaat",
|
||||
"DeveloperModeRequired": "Samsung TV staat niet in ontwikkelaarsmodus...",
|
||||
"DeveloperIPMismatch": "Samsung ontwikkelaarsmodus IP komt niet overeen met het lokale IP van dit apparaat, wilt u doorgaan?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Omgekeerd IP-adres (voor Arabisch/Hebreeuws)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Thema",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "Server-URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Geavanceerde instellingen",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Stijl",
|
||||
"lblCssSettings": "Aangepaste CSS-instellingen",
|
||||
"lblCustomCss": "Aangepaste CSS-code",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Toepassingsinstellingen",
|
||||
"lblRefreshUsers": "Gebruikers vernieuwen",
|
||||
"lblUserSelectionHint": "Geselecteerde gebruikers worden geconfigureerd tijdens de installatie",
|
||||
"lblYtDlpServer": "Lokale streaming server",
|
||||
"lblValidateStream": "Valideer de server"
|
||||
"subnetMismatch": "De apparaten bevinden zich in verschillende subnetten (netwerken).",
|
||||
"UpdateAvailable": "Update beschikbaar",
|
||||
"UpdateCurrentVersion": "Huidige versie:",
|
||||
"UpdateLatestVersion": "Nieuwste versie:",
|
||||
"UpdateReleaseNotes": "Release-opmerkingen:",
|
||||
"UpdateManual": "Releases openen",
|
||||
"UpdateAutomatic": "Nu bijwerken",
|
||||
"UpdateSkip": "Deze versie overslaan",
|
||||
"UpdateDownloading": "Update downloaden...",
|
||||
"UpdateApplying": "Update toepassen",
|
||||
"UpdateApplyingMessage": "De applicatie wordt opnieuw gestart om de update te voltooien.",
|
||||
"UpdateError": "Update mislukt",
|
||||
"UpdateCheckFailed": "Controle op updates mislukt",
|
||||
"IncompatiblePackage": "Pakketversie niet compatibel met apparaat.",
|
||||
"IncompatiblePackageDetailed": "Pakketversie {0} niet compatibel met Tizen OS {1}",
|
||||
"lblMdnsWarning": "Waarschuwing: Je gebruikt een mDNS-hostnaam (.local). Samsung TV's kunnen .local-adressen niet betrouwbaar omzetten, waardoor de server als \"undefined\" op je TV kan verschijnen — vooral na netwerkonderbrekingen. Gebruik in plaats daarvan een direct IP-adres (bijv. 192.168.1.100) voor een stabiele verbinding."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/no.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/no.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/pl.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/pl.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/pt.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/pt.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Aviso: Você está usando um nome de host mDNS (.local). TVs Samsung não conseguem resolver endereços .local de forma confiável, o que pode fazer o servidor aparecer como \"undefined\" na sua TV — especialmente após interrupções de rede. Use um endereço IP direto (ex. 192.168.1.100) para uma conexão estável."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ro.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ro.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ru.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/ru.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/sr.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/sr.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/sv.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/sv.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
"lblForceLogin": "Samsung sertifikasını zorla",
|
||||
"DeveloperModeRequired": "Samsung TV geliştirici modunda değil...",
|
||||
"DeveloperIPMismatch": "Samsung Geliştirici modu IP'si bu cihazın yerel IP'siyle eşleşmiyor, devam etmek istiyor musunuz?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Ters IP (Arapça/İbranice için)",
|
||||
"ServerIP": "Sunucu IP",
|
||||
"Theme": "Tema",
|
||||
@@ -113,6 +114,8 @@
|
||||
"lblServerUrl": "Sunucu URL",
|
||||
"lblConnectionStatus": "Sunucu Durumu",
|
||||
"lblAdvancedSettings": "Gelişmiş Ayarlar",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Stili",
|
||||
"lblCssSettings": "Özel CSS Ayarları",
|
||||
"lblCustomCss": "Özel CSS Kodu",
|
||||
@@ -133,6 +136,20 @@
|
||||
"lblMainSettings": "Uygulama Ayarları",
|
||||
"lblRefreshUsers": "Kullanıcıları Yenile",
|
||||
"lblUserSelectionHint": "Seçilen kullanıcılar kurulum sırasında yapılandırılacak",
|
||||
"lblYtDlpServer": "Yerel yayın sunucusu",
|
||||
"lblValidateStream": "Sunucuyu Doğrula"
|
||||
"subnetMismatch": "Cihazlar farklı alt ağlarda (ağda) bulunmaktadır.",
|
||||
"UpdateAvailable": "Güncelleme mevcut",
|
||||
"UpdateCurrentVersion": "Mevcut sürüm:",
|
||||
"UpdateLatestVersion": "Son sürüm:",
|
||||
"UpdateReleaseNotes": "Sürüm notları:",
|
||||
"UpdateManual": "Sürümleri aç",
|
||||
"UpdateAutomatic": "Şimdi güncelle",
|
||||
"UpdateSkip": "Bu sürümü atla",
|
||||
"UpdateDownloading": "Güncelleme indiriliyor...",
|
||||
"UpdateApplying": "Güncelleme uygulanıyor",
|
||||
"UpdateApplyingMessage": "Güncellemeyi tamamlamak için uygulama yeniden başlatılacak.",
|
||||
"UpdateError": "Güncelleme başarısız",
|
||||
"UpdateCheckFailed": "Güncelleme kontrolü başarısız",
|
||||
"IncompatiblePackage": "Paket sürümü cihazla uyumlu değil.",
|
||||
"IncompatiblePackageDetailed": "Paket sürümü {0}, Tizen OS {1} ile uyumlu değil",
|
||||
"lblMdnsWarning": "Uyarı: Bir mDNS ana bilgisayar adı (.local) kullanıyorsunuz. Samsung TV'ler .local adreslerini güvenilir şekilde çözümleyemez, bu da sunucunun TV'nizde \"undefined\" olarak görünmesine neden olabilir — özellikle ağ kesintilerinden sonra. Kararlı bir bağlantı için bunun yerine doğrudan bir IP adresi (ör. 192.168.1.100) kullanın."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/uk.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/uk.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/vi.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/vi.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
155
Jellyfin2Samsung-CrossOS/Assets/Localization/zh.json
Normal file
155
Jellyfin2Samsung-CrossOS/Assets/Localization/zh.json
Normal file
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"DownloadAndInstall": "Download & Install",
|
||||
"InstallationFailed": "Installation failed",
|
||||
"InstallationSuccessfulOn": "{0} has been successfully installed!",
|
||||
"DownloadFailed": "Download failed:",
|
||||
"FailedLoadingReleases": "Failed to load releases:",
|
||||
"InstallTizenSdb": "The Tizen SDB is required but not found. Retrying to download.",
|
||||
"FailedTizenSdb": "Tizen SDB is required but could not be found and downloaded.",
|
||||
"ConnectingToDevice": "Connecting to device...",
|
||||
"TvNameNotFound": "TV Name could not be found...",
|
||||
"TvDuidNotFound": "TV duid could not be found...",
|
||||
"CreatingCertificateProfile": "Creating new certificate profile...",
|
||||
"InstallationSuccessful": "Installation successful",
|
||||
"InstallingPackage": "Installing package on device...",
|
||||
"Output": "Output",
|
||||
"LoadingReleases": "Loading releases...",
|
||||
"ScanningNetwork": "Scanning network for Samsung TV...",
|
||||
"Ready": "Ready for use...",
|
||||
"NoDevicesFound": "No devices found",
|
||||
"GenPassword": "Generating random password...",
|
||||
"GenKeyPair": "Generating keypair",
|
||||
"CreateAuthorCsr": "Creating Author CSR...",
|
||||
"CreateDistributorCSR": "Generating Distributor CSR with DUID...",
|
||||
"PostAuthorCSR": "Posting to Samsung author endpoint...",
|
||||
"PostDistributorCSR": "Posting to Samsung distributor...",
|
||||
"ExportPfxCertificates": "Exporting PFX certificates...",
|
||||
"SelectWGT": "Select WGT and/or TPK file(s)",
|
||||
"lblRelease": "Release",
|
||||
"lblVersion": "Version",
|
||||
"lblSelectTv": "Select TV",
|
||||
"lblLanguage": "Language",
|
||||
"lblCustomWgt": "WGT File",
|
||||
"lblSettings": "Application settings",
|
||||
"lblDeletePrevious": "Remove old Jellyfin",
|
||||
"IpWindowTitle": "Enter TV IP",
|
||||
"IpWindowDescription": "Please enter the device's IP address:",
|
||||
"InvalidDeviceIp": "Invalid device IP or device not found.",
|
||||
"IpNotListed": "My IP is not listed...",
|
||||
"lblCertifcate": "Certificate",
|
||||
"lblOther": "Other",
|
||||
"DownloadCompleted": "Download completed...",
|
||||
"NoPackageToInstall": "No package to install",
|
||||
"NoDeviceSelected": "No device selected",
|
||||
"UsingCustomWGT": "Using custom WGT file",
|
||||
"CheckingTizenSdb": "Checking Tizen SDB...",
|
||||
"InitializationFailed": "Initialization failed...",
|
||||
"lblForceLogin": "Force Samsung certificate",
|
||||
"DeveloperModeRequired": "Samsung TV is not in developer mode...",
|
||||
"DeveloperIPMismatch": "Samsung Developer mode IP doesn't match this devices local IP, do you wish to continue?",
|
||||
"DeveloperIPReversed": "IP is in reversed order on the TV, please re-enable developer mode and type your local IP in reversed order. (ex: 192.168.1.2 → 2.1.168.192)",
|
||||
"lblRTL": "Reverse IP (for Arabic/Hebrew)",
|
||||
"ServerIP": "Server IP",
|
||||
"Theme": "Theme",
|
||||
"lblEnableBackdrops": "Enable backdrops",
|
||||
"lblEnableThemeSongs": "Enable theme songs",
|
||||
"lblEnableThemeVideos": "Enable theme videos",
|
||||
"lblBackdropScreensaver": "Backdrop screensaver",
|
||||
"lblDetailsBanner": "Details banner",
|
||||
"lblCinemaMode": "Cinema mode",
|
||||
"lblNextUpEnabled": "Next up enabled",
|
||||
"lblEnableExternalVideoPlayers": "Enable external video players",
|
||||
"lblSkipIntros": "Skip intros",
|
||||
"lblSubtitleMode": "Subtitle mode",
|
||||
"lblAudioLanguagePreference": "Audio language preference",
|
||||
"lblSubtitleLanguagePreference": "Subtitle language preference",
|
||||
"lblJellyfinConfig": "Jellyfin settings",
|
||||
"AudioLanguage": "e.g. en | da | nl",
|
||||
"lblServerSettings": "Server settings",
|
||||
"lblBrowserSettings": "Browser settings",
|
||||
"lblSelectUsers": "Select user(s) to update",
|
||||
"lblValidation": "🍺 Buy me a beer",
|
||||
"btn_Close": "Close",
|
||||
"lbleasyRight": "That was easy, right?",
|
||||
"NoDevicesFoundRetry": "No devices found, retry with virtual NIC?",
|
||||
"RetySearchMsg": "No TV's found, want to search again with virtual network cards included in the search?",
|
||||
"keyYes": "Yes",
|
||||
"keyNo": "No",
|
||||
"keyContinue": "Continue",
|
||||
"keyStop": "Stop",
|
||||
"keyConfirm": "Confirm",
|
||||
"packageAndSign": "Packaging and signing...",
|
||||
"alreadyInstalled": "Jellyfin couldn't not be installed because its already installed, please remove first and try again...",
|
||||
"diagnoseTv": "Diagnose TV capabilities",
|
||||
"modiyConfigRequired": "Jellyfin app needs a new app id!",
|
||||
"deleteExistingVersion": "Deleting an existing version...",
|
||||
"deleteExistingFailed": "Deleting the existing version failed. Please delete it manually...",
|
||||
"deleteExistingSuccess": "Existing version successfully deleted...",
|
||||
"deleteExistingNotAllowed": "Deletion not allowed. Enable the setting to allow the tool to remove the existing app...",
|
||||
"lblLocalIP": "Local IP:",
|
||||
"lblTryOverwrite": "Overwrite existing version",
|
||||
"lblUseServerScripts": "Download Jellyfin plugins",
|
||||
"lblEnableDevLogs": "Enable TV Debug",
|
||||
"lblOpenDebugWindow": "Open TV Log",
|
||||
"lblStartLog": "Start",
|
||||
"lblStopLog": "Stop",
|
||||
"lblSaveLogs": "Save",
|
||||
"lblLaunchOnInstall": "Open after installation",
|
||||
"insufficientSpace": "Your device has insufficient space, please remove some apps and try again...",
|
||||
"lblKeepWGTFile": "Preserve WGT file",
|
||||
"AuthorMismatch": "Author Certificate mismatch, please re-sign the package with correct certificate.",
|
||||
"FixYouTube153": "Fix YouTube Plugin (Error 153)",
|
||||
"lblBasePath": "Base Path",
|
||||
"lblJellyfinUsername": "Username",
|
||||
"lblJellyfinPassword": "Password",
|
||||
"lblAuthenticate": "Test Login",
|
||||
"lblAutoLoginSettings": "Auto-Login Settings",
|
||||
"lblBasePathHint": "Tip: Paste full URL (e.g., https://host.com/path/jellyfin) to auto-fill all fields",
|
||||
"lblTestServer": "Test Server",
|
||||
"lblLogout": "Logout",
|
||||
"lblEnableAutoLoginConfig": "Enable config patching for auto-login",
|
||||
"lblTabServer": "Server",
|
||||
"lblTabPlayback": "Playback",
|
||||
"lblServerInputMode": "Input Mode",
|
||||
"lblServerUrl": "Server URL",
|
||||
"lblConnectionStatus": "Server Status",
|
||||
"lblAdvancedSettings": "Advanced Settings",
|
||||
"lblGitHubToken": "GitHub Token (PAT)",
|
||||
"lblGitHubTokenHint": "Optional. Prevents API rate limiting when fetching releases. Create one at GitHub > Settings > Developer settings > Personal access tokens.",
|
||||
"lblTabCss": "CSS Style",
|
||||
"lblCssSettings": "Custom CSS Settings",
|
||||
"lblCustomCss": "Custom CSS Code",
|
||||
"lblCssHint": "Enter custom CSS or use @import to load external themes",
|
||||
"lblValidateCss": "Validate CSS",
|
||||
"lblCssValidationStatus": "Validation Status",
|
||||
"lblCssEmpty": "No CSS to validate",
|
||||
"lblClearCss": "Clear",
|
||||
"lblCssValidating": "Validating...",
|
||||
"lblCssUrlFailed": "{0} URL(s) unreachable",
|
||||
"lblCssUrlsValid": "{0} URL(s) validated successfully",
|
||||
"lblCssSyntaxValid": "CSS syntax valid",
|
||||
"lblCssUnmatchedBrace": "Unmatched braces { }",
|
||||
"lblCssUnmatchedParen": "Unmatched parentheses ( )",
|
||||
"lblJellyThemes": "JellyThemes",
|
||||
"lblJellyThemesHint": "Click a theme to insert it and auto-validate. Visit the GitHub repo for previews.",
|
||||
"lblTabMainSettings": "Main",
|
||||
"lblMainSettings": "Application Settings",
|
||||
"lblRefreshUsers": "Refresh Users",
|
||||
"lblUserSelectionHint": "Selected users will be configured during installation",
|
||||
"subnetMismatch": "Devices are in different subnets (network)",
|
||||
"UpdateAvailable": "Update Available",
|
||||
"UpdateCurrentVersion": "Current version:",
|
||||
"UpdateLatestVersion": "Latest version:",
|
||||
"UpdateReleaseNotes": "Release notes:",
|
||||
"UpdateManual": "Open Releases",
|
||||
"UpdateAutomatic": "Update Now",
|
||||
"UpdateSkip": "Skip this version",
|
||||
"UpdateDownloading": "Downloading update...",
|
||||
"UpdateApplying": "Applying Update",
|
||||
"UpdateApplyingMessage": "The application will restart to complete the update.",
|
||||
"UpdateError": "Update failed",
|
||||
"UpdateCheckFailed": "Failed to check for updates",
|
||||
"IncompatiblePackage": "Package version not compatible with device.",
|
||||
"IncompatiblePackageDetailed": "Package version {0} not compatible with Tizen OS {1}",
|
||||
"lblMdnsWarning": "Warning: You are using an mDNS hostname (.local). Samsung TVs cannot reliably resolve .local addresses, which may cause the server to appear as \"undefined\" on your TV — especially after network interruptions. Use a direct IP address (e.g. 192.168.1.100) instead for a stable connection."
|
||||
}
|
||||
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/linux-x64/esbuild
Normal file
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/linux-x64/esbuild
Normal file
Binary file not shown.
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/macos-x64/esbuild
Normal file
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/macos-x64/esbuild
Normal file
Binary file not shown.
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/win-x64/esbuild.exe
Normal file
BIN
Jellyfin2Samsung-CrossOS/Assets/esbuild/win-x64/esbuild.exe
Normal file
Binary file not shown.
@@ -3,7 +3,6 @@ using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -2,6 +2,7 @@ using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
@@ -35,6 +36,10 @@ namespace Jellyfin2Samsung.Helpers.API
|
||||
string jsonContent = await response.Content.ReadAsStringAsync();
|
||||
var jsonObject = JsonNode.Parse(jsonContent);
|
||||
|
||||
var logFilePath = Path.Combine(AppContext.BaseDirectory, "Logs", $"debug_tv_api_{DateTime.Now:yyyy-MM-dd_HH-mm-ss-fff}.log");
|
||||
await File.WriteAllTextAsync(logFilePath, jsonContent);
|
||||
|
||||
|
||||
var deviceNode = jsonObject?["device"];
|
||||
if (deviceNode == null)
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Jellyfin2Samsung.Helpers
|
||||
public string Certificate { get; set; } = "Jelly2Sams";
|
||||
public bool DeletePreviousInstall { get; set; } = false;
|
||||
public string UserCustomIP { get; set; } = "";
|
||||
public string SavedNetworkInterfaceName { get; set; } = "";
|
||||
public bool ForceSamsungLogin { get; set; } = false;
|
||||
public bool RTLReading { get; set; } = false;
|
||||
public string JellyfinIP { get; set; } = "";
|
||||
@@ -45,6 +46,7 @@ namespace Jellyfin2Samsung.Helpers
|
||||
public string JellyfinAccessToken { get; set; } = "";
|
||||
public string JellyfinServerId { get; set; } = "";
|
||||
public string JellyfinServerLocalAddress { get; set; } = "";
|
||||
public string JellyfinServerName { get; set; } = "";
|
||||
public string AudioLanguagePreference { get; set; } = "";
|
||||
public string SubtitleLanguagePreference { get; set; } = "";
|
||||
public bool EnableBackdrops { get; set; } = false;
|
||||
@@ -72,18 +74,25 @@ namespace Jellyfin2Samsung.Helpers
|
||||
public bool PatchYoutubePlugin { get; set; } = false;
|
||||
public string CustomCss { get; set; } = "";
|
||||
public bool DarkMode { get; set; } = false;
|
||||
public string GitHubToken { get; set; } = "";
|
||||
public string LocalYoutubeServer { get; set; } = string.Empty;
|
||||
|
||||
// ----- Updater settings -----
|
||||
public bool CheckForUpdatesOnStartup { get; set; } = true;
|
||||
public string SkippedUpdateVersion { get; set; } = string.Empty;
|
||||
public DateTime? LastUpdateCheck { get; set; } = null;
|
||||
|
||||
// ----- Application-scoped settings (readonly at runtime) -----
|
||||
public string ReleasesUrl { get; set; } = "https://api.github.com/repos/jeppevinkel/jellyfin-tizen-builds/releases";
|
||||
public string AuthorEndpoint { get; set; } = "https://dev.tizen.samsung.com/apis/v2/authors";
|
||||
public string AppVersion { get; set; } = "v2.0.0.0";
|
||||
public string AppVersion { get; set; } = "v2.2.0.8";
|
||||
public string TizenSdb { get; set; } = "https://api.github.com/repos/PatrickSt1991/tizen-sdb/releases";
|
||||
public string JellyfinAvRelease { get; set; } = "https://api.github.com/repos/PatrickSt1991/tizen-jellyfin-avplay/releases";
|
||||
public string JellyfinAvReleaseFork { get; set; } = "https://api.github.com/repos/asamahy/tizen-jellyfin-avplay/releases";
|
||||
public string JellyfinLegacy { get; set; } = "https://api.github.com/repos/jeppevinkel/jellyfin-tizen-builds/releases/tags/2024-10-27-1821";
|
||||
public string CommunityRelease { get; set; } = "https://api.github.com/repos/PatrickSt1991/tizen-community-packages/releases";
|
||||
public string MoonfinRelease { get; set; } = "https://api.github.com/repos/Moonfin-Client/Tizen/releases";
|
||||
public string MoonfinRelease { get; set; } = "https://api.github.com/repos/Moonfin-Client/Smart-TV/releases";
|
||||
public string LiteFinRelease { get; set; } = "https://api.github.com/repos/MoazSalem/litefin/releases";
|
||||
public string ReleaseInfo { get; set; } = "https://raw.githubusercontent.com/jeppevinkel/jellyfin-tizen-builds/refs/heads/master/README.md";
|
||||
public string CommunityInfo { get; set; } = "https://raw.githubusercontent.com/PatrickSt1991/tizen-community-packages/refs/heads/main/README.md";
|
||||
public AppSettings() { }
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
@@ -16,49 +18,86 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<GitHubRelease?> GetLatestReleaseAsync(string url, string displayName)
|
||||
public async Task<List<GitHubRelease>> GetReleasesAsync(string url, string prefix, string displayName, int take = 1)
|
||||
{
|
||||
if (take < 1) take = 1;
|
||||
|
||||
try
|
||||
{
|
||||
using var response = await _httpClient.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
await using var stream = await response.Content.ReadAsStreamAsync();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
|
||||
// Try to parse as array first (normal GitHub releases endpoint)
|
||||
try
|
||||
{
|
||||
var releases = await JsonSerializer.DeserializeAsync<List<GitHubRelease>>(
|
||||
stream,
|
||||
var releases = JsonSerializer.Deserialize<List<GitHubRelease>>(
|
||||
json,
|
||||
JsonSerializerOptionsProvider.Default);
|
||||
|
||||
var latest = releases?.Count > 0 ? releases[0] : null;
|
||||
if (releases == null || releases.Count == 0)
|
||||
return new List<GitHubRelease>();
|
||||
|
||||
if (latest != null)
|
||||
latest.Name = displayName;
|
||||
releases = releases
|
||||
.Select(r =>
|
||||
{
|
||||
r.Assets = r.Assets?
|
||||
.Where(a =>
|
||||
!string.IsNullOrWhiteSpace(a.FileName) &&
|
||||
(a.FileName.EndsWith(".wgt", StringComparison.OrdinalIgnoreCase) ||
|
||||
a.FileName.EndsWith(".tpk", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList() ?? new List<Asset>();
|
||||
|
||||
return latest;
|
||||
return r;
|
||||
})
|
||||
.Where(r => r.Assets.Count > 0)
|
||||
.ToList();
|
||||
|
||||
if (releases.Count == 0)
|
||||
return new List<GitHubRelease>();
|
||||
|
||||
var result = releases.Count > take ? releases.GetRange(0, take) : releases;
|
||||
|
||||
foreach (var r in result)
|
||||
{
|
||||
r.Name = string.IsNullOrWhiteSpace(displayName)
|
||||
? $"{prefix}{r.Name}"
|
||||
: displayName;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
// If array parsing fails, try parsing as single object
|
||||
stream.Position = 0;
|
||||
|
||||
var latest = await JsonSerializer.DeserializeAsync<GitHubRelease>(
|
||||
stream,
|
||||
var latest = JsonSerializer.Deserialize<GitHubRelease>(
|
||||
json,
|
||||
JsonSerializerOptionsProvider.Default);
|
||||
|
||||
if (latest != null)
|
||||
latest.Name = displayName;
|
||||
if (latest == null)
|
||||
return new List<GitHubRelease>();
|
||||
|
||||
return latest;
|
||||
latest.Assets = latest.Assets?
|
||||
.Where(a =>
|
||||
!string.IsNullOrWhiteSpace(a.FileName) &&
|
||||
(a.FileName.EndsWith(".wgt", StringComparison.OrdinalIgnoreCase) ||
|
||||
a.FileName.EndsWith(".tpk", StringComparison.OrdinalIgnoreCase)))
|
||||
.ToList() ?? new List<Asset>();
|
||||
|
||||
if (latest.Assets.Count == 0)
|
||||
return new List<GitHubRelease>();
|
||||
|
||||
latest.Name = string.IsNullOrWhiteSpace(displayName)
|
||||
? $"{prefix}{latest.Name}"
|
||||
: displayName;
|
||||
|
||||
return new List<GitHubRelease> { latest };
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine($"Failed to fetch release from {url}: {ex}");
|
||||
return null;
|
||||
return new List<GitHubRelease>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,23 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
public const string CustomWgtFile = "Custom WGT File";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// preview image URLS for different apps.
|
||||
/// </summary>
|
||||
public static class PreviewImages
|
||||
{
|
||||
public const string Jellyfin = "https://jellyfin.org/assets/images/10.8-home-4a73a92bf90d1eeffa5081201ca9c7bb.png";
|
||||
public const string Moonfin = "https://iili.io/fs8W4Re.png";
|
||||
public const string Moonlight = "https://iili.io/fsvn6mJ.png";
|
||||
public const string Fireplace = "https://raw.githubusercontent.com/thonythony/fireplace/refs/heads/master/icon.jpg";
|
||||
public const string TVApp = "https://iili.io/fsvaHsn.png";
|
||||
public const string Twitch = "https://iili.io/fsvUNu2.md.gif";
|
||||
public const string ClubInfoBoard = "https://iili.io/fsviHQV.png";
|
||||
public const string Doom = "https://iili.io/fyofVqu.png";
|
||||
public const string TTD = "https://iili.io/qFBP2vn.png";
|
||||
public const string Litefin = "https://iili.io/qerI0la.png";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tizen installation error codes returned by the SDB tool.
|
||||
/// </summary>
|
||||
@@ -30,6 +47,7 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
public const string InstallCompleted = "install completed";
|
||||
public const string ResignFailed = "Re-sign failed";
|
||||
public const string Failed = "failed";
|
||||
public const string NotInstalled = "failed[132]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -174,6 +192,19 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
public const string AlphaNumeric = Alpha + "0123456789";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updater related constants.
|
||||
/// </summary>
|
||||
public static class Updater
|
||||
{
|
||||
public const string RepoOwner = "Jellyfin2Samsung";
|
||||
public const string RepoName = "Samsung-Jellyfin-Installer";
|
||||
public const string AtomFeedUrl = "https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer/releases.atom";
|
||||
public const string ReleasesPageUrl = "https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer/releases";
|
||||
public const string LatestReleaseApiUrl = "https://api.github.com/repos/Jellyfin2Samsung/Samsung-Jellyfin-Installer/releases/latest";
|
||||
public const int UpdateCheckTimeoutSeconds = 10;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Localization keys used for status messages.
|
||||
/// </summary>
|
||||
@@ -209,6 +240,22 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
public const string InvalidDeviceIp = "InvalidDeviceIp";
|
||||
public const string LblOther = "lblOther";
|
||||
public const string IpNotListed = "IpNotListed";
|
||||
public const string IncompatiblePackage = "IncompatiblePackage";
|
||||
public const string IncompatiblePackageDetailed = "IncompatiblePackageDetailed";
|
||||
|
||||
// Updater localization keys
|
||||
public const string UpdateAvailable = "UpdateAvailable";
|
||||
public const string UpdateCurrentVersion = "UpdateCurrentVersion";
|
||||
public const string UpdateLatestVersion = "UpdateLatestVersion";
|
||||
public const string UpdateReleaseNotes = "UpdateReleaseNotes";
|
||||
public const string UpdateManual = "UpdateManual";
|
||||
public const string UpdateAutomatic = "UpdateAutomatic";
|
||||
public const string UpdateSkip = "UpdateSkip";
|
||||
public const string UpdateDownloading = "UpdateDownloading";
|
||||
public const string UpdateApplying = "UpdateApplying";
|
||||
public const string UpdateApplyingMessage = "UpdateApplyingMessage";
|
||||
public const string UpdateError = "UpdateError";
|
||||
public const string UpdateCheckFailed = "UpdateCheckFailed";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Avalonia.Platform.Storage;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -18,61 +17,40 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
private static readonly string[] tpkItem = ["*.tpk"];
|
||||
private static readonly string[] allItem = ["*.wgt", "*.tpk"];
|
||||
|
||||
public async Task<string?> BrowseWgtFilesAsync(IStorageProvider storageProvider)
|
||||
|
||||
public async Task<string?> BrowseWgtFilesAsync(IStorageProvider storageProvider)
|
||||
{
|
||||
var fileTypes = new List<FilePickerFileType>
|
||||
{
|
||||
new("WGT Files")
|
||||
{
|
||||
var fileTypes = new List<FilePickerFileType>
|
||||
{
|
||||
new("WGT Files")
|
||||
{
|
||||
Patterns = wgtItem
|
||||
},
|
||||
new("TPK Files")
|
||||
{
|
||||
Patterns = tpkItem
|
||||
},
|
||||
new("All Supported Files")
|
||||
{
|
||||
Patterns = allItem
|
||||
}
|
||||
};
|
||||
|
||||
var options = new FilePickerOpenOptions
|
||||
{
|
||||
Title = "Select WGT/TPK File",
|
||||
FileTypeFilter = fileTypes,
|
||||
AllowMultiple = true
|
||||
};
|
||||
|
||||
var files = await storageProvider.OpenFilePickerAsync(options);
|
||||
|
||||
if (files?.Any() == true)
|
||||
{
|
||||
var newPaths = new List<string>();
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
var originalPath = file.Path.LocalPath;
|
||||
var directory = Path.GetDirectoryName(originalPath);
|
||||
var baseName = Path.GetFileNameWithoutExtension(originalPath);
|
||||
var extension = Path.GetExtension(originalPath);
|
||||
|
||||
var randomSuffix = new string(Enumerable.Range(0, 4)
|
||||
.Select(_ => Constants.CharacterSets.Alpha[Random.Shared.Next(Constants.CharacterSets.Alpha.Length)])
|
||||
.ToArray());
|
||||
|
||||
var newFileName = $"{baseName}{randomSuffix}{extension}";
|
||||
var newFilePath = Path.Combine(directory ?? Environment.CurrentDirectory, newFileName);
|
||||
|
||||
File.Copy(originalPath, newFilePath, overwrite: true);
|
||||
|
||||
newPaths.Add(newFilePath);
|
||||
}
|
||||
|
||||
return string.Join(";", newPaths);
|
||||
}
|
||||
|
||||
return null;
|
||||
Patterns = wgtItem
|
||||
},
|
||||
new("TPK Files")
|
||||
{
|
||||
Patterns = tpkItem
|
||||
},
|
||||
new("All Supported Files")
|
||||
{
|
||||
Patterns = allItem
|
||||
}
|
||||
};
|
||||
|
||||
var options = new FilePickerOpenOptions
|
||||
{
|
||||
Title = "Select WGT/TPK File",
|
||||
FileTypeFilter = fileTypes,
|
||||
AllowMultiple = true
|
||||
};
|
||||
|
||||
var files = await storageProvider.OpenFilePickerAsync(options);
|
||||
|
||||
if (files?.Any() == true)
|
||||
return string.Join(";", files.Select(f => f.Path.LocalPath));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<ExtensionEntry> ParseExtensions(string output)
|
||||
{
|
||||
var extensions = new List<ExtensionEntry>();
|
||||
@@ -112,6 +90,16 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
var match = RegexPatterns.WgtConfig.TizenApplicationId.Match(configContent);
|
||||
return match.Success ? match.Groups["pkg"].Value : null;
|
||||
}
|
||||
public static async Task<string?> ReadExtractedWgtPackageId(string workspaceRoot)
|
||||
{
|
||||
var configPath = Path.Combine(workspaceRoot, "config.xml");
|
||||
if (!File.Exists(configPath))
|
||||
return null;
|
||||
|
||||
var configContent = await File.ReadAllTextAsync(configPath, Encoding.UTF8);
|
||||
var match = RegexPatterns.WgtConfig.TizenApplicationId.Match(configContent);
|
||||
return match.Success ? match.Groups["pkg"].Value : null;
|
||||
}
|
||||
public static async Task<bool> ModifyWgtPackageId(string wgtPath)
|
||||
{
|
||||
if (!File.Exists(wgtPath))
|
||||
@@ -165,5 +153,60 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
sb.Append(Constants.CharacterSets.AlphaNumeric[Random.Shared.Next(Constants.CharacterSets.AlphaNumeric.Length)]);
|
||||
return sb.ToString();
|
||||
}
|
||||
public static async Task<string?> ReadWgtApplicationId(string wgtPath)
|
||||
{
|
||||
if (!File.Exists(wgtPath))
|
||||
return null;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var originalStream = File.OpenRead(wgtPath))
|
||||
await originalStream.CopyToAsync(memoryStream);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read, true);
|
||||
var configEntry = archive.GetEntry("config.xml");
|
||||
if (configEntry == null)
|
||||
return null;
|
||||
|
||||
string configContent;
|
||||
using (var reader = new StreamReader(configEntry.Open(), Encoding.UTF8))
|
||||
configContent = await reader.ReadToEndAsync();
|
||||
|
||||
// Prefer using a RegexPatterns entry if you want; otherwise this is safe and specific:
|
||||
var match = Regex.Match(
|
||||
configContent,
|
||||
@"<tizen:application\b[^>]*\bid\s*=\s*""(?<id>[^""]+)""",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
return match.Success ? match.Groups["id"].Value : null;
|
||||
}
|
||||
public static async Task<string?> ReadWgtRequiredVersion(string wgtPath)
|
||||
{
|
||||
if (!File.Exists(wgtPath))
|
||||
return null;
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
using (var originalStream = File.OpenRead(wgtPath))
|
||||
await originalStream.CopyToAsync(memoryStream);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
|
||||
using var archive = new ZipArchive(memoryStream, ZipArchiveMode.Read, true);
|
||||
var configEntry = archive.GetEntry("config.xml");
|
||||
if (configEntry == null)
|
||||
return null;
|
||||
|
||||
string configContent;
|
||||
using (var reader = new StreamReader(configEntry.Open(), Encoding.UTF8))
|
||||
configContent = await reader.ReadToEndAsync();
|
||||
|
||||
var match = Regex.Match(
|
||||
configContent,
|
||||
@"<tizen:application\b[^>]*\brequired_version\s*=\s*""(?<version>[^""]+)""",
|
||||
RegexOptions.IgnoreCase);
|
||||
|
||||
return match.Success ? match.Groups["version"].Value : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
94
Jellyfin2Samsung-CrossOS/Helpers/Core/GitHubAuthHandler.cs
Normal file
94
Jellyfin2Samsung-CrossOS/Helpers/Core/GitHubAuthHandler.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Helpers.Core
|
||||
{
|
||||
public class GitHubAuthHandler : DelegatingHandler
|
||||
{
|
||||
private readonly string? _token;
|
||||
|
||||
public GitHubAuthHandler(string? token)
|
||||
: base(new HttpClientHandler())
|
||||
{
|
||||
_token = token;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_token) && IsGitHubRequest(request.RequestUri))
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
|
||||
}
|
||||
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
private static bool IsGitHubRequest(Uri? uri)
|
||||
{
|
||||
if (uri == null) return false;
|
||||
var host = uri.Host;
|
||||
return host.Equals("api.github.com", StringComparison.OrdinalIgnoreCase)
|
||||
|| host.Equals("raw.githubusercontent.com", StringComparison.OrdinalIgnoreCase)
|
||||
|| host.Equals("github.com", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string? ResolveToken(AppSettings settings)
|
||||
{
|
||||
// 1. Explicit setting
|
||||
if (!string.IsNullOrWhiteSpace(settings.GitHubToken))
|
||||
{
|
||||
Trace.TraceInformation("[GitHubAuth] Using token from app settings");
|
||||
return settings.GitHubToken.Trim();
|
||||
}
|
||||
|
||||
// 2. Environment variable
|
||||
var envToken = Environment.GetEnvironmentVariable("GITHUB_TOKEN");
|
||||
if (!string.IsNullOrWhiteSpace(envToken))
|
||||
{
|
||||
Trace.TraceInformation("[GitHubAuth] Using token from GITHUB_TOKEN environment variable");
|
||||
return envToken.Trim();
|
||||
}
|
||||
|
||||
// 3. GitHub CLI (gh auth token)
|
||||
try
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "gh",
|
||||
Arguments = "auth token",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var process = Process.Start(psi);
|
||||
if (process != null)
|
||||
{
|
||||
var output = process.StandardOutput.ReadToEnd().Trim();
|
||||
process.WaitForExit(5000);
|
||||
|
||||
if (process.ExitCode == 0 && !string.IsNullOrWhiteSpace(output))
|
||||
{
|
||||
Trace.TraceInformation("[GitHubAuth] Using token from GitHub CLI (gh auth token)");
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// gh CLI not installed or not authenticated — ignore
|
||||
}
|
||||
|
||||
// 4. No token available — unauthenticated requests
|
||||
Trace.TraceInformation("[GitHubAuth] No token found — using unauthenticated requests");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
namespace Jellyfin2Samsung.Helpers.Core
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Jellyfin2Samsung.Helpers.Core
|
||||
{
|
||||
public static class HtmlUtils
|
||||
{
|
||||
@@ -9,12 +13,10 @@
|
||||
|
||||
return html.Replace("<head>", "<head><base href=\".\">");
|
||||
}
|
||||
|
||||
public static string RewriteLocalPaths(string html)
|
||||
{
|
||||
return RegexPatterns.Html.LocalPaths.Replace(html, "$1=\"$2\"");
|
||||
}
|
||||
|
||||
public static string CleanAndApplyCsp(string html)
|
||||
{
|
||||
html = RegexPatterns.Html.CspMeta.Replace(html, "");
|
||||
@@ -39,5 +41,52 @@
|
||||
.Replace("\n", "\\n")
|
||||
.Replace("\r", "\\r");
|
||||
}
|
||||
public static string RemoveMarkdownTable(string html)
|
||||
{
|
||||
if (string.IsNullOrEmpty(html))
|
||||
return html;
|
||||
|
||||
var tablePattern = @"(\|[^\n]+\|\s*\n)+";
|
||||
|
||||
return Regex.Replace(html, tablePattern, string.Empty, RegexOptions.Multiline);
|
||||
}
|
||||
public static string StripHtml(string html)
|
||||
{
|
||||
if (string.IsNullOrEmpty(html))
|
||||
return string.Empty;
|
||||
|
||||
// Simple HTML stripping - replace common tags
|
||||
var text = html
|
||||
.Replace("<br>", "\n")
|
||||
.Replace("<br/>", "\n")
|
||||
.Replace("<br />", "\n")
|
||||
.Replace("</p>", "\n")
|
||||
.Replace("</li>", "\n")
|
||||
.Replace("<li>", "• ");
|
||||
|
||||
// Remove all remaining HTML tags
|
||||
while (text.Contains('<') && text.Contains('>'))
|
||||
{
|
||||
var start = text.IndexOf('<');
|
||||
var end = text.IndexOf('>', start);
|
||||
if (end > start)
|
||||
text = text.Remove(start, end - start + 1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Decode common HTML entities
|
||||
text = text
|
||||
.Replace(" ", " ")
|
||||
.Replace("&", "&")
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace(""", "\"")
|
||||
.Replace("'", "'");
|
||||
|
||||
// Clean up whitespace
|
||||
var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
return string.Join("\n", lines.Select(l => l.Trim()).Where(l => !string.IsNullOrEmpty(l)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ using Jellyfin2Samsung.Models;
|
||||
using Jellyfin2Samsung.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -22,9 +21,10 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
private readonly IDialogService _dialogService = dialogService;
|
||||
private readonly INetworkService _networkService = networkService;
|
||||
|
||||
public async Task<string?> DownloadReleaseAsync(GitHubRelease release, Asset selectedAsset, ProgressCallback? progress = null)
|
||||
public async Task<string?> DownloadReleaseAsync(GitHubRelease release, Asset? selectedAsset, ProgressCallback? progress = null)
|
||||
{
|
||||
if (release?.PrimaryDownloadUrl == null) return null;
|
||||
if (selectedAsset?.DownloadUrl == null) return null;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -39,14 +39,30 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public async Task<bool> InstallPackageAsync(string packagePath, NetworkDevice selectedDevice, CancellationToken cancellationToken, ProgressCallback? progress = null, Action? onSamsungLoginStarted = null)
|
||||
public async Task<bool> InstallPackageAsync(string? packagePath, NetworkDevice selectedDevice, CancellationToken cancellationToken, ProgressCallback? progress = null, Action? onSamsungLoginStarted = null)
|
||||
{
|
||||
if(selectedDevice.DeveloperIP == null) return false;
|
||||
|
||||
var localIps = _networkService.GetRelevantLocalIPs()
|
||||
.Select(ip => ip.ToString())
|
||||
.ToList();
|
||||
|
||||
bool ipMismatch = !localIps.Contains(selectedDevice.DeveloperIP) && !string.IsNullOrEmpty(selectedDevice.DeveloperIP);
|
||||
|
||||
if (!string.IsNullOrEmpty(AppSettings.Default.LocalIp)
|
||||
&& !string.IsNullOrEmpty(selectedDevice.DeveloperIP)
|
||||
&& _networkService.IsDifferentSubnet(AppSettings.Default.LocalIp, selectedDevice.DeveloperIP))
|
||||
{
|
||||
bool continueExecution =
|
||||
await _dialogService.ShowConfirmationAsync(
|
||||
"Subnet Mismatch",
|
||||
"subnetMismatch".Localized(),
|
||||
"keyContinue".Localized(),
|
||||
"keyStop".Localized());
|
||||
|
||||
if (!continueExecution)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(packagePath) || !File.Exists(packagePath))
|
||||
{
|
||||
@@ -81,7 +97,26 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
|
||||
if (ipMismatch)
|
||||
{
|
||||
bool continueExecution = await _dialogService.ShowConfirmationAsync("IP Mismatch","DeveloperIPMismatch".Localized(), "keyContinue".Localized(), "keyStop".Localized());
|
||||
bool isReversedIp = localIps
|
||||
.Select(ip => _networkService.InvertIPAddress(ip))
|
||||
.Contains(selectedDevice.DeveloperIP);
|
||||
|
||||
if (isReversedIp)
|
||||
{
|
||||
bool continueExecution = await _dialogService.ShowConfirmationAsync(
|
||||
"IP Reversed",
|
||||
"DeveloperIPReversed".Localized(),
|
||||
"keyContinue".Localized(),
|
||||
"keyStop".Localized());
|
||||
if (!continueExecution)
|
||||
return false;
|
||||
ipMismatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipMismatch)
|
||||
{
|
||||
bool continueExecution = await _dialogService.ShowConfirmationAsync("IP Mismatch", "DeveloperIPMismatch".Localized(), "keyContinue".Localized(), "keyStop".Localized());
|
||||
if (!continueExecution)
|
||||
return false;
|
||||
}
|
||||
@@ -128,8 +163,10 @@ namespace Jellyfin2Samsung.Helpers.Core
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public async Task<bool> InstallCustomPackagesAsync(string[] packagePaths, NetworkDevice device, CancellationToken cancellationToken, Action<string> onProgress, Action? onSamsungLoginStarted = null)
|
||||
public async Task<bool> InstallCustomPackagesAsync(string[] packagePaths, NetworkDevice? device, CancellationToken cancellationToken, Action<string> onProgress, Action? onSamsungLoginStarted = null)
|
||||
{
|
||||
if (device == null) return false;
|
||||
|
||||
onProgress("UsingCustomWGT".Localized());
|
||||
|
||||
var allSuccessful = true;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
@@ -41,8 +41,9 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin
|
||||
// Apply YouTube plugin patch if enabled
|
||||
if (AppSettings.Default.PatchYoutubePlugin)
|
||||
{
|
||||
await _youTube.CorsAsync(ws);
|
||||
await _youTube.FixAsync(ws);
|
||||
await _youTube.PatchPluginAsync(ws);
|
||||
await _youTube.UpdateCorsAsync(ws);
|
||||
await _youTube.CreateYouTubeResolverAsync(ws);
|
||||
}
|
||||
|
||||
// Always update server address
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,6 @@
|
||||
using Jellyfin2Samsung.Helpers.API;
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Helpers.Jellyfin.Plugins;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -9,7 +8,6 @@ using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
{
|
||||
@@ -78,6 +76,18 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
if (!servers.Any(s => s?.GetValue<string>() == serverUrl))
|
||||
servers.Add(serverUrl);
|
||||
|
||||
// Add LocalAddress (IP-based) as fallback when the primary URL uses mDNS (.local)
|
||||
// Samsung TVs (Tizen) cannot reliably resolve mDNS hostnames, especially after network disruptions
|
||||
var localAddress = UrlHelper.NormalizeServerUrl(AppSettings.Default.JellyfinServerLocalAddress);
|
||||
if (!string.IsNullOrEmpty(localAddress) &&
|
||||
localAddress != serverUrl &&
|
||||
UrlHelper.IsValidHttpUrl(localAddress) &&
|
||||
!servers.Any(s => s?.GetValue<string>() == localAddress))
|
||||
{
|
||||
servers.Add(localAddress);
|
||||
Trace.WriteLine($"[UpdateServerAddress] Added LocalAddress fallback: {localAddress}");
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(path, config.ToJsonString());
|
||||
|
||||
}
|
||||
@@ -94,6 +104,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
var serverUrl = UrlHelper.NormalizeServerUrl(AppSettings.Default.JellyfinFullUrl);
|
||||
var serverId = AppSettings.Default.JellyfinServerId;
|
||||
var localAddress = AppSettings.Default.JellyfinServerLocalAddress;
|
||||
var serverName = AppSettings.Default.JellyfinServerName;
|
||||
|
||||
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(userId) || string.IsNullOrEmpty(serverUrl))
|
||||
{
|
||||
@@ -101,19 +112,21 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
return;
|
||||
}
|
||||
|
||||
// If server ID is not stored, try to fetch it now
|
||||
if (string.IsNullOrEmpty(serverId))
|
||||
// If server ID or server name is not stored, try to fetch it now
|
||||
if (string.IsNullOrEmpty(serverId) || string.IsNullOrEmpty(serverName))
|
||||
{
|
||||
Trace.WriteLine("[InjectAutoLogin] Server ID not cached, fetching from server...");
|
||||
Trace.WriteLine("[InjectAutoLogin] Server ID/Name not cached, fetching from server...");
|
||||
var serverInfo = await _apiClient.GetPublicSystemInfoAsync(serverUrl);
|
||||
if (serverInfo != null && !string.IsNullOrEmpty(serverInfo.Id))
|
||||
{
|
||||
serverId = serverInfo.Id;
|
||||
localAddress = serverInfo.LocalAddress ?? "";
|
||||
serverName = serverInfo.ServerName ?? "";
|
||||
AppSettings.Default.JellyfinServerId = serverId;
|
||||
AppSettings.Default.JellyfinServerLocalAddress = localAddress;
|
||||
AppSettings.Default.JellyfinServerName = serverName;
|
||||
AppSettings.Default.Save();
|
||||
Trace.WriteLine($"[InjectAutoLogin] Fetched and stored server ID: {serverId}");
|
||||
Trace.WriteLine($"[InjectAutoLogin] Fetched and stored server ID: {serverId}, Name: {serverName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -140,6 +153,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
credentialsScript.AppendLine($" var serverUrl = '{HtmlUtils.EscapeJsString(serverUrl)}';");
|
||||
credentialsScript.AppendLine($" var serverId = '{HtmlUtils.EscapeJsString(serverId)}';");
|
||||
credentialsScript.AppendLine($" var localAddress = '{HtmlUtils.EscapeJsString(localAddress)}';");
|
||||
credentialsScript.AppendLine($" var serverName = '{HtmlUtils.EscapeJsString(serverName)}';");
|
||||
credentialsScript.AppendLine($" var userId = '{HtmlUtils.EscapeJsString(userId)}';");
|
||||
credentialsScript.AppendLine($" var accessToken = '{HtmlUtils.EscapeJsString(accessToken)}';");
|
||||
credentialsScript.AppendLine();
|
||||
@@ -147,6 +161,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
credentialsScript.AppendLine(" // Using real server ID (GUID) from /System/Info/Public");
|
||||
credentialsScript.AppendLine(" var credentials = {");
|
||||
credentialsScript.AppendLine(" Servers: [{");
|
||||
credentialsScript.AppendLine(" Name: serverName || serverUrl,");
|
||||
credentialsScript.AppendLine(" ManualAddress: serverUrl,");
|
||||
credentialsScript.AppendLine(" LocalAddress: localAddress || serverUrl,");
|
||||
credentialsScript.AppendLine(" Id: serverId,");
|
||||
@@ -159,7 +174,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Patches
|
||||
credentialsScript.AppendLine(" // Store in localStorage");
|
||||
credentialsScript.AppendLine(" localStorage.setItem('jellyfin_credentials', JSON.stringify(credentials));");
|
||||
credentialsScript.AppendLine();
|
||||
credentialsScript.AppendLine(" console.log('[Auto-Login] Credentials injected for server: ' + serverUrl + ' with ID: ' + serverId);");
|
||||
credentialsScript.AppendLine(" console.log('[Auto-Login] Credentials injected for server: ' + serverName + ' (' + serverUrl + ') with ID: ' + serverId);");
|
||||
credentialsScript.AppendLine(" } catch(e) {");
|
||||
credentialsScript.AppendLine(" console.error('[Auto-Login] Failed to inject credentials:', e);");
|
||||
credentialsScript.AppendLine(" }");
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
namespace Jellyfin2Samsung.Helpers.Jellyfin.Fixes
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps ISO 639 language codes to search keywords for DuckDuckGo Lite trailer search.
|
||||
/// Covers all languages supported by Jellyfin's localization system.
|
||||
/// Each entry contains the English name + native name for maximum DDG search match rate.
|
||||
/// </summary>
|
||||
public static class TrailerLanguageMap
|
||||
{
|
||||
public const string JsObject = @"{
|
||||
ab:'Abkhaz Аԥсуа',
|
||||
af:'Afrikaans',
|
||||
ar:'Arabic العربية',
|
||||
as:'Assamese অসমীয়া',
|
||||
be:'Belarusian Беларуская',
|
||||
bg:'Bulgarian Български',
|
||||
bn:'Bengali বাংলা',
|
||||
ca:'Catalan Català',
|
||||
chr:'Cherokee ᏣᎳᎩ',
|
||||
cs:'Czech Český',
|
||||
cy:'Welsh Cymraeg',
|
||||
da:'Danish Dansk',
|
||||
de:'Deutsch German',
|
||||
el:'Greek Ελληνικά',
|
||||
en:'English',
|
||||
enm:'English',
|
||||
eo:'Esperanto',
|
||||
es:'Spanish Español',
|
||||
et:'Estonian Eesti',
|
||||
eu:'Basque Euskara',
|
||||
fa:'Persian Farsi فارسی',
|
||||
fi:'Finnish Suomi',
|
||||
fil:'Filipino Tagalog',
|
||||
fo:'Faroese Føroyskt',
|
||||
fr:'French Français',
|
||||
ga:'Irish Gaeilge',
|
||||
gl:'Galician Galego',
|
||||
gsw:'Swiss German Schweizerdeutsch',
|
||||
he:'Hebrew עברית',
|
||||
hi:'Hindi हिन्दी',
|
||||
hr:'Croatian Hrvatski',
|
||||
ht:'Haitian Creole Kreyòl',
|
||||
hu:'Hungarian Magyar',
|
||||
hy:'Armenian Հայերեն',
|
||||
id:'Indonesian Bahasa',
|
||||
is:'Icelandic Íslenska',
|
||||
it:'Italian Italiano',
|
||||
ja:'Japanese 日本語',
|
||||
jbo:'Lojban',
|
||||
ka:'Georgian ქართული',
|
||||
kab:'Kabyle Taqbaylit',
|
||||
kk:'Kazakh Қазақша',
|
||||
km:'Khmer ភាសាខ្មែរ',
|
||||
kn:'Kannada ಕನ್ನಡ',
|
||||
ko:'Korean 한국어',
|
||||
kw:'Cornish Kernewek',
|
||||
ky:'Kyrgyz Кыргызча',
|
||||
lb:'Luxembourgish Lëtzebuergesch',
|
||||
lt:'Lithuanian Lietuvių',
|
||||
lv:'Latvian Latviešu',
|
||||
lzh:'Chinese 中文',
|
||||
mi:'Maori Māori',
|
||||
mk:'Macedonian Македонски',
|
||||
ml:'Malayalam മലയാളം',
|
||||
mn:'Mongolian Монгол',
|
||||
mr:'Marathi मराठी',
|
||||
ms:'Malay Melayu',
|
||||
mt:'Maltese Malti',
|
||||
my:'Burmese Myanmar မြန်မာ',
|
||||
nb:'Norwegian Norsk',
|
||||
ne:'Nepali नेपाली',
|
||||
nl:'Dutch Nederlands',
|
||||
nn:'Norwegian Norsk',
|
||||
oc:'Occitan',
|
||||
or:'Odia ଓଡ଼ିଆ',
|
||||
pa:'Punjabi ਪੰਜਾਬੀ',
|
||||
pl:'Polish Polski',
|
||||
pr:'English',
|
||||
pt:'Portuguese Português',
|
||||
ro:'Romanian Română',
|
||||
ru:'Russian Русский',
|
||||
si:'Sinhala සිංහල',
|
||||
sk:'Slovak Slovenský',
|
||||
sl:'Slovenian Slovenščina',
|
||||
sn:'Shona',
|
||||
sq:'Albanian Shqip',
|
||||
sr:'Serbian Српски',
|
||||
sv:'Swedish Svenska',
|
||||
sw:'Swahili Kiswahili',
|
||||
ta:'Tamil தமிழ்',
|
||||
te:'Telugu తెలుగు',
|
||||
th:'Thai ไทย',
|
||||
tr:'Turkish Türkçe',
|
||||
ug:'Uyghur ئۇيغۇرچە',
|
||||
uk:'Ukrainian Українська',
|
||||
ur:'Urdu اردو',
|
||||
uz:'Uzbek Oʻzbekcha',
|
||||
vi:'Vietnamese Tiếng Việt',
|
||||
zh:'Chinese 中文',
|
||||
zu:'Zulu isiZulu'
|
||||
}";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace Jellyfin2Samsung.Helpers.Jellyfin.Plugins.EditorsChoice
|
||||
namespace Jellyfin2Samsung.Helpers.Jellyfin.Plugins.EditorsChoice
|
||||
{
|
||||
public class PatchEditorsChoice
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Helpers.Jellyfin.Plugins;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Plugins
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public async Task<byte[]?> DownloadBytesAsync(string url)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Jellyfin2Samsung.Helpers.Jellyfin.Plugins
|
||||
UseBabel = true
|
||||
}
|
||||
];
|
||||
public static readonly List<ServerAssetRule> ServerAssetRules =
|
||||
public static readonly List<ServerAssetRule> ServerAssetRules =
|
||||
[
|
||||
new ServerAssetRule(
|
||||
pluginName: "GenericPluginAsset",
|
||||
|
||||
@@ -5,7 +5,6 @@ using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Helpers.Jellyfin.Plugins
|
||||
|
||||
@@ -31,25 +31,25 @@ namespace Jellyfin2Samsung.Helpers.Tizen.Certificate
|
||||
if (!Directory.Exists(certificateFolders))
|
||||
return certificates;
|
||||
|
||||
|
||||
var p12Files = Directory.GetFiles(
|
||||
certificateFolders,
|
||||
"author.p12",
|
||||
SearchOption.AllDirectories);
|
||||
|
||||
foreach(var p12Path in p12Files)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(p12Path);
|
||||
if (directory == null)
|
||||
continue;
|
||||
var p12Files = Directory.GetFiles(
|
||||
certificateFolders,
|
||||
"author.p12",
|
||||
SearchOption.AllDirectories);
|
||||
|
||||
var passwordPath = Path.Combine(directory, "password.txt");
|
||||
if (!File.Exists(passwordPath))
|
||||
continue;
|
||||
foreach (var p12Path in p12Files)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(p12Path);
|
||||
if (directory == null)
|
||||
continue;
|
||||
|
||||
var password = File.ReadAllText(passwordPath).Trim();
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
continue;
|
||||
var passwordPath = Path.Combine(directory, "password.txt");
|
||||
if (!File.Exists(passwordPath))
|
||||
continue;
|
||||
|
||||
var password = File.ReadAllText(passwordPath).Trim();
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
continue;
|
||||
try
|
||||
{
|
||||
var cert = new X509Certificate2(
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Jellyfin2Samsung.Helpers.Tizen.Devices
|
||||
_tizenApiClient = tizenApiClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public async Task<List<NetworkDevice>> ScanForDevicesAsync(CancellationToken cancellationToken = default, bool virtualScan = false)
|
||||
{
|
||||
@@ -30,6 +30,9 @@ namespace Jellyfin2Samsung.Helpers.Tizen.Devices
|
||||
|
||||
foreach (NetworkDevice device in networkDevices)
|
||||
{
|
||||
// Check for cancellation before processing each device
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (await _networkService.IsPortOpenAsync(device.IpAddress, 8001, cancellationToken))
|
||||
{
|
||||
try
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Jellyfin2Samsung.Interfaces
|
||||
string GetLocalIPAddress();
|
||||
string InvertIPAddress(string ipAddress);
|
||||
Task<string?> GetManufacturerFromIp(string ipAddress);
|
||||
Task<string?> GetPrimaryOutboundIPAddressAsync();
|
||||
bool IsDifferentSubnet(string ip1, string ip2);
|
||||
Task<IReadOnlyList<NetworkInterfaceOption>> GetNetworkInterfaceOptionsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
62
Jellyfin2Samsung-CrossOS/Interfaces/IUpdateDialogService.cs
Normal file
62
Jellyfin2Samsung-CrossOS/Interfaces/IUpdateDialogService.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the user's choice in the update dialog.
|
||||
/// </summary>
|
||||
public enum UpdateDialogChoice
|
||||
{
|
||||
/// <summary>
|
||||
/// User cancelled or closed the dialog.
|
||||
/// </summary>
|
||||
Cancel,
|
||||
|
||||
/// <summary>
|
||||
/// User chose to open the releases page manually.
|
||||
/// </summary>
|
||||
Manual,
|
||||
|
||||
/// <summary>
|
||||
/// User chose to download and install the update automatically.
|
||||
/// </summary>
|
||||
Automatic,
|
||||
|
||||
/// <summary>
|
||||
/// User chose to skip this update.
|
||||
/// </summary>
|
||||
Skip
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Service for showing update-related dialogs.
|
||||
/// </summary>
|
||||
public interface IUpdateDialogService
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows the update available dialog with options for manual or automatic update.
|
||||
/// </summary>
|
||||
/// <param name="updateInfo">Information about the available update.</param>
|
||||
/// <returns>The user's choice.</returns>
|
||||
Task<UpdateDialogChoice> ShowUpdateAvailableDialogAsync(UpdateCheckResult updateInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Shows a progress dialog while downloading the update.
|
||||
/// </summary>
|
||||
/// <param name="progress">Progress reporter (0-100).</param>
|
||||
/// <returns>True if download completed, false if cancelled.</returns>
|
||||
Task<bool> ShowDownloadProgressAsync(System.IProgress<int> progress);
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message that the update is being applied and the app will restart.
|
||||
/// </summary>
|
||||
Task ShowApplyingUpdateMessageAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Shows an error message related to the update process.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">The error message to display.</param>
|
||||
Task ShowUpdateErrorAsync(string errorMessage);
|
||||
}
|
||||
}
|
||||
56
Jellyfin2Samsung-CrossOS/Interfaces/IUpdaterService.cs
Normal file
56
Jellyfin2Samsung-CrossOS/Interfaces/IUpdaterService.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for checking and applying application updates via GitHub releases.
|
||||
/// Uses the Atom feed endpoint to avoid API rate limiting.
|
||||
/// </summary>
|
||||
public interface IUpdaterService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a newer version of the application is available.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>Update check result containing version information and download URLs.</returns>
|
||||
Task<UpdateCheckResult> CheckForUpdateAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the latest release to a temporary location.
|
||||
/// </summary>
|
||||
/// <param name="downloadUrl">The URL to download the release from.</param>
|
||||
/// <param name="progress">Progress callback reporting download percentage (0-100).</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>Path to the downloaded file.</returns>
|
||||
Task<string> DownloadUpdateAsync(
|
||||
string downloadUrl,
|
||||
IProgress<int>? progress = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Applies the downloaded update by extracting, replacing files, and scheduling a restart.
|
||||
/// </summary>
|
||||
/// <param name="downloadedFilePath">Path to the downloaded update archive.</param>
|
||||
/// <param name="cancellationToken">Cancellation token for the operation.</param>
|
||||
/// <returns>True if the update was successfully prepared and app should restart.</returns>
|
||||
Task<bool> ApplyUpdateAsync(string downloadedFilePath, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the GitHub releases page in the default browser.
|
||||
/// </summary>
|
||||
void OpenReleasesPage();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL of the GitHub releases page.
|
||||
/// </summary>
|
||||
string ReleasesPageUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current application version.
|
||||
/// </summary>
|
||||
string CurrentVersion { get; }
|
||||
}
|
||||
}
|
||||
@@ -94,6 +94,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Tmds.DBus.Protocol" Version="0.21.3" />
|
||||
<AvaloniaResource Update="Assets\esbuild\linux-x64\esbuild">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</AvaloniaResource>
|
||||
|
||||
@@ -6,5 +6,6 @@ namespace Jellyfin2Samsung.Models
|
||||
{
|
||||
[ObservableProperty] private string fileName = string.Empty;
|
||||
[ObservableProperty] private string description = string.Empty;
|
||||
[ObservableProperty] private string repoUrl = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
73
Jellyfin2Samsung-CrossOS/Models/GitHubAtomEntry.cs
Normal file
73
Jellyfin2Samsung-CrossOS/Models/GitHubAtomEntry.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
|
||||
namespace Jellyfin2Samsung.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a release entry parsed from the GitHub Atom feed.
|
||||
/// The Atom feed does not have rate limits unlike the REST API.
|
||||
/// </summary>
|
||||
public class GitHubAtomEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique ID of the release (e.g., "tag:github.com,2008:Repository/123456/v1.0.0").
|
||||
/// </summary>
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The release title/name.
|
||||
/// </summary>
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// When the release was last updated.
|
||||
/// </summary>
|
||||
public DateTime? Updated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The link to the release page.
|
||||
/// </summary>
|
||||
public string Link { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The release content/description (HTML).
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The author who published the release.
|
||||
/// </summary>
|
||||
public string AuthorName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the tag name (version) from the release ID or link.
|
||||
/// </summary>
|
||||
public string TagName
|
||||
{
|
||||
get
|
||||
{
|
||||
// Try to extract from link first: https://github.com/owner/repo/releases/tag/v1.0.0
|
||||
if (!string.IsNullOrEmpty(Link))
|
||||
{
|
||||
const string tagMarker = "/releases/tag/";
|
||||
var tagIndex = Link.IndexOf(tagMarker, StringComparison.OrdinalIgnoreCase);
|
||||
if (tagIndex >= 0)
|
||||
{
|
||||
return Link.Substring(tagIndex + tagMarker.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: extract from ID: tag:github.com,2008:Repository/123456/v1.0.0
|
||||
if (!string.IsNullOrEmpty(Id))
|
||||
{
|
||||
var lastSlash = Id.LastIndexOf('/');
|
||||
if (lastSlash >= 0 && lastSlash < Id.Length - 1)
|
||||
{
|
||||
return Id.Substring(lastSlash + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -40,6 +41,10 @@ namespace Jellyfin2Samsung.Models
|
||||
[JsonPropertyName("size")]
|
||||
public long Size { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsDefault => FileName.Equals("Jellyfin.wgt", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public string DisplayText => $"{FileName} ({FormatFileSize(Size)})";
|
||||
|
||||
|
||||
@@ -32,4 +32,12 @@
|
||||
public string Name = "";
|
||||
public bool Activated;
|
||||
}
|
||||
public class NetworkInterfaceOption
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string IpAddress { get; set; } = string.Empty;
|
||||
|
||||
public string DisplayText => $"{Name} - {IpAddress}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
9
Jellyfin2Samsung-CrossOS/Models/ProviderOption.cs
Normal file
9
Jellyfin2Samsung-CrossOS/Models/ProviderOption.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace Jellyfin2Samsung.Models;
|
||||
|
||||
public sealed class ProviderOption
|
||||
{
|
||||
public string DisplayName { get; init; } = "";
|
||||
public IImage? PreviewImage { get; init; } // can be a Bitmap later
|
||||
}
|
||||
86
Jellyfin2Samsung-CrossOS/Models/UpdateCheckResult.cs
Normal file
86
Jellyfin2Samsung-CrossOS/Models/UpdateCheckResult.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
|
||||
namespace Jellyfin2Samsung.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of checking for application updates.
|
||||
/// </summary>
|
||||
public class UpdateCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether a newer version is available.
|
||||
/// </summary>
|
||||
public bool IsUpdateAvailable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current installed version.
|
||||
/// </summary>
|
||||
public string CurrentVersion { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The latest available version.
|
||||
/// </summary>
|
||||
public string LatestVersion { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// URL to download the update for the current platform.
|
||||
/// </summary>
|
||||
public string? DownloadUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// URL to the GitHub releases page.
|
||||
/// </summary>
|
||||
public string ReleasesPageUrl { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Release title/name.
|
||||
/// </summary>
|
||||
public string ReleaseTitle { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Release notes or description.
|
||||
/// </summary>
|
||||
public string ReleaseNotes { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// When the release was published.
|
||||
/// </summary>
|
||||
public DateTime? PublishedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Error message if the check failed.
|
||||
/// </summary>
|
||||
public string? ErrorMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the check completed successfully.
|
||||
/// </summary>
|
||||
public bool IsSuccess => string.IsNullOrEmpty(ErrorMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a failed result with an error message.
|
||||
/// </summary>
|
||||
public static UpdateCheckResult Failed(string errorMessage, string currentVersion)
|
||||
{
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
IsUpdateAvailable = false,
|
||||
CurrentVersion = currentVersion,
|
||||
ErrorMessage = errorMessage
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a result indicating no update is available.
|
||||
/// </summary>
|
||||
public static UpdateCheckResult NoUpdateAvailable(string currentVersion)
|
||||
{
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
IsUpdateAvailable = false,
|
||||
CurrentVersion = currentVersion,
|
||||
LatestVersion = currentVersion
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,4 @@ namespace Jellyfin2Samsung
|
||||
.WithInterFont()
|
||||
.LogToTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Markup.Xaml.Styling;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.Markup.Xaml.Styling;
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung;
|
||||
using Jellyfin2Samsung.Helpers;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Services
|
||||
{
|
||||
|
||||
@@ -5,100 +5,120 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Jellyfin2Samsung.Services
|
||||
{
|
||||
public class LocalizationService : ILocalizationService
|
||||
{
|
||||
private const string DefaultLanguage = "en";
|
||||
private const string LocalizationFolderUri = "avares://Jellyfin2Samsung/Assets/Localization/";
|
||||
|
||||
private Dictionary<string, string> _currentStrings = new();
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _allStrings = new();
|
||||
private string _currentLanguage = "en";
|
||||
private string _currentLanguage = DefaultLanguage;
|
||||
|
||||
public string CurrentLanguage => _currentLanguage;
|
||||
public IEnumerable<string> AvailableLanguages => _allStrings.Keys;
|
||||
public IEnumerable<string> AvailableLanguages => _allStrings.Keys.OrderBy(x => x);
|
||||
public event EventHandler? LanguageChanged;
|
||||
|
||||
public LocalizationService()
|
||||
{
|
||||
LoadLanguagesAsync();
|
||||
LoadLanguages();
|
||||
}
|
||||
|
||||
private void LoadLanguagesAsync()
|
||||
private void LoadLanguages()
|
||||
{
|
||||
var languages = new[] { "en", "da", "nl", "fr", "de", "tr" };
|
||||
_allStrings.Clear();
|
||||
|
||||
foreach (var lang in languages)
|
||||
var folderUri = new Uri(LocalizationFolderUri);
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = new Uri($"avares://Jellyfin2Samsung/Assets/Localization/{lang}.json");
|
||||
var asset = AssetLoader.Open(uri);
|
||||
var assetUris = AssetLoader.GetAssets(folderUri, null);
|
||||
|
||||
using var reader = new StreamReader(asset);
|
||||
var json = reader.ReadToEnd();
|
||||
var strings = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
||||
|
||||
if (strings != null)
|
||||
{
|
||||
_allStrings[lang] = strings;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
foreach (var assetUri in assetUris)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine($"Failed to load language {lang}: {ex}");
|
||||
if (!assetUri.AbsolutePath.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
var fileName = Path.GetFileNameWithoutExtension(assetUri.AbsolutePath);
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
continue;
|
||||
|
||||
TryLoadLanguage(fileName);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Trace.WriteLine($"Failed to enumerate localization assets: {ex}");
|
||||
}
|
||||
|
||||
// Always make sure English is attempted as fallback language
|
||||
if (!_allStrings.ContainsKey(DefaultLanguage))
|
||||
{
|
||||
TryLoadLanguage(DefaultLanguage);
|
||||
}
|
||||
|
||||
// Set initial language based on system culture
|
||||
var systemLang = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
|
||||
string configLang = AppSettings.Default.Language;
|
||||
var configLang = AppSettings.Default.Language;
|
||||
|
||||
if (string.IsNullOrEmpty(configLang))
|
||||
var initialLang =
|
||||
!string.IsNullOrWhiteSpace(configLang) && _allStrings.ContainsKey(configLang)
|
||||
? configLang
|
||||
: _allStrings.ContainsKey(systemLang)
|
||||
? systemLang
|
||||
: DefaultLanguage;
|
||||
|
||||
SetLanguage(initialLang);
|
||||
}
|
||||
|
||||
private void TryLoadLanguage(string lang)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_allStrings.ContainsKey(systemLang))
|
||||
var uri = new Uri($"{LocalizationFolderUri}{lang}.json");
|
||||
|
||||
using var asset = AssetLoader.Open(uri);
|
||||
using var reader = new StreamReader(asset);
|
||||
var json = reader.ReadToEnd();
|
||||
|
||||
var strings = JsonSerializer.Deserialize<Dictionary<string, string>>(json);
|
||||
if (strings != null)
|
||||
{
|
||||
SetLanguage(systemLang);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLanguage("en");
|
||||
_allStrings[lang] = strings;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetLanguage(configLang);
|
||||
System.Diagnostics.Trace.WriteLine($"Failed to load language {lang}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public string GetString(string key)
|
||||
{
|
||||
if (_currentStrings.TryGetValue(key, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// Fallback to English if key not found in current language
|
||||
if (_currentLanguage != "en" && _allStrings.TryGetValue("en", out var englishStrings))
|
||||
{
|
||||
if (englishStrings.TryGetValue(key, out var englishValue))
|
||||
{
|
||||
return englishValue;
|
||||
}
|
||||
}
|
||||
if (_allStrings.TryGetValue(DefaultLanguage, out var englishStrings) &&
|
||||
englishStrings.TryGetValue(key, out var englishValue))
|
||||
return englishValue;
|
||||
|
||||
// Return the key itself if no translation found
|
||||
return key;
|
||||
}
|
||||
|
||||
public void SetLanguage(string languageCode)
|
||||
{
|
||||
if (_allStrings.TryGetValue(languageCode, out var strings))
|
||||
if (!_allStrings.TryGetValue(languageCode, out var strings))
|
||||
{
|
||||
_currentLanguage = languageCode;
|
||||
_currentStrings = strings;
|
||||
LanguageChanged?.Invoke(this, EventArgs.Empty);
|
||||
languageCode = DefaultLanguage;
|
||||
strings = _allStrings.GetValueOrDefault(DefaultLanguage, new Dictionary<string, string>());
|
||||
}
|
||||
|
||||
_currentLanguage = languageCode;
|
||||
_currentStrings = strings;
|
||||
LanguageChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -70,18 +69,20 @@ namespace Jellyfin2Samsung.Services
|
||||
public async Task<IEnumerable<NetworkDevice>> FindTizenTvsAsync(CancellationToken cancellationToken = default, bool virtualScan = false)
|
||||
{
|
||||
var foundDevices = new List<NetworkDevice>();
|
||||
var localIps = GetRelevantLocalIPs(virtualScan);
|
||||
var localInfos = GetLocalNetworkInfos(virtualScan);
|
||||
var lockObject = new object();
|
||||
|
||||
// Group by network prefix to avoid scanning the same network multiple times
|
||||
var uniqueNetworks = localIps
|
||||
.Select(ip => GetNetworkPrefix(ip))
|
||||
.Distinct()
|
||||
// Deduplicate by actual network address so overlapping interfaces don't double-scan
|
||||
var uniqueNetworks = localInfos
|
||||
.Select(info => (
|
||||
Network: GetNetworkAddress(info.Address, info.Mask),
|
||||
Broadcast: GetBroadcastAddress(info.Address, info.Mask)
|
||||
))
|
||||
.DistinctBy(r => r.Network.ToString())
|
||||
.ToList();
|
||||
|
||||
await Task.WhenAll(uniqueNetworks.SelectMany(networkPrefix =>
|
||||
Enumerable.Range(1, 254)
|
||||
.Select(i => $"{networkPrefix}.{i}")
|
||||
await Task.WhenAll(uniqueNetworks.SelectMany(range =>
|
||||
GetHostAddresses(range.Network, range.Broadcast)
|
||||
.Select(async ip =>
|
||||
{
|
||||
try
|
||||
@@ -169,10 +170,103 @@ namespace Jellyfin2Samsung.Services
|
||||
}
|
||||
}
|
||||
|
||||
private string GetNetworkPrefix(IPAddress ip)
|
||||
// Returns all local interface IPs with their actual subnet masks.
|
||||
// Falls back to /24 for the user-supplied custom IP since its mask can't be discovered.
|
||||
private List<(IPAddress Address, IPAddress Mask)> GetLocalNetworkInfos(bool virtualScan = false)
|
||||
{
|
||||
var infos = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.Where(ni =>
|
||||
virtualScan ||
|
||||
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet ||
|
||||
ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
|
||||
.SelectMany(ni => ni.GetIPProperties().UnicastAddresses)
|
||||
.Where(ua => ua.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
.Where(ua => !IPAddress.IsLoopback(ua.Address))
|
||||
.Where(ua => ua.IPv4Mask != null && !ua.IPv4Mask.Equals(IPAddress.Any))
|
||||
.Select(ua => (Address: ua.Address, Mask: ua.IPv4Mask))
|
||||
.ToList();
|
||||
|
||||
if (!string.IsNullOrEmpty(AppSettings.Default.UserCustomIP) &&
|
||||
IPAddress.TryParse(AppSettings.Default.UserCustomIP, out var customIp))
|
||||
{
|
||||
// Reuse the mask from a local interface whose network contains the custom IP;
|
||||
// otherwise fall back to /24 so we still scan the right /24 segment.
|
||||
var fallback = IPAddress.Parse("255.255.255.0");
|
||||
var matchingMask = infos
|
||||
.FirstOrDefault(i =>
|
||||
GetNetworkAddress(i.Address, i.Mask).Equals(GetNetworkAddress(customIp, i.Mask)))
|
||||
.Mask ?? fallback;
|
||||
infos.Add((customIp, matchingMask));
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
private static IPAddress GetNetworkAddress(IPAddress ip, IPAddress mask)
|
||||
{
|
||||
var ipBytes = ip.GetAddressBytes();
|
||||
var maskBytes = mask.GetAddressBytes();
|
||||
var result = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
result[i] = (byte)(ipBytes[i] & maskBytes[i]);
|
||||
return new IPAddress(result);
|
||||
}
|
||||
|
||||
private static IPAddress GetBroadcastAddress(IPAddress ip, IPAddress mask)
|
||||
{
|
||||
var ipBytes = ip.GetAddressBytes();
|
||||
var maskBytes = mask.GetAddressBytes();
|
||||
var result = new byte[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
result[i] = (byte)(ipBytes[i] | (byte)~maskBytes[i]);
|
||||
return new IPAddress(result);
|
||||
}
|
||||
|
||||
// Enumerates usable host addresses for a subnet (excludes network and broadcast addresses).
|
||||
// Caps at 1022 hosts (/22) to keep scans practical; larger subnets are narrowed to the
|
||||
// /24 block that contains the network address.
|
||||
private static IEnumerable<string> GetHostAddresses(IPAddress networkAddress, IPAddress broadcastAddress)
|
||||
{
|
||||
uint netInt = IpToUInt(networkAddress);
|
||||
uint broadInt = IpToUInt(broadcastAddress);
|
||||
uint hostCount = broadInt - netInt - 1;
|
||||
|
||||
if (hostCount > 1022)
|
||||
{
|
||||
// Narrow to /24 to avoid scanning thousands of addresses
|
||||
var bytes = networkAddress.GetAddressBytes();
|
||||
netInt = IpToUInt(new IPAddress(new byte[] { bytes[0], bytes[1], bytes[2], 0 }));
|
||||
broadInt = netInt + 255;
|
||||
}
|
||||
|
||||
for (uint i = netInt + 1; i < broadInt; i++)
|
||||
yield return UIntToIp(i);
|
||||
}
|
||||
|
||||
private static uint IpToUInt(IPAddress ip)
|
||||
{
|
||||
var bytes = ip.GetAddressBytes();
|
||||
return $"{bytes[0]}.{bytes[1]}.{bytes[2]}";
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
|
||||
private static string UIntToIp(uint value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
|
||||
return $"{bytes[0]}.{bytes[1]}.{bytes[2]}.{bytes[3]}";
|
||||
}
|
||||
|
||||
// Looks up the subnet mask assigned to a local interface IP.
|
||||
private static IPAddress? GetMaskForLocalIp(IPAddress target)
|
||||
{
|
||||
return NetworkInterface.GetAllNetworkInterfaces()
|
||||
.Where(ni => ni.OperationalStatus == OperationalStatus.Up)
|
||||
.SelectMany(ni => ni.GetIPProperties().UnicastAddresses)
|
||||
.Where(ua => ua.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
.FirstOrDefault(ua => ua.Address.Equals(target))
|
||||
?.IPv4Mask;
|
||||
}
|
||||
|
||||
public async Task<string?> GetManufacturerFromIp(string ipAddress)
|
||||
@@ -247,35 +341,54 @@ namespace Jellyfin2Samsung.Services
|
||||
Array.Reverse(parts);
|
||||
return string.Join(".", parts);
|
||||
}
|
||||
public async Task<string?> GetPrimaryOutboundIPAddressAsync()
|
||||
public bool IsDifferentSubnet(string ip1, string ip2)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
if (!IPAddress.TryParse(ip1, out var a) || !IPAddress.TryParse(ip2, out var b))
|
||||
return false;
|
||||
|
||||
// Use the actual mask from the local interface; fall back to /24 if not found
|
||||
var mask = GetMaskForLocalIp(a) ?? IPAddress.Parse("255.255.255.0");
|
||||
|
||||
var aBytes = a.GetAddressBytes();
|
||||
var bBytes = b.GetAddressBytes();
|
||||
var maskBytes = mask.GetAddressBytes();
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if ((aBytes[i] & maskBytes[i]) != (bBytes[i] & maskBytes[i]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public Task<IReadOnlyList<NetworkInterfaceOption>> GetNetworkInterfaceOptionsAsync()
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
var result = new List<NetworkInterfaceOption>();
|
||||
|
||||
foreach (var ni in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
var ipProps = ni.GetIPProperties();
|
||||
if (ni.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
var gateway = ipProps.GatewayAddresses
|
||||
.FirstOrDefault(g =>
|
||||
g.Address.AddressFamily == AddressFamily.InterNetwork &&
|
||||
!IPAddress.IsLoopback(g.Address));
|
||||
|
||||
if (gateway != null)
|
||||
foreach (var ua in ni.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
var ipv4 = ipProps.UnicastAddresses
|
||||
.FirstOrDefault(ua =>
|
||||
ua.Address.AddressFamily == AddressFamily.InterNetwork &&
|
||||
!IPAddress.IsLoopback(ua.Address));
|
||||
if (ua.Address.AddressFamily != AddressFamily.InterNetwork)
|
||||
continue;
|
||||
|
||||
if (ipv4 != null)
|
||||
return ipv4.Address.ToString();
|
||||
if (IPAddress.IsLoopback(ua.Address))
|
||||
continue;
|
||||
|
||||
result.Add(new NetworkInterfaceOption
|
||||
{
|
||||
Name = ni.Name,
|
||||
IpAddress = ua.Address.ToString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return (IReadOnlyList<NetworkInterfaceOption>)result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Jellyfin2Samsung.Extensions;
|
||||
using Jellyfin2Samsung.Extensions;
|
||||
using Jellyfin2Samsung.Helpers;
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Helpers.Tizen.Certificate;
|
||||
@@ -12,6 +12,7 @@ using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Pkcs;
|
||||
using Org.BouncyCastle.Security;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -27,7 +28,7 @@ namespace Jellyfin2Samsung.Services
|
||||
private readonly IDialogService _dialogService;
|
||||
|
||||
public TizenCertificateService(
|
||||
HttpClient httpClient,
|
||||
HttpClient httpClient,
|
||||
IDialogService dialogService)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
@@ -92,7 +93,9 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
private static byte[] GenerateAuthorCsr(AsymmetricCipherKeyPair keyPair)
|
||||
{
|
||||
var subject = new X509Name("C=, ST=, L=, O=, OU=, CN=Jelly2Sams");
|
||||
var subject = new X509Name(
|
||||
new ArrayList { X509Name.C, X509Name.ST, X509Name.L, X509Name.O, X509Name.OU, X509Name.CN },
|
||||
new ArrayList { "", "", "", "", "", "Jelly2Sams" });
|
||||
var csr = new Pkcs10CertificationRequest("SHA256withRSA", subject, keyPair.Public, null, keyPair.Private);
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
@@ -107,7 +110,9 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
private static byte[] GenerateDistributorCsr(AsymmetricCipherKeyPair keyPair, string duid, string userEmail)
|
||||
{
|
||||
var subject = new X509Name($"CN=TizenSDK, OU=, O=, L=, ST=, C=, emailAddress={userEmail}");
|
||||
var subject = new X509Name(
|
||||
new ArrayList { X509Name.CN, X509Name.OU, X509Name.O, X509Name.L, X509Name.ST, X509Name.C, X509Name.EmailAddress },
|
||||
new ArrayList { "TizenSDK", "", "", "", "", "", userEmail });
|
||||
var generalNames = new GeneralNames(new[]
|
||||
{
|
||||
new GeneralName(GeneralName.UniformResourceIdentifier, "URN:tizen:packageid="),
|
||||
@@ -146,7 +151,7 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
// Simplified Http Post for Author CSR
|
||||
private async Task<byte[]> PostAuthorCsrAsync(byte[] csrData, string accessToken, string userId)
|
||||
{
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, AppSettings.Default.AuthorEndpoint_V3);
|
||||
var content = new MultipartFormDataContent
|
||||
{
|
||||
@@ -198,7 +203,7 @@ namespace Jellyfin2Samsung.Services
|
||||
};
|
||||
|
||||
var v3Request = new HttpRequestMessage(HttpMethod.Post, AppSettings.Default.DistributorsEndpoint_V3);
|
||||
v3Request.Content = v3Content;
|
||||
v3Request.Content = v3Content;
|
||||
var v3Response = await _httpClient.SendAsync(v3Request);
|
||||
|
||||
if (!v3Response.IsSuccessStatusCode)
|
||||
@@ -323,4 +328,4 @@ namespace Jellyfin2Samsung.Services
|
||||
return PlatformService.GetX509KeyStorageFlags();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ namespace Jellyfin2Samsung.Services
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly AppSettings _appSettings;
|
||||
private readonly JellyfinPackagePatcher _jellyfinWebPackagePatcher;
|
||||
private readonly JellyfinApiClient _jellyfinApiClient;
|
||||
private readonly ProcessHelper _processHelper;
|
||||
|
||||
public string? TizenSdbPath { get; private set; }
|
||||
@@ -41,7 +40,6 @@ namespace Jellyfin2Samsung.Services
|
||||
_dialogService = dialogService;
|
||||
_appSettings = appSettings;
|
||||
_jellyfinWebPackagePatcher = jellyfinWebPackagePatcher;
|
||||
_jellyfinApiClient = jellyfinApiClient;
|
||||
_processHelper = processHelper;
|
||||
}
|
||||
|
||||
@@ -76,7 +74,7 @@ namespace Jellyfin2Samsung.Services
|
||||
return TizenSdbPath;
|
||||
}
|
||||
|
||||
string downloadedFile = await DownloadTizenSdbAsync(latestVersion);
|
||||
string downloadedFile = await DownloadTizenSdbAsync();
|
||||
|
||||
if (existingFile != null && File.Exists(existingFile))
|
||||
{
|
||||
@@ -154,25 +152,34 @@ namespace Jellyfin2Samsung.Services
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> DownloadTizenSdbAsync(string version = null)
|
||||
public async Task<string> DownloadTizenSdbAsync()
|
||||
{
|
||||
var json = await _httpClient.GetStringAsync(AppSettings.Default.TizenSdb);
|
||||
var releases = JsonSerializer.Deserialize<List<GitHubRelease>>(json, JsonSerializerOptionsProvider.Default);
|
||||
var firstRelease = releases?.FirstOrDefault();
|
||||
try
|
||||
{
|
||||
var json = await _httpClient.GetStringAsync(AppSettings.Default.TizenSdb);
|
||||
var releases = JsonSerializer.Deserialize<List<GitHubRelease>>(json, JsonSerializerOptionsProvider.Default);
|
||||
var firstRelease = (releases?.FirstOrDefault()) ?? throw new InvalidOperationException("No releases found");
|
||||
string nameMatch = PlatformService.GetAssetPlatformIdentifier();
|
||||
|
||||
if (firstRelease == null)
|
||||
throw new InvalidOperationException("No releases found");
|
||||
var matchedAsset = firstRelease.Assets.FirstOrDefault(a =>
|
||||
!string.IsNullOrEmpty(a.FileName) &&
|
||||
a.FileName.Contains(nameMatch, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
string nameMatch = PlatformService.GetAssetPlatformIdentifier();
|
||||
|
||||
var matchedAsset = firstRelease.Assets.FirstOrDefault(a =>
|
||||
!string.IsNullOrEmpty(a.FileName) &&
|
||||
a.FileName.Contains(nameMatch, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (matchedAsset == null)
|
||||
throw new InvalidOperationException($"No matching asset found for {nameMatch}");
|
||||
|
||||
return await DownloadPackageAsync(matchedAsset.DownloadUrl);
|
||||
return matchedAsset == null
|
||||
? throw new InvalidOperationException($"No matching asset found for {nameMatch}")
|
||||
: await DownloadPackageAsync(matchedAsset.DownloadUrl);
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.Forbidden)
|
||||
{
|
||||
throw new TimeoutException(
|
||||
"GitHub rate limit reached while checking for Tizen SDB.\n\n" +
|
||||
"To avoid this, set a GitHub Personal Access Token (PAT) in settings,\n" +
|
||||
"or set the GITHUB_TOKEN environment variable,\n" +
|
||||
"or install the GitHub CLI (gh) and run 'gh auth login'.\n\n" +
|
||||
"Alternatively, please try again later.",
|
||||
ex
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string> DownloadPackageAsync(string downloadUrl)
|
||||
@@ -217,7 +224,7 @@ namespace Jellyfin2Samsung.Services
|
||||
return InstallResult.FailureResult(Constants.LocalizationKeys.InstallTizenSdb.Localized());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// Step 1: Prepare device and check for existing installations
|
||||
@@ -235,8 +242,22 @@ namespace Jellyfin2Samsung.Services
|
||||
return InstallResult.FailureResult(Constants.LocalizationKeys.TvNameNotFound.Localized());
|
||||
}
|
||||
|
||||
// Step 3: Handle certificate selection/generation
|
||||
// Step 3: Check is WGT is compatible with TV's Tizen version
|
||||
var requiredTizenVersion = await FileHelper.ReadWgtRequiredVersion(packageUrl);
|
||||
if (requiredTizenVersion != null)
|
||||
{
|
||||
var requiredVersion = new Version(requiredTizenVersion);
|
||||
if (deviceInfo.TizenVersion < requiredVersion)
|
||||
{
|
||||
progress?.Invoke(Constants.LocalizationKeys.IncompatiblePackage.Localized());
|
||||
Trace.WriteLine($"Package requires Tizen {requiredVersion} but device has {deviceInfo.TizenVersion}");
|
||||
return InstallResult.FailureResult(string.Format(Constants.LocalizationKeys.IncompatiblePackageDetailed.Localized(), requiredTizenVersion, deviceInfo.TizenVersion));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Handle certificate selection/generation
|
||||
var certificateResult = await HandleCertificateAsync(
|
||||
tvIpAddress,
|
||||
deviceInfo,
|
||||
packageUrl,
|
||||
progress,
|
||||
@@ -246,12 +267,17 @@ namespace Jellyfin2Samsung.Services
|
||||
if (!certificateResult.Success)
|
||||
return certificateResult.InstallResult;
|
||||
|
||||
// Step 4: Apply Jellyfin configuration if needed
|
||||
await ApplyConfigurationAsync(packageUrl, progress);
|
||||
// Step 5: Apply Jellyfin configuration if needed
|
||||
if (packageUrl.Contains(Constants.AppIdentifiers.JellyfinAppName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Trace.WriteLine("Applying Jellyfin Configuration");
|
||||
await ApplyConfigurationAsync(packageUrl, progress);
|
||||
}
|
||||
|
||||
// Step 5: Resign package if needed
|
||||
// Step 6: Resign package if needed
|
||||
if (certificateResult.RequiresResign)
|
||||
{
|
||||
Trace.WriteLine("Resigning package with new certificate");
|
||||
progress?.Invoke(Constants.LocalizationKeys.PackageAndSign.Localized());
|
||||
var resignResults = await ResignPackageAsync(
|
||||
packageUrl,
|
||||
@@ -261,13 +287,14 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
if (resignResults.ExitCode != 0 || resignResults.Output.Contains(Constants.TizenErrorCodes.ResignFailed))
|
||||
{
|
||||
Trace.WriteLine($"Resign output: {resignResults.Output}");
|
||||
progress?.Invoke(Constants.LocalizationKeys.InstallationFailed.Localized());
|
||||
_appSettings.TryOverwrite = false;
|
||||
return InstallResult.FailureResult($"Package resigning failed: {resignResults.Output}");
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: Install package and handle results
|
||||
// Step 7: Install package and handle results
|
||||
progress?.Invoke(Constants.LocalizationKeys.InstallingPackage.Localized());
|
||||
|
||||
return await HandleInstallationResultAsync(
|
||||
@@ -308,6 +335,7 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
bool canDelete = await GetTvDiagnoseAsync(tvIpAddress);
|
||||
var (alreadyInstalled, appId) = await CheckForInstalledApp(tvIpAddress, packageUrl);
|
||||
Trace.WriteLine($"Diagnose canDelete: {canDelete}, alreadyInstalled: {alreadyInstalled}, appId: {appId}");
|
||||
|
||||
if (!canDelete && alreadyInstalled)
|
||||
{
|
||||
@@ -320,7 +348,11 @@ namespace Jellyfin2Samsung.Services
|
||||
if (_appSettings.DeletePreviousInstall)
|
||||
{
|
||||
progress?.Invoke(Constants.LocalizationKeys.DeleteExistingVersion.Localized());
|
||||
await UninstallPackageAsync(tvIpAddress, appId!);
|
||||
var uninstallResult = await UninstallPackageAsync(tvIpAddress, appId!);
|
||||
Trace.WriteLine($"Uninstall output: {uninstallResult.Output}");
|
||||
if (uninstallResult.Output.Contains(Constants.TizenErrorCodes.NotInstalled))
|
||||
return InstallResult.SuccessResult();
|
||||
|
||||
|
||||
var (stillInstalled, _) = await CheckForInstalledApp(tvIpAddress, packageUrl);
|
||||
if (stillInstalled)
|
||||
@@ -351,8 +383,7 @@ namespace Jellyfin2Samsung.Services
|
||||
if (string.IsNullOrEmpty(tvDuid))
|
||||
return null;
|
||||
|
||||
string tizenOs = await FetchTizenOsAsync(tvIpAddress);
|
||||
string sdkToolPath = await FetchSdkPathAsync(tvIpAddress);
|
||||
var (tizenOs, sdkToolPath) = await FetchCapabilitiesAsync(tvIpAddress);
|
||||
|
||||
if (string.IsNullOrEmpty(tizenOs))
|
||||
tizenOs = Constants.Defaults.TizenOsVersion;
|
||||
@@ -371,6 +402,7 @@ namespace Jellyfin2Samsung.Services
|
||||
#region Certificate Handling
|
||||
|
||||
private async Task<CertificateResult> HandleCertificateAsync(
|
||||
string tvIpAddress,
|
||||
DeviceInfo deviceInfo,
|
||||
string packageUrl,
|
||||
ProgressCallback? progress,
|
||||
@@ -472,7 +504,7 @@ namespace Jellyfin2Samsung.Services
|
||||
? Constants.Defaults.HomeDeveloperPath
|
||||
: deviceInfo.SdkToolPath;
|
||||
|
||||
await AllowPermitInstall(tvIpAddress: deviceInfo.Duid, deviceProfilePath, targetPath);
|
||||
await AllowPermitInstall(tvIpAddress, deviceProfilePath, targetPath);
|
||||
}
|
||||
|
||||
return new CertificateResult
|
||||
@@ -491,11 +523,13 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
private async Task ApplyConfigurationAsync(string packageUrl, ProgressCallback? progress)
|
||||
{
|
||||
// Only apply configuration if JellyfinIP is set and this is a Jellyfin package
|
||||
if (string.IsNullOrEmpty(_appSettings.JellyfinIP))
|
||||
return;
|
||||
|
||||
if (!packageUrl.Contains(Constants.AppIdentifiers.JellyfinAppName, StringComparison.OrdinalIgnoreCase))
|
||||
var name = Path.GetFileName(packageUrl);
|
||||
var isJellyfinPackage = name.Contains("jellyfin", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!isJellyfinPackage)
|
||||
return;
|
||||
|
||||
// Apply server settings via JS injection
|
||||
@@ -523,11 +557,13 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
if (_appSettings.TryOverwrite)
|
||||
{
|
||||
Trace.WriteLine("Installation failed, insufficient space! retrying with remove previous version");
|
||||
_appSettings.TryOverwrite = false;
|
||||
return await InstallPackageAsync(packageUrl, tvIpAddress, cancellationToken, progress, onSamsungLoginStarted);
|
||||
}
|
||||
|
||||
_appSettings.TryOverwrite = false;
|
||||
Trace.WriteLine("Installation failed, insufficient space!");
|
||||
return InstallResult.FailureResult($"Installation failed: {Constants.LocalizationKeys.InsufficientSpace.Localized()}");
|
||||
}
|
||||
|
||||
@@ -541,6 +577,7 @@ namespace Jellyfin2Samsung.Services
|
||||
{
|
||||
_appSettings.TryOverwrite = false;
|
||||
_appSettings.ForceSamsungLogin = true;
|
||||
_appSettings.DeletePreviousInstall = true;
|
||||
return await InstallPackageAsync(packageUrl, tvIpAddress, cancellationToken, progress, onSamsungLoginStarted);
|
||||
}
|
||||
|
||||
@@ -620,18 +657,17 @@ namespace Jellyfin2Samsung.Services
|
||||
return output.Output.Split('\n', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()?.Trim() ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> FetchTizenOsAsync(string tvIpAddress)
|
||||
private async Task<(string tizenOs, string sdkToolPath)> FetchCapabilitiesAsync(string tvIpAddress)
|
||||
{
|
||||
var output = await _processHelper.RunCommandAsync(TizenSdbPath!, $"capability {tvIpAddress}");
|
||||
var match = RegexPatterns.TizenCapability.PlatformVersion.Match(output.Output);
|
||||
return match.Success ? match.Groups[1].Value.Trim() : string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> FetchSdkPathAsync(string tvIpAddress)
|
||||
{
|
||||
var output = await _processHelper.RunCommandAsync(TizenSdbPath!, $"capability {tvIpAddress}");
|
||||
var match = RegexPatterns.TizenCapability.SdkToolPath.Match(output.Output);
|
||||
return match.Success ? match.Groups[1].Value.Trim() : Constants.Defaults.SdkToolPath;
|
||||
var versionMatch = RegexPatterns.TizenCapability.PlatformVersion.Match(output.Output);
|
||||
string tizenOs = versionMatch.Success ? versionMatch.Groups[1].Value.Trim() : string.Empty;
|
||||
|
||||
var pathMatch = RegexPatterns.TizenCapability.SdkToolPath.Match(output.Output);
|
||||
string sdkToolPath = pathMatch.Success ? pathMatch.Groups[1].Value.Trim() : Constants.Defaults.SdkToolPath;
|
||||
|
||||
return (tizenOs, sdkToolPath);
|
||||
}
|
||||
|
||||
private async Task<string> GetTvDuidAsync(string tvIpAddress)
|
||||
@@ -649,24 +685,42 @@ namespace Jellyfin2Samsung.Services
|
||||
|
||||
private async Task<(bool isInstalled, string? appId)> CheckForInstalledApp(string tvIpAddress, string packageUrl)
|
||||
{
|
||||
var output = await _processHelper.RunCommandAsync(TizenSdbPath!, $"apps {tvIpAddress}");
|
||||
var result = await _processHelper.RunCommandAsync(TizenSdbPath!, $"apps {tvIpAddress}");
|
||||
var output = result?.Output ?? string.Empty;
|
||||
|
||||
// Read what the WGT *claims* its app id is (best effort fallback for "no listing" cases)
|
||||
var wgtAppId = await FileHelper.ReadWgtApplicationId(packageUrl);
|
||||
|
||||
// Case 3: no listing -> assume installed, return WGT app id as best-effort
|
||||
if (string.IsNullOrWhiteSpace(output) ||
|
||||
output.Contains("Could not retrieve app list", StringComparison.OrdinalIgnoreCase) ||
|
||||
output.Contains("Remote closed channel", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return (true, wgtAppId);
|
||||
}
|
||||
|
||||
// Case 1/2: listing returned -> parse TV output
|
||||
var baseSearch = Path.GetFileNameWithoutExtension(packageUrl).Split('-')[0];
|
||||
|
||||
var blockRegex = RegexPatterns.TizenApp.CreateAppBlockByTitleRegex(baseSearch);
|
||||
var blockMatch = blockRegex.Match(output.Output);
|
||||
var blockMatch = blockRegex.Match(output);
|
||||
|
||||
// Case 2: listing returned but not present
|
||||
if (!blockMatch.Success)
|
||||
return (false, null);
|
||||
|
||||
// Case 1: listing returned and present -> TV's id is the uninstall/overwrite truth
|
||||
var block = blockMatch.Value;
|
||||
var appIdMatch = RegexPatterns.TizenApp.AppTizenId.Match(block);
|
||||
string tvAppId = appIdMatch.Groups[1].Value.Trim();
|
||||
string? packageAppId = await FileHelper.ReadWgtPackageId(packageUrl);
|
||||
var tvAppId = appIdMatch.Success ? appIdMatch.Groups[1].Value.Trim() : null;
|
||||
|
||||
if (tvAppId == $"{packageAppId}.{Constants.AppIdentifiers.JellyfinAppName}")
|
||||
return (true, tvAppId);
|
||||
// If we matched by title but ID isn't matching Config ID (jellyfin-secondary)
|
||||
Debug.WriteLine($"TV APP ID: {tvAppId} - CONFIG APP ID: {wgtAppId}");
|
||||
Trace.WriteLine($"TV APP ID: {tvAppId} - CONFIG APP ID: {wgtAppId}");
|
||||
if (tvAppId != wgtAppId)
|
||||
return (false, null);
|
||||
|
||||
return (false, null);
|
||||
// If we matched by title but couldn't parse ID, fall back to WGT ID
|
||||
return (true, !string.IsNullOrWhiteSpace(tvAppId) ? tvAppId : wgtAppId);
|
||||
}
|
||||
|
||||
private async Task<string> GetInstalledAppId(string tvIpAddress, string appTitle)
|
||||
|
||||
101
Jellyfin2Samsung-CrossOS/Services/UpdateDialogService.cs
Normal file
101
Jellyfin2Samsung-CrossOS/Services/UpdateDialogService.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Threading;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using Jellyfin2Samsung.Views;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for showing update-related dialogs.
|
||||
/// </summary>
|
||||
public class UpdateDialogService : IUpdateDialogService
|
||||
{
|
||||
private readonly IDialogService _dialogService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private UpdateDialog? _currentDialog;
|
||||
|
||||
public UpdateDialogService(IDialogService dialogService, ILocalizationService localizationService)
|
||||
{
|
||||
_dialogService = dialogService;
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
private Window? GetMainWindow()
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
return desktop.MainWindow;
|
||||
return null;
|
||||
}
|
||||
|
||||
private string L(string key) => _localizationService.GetString(key);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<UpdateDialogChoice> ShowUpdateAvailableDialogAsync(UpdateCheckResult updateInfo)
|
||||
{
|
||||
var mainWindow = GetMainWindow();
|
||||
if (mainWindow == null)
|
||||
return UpdateDialogChoice.Cancel;
|
||||
|
||||
// Ensure we're on the UI thread
|
||||
if (!Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
return await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
await ShowUpdateAvailableDialogAsync(updateInfo));
|
||||
}
|
||||
|
||||
_currentDialog = new UpdateDialog();
|
||||
_currentDialog.Initialize(updateInfo);
|
||||
|
||||
var result = await _currentDialog.ShowDialogAsync(mainWindow);
|
||||
_currentDialog = null;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> ShowDownloadProgressAsync(IProgress<int> progress)
|
||||
{
|
||||
// This is handled within the UpdateDialog itself via the IsDownloading property
|
||||
// The progress is reported through the dialog's UpdateProgress method
|
||||
await Task.CompletedTask;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ShowApplyingUpdateMessageAsync()
|
||||
{
|
||||
var mainWindow = GetMainWindow();
|
||||
if (mainWindow == null)
|
||||
return;
|
||||
|
||||
if (!Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
await ShowApplyingUpdateMessageAsync());
|
||||
return;
|
||||
}
|
||||
|
||||
await _dialogService.ShowMessageAsync(
|
||||
L("UpdateApplying"),
|
||||
L("UpdateApplyingMessage"));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ShowUpdateErrorAsync(string errorMessage)
|
||||
{
|
||||
if (!Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
await ShowUpdateErrorAsync(errorMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
await _dialogService.ShowErrorAsync($"{L("UpdateError")}: {errorMessage}");
|
||||
}
|
||||
}
|
||||
}
|
||||
484
Jellyfin2Samsung-CrossOS/Services/UpdaterService.cs
Normal file
484
Jellyfin2Samsung-CrossOS/Services/UpdaterService.cs
Normal file
@@ -0,0 +1,484 @@
|
||||
using Jellyfin2Samsung.Helpers;
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Jellyfin2Samsung.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for checking and applying application updates via GitHub.
|
||||
/// Uses the Atom feed endpoint to avoid API rate limiting.
|
||||
/// </summary>
|
||||
public class UpdaterService : IUpdaterService
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
private const string RepoOwner = "Jellyfin2Samsung";
|
||||
private const string RepoName = "Samsung-Jellyfin-Installer";
|
||||
private const string AtomFeedUrl = $"https://github.com/{RepoOwner}/{RepoName}/releases.atom";
|
||||
private const string ReleasesApiUrl = $"https://api.github.com/repos/{RepoOwner}/{RepoName}/releases/latest";
|
||||
|
||||
public string ReleasesPageUrl => $"https://github.com/{RepoOwner}/{RepoName}/releases";
|
||||
public string CurrentVersion => AppSettings.Default.AppVersion;
|
||||
|
||||
public UpdaterService(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<UpdateCheckResult> CheckForUpdateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
// First try Atom feed (no rate limit)
|
||||
var atomResult = await CheckViaAtomFeedAsync(cancellationToken);
|
||||
if (atomResult.IsSuccess && atomResult.IsUpdateAvailable)
|
||||
{
|
||||
// Get download URL from API (only if update available)
|
||||
await EnrichWithDownloadUrlAsync(atomResult, cancellationToken);
|
||||
}
|
||||
return atomResult;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Update check failed: {ex}");
|
||||
return UpdateCheckResult.Failed($"Failed to check for updates: {ex.Message}", CurrentVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<UpdateCheckResult> CheckViaAtomFeedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, AtomFeedUrl);
|
||||
request.Headers.Accept.ParseAdd("application/atom+xml");
|
||||
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var atomXml = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
var latestEntry = ParseAtomFeed(atomXml);
|
||||
|
||||
if (latestEntry == null)
|
||||
{
|
||||
return UpdateCheckResult.NoUpdateAvailable(CurrentVersion);
|
||||
}
|
||||
|
||||
var latestVersion = latestEntry.TagName;
|
||||
var isUpdateAvailable = IsVersionGreater(latestVersion, CurrentVersion);
|
||||
|
||||
return new UpdateCheckResult
|
||||
{
|
||||
IsUpdateAvailable = isUpdateAvailable,
|
||||
CurrentVersion = CurrentVersion,
|
||||
LatestVersion = latestVersion,
|
||||
ReleaseTitle = latestEntry.Title,
|
||||
ReleaseNotes = HtmlUtils.StripHtml(HtmlUtils.RemoveMarkdownTable(latestEntry.Content)),
|
||||
ReleasesPageUrl = latestEntry.Link,
|
||||
PublishedAt = latestEntry.Updated
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Atom feed check failed: {ex}");
|
||||
return UpdateCheckResult.Failed($"Failed to parse release feed: {ex.Message}", CurrentVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private GitHubAtomEntry? ParseAtomFeed(string atomXml)
|
||||
{
|
||||
try
|
||||
{
|
||||
var doc = XDocument.Parse(atomXml);
|
||||
XNamespace atom = "http://www.w3.org/2005/Atom";
|
||||
|
||||
// Get all entries and filter out beta/pre-releases
|
||||
var entry = doc.Descendants(atom + "entry").FirstOrDefault(e =>
|
||||
{
|
||||
var title = e.Element(atom + "title")?.Value ?? string.Empty;
|
||||
|
||||
// Filter out entries with beta
|
||||
return !title.Contains("beta", StringComparison.OrdinalIgnoreCase);
|
||||
});
|
||||
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
return new GitHubAtomEntry
|
||||
{
|
||||
Id = entry.Element(atom + "id")?.Value ?? string.Empty,
|
||||
Title = entry.Element(atom + "title")?.Value ?? string.Empty,
|
||||
Updated = DateTime.TryParse(entry.Element(atom + "updated")?.Value, out var updated) ? updated : null,
|
||||
Link = entry.Element(atom + "link")?.Attribute("href")?.Value ?? string.Empty,
|
||||
Content = entry.Element(atom + "content")?.Value ?? string.Empty,
|
||||
AuthorName = entry.Element(atom + "author")?.Element(atom + "name")?.Value ?? string.Empty
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to parse Atom feed: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task EnrichWithDownloadUrlAsync(UpdateCheckResult result, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var request = new HttpRequestMessage(HttpMethod.Get, ReleasesApiUrl);
|
||||
using var response = await _httpClient.SendAsync(request, cancellationToken);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
// API rate limited or error - user can still use manual download
|
||||
Trace.WriteLine($"GitHub API returned {response.StatusCode}, download URL unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync(cancellationToken);
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
var root = doc.RootElement;
|
||||
|
||||
if (!root.TryGetProperty("assets", out var assets))
|
||||
return;
|
||||
|
||||
var platformSuffix = GetPlatformSuffix();
|
||||
foreach (var asset in assets.EnumerateArray())
|
||||
{
|
||||
if (!asset.TryGetProperty("name", out var nameElement))
|
||||
continue;
|
||||
|
||||
var name = nameElement.GetString() ?? string.Empty;
|
||||
|
||||
// Match platform-specific archive
|
||||
if (name.Contains(platformSuffix, StringComparison.OrdinalIgnoreCase) &&
|
||||
(name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) ||
|
||||
name.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
if (asset.TryGetProperty("browser_download_url", out var urlElement))
|
||||
{
|
||||
result.DownloadUrl = urlElement.GetString();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to find any zip file
|
||||
foreach (var asset in assets.EnumerateArray())
|
||||
{
|
||||
if (!asset.TryGetProperty("name", out var nameElement))
|
||||
continue;
|
||||
|
||||
var name = nameElement.GetString() ?? string.Empty;
|
||||
if (name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (asset.TryGetProperty("browser_download_url", out var urlElement))
|
||||
{
|
||||
result.DownloadUrl = urlElement.GetString();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to get download URL: {ex}");
|
||||
// Not critical - user can still download manually
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetPlatformSuffix()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return "win";
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
return "linux";
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
return "osx";
|
||||
|
||||
return "win"; // Default fallback
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> DownloadUpdateAsync(
|
||||
string downloadUrl,
|
||||
IProgress<int>? progress = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), "Jellyfin2Samsung_Update");
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
var fileName = Path.GetFileName(new Uri(downloadUrl).LocalPath);
|
||||
var downloadPath = Path.Combine(tempDir, fileName);
|
||||
|
||||
// Clean up old downloads
|
||||
if (File.Exists(downloadPath))
|
||||
File.Delete(downloadPath);
|
||||
|
||||
using var response = await _httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
|
||||
var downloadedBytes = 0L;
|
||||
|
||||
await using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||
await using var fileStream = new FileStream(downloadPath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
||||
|
||||
var buffer = new byte[8192];
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = await contentStream.ReadAsync(buffer, cancellationToken)) > 0)
|
||||
{
|
||||
await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
|
||||
downloadedBytes += bytesRead;
|
||||
|
||||
if (totalBytes > 0)
|
||||
{
|
||||
var percentage = (int)((downloadedBytes * 100) / totalBytes);
|
||||
progress?.Report(percentage);
|
||||
}
|
||||
}
|
||||
|
||||
progress?.Report(100);
|
||||
return downloadPath;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> ApplyUpdateAsync(string downloadedFilePath, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var appDir = AppContext.BaseDirectory;
|
||||
var updateDir = Path.Combine(Path.GetTempPath(), "Jellyfin2Samsung_Update", "extracted");
|
||||
var backupDir = Path.Combine(Path.GetTempPath(), "Jellyfin2Samsung_Update", "backup");
|
||||
|
||||
// Clean extraction directory
|
||||
if (Directory.Exists(updateDir))
|
||||
Directory.Delete(updateDir, true);
|
||||
Directory.CreateDirectory(updateDir);
|
||||
|
||||
// Extract update
|
||||
if (downloadedFilePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ZipFile.ExtractToDirectory(downloadedFilePath, updateDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException("Only ZIP archives are supported for automatic updates.");
|
||||
}
|
||||
|
||||
// Find the actual application directory (might be in a subfolder)
|
||||
var extractedAppDir = FindApplicationDirectory(updateDir);
|
||||
if (extractedAppDir == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find application files in the update package.");
|
||||
}
|
||||
|
||||
// Create update script
|
||||
var scriptPath = CreateUpdateScript(extractedAppDir, appDir, backupDir);
|
||||
|
||||
// Launch the update script and exit
|
||||
LaunchUpdateScript(scriptPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to apply update: {ex}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string? FindApplicationDirectory(string extractedDir)
|
||||
{
|
||||
// Check if the main executable is directly in the extracted directory
|
||||
var exeName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? "Jellyfin2Samsung.exe"
|
||||
: "Jellyfin2Samsung";
|
||||
|
||||
if (File.Exists(Path.Combine(extractedDir, exeName)))
|
||||
return extractedDir;
|
||||
|
||||
// Check subdirectories
|
||||
foreach (var subDir in Directory.GetDirectories(extractedDir))
|
||||
{
|
||||
if (File.Exists(Path.Combine(subDir, exeName)))
|
||||
return subDir;
|
||||
|
||||
// Check one level deeper
|
||||
foreach (var subSubDir in Directory.GetDirectories(subDir))
|
||||
{
|
||||
if (File.Exists(Path.Combine(subSubDir, exeName)))
|
||||
return subSubDir;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private string CreateUpdateScript(string sourceDir, string targetDir, string backupDir)
|
||||
{
|
||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
var scriptExtension = isWindows ? ".bat" : ".sh";
|
||||
var scriptPath = Path.Combine(Path.GetTempPath(), $"jellyfin2samsung_update{scriptExtension}");
|
||||
|
||||
var exeName = isWindows ? "Jellyfin2Samsung.exe" : "Jellyfin2Samsung";
|
||||
var processId = Environment.ProcessId;
|
||||
|
||||
string scriptContent;
|
||||
|
||||
if (isWindows)
|
||||
{
|
||||
scriptContent = $@"@echo off
|
||||
chcp 65001 > nul
|
||||
echo Waiting for application to close...
|
||||
:waitloop
|
||||
tasklist /FI ""PID eq {processId}"" 2>NUL | find /I ""{processId}"" >NUL
|
||||
if not errorlevel 1 (
|
||||
timeout /t 1 /nobreak > nul
|
||||
goto waitloop
|
||||
)
|
||||
|
||||
echo Creating backup...
|
||||
if exist ""{backupDir}"" rmdir /s /q ""{backupDir}""
|
||||
mkdir ""{backupDir}""
|
||||
xcopy ""{targetDir}\*"" ""{backupDir}\"" /E /H /Y /Q
|
||||
|
||||
echo Installing update...
|
||||
xcopy ""{sourceDir}\*"" ""{targetDir}\"" /E /H /Y /Q
|
||||
|
||||
echo Starting application...
|
||||
start """" ""{Path.Combine(targetDir, exeName)}""
|
||||
|
||||
echo Cleaning up...
|
||||
timeout /t 2 /nobreak > nul
|
||||
rmdir /s /q ""{Path.Combine(Path.GetTempPath(), "Jellyfin2Samsung_Update")}""
|
||||
|
||||
del ""%~f0""
|
||||
";
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptContent = $@"#!/bin/bash
|
||||
echo ""Waiting for application to close...""
|
||||
while kill -0 {processId} 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo ""Creating backup...""
|
||||
rm -rf ""{backupDir}""
|
||||
mkdir -p ""{backupDir}""
|
||||
cp -r ""{targetDir}/""* ""{backupDir}/""
|
||||
|
||||
echo ""Installing update...""
|
||||
cp -rf ""{sourceDir}/""* ""{targetDir}/""
|
||||
chmod +x ""{Path.Combine(targetDir, exeName)}""
|
||||
|
||||
echo ""Starting application...""
|
||||
nohup ""{Path.Combine(targetDir, exeName)}"" &
|
||||
|
||||
echo ""Cleaning up...""
|
||||
sleep 2
|
||||
rm -rf ""{Path.Combine(Path.GetTempPath(), "Jellyfin2Samsung_Update")}""
|
||||
rm -- ""$0""
|
||||
";
|
||||
}
|
||||
|
||||
File.WriteAllText(scriptPath, scriptContent);
|
||||
|
||||
if (!isWindows)
|
||||
{
|
||||
// Make script executable on Unix
|
||||
Process.Start("chmod", $"+x \"{scriptPath}\"")?.WaitForExit();
|
||||
}
|
||||
|
||||
return scriptPath;
|
||||
}
|
||||
|
||||
private void LaunchUpdateScript(string scriptPath)
|
||||
{
|
||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = true,
|
||||
CreateNoWindow = !isWindows, // Show window on Windows for user feedback
|
||||
WindowStyle = isWindows ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden
|
||||
};
|
||||
|
||||
if (isWindows)
|
||||
{
|
||||
startInfo.FileName = "cmd.exe";
|
||||
startInfo.Arguments = $"/c \"{scriptPath}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
startInfo.FileName = "/bin/bash";
|
||||
startInfo.Arguments = scriptPath;
|
||||
}
|
||||
|
||||
Process.Start(startInfo);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenReleasesPage()
|
||||
{
|
||||
try
|
||||
{
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = ReleasesPageUrl,
|
||||
UseShellExecute = true
|
||||
};
|
||||
Process.Start(psi);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to open releases page: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsVersionGreater(string latestVersion, string currentVersion)
|
||||
{
|
||||
// Clean version strings
|
||||
var latestClean = CleanVersionString(latestVersion);
|
||||
var currentClean = CleanVersionString(currentVersion);
|
||||
|
||||
if (Version.TryParse(latestClean, out var latest) &&
|
||||
Version.TryParse(currentClean, out var current))
|
||||
{
|
||||
return latest > current;
|
||||
}
|
||||
|
||||
// Fallback to string comparison
|
||||
return string.Compare(latestClean, currentClean, StringComparison.OrdinalIgnoreCase) > 0;
|
||||
}
|
||||
|
||||
private static string CleanVersionString(string version)
|
||||
{
|
||||
if (string.IsNullOrEmpty(version))
|
||||
return "0.0.0";
|
||||
|
||||
// Remove 'v' prefix
|
||||
var cleaned = version.TrimStart('v', 'V');
|
||||
|
||||
// Remove suffixes like -beta, -alpha, -rc
|
||||
var dashIndex = cleaned.IndexOf('-');
|
||||
if (dashIndex > 0)
|
||||
cleaned = cleaned.Substring(0, dashIndex);
|
||||
|
||||
return cleaned;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.Templates;
|
||||
using Jellyfin2Samsung.ViewModels;
|
||||
using System;
|
||||
|
||||
namespace Jellyfin2Samsung
|
||||
{
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Jellyfin2Samsung.Helpers;
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.ViewModels
|
||||
{
|
||||
@@ -17,21 +22,53 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
public ObservableCollection<BuildVersion> JellyfinVersions { get; } = new();
|
||||
public ObservableCollection<BuildVersion> CommunityApps { get; } = new();
|
||||
|
||||
public ObservableCollection<ProviderOption> ProviderOptions { get; } = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private ProviderOption? selectedProviderOption;
|
||||
|
||||
private static readonly HttpClient _http = new();
|
||||
private readonly Dictionary<string, Bitmap?> _bitmapCache = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
private bool _isLoading;
|
||||
private int _rebuildVersion;
|
||||
|
||||
public BuildInfoViewModel()
|
||||
{
|
||||
CommunityApps.CollectionChanged += (_, __) =>
|
||||
{
|
||||
if (_isLoading) return;
|
||||
QueueRebuild();
|
||||
};
|
||||
|
||||
JellyfinVersions.CollectionChanged += (_, __) =>
|
||||
{
|
||||
if (_isLoading) return;
|
||||
QueueRebuild();
|
||||
};
|
||||
|
||||
_ = LoadAsync();
|
||||
}
|
||||
|
||||
private void QueueRebuild()
|
||||
{
|
||||
// Start a rebuild, but only the latest one is allowed to apply results
|
||||
var version = Interlocked.Increment(ref _rebuildVersion);
|
||||
_ = RebuildProviderOptionsAsync(version);
|
||||
}
|
||||
|
||||
public async Task LoadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var client = new HttpClient();
|
||||
_isLoading = true;
|
||||
|
||||
var jellyfinMd = await client.GetStringAsync(AppSettings.Default.ReleaseInfo);
|
||||
var communityMd = await client.GetStringAsync(AppSettings.Default.CommunityInfo);
|
||||
var jellyfinMd = await _http.GetStringAsync(AppSettings.Default.ReleaseInfo);
|
||||
var communityMd = await _http.GetStringAsync(AppSettings.Default.CommunityInfo);
|
||||
|
||||
JellyfinVersions.Clear();
|
||||
CommunityApps.Clear();
|
||||
|
||||
// Parse Jellyfin table
|
||||
ParseVersionsTable(jellyfinMd, JellyfinVersions);
|
||||
|
||||
JellyfinVersions.Add(new BuildVersion
|
||||
@@ -40,7 +77,12 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
Description = "Moonfin is optimized for the viewing experience on Samsung Smart TVs."
|
||||
});
|
||||
|
||||
// Static Jellyfin entries
|
||||
JellyfinVersions.Add(new BuildVersion
|
||||
{
|
||||
FileName = "Litefin",
|
||||
Description = "Litefin is designed to provide a premium media browsing and playback experience, even on legacy hardware."
|
||||
});
|
||||
|
||||
JellyfinVersions.Add(new BuildVersion
|
||||
{
|
||||
FileName = "Legacy",
|
||||
@@ -56,19 +98,144 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
JellyfinVersions.Add(new BuildVersion
|
||||
{
|
||||
FileName = "AVPlay 10.10.z - SmartHub",
|
||||
Description = "Includes AVPlay video player patches for better Samsung TV compatibility for10.10.z SmartHub variant"
|
||||
Description = "Includes AVPlay video player patches for better Samsung TV compatibility for 10.10.z SmartHub variant"
|
||||
});
|
||||
|
||||
// Parse community apps
|
||||
ParseApplicationsTable(communityMd, CommunityApps);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to load build info: {ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isLoading = false;
|
||||
}
|
||||
|
||||
// Do a single authoritative rebuild after load finishes
|
||||
var version = Interlocked.Increment(ref _rebuildVersion);
|
||||
await RebuildProviderOptionsAsync(version);
|
||||
|
||||
if (SelectedProviderOption is null && ProviderOptions.Count > 0)
|
||||
SelectedProviderOption = ProviderOptions[0];
|
||||
}
|
||||
|
||||
// Remove markdown formatting like **bold**, emoji, etc.
|
||||
private async Task RebuildProviderOptionsAsync(int version)
|
||||
{
|
||||
// If a newer rebuild was queued, abandon this one
|
||||
if (version != Volatile.Read(ref _rebuildVersion))
|
||||
return;
|
||||
|
||||
// Map ONLY the apps that truly need unique thumbnails
|
||||
var communityPreviewUrls = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "Moonlight", Constants.PreviewImages.Moonlight },
|
||||
{ "Fireplace", Constants.PreviewImages.Fireplace },
|
||||
{ "TVApp", Constants.PreviewImages.TVApp },
|
||||
{ "Twitch", Constants.PreviewImages.Twitch },
|
||||
{ "Club Info Board", Constants.PreviewImages.ClubInfoBoard },
|
||||
{ "Doom", Constants.PreviewImages.Doom },
|
||||
{ "TransportTycoonDeluxe", Constants.PreviewImages.TTD },
|
||||
};
|
||||
|
||||
// Exceptions inside JellyfinVersions that should NOT use Jellyfin image
|
||||
var jellyfinOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "Moonfin", Constants.PreviewImages.Moonfin },
|
||||
{ "Litefin", Constants.PreviewImages.Litefin },
|
||||
};
|
||||
|
||||
var jellyfinBitmap = await LoadBitmapAsync(Constants.PreviewImages.Jellyfin);
|
||||
|
||||
// Build locally (don’t mutate ObservableCollection from background thread)
|
||||
var built = new List<ProviderOption>();
|
||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
void AddIfNew(string name, Bitmap? bmp)
|
||||
{
|
||||
name = (name ?? string.Empty).Trim();
|
||||
if (name.Length == 0) return;
|
||||
if (!seen.Add(name)) return;
|
||||
|
||||
built.Add(new ProviderOption
|
||||
{
|
||||
DisplayName = name,
|
||||
PreviewImage = bmp
|
||||
});
|
||||
}
|
||||
|
||||
// 1) Jellyfin top entry
|
||||
AddIfNew("Jellyfin", jellyfinBitmap);
|
||||
|
||||
// 2) Community apps
|
||||
foreach (var app in CommunityApps)
|
||||
{
|
||||
var name = (app.FileName ?? string.Empty).Trim();
|
||||
if (name.Length == 0) continue;
|
||||
|
||||
var url = communityPreviewUrls.FirstOrDefault(kvp =>
|
||||
name.Contains(kvp.Key, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
|
||||
var bmp = url is not null ? await LoadBitmapAsync(url) : null;
|
||||
|
||||
AddIfNew(name, bmp);
|
||||
}
|
||||
|
||||
// 3) Jellyfin builds (default Jellyfin image, override forks like Moonfin)
|
||||
foreach (var build in JellyfinVersions)
|
||||
{
|
||||
var name = (build.FileName ?? string.Empty).Trim();
|
||||
if (name.Length == 0) continue;
|
||||
|
||||
Bitmap? bmp = jellyfinBitmap;
|
||||
|
||||
var overrideUrl = jellyfinOverrides.FirstOrDefault(kvp =>
|
||||
name.Contains(kvp.Key, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
|
||||
if (overrideUrl is not null)
|
||||
bmp = await LoadBitmapAsync(overrideUrl);
|
||||
|
||||
AddIfNew(name, bmp);
|
||||
}
|
||||
|
||||
// If a newer rebuild was queued while we were downloading images, abandon this one
|
||||
if (version != Volatile.Read(ref _rebuildVersion))
|
||||
return;
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
ProviderOptions.Clear();
|
||||
foreach (var opt in built)
|
||||
ProviderOptions.Add(opt);
|
||||
|
||||
if (SelectedProviderOption is null && ProviderOptions.Count > 0)
|
||||
SelectedProviderOption = ProviderOptions[0];
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<Bitmap?> LoadBitmapAsync(string url)
|
||||
{
|
||||
if (_bitmapCache.TryGetValue(url, out var cached))
|
||||
return cached;
|
||||
|
||||
try
|
||||
{
|
||||
var bytes = await _http.GetByteArrayAsync(url);
|
||||
await using var ms = new MemoryStream(bytes);
|
||||
var bmp = new Bitmap(ms);
|
||||
_bitmapCache[url] = bmp;
|
||||
return bmp;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to load image '{url}': {ex.Message}");
|
||||
_bitmapCache[url] = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// -------- your existing parsing methods (unchanged) --------
|
||||
|
||||
private static string CleanText(string input)
|
||||
{
|
||||
var text = RegexPatterns.BuildInfo.MarkdownBold.Replace(input, "$1");
|
||||
@@ -79,22 +246,19 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
private void ParseVersionsTable(string md, ObservableCollection<BuildVersion> target)
|
||||
{
|
||||
var match = RegexPatterns.BuildInfo.VersionsTable.Match(md);
|
||||
|
||||
if (!match.Success) return;
|
||||
|
||||
var table = match.Groups["table"].Value;
|
||||
|
||||
var rows = RegexPatterns.BuildInfo.TableRow2Columns.Matches(table);
|
||||
|
||||
bool headerSkipped = false;
|
||||
|
||||
foreach (Match row in rows)
|
||||
foreach (System.Text.RegularExpressions.Match row in rows)
|
||||
{
|
||||
var col1 = row.Groups[1].Value.Trim();
|
||||
var col2 = row.Groups[2].Value.Trim();
|
||||
|
||||
if (!headerSkipped &&
|
||||
col1.Equals("File name", StringComparison.OrdinalIgnoreCase))
|
||||
if (!headerSkipped && col1.Equals("File name", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
headerSkipped = true;
|
||||
continue;
|
||||
@@ -114,22 +278,20 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
private void ParseApplicationsTable(string md, ObservableCollection<BuildVersion> target)
|
||||
{
|
||||
var match = RegexPatterns.BuildInfo.ApplicationsTable.Match(md);
|
||||
|
||||
if (!match.Success) return;
|
||||
|
||||
var table = match.Groups["table"].Value;
|
||||
|
||||
var rows = RegexPatterns.BuildInfo.TableRow3Columns.Matches(table);
|
||||
|
||||
bool headerSkipped = false;
|
||||
|
||||
foreach (Match row in rows)
|
||||
foreach (System.Text.RegularExpressions.Match row in rows)
|
||||
{
|
||||
var col1 = row.Groups[1].Value.Trim();
|
||||
var col2 = row.Groups[2].Value.Trim();
|
||||
var col3 = row.Groups[3].Value.Trim();
|
||||
|
||||
if (!headerSkipped &&
|
||||
col1.Contains("Application", StringComparison.OrdinalIgnoreCase))
|
||||
if (!headerSkipped && col1.Contains("Application", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
headerSkipped = true;
|
||||
continue;
|
||||
@@ -154,4 +316,4 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
|
||||
public event Action? OnRequestClose;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using System;
|
||||
|
||||
@@ -31,6 +31,6 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
|
||||
private void OnOk() => _close(true);
|
||||
|
||||
private void OnCancel() => _close(false);
|
||||
private void OnCancel() => _close(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
|
||||
[ObservableProperty]
|
||||
private JellyTheme? selectedJellyTheme;
|
||||
|
||||
|
||||
[ObservableProperty]
|
||||
private Bitmap? selectedJellyThemePreview;
|
||||
|
||||
@@ -83,8 +83,6 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool streamValidated = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private string streamValidationStatus = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool enableBackdrops;
|
||||
@@ -139,6 +137,9 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool canOpenDebugWindow;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool showMdnsWarning = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private string selectedServerInputMode = "IP : Port";
|
||||
|
||||
@@ -182,10 +183,20 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
[ObservableProperty]
|
||||
private bool darkMode;
|
||||
|
||||
[ObservableProperty]
|
||||
private string gitHubToken = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool showGitHubToken = false;
|
||||
|
||||
[ObservableProperty]
|
||||
private NetworkInterfaceOption? selectedNetworkInterface;
|
||||
|
||||
public ObservableCollection<LanguageOption> AvailableLanguages { get; }
|
||||
public ObservableCollection<ExistingCertificates> AvailableCertificates { get; } = new();
|
||||
public ObservableCollection<JellyfinUser> AvailableJellyfinUsers { get; } = new();
|
||||
public ObservableCollection<JellyfinUser> SelectedJellyfinUsers { get; }
|
||||
public ObservableCollection<NetworkInterfaceOption> NetworkInterfaces { get; } = new();
|
||||
// ========== End Main Settings Properties ==========
|
||||
|
||||
public ObservableCollection<string> AvailableThemes { get; } = new()
|
||||
@@ -214,11 +225,13 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
public ObservableCollection<string> AvailableServerInputModes { get; } = new()
|
||||
{
|
||||
"IP : Port",
|
||||
"IP : Port : Path",
|
||||
"Full URL"
|
||||
};
|
||||
|
||||
// Computed properties for server input mode visibility
|
||||
public bool IsServerIpPortMode => SelectedServerInputMode == "IP : Port";
|
||||
public bool IsServerIpPortBasePathMode => SelectedServerInputMode == "IP : Port : Path";
|
||||
public bool IsServerFullUrlMode => SelectedServerInputMode == "Full URL";
|
||||
public bool HasSelectedJellyTheme => SelectedJellyTheme != null;
|
||||
public bool CanClearCss => !string.IsNullOrWhiteSpace(CustomCss);
|
||||
@@ -287,6 +300,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
public string LblValidateCss => _localizationService.GetString("lblValidateCss");
|
||||
public string LblCssValidationStatus => _localizationService.GetString("lblCssValidationStatus");
|
||||
public string LblClearCss => _localizationService.GetString("lblClearCss");
|
||||
public string LblMdnsWarning => _localizationService.GetString("lblMdnsWarning");
|
||||
|
||||
|
||||
// Main Settings Tab labels
|
||||
@@ -303,6 +317,9 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
public string LblRTL => _localizationService.GetString("lblRTL");
|
||||
public string LblKeepWGTFile => _localizationService.GetString("lblKeepWGTFile");
|
||||
public string LblSettingsHeader => _localizationService.GetString("lblSettings");
|
||||
public string LblGitHubToken => _localizationService.GetString("lblGitHubToken");
|
||||
public string LblGitHubTokenHint => _localizationService.GetString("lblGitHubTokenHint");
|
||||
public char GitHubTokenPasswordChar => ShowGitHubToken ? '\0' : '*';
|
||||
|
||||
public bool CanLogin => ServerValidated &&
|
||||
!string.IsNullOrWhiteSpace(JellyfinUsername) &&
|
||||
@@ -344,7 +361,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
InitializeAsyncSettings();
|
||||
InitializeMainSettings();
|
||||
UpdateServerIpStatus();
|
||||
_ = LoadLocalIpAsync();
|
||||
_ = LoadNetworkInterfacesAsync();
|
||||
_ = InitializeCertificatesAsync();
|
||||
}
|
||||
|
||||
@@ -417,6 +434,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
OnPropertyChanged(nameof(LblCssHint));
|
||||
OnPropertyChanged(nameof(LblValidateCss));
|
||||
OnPropertyChanged(nameof(LblCssValidationStatus));
|
||||
OnPropertyChanged(nameof(LblMdnsWarning));
|
||||
// Main Settings Tab labels
|
||||
OnPropertyChanged(nameof(LblTabMainSettings));
|
||||
OnPropertyChanged(nameof(LblMainSettings));
|
||||
@@ -431,6 +449,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
OnPropertyChanged(nameof(LblRTL));
|
||||
OnPropertyChanged(nameof(LblKeepWGTFile));
|
||||
OnPropertyChanged(nameof(LblSettingsHeader));
|
||||
OnPropertyChanged(nameof(LblGitHubToken));
|
||||
OnPropertyChanged(nameof(LblGitHubTokenHint));
|
||||
}
|
||||
|
||||
partial void OnAudioLanguagePreferenceChanged(string? value)
|
||||
@@ -476,6 +496,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
partial void OnSelectedServerInputModeChanged(string value)
|
||||
{
|
||||
OnPropertyChanged(nameof(IsServerIpPortMode));
|
||||
OnPropertyChanged(nameof(IsServerIpPortBasePathMode));
|
||||
OnPropertyChanged(nameof(IsServerFullUrlMode));
|
||||
|
||||
// Save the selected mode to persist across restarts
|
||||
@@ -518,6 +539,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
OnPropertyChanged(nameof(JellyfinBasePath));
|
||||
AppSettings.Default.Save();
|
||||
|
||||
CheckForMdnsHostname(uri.Host);
|
||||
|
||||
// Auto-validate the server connection
|
||||
_ = AutoValidateServerAsync();
|
||||
}
|
||||
@@ -664,6 +687,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
AppSettings.Default.JellyfinUserId = "";
|
||||
AppSettings.Default.JellyfinServerId = "";
|
||||
AppSettings.Default.JellyfinServerLocalAddress = "";
|
||||
AppSettings.Default.JellyfinServerName = "";
|
||||
AppSettings.Default.IsJellyfinAdmin = false;
|
||||
AppSettings.Default.Save();
|
||||
|
||||
@@ -687,28 +711,6 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
await LoadJellyfinUsersAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task TestConnectionAsync()
|
||||
{
|
||||
StreamValidated = false;
|
||||
StreamValidationStatus = "Validating...";
|
||||
|
||||
var testUrl = UrlHelper.CombineUrl(LocalYoutubeServer, "/health");
|
||||
var isReachable = await _jellyfinApiClient.TestServerConnectionAsync(testUrl);
|
||||
|
||||
if (isReachable)
|
||||
{
|
||||
StreamValidated = true;
|
||||
StreamValidationStatus = "✓ Connected";
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamValidated = false;
|
||||
StreamValidationStatus = "✕ Unreachable";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task TestServerAsync()
|
||||
{
|
||||
@@ -759,6 +761,12 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
Trace.WriteLine($"[ServerID] Stored server LocalAddress: {serverInfo.LocalAddress}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(serverInfo.ServerName))
|
||||
{
|
||||
AppSettings.Default.JellyfinServerName = serverInfo.ServerName;
|
||||
Trace.WriteLine($"[ServerID] Stored server name: {serverInfo.ServerName}");
|
||||
}
|
||||
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
}
|
||||
@@ -1209,6 +1217,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
SelectedJellyfinProtocol = uri.Scheme;
|
||||
JellyfinServerIp = uri.Host;
|
||||
SelectedJellyfinPort = uri.Port.ToString();
|
||||
CheckForMdnsHostname(uri.Host);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1227,6 +1236,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
selectedServerInputMode = savedMode;
|
||||
OnPropertyChanged(nameof(SelectedServerInputMode));
|
||||
OnPropertyChanged(nameof(IsServerIpPortMode));
|
||||
OnPropertyChanged(nameof(IsServerIpPortBasePathMode));
|
||||
OnPropertyChanged(nameof(IsServerFullUrlMode));
|
||||
}
|
||||
|
||||
@@ -1296,9 +1306,21 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
Trace.WriteLine($"Updated Jellyfin IP: {AppSettings.Default.JellyfinIP}");
|
||||
AppSettings.Default.Save();
|
||||
UpdateServerIpStatus();
|
||||
CheckForMdnsHostname(JellyfinServerIp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given hostname is an mDNS (.local) address and shows a warning.
|
||||
/// Samsung TVs (Tizen) cannot reliably resolve mDNS hostnames, which causes
|
||||
/// the server to appear as "undefined" on the TV after network disruptions.
|
||||
/// </summary>
|
||||
private void CheckForMdnsHostname(string? hostname)
|
||||
{
|
||||
ShowMdnsWarning = !string.IsNullOrEmpty(hostname) &&
|
||||
hostname.EndsWith(".local", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private void UpdateServerIpStatus()
|
||||
{
|
||||
ServerIpSet = !string.IsNullOrEmpty(AppSettings.Default.JellyfinIP) ||
|
||||
@@ -1332,27 +1354,43 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
OpenAfterInstall = AppSettings.Default.OpenAfterInstall;
|
||||
KeepWGTFile = AppSettings.Default.KeepWGTFile;
|
||||
DarkMode = AppSettings.Default.DarkMode;
|
||||
GitHubToken = AppSettings.Default.GitHubToken ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task LoadLocalIpAsync()
|
||||
private async Task LoadNetworkInterfacesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var ip = await _networkService.GetPrimaryOutboundIPAddressAsync();
|
||||
var interfaces = await _networkService.GetNetworkInterfaceOptionsAsync();
|
||||
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
LocalIP = ip ?? string.Empty;
|
||||
AppSettings.Default.LocalIp = ip;
|
||||
AppSettings.Default.Save();
|
||||
NetworkInterfaces.Clear();
|
||||
|
||||
foreach (var ni in interfaces)
|
||||
NetworkInterfaces.Add(ni);
|
||||
|
||||
// Restore previous selection: match by name first (stable across DHCP changes),
|
||||
// fall back to IP match, then default to first interface
|
||||
var savedName = AppSettings.Default.SavedNetworkInterfaceName;
|
||||
var savedIp = AppSettings.Default.LocalIp;
|
||||
SelectedNetworkInterface =
|
||||
(!string.IsNullOrEmpty(savedName)
|
||||
? NetworkInterfaces.FirstOrDefault(i => i.Name == savedName)
|
||||
: null)
|
||||
?? (!string.IsNullOrEmpty(savedIp)
|
||||
? NetworkInterfaces.FirstOrDefault(i => i.IpAddress == savedIp)
|
||||
: null)
|
||||
?? NetworkInterfaces.FirstOrDefault();
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Failed to get local IP: {ex}");
|
||||
Trace.WriteLine($"Failed to load network interfaces: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task InitializeCertificatesAsync()
|
||||
{
|
||||
var certificates = _certificateHelper.GetAvailableCertificates(AppSettings.CertificatePath);
|
||||
@@ -1399,6 +1437,18 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnSelectedNetworkInterfaceChanged(NetworkInterfaceOption? value)
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
LocalIP = value.IpAddress;
|
||||
AppSettings.Default.LocalIp = value.IpAddress;
|
||||
AppSettings.Default.SavedNetworkInterfaceName = value.Name;
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
|
||||
|
||||
// Property changed handlers for Main Settings
|
||||
partial void OnSelectedLanguageChanged(LanguageOption? value)
|
||||
{
|
||||
@@ -1449,6 +1499,12 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
|
||||
partial void OnDeletePreviousInstallChanged(bool value)
|
||||
{
|
||||
AppSettings.Default.DeletePreviousInstall = value;
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
|
||||
partial void OnRtlReadingChanged(bool value)
|
||||
{
|
||||
AppSettings.Default.RTLReading = value;
|
||||
@@ -1472,6 +1528,17 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
_themeService.SetTheme(value);
|
||||
}
|
||||
|
||||
partial void OnGitHubTokenChanged(string value)
|
||||
{
|
||||
AppSettings.Default.GitHubToken = value;
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
|
||||
partial void OnShowGitHubTokenChanged(bool value)
|
||||
{
|
||||
OnPropertyChanged(nameof(GitHubTokenPasswordChar));
|
||||
}
|
||||
|
||||
// ========== End Main Settings Methods ==========
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -8,10 +8,7 @@ using Jellyfin2Samsung.Helpers.API;
|
||||
using Jellyfin2Samsung.Helpers.Core;
|
||||
using Jellyfin2Samsung.Helpers.Tizen.Devices;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Services;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using Jellyfin2Samsung.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -31,6 +28,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
private readonly INetworkService _networkService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IThemeService _themeService;
|
||||
private readonly IUpdaterService _updaterService;
|
||||
private readonly IUpdateDialogService _updateDialogService;
|
||||
private readonly FileHelper _fileHelper;
|
||||
private readonly DeviceHelper _deviceHelper;
|
||||
private readonly TizenApiClient _tizenApiClient;
|
||||
@@ -38,6 +37,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
private readonly JellyfinConfigViewModel _settingsViewModel;
|
||||
private readonly AddLatestRelease _addLatestRelease;
|
||||
private CancellationTokenSource? _samsungLoginCts;
|
||||
private CancellationTokenSource? _initializationCts;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<GitHubRelease> releases = new ObservableCollection<GitHubRelease>();
|
||||
@@ -81,7 +81,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
private string L(string key) => _localizationService.GetString(key);
|
||||
|
||||
public bool EnableDevicesInput => !IsLoadingDevices;
|
||||
public string LblRelease => _localizationService.GetString("lblRelease");
|
||||
public string LblRelease => _localizationService.GetString("lblRelease");
|
||||
public string LblVersion => _localizationService.GetString("lblVersion");
|
||||
public string LblSelectTv => _localizationService.GetString("lblSelectTv");
|
||||
public string DownloadAndInstall => _localizationService.GetString("DownloadAndInstall");
|
||||
@@ -97,6 +97,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
INetworkService networkService,
|
||||
ILocalizationService localizationService,
|
||||
IThemeService themeService,
|
||||
IUpdaterService updaterService,
|
||||
IUpdateDialogService updateDialogService,
|
||||
HttpClient httpClient,
|
||||
DeviceHelper deviceHelper,
|
||||
TizenApiClient tizenApiClient,
|
||||
@@ -113,6 +115,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
_packageHelper = packageHelper;
|
||||
_localizationService = localizationService;
|
||||
_themeService = themeService;
|
||||
_updaterService = updaterService;
|
||||
_updateDialogService = updateDialogService;
|
||||
_fileHelper = fileHelper;
|
||||
_settingsViewModel = settingsViewModel;
|
||||
|
||||
@@ -166,10 +170,14 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
AvailableAssets = value != null
|
||||
? new ObservableCollection<Asset>(value.Assets)
|
||||
: new ObservableCollection<Asset>();
|
||||
SelectedAsset = AvailableAssets.FirstOrDefault();
|
||||
|
||||
SelectedAsset =
|
||||
AvailableAssets.FirstOrDefault(a => a.IsDefault)
|
||||
?? AvailableAssets.FirstOrDefault();
|
||||
|
||||
RefreshCanExecuteChanged();
|
||||
}
|
||||
|
||||
partial void OnSelectedAssetChanged(Asset? value)
|
||||
{
|
||||
RefreshCanExecuteChanged();
|
||||
@@ -217,8 +225,17 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
// Create a new CTS for initialization that can be cancelled when update dialog shows
|
||||
_initializationCts?.Cancel();
|
||||
_initializationCts?.Dispose();
|
||||
_initializationCts = new CancellationTokenSource();
|
||||
var token = _initializationCts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
// Check for updates first (non-blocking, runs in background)
|
||||
_ = CheckForUpdatesAsync();
|
||||
|
||||
SetStatus("CheckingTizenSdb");
|
||||
|
||||
string tizenSdb = await _tizenInstaller.EnsureTizenSdbAvailable();
|
||||
@@ -228,14 +245,21 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
SetStatus("FailedTizenSdb");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
ProcessHelper.KillSdbServers();
|
||||
|
||||
await LoadReleasesAsync();
|
||||
await LoadReleasesAsync(token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
||||
SetStatus("ScanningNetwork");
|
||||
await LoadDevicesAsync();
|
||||
await LoadDevicesAsync(token);
|
||||
CustomWgtPath = AppSettings.Default.CustomWgtPath ?? "";
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Initialization was cancelled (likely due to update dialog)
|
||||
Trace.WriteLine("Initialization cancelled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetStatus("InitializationFailed");
|
||||
@@ -243,6 +267,153 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForUpdatesAsync()
|
||||
{
|
||||
// Skip if update check is disabled
|
||||
if (!AppSettings.Default.CheckForUpdatesOnStartup)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(Constants.Updater.UpdateCheckTimeoutSeconds));
|
||||
var updateResult = await _updaterService.CheckForUpdateAsync(cts.Token);
|
||||
|
||||
if (!updateResult.IsSuccess || !updateResult.IsUpdateAvailable)
|
||||
return;
|
||||
|
||||
// Skip if user previously skipped this version
|
||||
if (updateResult.LatestVersion == AppSettings.Default.SkippedUpdateVersion)
|
||||
return;
|
||||
|
||||
// Cancel initialization tasks (network scan, release loading) before showing dialog
|
||||
// This prevents the UI from freezing while background tasks continue
|
||||
_initializationCts?.Cancel();
|
||||
|
||||
// Show update dialog on UI thread
|
||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
var choice = await _updateDialogService.ShowUpdateAvailableDialogAsync(updateResult);
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
case UpdateDialogChoice.Manual:
|
||||
_updaterService.OpenReleasesPage();
|
||||
// Resume initialization after user sees the releases page
|
||||
_ = ResumeInitializationAsync();
|
||||
break;
|
||||
|
||||
case UpdateDialogChoice.Automatic:
|
||||
await PerformAutomaticUpdateAsync(updateResult);
|
||||
// No resume needed - app will restart
|
||||
break;
|
||||
|
||||
case UpdateDialogChoice.Skip:
|
||||
AppSettings.Default.SkippedUpdateVersion = updateResult.LatestVersion;
|
||||
AppSettings.Default.Save();
|
||||
// Resume initialization after user skips
|
||||
_ = ResumeInitializationAsync();
|
||||
break;
|
||||
|
||||
case UpdateDialogChoice.Cancel:
|
||||
default:
|
||||
// Resume initialization after user cancels
|
||||
_ = ResumeInitializationAsync();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Update last check time
|
||||
AppSettings.Default.LastUpdateCheck = DateTime.UtcNow;
|
||||
AppSettings.Default.Save();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Timeout - silently ignore
|
||||
Trace.WriteLine("Update check timed out");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Don't show errors for update check failures - it's not critical
|
||||
Trace.WriteLine($"Update check failed: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PerformAutomaticUpdateAsync(Models.UpdateCheckResult updateResult)
|
||||
{
|
||||
if (string.IsNullOrEmpty(updateResult.DownloadUrl))
|
||||
{
|
||||
await _updateDialogService.ShowUpdateErrorAsync(L("UpdateError"));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Download update
|
||||
var progress = new Progress<int>(p =>
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
StatusBar = $"{L("UpdateDownloading")} {p}%";
|
||||
});
|
||||
});
|
||||
|
||||
StatusBar = L("UpdateDownloading");
|
||||
var downloadedPath = await _updaterService.DownloadUpdateAsync(updateResult.DownloadUrl, progress);
|
||||
|
||||
// Apply update
|
||||
await _updateDialogService.ShowApplyingUpdateMessageAsync();
|
||||
var success = await _updaterService.ApplyUpdateAsync(downloadedPath);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Exit application - update script will restart it
|
||||
if (Avalonia.Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||
{
|
||||
desktop.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Automatic update failed: {ex}");
|
||||
await _updateDialogService.ShowUpdateErrorAsync(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ResumeInitializationAsync()
|
||||
{
|
||||
// Create a new CTS for the resumed initialization
|
||||
_initializationCts?.Dispose();
|
||||
_initializationCts = new CancellationTokenSource();
|
||||
var token = _initializationCts.Token;
|
||||
|
||||
try
|
||||
{
|
||||
// Only load releases and devices if they haven't been loaded yet
|
||||
if (!Releases.Any())
|
||||
{
|
||||
await LoadReleasesAsync(token);
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
|
||||
if (!AvailableDevices.Any(d => d.IpAddress != L("lblOther")))
|
||||
{
|
||||
SetStatus("ScanningNetwork");
|
||||
await LoadDevicesAsync(token);
|
||||
}
|
||||
|
||||
CustomWgtPath = AppSettings.Default.CustomWgtPath ?? "";
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Trace.WriteLine("Resumed initialization cancelled");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.WriteLine($"Resumed initialization failed: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanRefresh))]
|
||||
private async Task RefreshAsync()
|
||||
{
|
||||
@@ -290,24 +461,34 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
|
||||
if (customPaths?.Length > 0)
|
||||
{
|
||||
await _packageHelper.InstallCustomPackagesAsync(
|
||||
customPaths,
|
||||
SelectedDevice,
|
||||
_samsungLoginCts.Token,
|
||||
progress => Dispatcher.UIThread.Post(() => StatusBar = progress),
|
||||
onSamsungLoginStarted: OnSamsungLoginStarted);
|
||||
|
||||
try
|
||||
{
|
||||
await _packageHelper.InstallCustomPackagesAsync(
|
||||
customPaths,
|
||||
SelectedDevice,
|
||||
_samsungLoginCts.Token,
|
||||
progress => Dispatcher.UIThread.Post(() => StatusBar = progress),
|
||||
onSamsungLoginStarted: OnSamsungLoginStarted);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsSamsungLoginActive = false;
|
||||
_samsungLoginCts.Dispose();
|
||||
_samsungLoginCts = null;
|
||||
}
|
||||
|
||||
foreach (var customPath in customPaths)
|
||||
if (!AppSettings.Default.KeepWGTFile)
|
||||
_packageHelper.CleanupDownloadedPackage(customPath);
|
||||
|
||||
AppSettings.Default.CustomWgtPath = null;
|
||||
AppSettings.Default.Save();
|
||||
CustomWgtPath = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(SelectedRelease == null || SelectedDevice == null)
|
||||
return;
|
||||
|
||||
string? downloadPath = await _packageHelper.DownloadReleaseAsync(
|
||||
SelectedRelease,
|
||||
SelectedAsset,
|
||||
@@ -331,7 +512,7 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
_samsungLoginCts = null;
|
||||
}
|
||||
|
||||
if(!AppSettings.Default.KeepWGTFile)
|
||||
if (!AppSettings.Default.KeepWGTFile)
|
||||
_packageHelper.CleanupDownloadedPackage(downloadPath);
|
||||
}
|
||||
}
|
||||
@@ -429,40 +610,34 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadReleasesAsync()
|
||||
private async Task LoadReleasesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
try
|
||||
{
|
||||
var list = new List<GitHubRelease>();
|
||||
|
||||
async Task fetch(string url, string name)
|
||||
async Task fetch(string url, string prefix, string name, int take = 1)
|
||||
{
|
||||
var release = await _addLatestRelease.GetLatestReleaseAsync(url, name);
|
||||
if (release != null)
|
||||
list.Add(release);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
var release = await _addLatestRelease.GetReleasesAsync(url, prefix, name, take);
|
||||
if (release.Count > 0)
|
||||
list.AddRange(release);
|
||||
}
|
||||
|
||||
await fetch(AppSettings.Default.ReleasesUrl, Constants.AppIdentifiers.JellyfinAppName);
|
||||
await fetch(AppSettings.Default.MoonfinRelease, "Moonfin");
|
||||
await fetch(AppSettings.Default.JellyfinAvRelease, "Jellyfin - AVPlay");
|
||||
await fetch(AppSettings.Default.JellyfinAvRelease, "Jellyfin - AVPlay - 10.10z SmartHub");
|
||||
await fetch(AppSettings.Default.JellyfinLegacy, "Jellyfin - Legacy");
|
||||
await fetch(AppSettings.Default.CommunityRelease, "Tizen Community");
|
||||
|
||||
await fetch(AppSettings.Default.ReleasesUrl, "Jellyfin - ", string.Empty, 5);
|
||||
await fetch(AppSettings.Default.MoonfinRelease, string.Empty, "Moonfin", 1);
|
||||
await fetch(AppSettings.Default.LiteFinRelease, string.Empty, "Litefin", 1);
|
||||
await fetch(AppSettings.Default.JellyfinAvRelease, string.Empty, "Jellyfin - AVPlay", 1);
|
||||
await fetch(AppSettings.Default.JellyfinAvRelease, string.Empty, "Jellyfin - AVPlay - 10.10z SmartHub", 1);
|
||||
await fetch(AppSettings.Default.JellyfinLegacy, string.Empty, "Jellyfin - Legacy", 1);
|
||||
await fetch(AppSettings.Default.CommunityRelease, string.Empty, "Tizen Community", 1);
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Releases.Clear();
|
||||
foreach (var r in list)
|
||||
Releases.Add(r);
|
||||
|
||||
Releases.Add(new GitHubRelease
|
||||
{
|
||||
Name = Constants.AppIdentifiers.CustomWgtFile,
|
||||
TagName = string.Empty,
|
||||
PublishedAt = string.Empty,
|
||||
Url = string.Empty,
|
||||
Assets = new List<Asset>()
|
||||
});
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -471,11 +646,22 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Always add the custom WGT option, regardless of GitHub failures
|
||||
if (!Releases.Any(r => r.Name == Constants.AppIdentifiers.CustomWgtFile))
|
||||
{
|
||||
Releases.Add(new GitHubRelease
|
||||
{
|
||||
Name = Constants.AppIdentifiers.CustomWgtFile,
|
||||
TagName = string.Empty,
|
||||
PublishedAt = string.Empty,
|
||||
Url = string.Empty,
|
||||
Assets = new List<Asset>()
|
||||
});
|
||||
}
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadDevicesAsync(CancellationToken cancellationToken = default, bool virtualScan = false)
|
||||
{
|
||||
IsLoadingDevices = true;
|
||||
@@ -530,6 +716,10 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
throw; // Re-throw to be handled by caller
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await _dialogService.ShowErrorAsync($"Failed to load devices: {ex}");
|
||||
@@ -555,8 +745,8 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
return;
|
||||
|
||||
var dialog = new IpInputDialog();
|
||||
|
||||
|
||||
|
||||
|
||||
string? ip = await dialog.ShowDialogAsync(desktop.MainWindow);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ip))
|
||||
@@ -611,9 +801,17 @@ namespace Jellyfin2Samsung.ViewModels
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeSamsungCts();
|
||||
DisposeInitializationCts();
|
||||
_localizationService.LanguageChanged -= OnLanguageChanged;
|
||||
_themeService.ThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
|
||||
private void DisposeInitializationCts()
|
||||
{
|
||||
_initializationCts?.Cancel();
|
||||
_initializationCts?.Dispose();
|
||||
_initializationCts = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
112
Jellyfin2Samsung-CrossOS/ViewModels/UpdateDialogViewModel.cs
Normal file
112
Jellyfin2Samsung-CrossOS/ViewModels/UpdateDialogViewModel.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using System;
|
||||
|
||||
namespace Jellyfin2Samsung.ViewModels
|
||||
{
|
||||
public partial class UpdateDialogViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ILocalizationService _localizationService;
|
||||
|
||||
[ObservableProperty]
|
||||
private string currentVersion = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string latestVersion = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string releaseTitle = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string releaseNotes = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private DateTime? publishedAt;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool hasDownloadUrl;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool isDownloading;
|
||||
|
||||
[ObservableProperty]
|
||||
private int downloadProgress;
|
||||
|
||||
[ObservableProperty]
|
||||
private string downloadStatus = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The user's choice after interacting with the dialog.
|
||||
/// </summary>
|
||||
public UpdateDialogChoice? DialogResult { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event raised when the dialog should be closed.
|
||||
/// </summary>
|
||||
public event EventHandler? RequestClose;
|
||||
|
||||
// Localized strings
|
||||
public string L(string key) => _localizationService.GetString(key);
|
||||
public string DialogTitle => L("UpdateAvailable");
|
||||
public string CurrentVersionLabel => L("UpdateCurrentVersion");
|
||||
public string LatestVersionLabel => L("UpdateLatestVersion");
|
||||
public string ReleaseNotesLabel => L("UpdateReleaseNotes");
|
||||
public string ManualButtonText => L("UpdateManual");
|
||||
public string AutomaticButtonText => L("UpdateAutomatic");
|
||||
public string SkipButtonText => L("UpdateSkip");
|
||||
public string DownloadingText => L("UpdateDownloading");
|
||||
|
||||
public UpdateDialogViewModel(ILocalizationService localizationService)
|
||||
{
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
public void Initialize(UpdateCheckResult updateInfo)
|
||||
{
|
||||
CurrentVersion = updateInfo.CurrentVersion;
|
||||
LatestVersion = updateInfo.LatestVersion;
|
||||
ReleaseTitle = updateInfo.ReleaseTitle;
|
||||
ReleaseNotes = updateInfo.ReleaseNotes;
|
||||
PublishedAt = updateInfo.PublishedAt;
|
||||
HasDownloadUrl = !string.IsNullOrEmpty(updateInfo.DownloadUrl);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SelectManual()
|
||||
{
|
||||
DialogResult = UpdateDialogChoice.Manual;
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand(CanExecute = nameof(CanSelectAutomatic))]
|
||||
private void SelectAutomatic()
|
||||
{
|
||||
DialogResult = UpdateDialogChoice.Automatic;
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void SelectSkip()
|
||||
{
|
||||
DialogResult = UpdateDialogChoice.Skip;
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Cancel()
|
||||
{
|
||||
DialogResult = UpdateDialogChoice.Cancel;
|
||||
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private bool CanSelectAutomatic() => HasDownloadUrl && !IsDownloading;
|
||||
|
||||
public void UpdateDownloadProgress(int progress, string status)
|
||||
{
|
||||
DownloadProgress = progress;
|
||||
DownloadStatus = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@
|
||||
xmlns:viewModels="clr-namespace:Jellyfin2Samsung.ViewModels"
|
||||
x:DataType="viewModels:BuildInfoViewModel"
|
||||
SizeToContent="WidthAndHeight"
|
||||
MinWidth="650"
|
||||
MaxWidth="900"
|
||||
MinHeight="500"
|
||||
MinWidth="900"
|
||||
MaxWidth="1200"
|
||||
MinHeight="520"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Title="Jellyfin Tizen Versions">
|
||||
|
||||
@@ -15,112 +15,175 @@
|
||||
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
|
||||
</Window.Styles>
|
||||
|
||||
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
|
||||
Padding="16"
|
||||
CornerRadius="8"
|
||||
Margin="12"
|
||||
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1">
|
||||
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
|
||||
Padding="16"
|
||||
CornerRadius="8"
|
||||
Margin="12"
|
||||
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1">
|
||||
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="15"
|
||||
OffsetX="2"
|
||||
OffsetY="2"
|
||||
Color="#00000020"
|
||||
Opacity="0.15"/>
|
||||
OffsetX="2"
|
||||
OffsetY="2"
|
||||
Color="#00000020"
|
||||
Opacity="0.15"/>
|
||||
</Border.Effect>
|
||||
|
||||
<StackPanel Spacing="20">
|
||||
<StackPanel Spacing="16">
|
||||
|
||||
<!-- Header -->
|
||||
<Border Background="#2C3E50"
|
||||
Padding="14,10"
|
||||
CornerRadius="6">
|
||||
Padding="14,10"
|
||||
CornerRadius="6">
|
||||
<TextBlock Text="Jellyfin Tizen Build Versions"
|
||||
Foreground="White"
|
||||
FontWeight="SemiBold"
|
||||
FontSize="18"
|
||||
HorizontalAlignment="Center"/>
|
||||
Foreground="White"
|
||||
FontWeight="SemiBold"
|
||||
FontSize="18"
|
||||
HorizontalAlignment="Center"/>
|
||||
</Border>
|
||||
|
||||
<!-- Two-column layout -->
|
||||
<Grid ColumnDefinitions="*,340"
|
||||
ColumnSpacing="18">
|
||||
|
||||
<Border Background="#34495E"
|
||||
Padding="10"
|
||||
CornerRadius="6">
|
||||
<TextBlock Text="Official Jellyfin Builds"
|
||||
Foreground="White"
|
||||
FontWeight="Medium"
|
||||
FontSize="15"/>
|
||||
</Border>
|
||||
<!-- ================= LEFT COLUMN ================= -->
|
||||
<StackPanel Grid.Column="0" Spacing="16">
|
||||
|
||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Height="200">
|
||||
<DataGrid ItemsSource="{Binding JellyfinVersions}"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
GridLinesVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="File name" Width="180"
|
||||
Binding="{Binding FileName}" />
|
||||
<DataGridTemplateColumn Header="Description" Width="*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Description}"
|
||||
TextWrapping="Wrap"
|
||||
Padding="4"
|
||||
VerticalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
<Border Background="#34495E"
|
||||
Padding="10"
|
||||
CornerRadius="6">
|
||||
<TextBlock Text="Jellyfin Builds"
|
||||
Foreground="White"
|
||||
FontWeight="Medium"
|
||||
FontSize="15"/>
|
||||
</Border>
|
||||
|
||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Height="220">
|
||||
<DataGrid ItemsSource="{Binding JellyfinVersions}"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
GridLinesVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="File name" Width="180"
|
||||
Binding="{Binding FileName}" />
|
||||
<DataGridTemplateColumn Header="Description" Width="*">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Description}"
|
||||
TextWrapping="Wrap"
|
||||
Padding="4"
|
||||
VerticalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
<Border Background="#34495E"
|
||||
Padding="10"
|
||||
CornerRadius="6"
|
||||
Margin="0,6,0,0">
|
||||
<TextBlock Text="Community Applications"
|
||||
Foreground="White"
|
||||
FontWeight="Medium"
|
||||
FontSize="15"/>
|
||||
</Border>
|
||||
|
||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Height="280">
|
||||
<DataGrid ItemsSource="{Binding CommunityApps}"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
GridLinesVisibility="All"
|
||||
RowHeight="32"
|
||||
IsReadOnly="True"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Application" Width="180"
|
||||
Binding="{Binding FileName}" />
|
||||
<DataGridTextColumn Header="Description" Width="*"
|
||||
Binding="{Binding Description}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<!-- ================= RIGHT COLUMN ================= -->
|
||||
<!-- CompileBindings off so you can iterate on option types freely -->
|
||||
<StackPanel Grid.Column="1"
|
||||
Spacing="10"
|
||||
x:CompileBindings="False">
|
||||
|
||||
<Border Background="#34495E"
|
||||
Padding="10"
|
||||
CornerRadius="6">
|
||||
<TextBlock Text="App Preview"
|
||||
Foreground="White"
|
||||
FontWeight="Medium"
|
||||
FontSize="15"/>
|
||||
</Border>
|
||||
|
||||
<!-- Dropdown: TEXT ONLY (no preview image here) -->
|
||||
<ComboBox ItemsSource="{Binding ProviderOptions}"
|
||||
SelectedItem="{Binding SelectedProviderOption, Mode=TwoWay}"
|
||||
MinWidth="320"
|
||||
HorizontalAlignment="Stretch"
|
||||
DisplayMemberBinding="{Binding DisplayName}"/>
|
||||
|
||||
<!-- Preview card: image shows ONLY here -->
|
||||
<Border Height="320"
|
||||
CornerRadius="12"
|
||||
ClipToBounds="True"
|
||||
Background="#22000000">
|
||||
<Grid>
|
||||
<!-- fallback text behind -->
|
||||
<StackPanel VerticalAlignment="Center"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="6"
|
||||
Margin="12">
|
||||
<TextBlock Text="No preview available"
|
||||
FontWeight="SemiBold"
|
||||
FontSize="14"
|
||||
HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="This app has no thumbnail."
|
||||
Opacity="0.7"
|
||||
FontSize="12"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- image on top (if null, it simply won't render) -->
|
||||
<Image Source="{Binding SelectedProviderOption.PreviewImage}"
|
||||
Stretch="Uniform"
|
||||
StretchDirection="DownOnly"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
|
||||
<Border Background="#34495E"
|
||||
Padding="10"
|
||||
CornerRadius="6"
|
||||
Margin="0,10,0,0">
|
||||
<TextBlock Text="Community Applications"
|
||||
Foreground="White"
|
||||
FontWeight="Medium"
|
||||
FontSize="15"/>
|
||||
</Border>
|
||||
|
||||
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4"
|
||||
Height="260">
|
||||
<DataGrid ItemsSource="{Binding CommunityApps}"
|
||||
AutoGenerateColumns="False"
|
||||
HeadersVisibility="Column"
|
||||
GridLinesVisibility="All"
|
||||
RowHeight="32"
|
||||
IsReadOnly="True"
|
||||
ColumnWidth="*">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Application" Width="180"
|
||||
Binding="{Binding FileName}" />
|
||||
<DataGridTextColumn Header="Description" Width="*"
|
||||
Binding="{Binding Description}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- Footer -->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,10,0,0">
|
||||
HorizontalAlignment="Right"
|
||||
Margin="0,6,0,0">
|
||||
<Button Content="Close"
|
||||
Width="100"
|
||||
Command="{Binding CloseCommand}"/>
|
||||
Width="100"
|
||||
Command="{Binding CloseCommand}"/>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Border>
|
||||
|
||||
</Window>
|
||||
</Window>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Avalonia.Controls;
|
||||
using Jellyfin2Samsung;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
@@ -133,11 +133,50 @@
|
||||
Text="{Binding LblLocalIP}"
|
||||
Classes="label"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="{Binding LocalIP, Mode=TwoWay}"
|
||||
Classes="subtitle"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<ComboBox Grid.Column="1"
|
||||
Classes="clean"
|
||||
ItemsSource="{Binding NetworkInterfaces}"
|
||||
SelectedItem="{Binding SelectedNetworkInterface, Mode=TwoWay}"
|
||||
DisplayMemberBinding="{Binding DisplayText}"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
</Grid>
|
||||
|
||||
<!-- GitHub Token (PAT) -->
|
||||
<Grid ColumnDefinitions="180,*" ColumnSpacing="12">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding LblGitHubToken}"
|
||||
Classes="label"
|
||||
VerticalAlignment="Center"/>
|
||||
<Grid Grid.Column="1" ColumnDefinitions="*,Auto" ColumnSpacing="8">
|
||||
<TextBox Grid.Column="0"
|
||||
Text="{Binding GitHubToken}"
|
||||
PasswordChar="{Binding GitHubTokenPasswordChar}"
|
||||
Watermark="ghp_xxxxxxxxxxxxxxxxxxxx"
|
||||
Classes="clean"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
<ToggleButton Grid.Column="1"
|
||||
IsChecked="{Binding ShowGitHubToken}"
|
||||
VerticalAlignment="Center"
|
||||
Width="36"
|
||||
Height="36"
|
||||
CornerRadius="4"
|
||||
Padding="0"
|
||||
ToolTip.Tip="Show/Hide token">
|
||||
<Panel>
|
||||
<fa:SymbolIcon Symbol="View" Width="16" Height="16" IsVisible="{Binding !ShowGitHubToken}"/>
|
||||
<fa:SymbolIcon Symbol="Cancel" Width="16" Height="16" IsVisible="{Binding ShowGitHubToken}"/>
|
||||
</Panel>
|
||||
</ToggleButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<TextBlock Text="{Binding LblGitHubTokenHint}"
|
||||
FontSize="11"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
FontStyle="Italic"
|
||||
TextWrapping="Wrap"
|
||||
Margin="180,0,0,0"/>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<Border Height="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" Margin="0,4"/>
|
||||
@@ -279,6 +318,57 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Server IP:Port:BasePath Mode -->
|
||||
<Grid ColumnDefinitions="180,*" ColumnSpacing="12"
|
||||
IsVisible="{Binding IsServerIpPortBasePathMode}">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding ServerIP}"
|
||||
Classes="label"
|
||||
VerticalAlignment="Center"/>
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ComboBox Grid.Column="0"
|
||||
SelectedItem="{Binding SelectedJellyfinProtocol, Mode=TwoWay}"
|
||||
Classes="clean">
|
||||
<x:String>http</x:String>
|
||||
<x:String>https</x:String>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Grid.Column="1"
|
||||
Text="://"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<TextBox Grid.Column="2"
|
||||
Text="{Binding JellyfinServerIp}"
|
||||
Classes="clean"
|
||||
Watermark="192.168.1.100"
|
||||
HorizontalAlignment="Stretch"/>
|
||||
|
||||
<TextBlock Grid.Column="3"
|
||||
Text=":"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<AutoCompleteBox Grid.Column="4"
|
||||
ItemsSource="{Binding JellyfinPorts}"
|
||||
Text="{Binding SelectedJellyfinPort, Mode=TwoWay}"
|
||||
Classes="clean" />
|
||||
|
||||
<TextBox Grid.Column="5"
|
||||
Text="{Binding JellyfinBasePath}"
|
||||
Watermark="/jellyfin"
|
||||
Classes="clean"
|
||||
MinWidth="80"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Server Full URL Mode -->
|
||||
<Grid ColumnDefinitions="180,*" ColumnSpacing="12"
|
||||
IsVisible="{Binding IsServerFullUrlMode}">
|
||||
@@ -318,6 +408,18 @@
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- mDNS (.local) Warning -->
|
||||
<Border IsVisible="{Binding ShowMdnsWarning}"
|
||||
Background="#FFF3CD"
|
||||
CornerRadius="4"
|
||||
Padding="10,8"
|
||||
Margin="0,4,0,0">
|
||||
<TextBlock Text="{Binding LblMdnsWarning}"
|
||||
Foreground="#856404"
|
||||
FontSize="12"
|
||||
TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<Border Height="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" Margin="0,4"/>
|
||||
@@ -452,7 +554,7 @@
|
||||
</Button>
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
IsChecked="{Binding EnableDevLogs, Mode=TwoWay}"
|
||||
IsEnabled="{Binding IsAuthenticated}"/>
|
||||
IsEnabled="{Binding ServerIpSet}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -464,7 +566,7 @@
|
||||
VerticalAlignment="Center"/>
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
IsChecked="{Binding UseServerScripts}"
|
||||
IsEnabled="{Binding IsAuthenticated}"/>
|
||||
IsEnabled="{Binding ServerIpSet}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Fix YouTube 153 -->
|
||||
@@ -474,41 +576,12 @@
|
||||
Classes="label"
|
||||
VerticalAlignment="Center"/>
|
||||
<ToggleSwitch Grid.Column="1"
|
||||
IsEnabled="{Binding IsAuthenticated}"
|
||||
IsEnabled="{Binding ServerIpSet}"
|
||||
IsChecked="{Binding PatchYoutubePlugin}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- TextBox shown only when PatchYoutubePlugin == true -->
|
||||
<Grid ColumnDefinitions="180,*" ColumnSpacing="12">
|
||||
<TextBlock Grid.Column="0"
|
||||
Text="{Binding LblYtDlpServer}"
|
||||
Classes="label"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding PatchYoutubePlugin}"/>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="8">
|
||||
|
||||
<TextBox
|
||||
Text="{Binding LocalYoutubeServer}"
|
||||
Watermark="http://192.168.1.123:8123"
|
||||
IsVisible="{Binding PatchYoutubePlugin}" />
|
||||
|
||||
<Button
|
||||
Classes="secondary"
|
||||
Content="{Binding LblValidateStream}"
|
||||
Command="{Binding TestConnectionCommand}"
|
||||
IsVisible="{Binding PatchYoutubePlugin}"
|
||||
Padding="8,4"
|
||||
FontSize="12"/>
|
||||
|
||||
<TextBlock
|
||||
Text="{Binding StreamValidationStatus}"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding PatchYoutubePlugin}"
|
||||
Margin="6,0,0,0"/>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
<!-- Spacer -->
|
||||
<Border Height="24"/>
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
@@ -728,7 +801,7 @@
|
||||
|
||||
<StackPanel Spacing="8" HorizontalAlignment="Center">
|
||||
<TextBlock
|
||||
Text="{Binding SelectedJellyTheme.Name}"
|
||||
Text="{Binding SelectedJellyTheme.Name, FallbackValue=''}"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center"
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
<StackPanel Spacing="14">
|
||||
<Border Background="#2C3E50"
|
||||
Padding="24,20"
|
||||
CornerRadius="8"
|
||||
BorderBrush="#E9ECEF"
|
||||
BorderThickness="1">
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="12" HorizontalAlignment="Center">
|
||||
<TextBlock
|
||||
Text="Jellyfin Installer for Samsung (Tizen)"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using Jellyfin2Samsung.ViewModels;
|
||||
|
||||
211
Jellyfin2Samsung-CrossOS/Views/UpdateDialog.axaml
Normal file
211
Jellyfin2Samsung-CrossOS/Views/UpdateDialog.axaml
Normal file
@@ -0,0 +1,211 @@
|
||||
<Window xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="clr-namespace:Jellyfin2Samsung.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="480"
|
||||
d:DesignHeight="420"
|
||||
x:Class="Jellyfin2Samsung.Views.UpdateDialog"
|
||||
x:DataType="viewModels:UpdateDialogViewModel"
|
||||
Width="480"
|
||||
MinHeight="360"
|
||||
SizeToContent="Height"
|
||||
Title="{Binding DialogTitle}"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False"
|
||||
CornerRadius="12">
|
||||
|
||||
<Window.Styles>
|
||||
<StyleInclude Source="avares://Avalonia.Themes.Fluent/FluentTheme.xaml"/>
|
||||
</Window.Styles>
|
||||
|
||||
<Border Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
|
||||
Padding="24"
|
||||
CornerRadius="12"
|
||||
Margin="10"
|
||||
BorderBrush="{DynamicResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1">
|
||||
<Border.Effect>
|
||||
<DropShadowEffect BlurRadius="20"
|
||||
OffsetX="2"
|
||||
OffsetY="2"
|
||||
Color="#00000020"
|
||||
Opacity="0.2"/>
|
||||
</Border.Effect>
|
||||
|
||||
<StackPanel Spacing="16">
|
||||
|
||||
<!-- Header with Icon -->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="12">
|
||||
<PathIcon Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"
|
||||
Width="28"
|
||||
Height="28"
|
||||
Foreground="#22c55e"/>
|
||||
<TextBlock Text="{Binding DialogTitle}"
|
||||
FontSize="20"
|
||||
FontWeight="Bold"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Version Info -->
|
||||
<Border Background="{DynamicResource CardBackgroundFillColorSecondaryBrush}"
|
||||
CornerRadius="8"
|
||||
Padding="16">
|
||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto" RowSpacing="8" ColumnSpacing="12">
|
||||
|
||||
<!-- Current Version -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0"
|
||||
Text="{Binding CurrentVersionLabel}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1"
|
||||
Text="{Binding CurrentVersion}"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"
|
||||
VerticalAlignment="Center"/>
|
||||
|
||||
<!-- Latest Version -->
|
||||
<TextBlock Grid.Row="1" Grid.Column="0"
|
||||
Text="{Binding LatestVersionLabel}"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
VerticalAlignment="Center"/>
|
||||
<StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="{Binding LatestVersion}"
|
||||
Foreground="#22c55e"
|
||||
FontWeight="Bold"
|
||||
VerticalAlignment="Center"/>
|
||||
<Border Background="#22c55e"
|
||||
CornerRadius="4"
|
||||
Padding="6,2">
|
||||
<TextBlock Text="NEW"
|
||||
FontSize="10"
|
||||
FontWeight="Bold"
|
||||
Foreground="White"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Release Title -->
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
|
||||
Text="{Binding ReleaseTitle}"
|
||||
FontSize="13"
|
||||
FontStyle="Italic"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
IsVisible="{Binding ReleaseTitle, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Release Notes -->
|
||||
<StackPanel Spacing="8"
|
||||
IsVisible="{Binding ReleaseNotes, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock Text="{Binding ReleaseNotesLabel}"
|
||||
FontWeight="SemiBold"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"/>
|
||||
<ScrollViewer MaxHeight="120"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock Text="{Binding ReleaseNotes}"
|
||||
TextWrapping="Wrap"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"
|
||||
LineHeight="18"/>
|
||||
</ScrollViewer>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Download Progress (shown during download) -->
|
||||
<StackPanel Spacing="8" IsVisible="{Binding IsDownloading}">
|
||||
<TextBlock Text="{Binding DownloadStatus}"
|
||||
FontSize="13"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
HorizontalAlignment="Center"/>
|
||||
<ProgressBar Value="{Binding DownloadProgress}"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Height="8"
|
||||
CornerRadius="4"
|
||||
Foreground="#2563eb"/>
|
||||
<TextBlock Text="{Binding DownloadProgress, StringFormat={}{0}%}"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
HorizontalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Spacing="12" IsVisible="{Binding !IsDownloading}">
|
||||
|
||||
<!-- Primary Actions -->
|
||||
<StackPanel Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="12">
|
||||
|
||||
<!-- Manual Button -->
|
||||
<Button MinWidth="140"
|
||||
Height="40"
|
||||
Background="#6366f1"
|
||||
Foreground="White"
|
||||
CornerRadius="8"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Command="{Binding SelectManualCommand}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<PathIcon Data="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Foreground="White"/>
|
||||
<TextBlock Text="{Binding ManualButtonText}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<!-- Automatic Button -->
|
||||
<Button MinWidth="140"
|
||||
Height="40"
|
||||
Background="#22c55e"
|
||||
Foreground="White"
|
||||
CornerRadius="8"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Command="{Binding SelectAutomaticCommand}"
|
||||
IsEnabled="{Binding HasDownloadUrl}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<PathIcon Data="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z"
|
||||
Width="16"
|
||||
Height="16"
|
||||
Foreground="White"/>
|
||||
<TextBlock Text="{Binding AutomaticButtonText}"
|
||||
VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Skip Button -->
|
||||
<Button HorizontalAlignment="Center"
|
||||
MinWidth="100"
|
||||
Height="32"
|
||||
Background="Transparent"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}"
|
||||
BorderThickness="0"
|
||||
Command="{Binding SelectSkipCommand}">
|
||||
<TextBlock Text="{Binding SkipButtonText}"
|
||||
FontSize="12"
|
||||
TextDecorations="Underline"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Tooltip for disabled automatic button -->
|
||||
<TextBlock Text="(Automatic download unavailable - GitHub API rate limited)"
|
||||
FontSize="11"
|
||||
FontStyle="Italic"
|
||||
Foreground="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
|
||||
HorizontalAlignment="Center"
|
||||
IsVisible="{Binding !HasDownloadUrl}"/>
|
||||
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Window>
|
||||
51
Jellyfin2Samsung-CrossOS/Views/UpdateDialog.axaml.cs
Normal file
51
Jellyfin2Samsung-CrossOS/Views/UpdateDialog.axaml.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Styling;
|
||||
using Jellyfin2Samsung.Helpers;
|
||||
using Jellyfin2Samsung.Interfaces;
|
||||
using Jellyfin2Samsung.Models;
|
||||
using Jellyfin2Samsung.ViewModels;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jellyfin2Samsung.Views;
|
||||
|
||||
public partial class UpdateDialog : Window
|
||||
{
|
||||
public UpdateDialogViewModel ViewModel { get; }
|
||||
|
||||
public UpdateDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
var localizationService = App.Services.GetRequiredService<ILocalizationService>();
|
||||
ViewModel = new UpdateDialogViewModel(localizationService);
|
||||
DataContext = ViewModel;
|
||||
|
||||
// Apply theme
|
||||
RequestedThemeVariant = AppSettings.Default.DarkMode ? ThemeVariant.Dark : ThemeVariant.Light;
|
||||
|
||||
// Handle close request from ViewModel
|
||||
ViewModel.RequestClose += (_, _) => Close();
|
||||
}
|
||||
|
||||
public void Initialize(UpdateCheckResult updateInfo)
|
||||
{
|
||||
ViewModel.Initialize(updateInfo);
|
||||
}
|
||||
|
||||
public async Task<UpdateDialogChoice> ShowDialogAsync(Window parent)
|
||||
{
|
||||
await ShowDialog(parent);
|
||||
return ViewModel.DialogResult ?? UpdateDialogChoice.Cancel;
|
||||
}
|
||||
|
||||
public void UpdateProgress(int progress, string status)
|
||||
{
|
||||
ViewModel.UpdateDownloadProgress(progress, status);
|
||||
}
|
||||
|
||||
public void SetDownloading(bool isDownloading)
|
||||
{
|
||||
ViewModel.IsDownloading = isDownloading;
|
||||
}
|
||||
}
|
||||
41
README.md
41
README.md
@@ -18,9 +18,9 @@
|
||||
<br/>
|
||||
It handles device detection, certificates, and installation so you don’t have to fight with Tizen Studio or manual sideloading.
|
||||
<br/><br/>
|
||||
🌐 Available in: Danish, Dutch, English, French, German, Turkish
|
||||
🌐 Available in: Danish, Dutch, English, French, German, Portuguese, Turkish
|
||||
<br/>
|
||||
🇩🇰 🇳🇱 🇬🇧 🇫🇷 🇩🇪 🇹🇷
|
||||
🇩🇰 🇳🇱 🇬🇧 🇫🇷 🇩🇪 🇵🇹 🇹🇷
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -29,10 +29,10 @@
|
||||
|
||||
<!-- versions:start -->
|
||||
|
||||
| Channel | Version | Notes |
|
||||
|------------|---------|-------|
|
||||
| **Stable** | v1.8.7.0 | Recommended for most users |
|
||||
| **Beta** | v1.8.7.5-beta | Includes new features |
|
||||
| Channel | Version | Notes |
|
||||
|------------|---------------------------------------------------------------------|------------------------------|
|
||||
| **Stable** | [v2.2.0.7](https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer/releases/tag/v2.2.0.7) | Recommended for most users |
|
||||
| **Beta** | [v2.2.0.8-beta](https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer/releases/tag/v2.2.0.8-beta) | Includes new features |
|
||||
|
||||
<!-- versions:end -->
|
||||
|
||||
@@ -56,6 +56,8 @@ That’s it. No manual certificate handling required in most cases.
|
||||
🎥 Full walkthrough:
|
||||
https://www.youtube.com/watch?v=_8mSV5pW-ic
|
||||
|
||||
**NixOS:** Clone the [`Samsung2Jellyfin` branch](https://github.com/Jellyfin2Samsung/Samsung-Jellyfin-Installer.git) and run `nix-shell` — the shell environment will automatically build and launch the tool.
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation
|
||||
@@ -88,11 +90,24 @@ https://github.com/PatrickSt1991/tizen-community-packages
|
||||
|
||||
## 🛠️ Support & Contributing
|
||||
|
||||
- Bug reports & feature requests: [Issues](../../issues)
|
||||
- Ideas & questions: [Discussions](../../discussions)
|
||||
- Chat: https://discord.gg/7mga3zh8Cv
|
||||
Contributions of all kinds are welcome — whether it’s bug reports, feature requests, code, documentation, or translations.
|
||||
|
||||
Contributions are welcome — issues, PRs, translations, or documentation.
|
||||
- Bug reports & feature requests: [Issues](../../issues)
|
||||
- Ideas, feedback & questions: [Discussions](../../discussions)
|
||||
- Community chat: [Discord](https://discord.gg/7mga3zh8Cv)
|
||||
|
||||
## 🌍 Translations
|
||||
|
||||
Want to help translate **Jellyfin2Samsung**? Community translations are always appreciated.
|
||||
|
||||
You can contribute here:
|
||||
|
||||
- [Transifex](https://app.transifex.com/madebypatrick/jellyfin2samsung)
|
||||
- [Crowdin](https://crowdin.com/project/jellyfin2samsung)
|
||||
|
||||
You can help by translating missing strings, improving existing translations, or reviewing your language.
|
||||
|
||||
Translation updates are synced back into this repository automatically.
|
||||
|
||||
---
|
||||
|
||||
@@ -115,6 +130,8 @@ This project is made possible by the people who contribute their time, knowledge
|
||||
Special thanks to:
|
||||
- **jeppevinkel** — for providing the Jellyfin Tizen `.wgt` builds
|
||||
https://github.com/jeppevinkel/jellyfin-tizen-builds
|
||||
- **Hungry__Alpaca** — for the Moonfin client and related work
|
||||
https://github.com/Moonfin-Client/Tizen
|
||||
- **@RadicalMuffinMan** — for the Moonfin client and related work
|
||||
https://github.com/Moonfin-Client/Smart-TV
|
||||
- **@MoazSalem** — for the Litefin client and related work
|
||||
https://github.com/MoazSalem/litefin/
|
||||
|
||||
|
||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /Jellyfin2Samsung-CrossOS/Assets/Localization/en.json
|
||||
translation: /Jellyfin2Samsung-CrossOS/Assets/Localization/%two_letters_code%.json
|
||||
81
shell.nix
Normal file
81
shell.nix
Normal file
@@ -0,0 +1,81 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
let
|
||||
krb5WithUnversionedLib = pkgs.runCommand "krb5-unversioned-so" {} ''
|
||||
mkdir -p $out/lib
|
||||
ln -s ${pkgs.krb5}/lib/libgssapi_krb5.so.2 $out/lib/libgssapi_krb5.so
|
||||
'';
|
||||
|
||||
# FHS environment that provides /lib, /lib64, /usr/lib etc.
|
||||
# so dynamically linked binaries (TizenSdb, .NET native bits) just work.
|
||||
fhs = pkgs.buildFHSEnv {
|
||||
name = "jellyfin2samsung-fhs";
|
||||
|
||||
targetPkgs = p: with p; [
|
||||
# Build and Run
|
||||
dotnet-sdk_8
|
||||
patchelf
|
||||
file
|
||||
stdenv.cc.cc.lib
|
||||
openssl
|
||||
icu
|
||||
zlib
|
||||
libgcc.lib
|
||||
krb5
|
||||
krb5WithUnversionedLib
|
||||
|
||||
# Fonts and Rendering
|
||||
fontconfig
|
||||
freetype
|
||||
libGL
|
||||
|
||||
# X11 tools
|
||||
xorg.libX11
|
||||
xorg.libICE
|
||||
xorg.libSM
|
||||
xorg.libXext
|
||||
xorg.libXcursor
|
||||
xorg.libXi
|
||||
xorg.libXrandr
|
||||
xorg.libXrender
|
||||
xorg.libXinerama
|
||||
xorg.libXcomposite
|
||||
xorg.libXdamage
|
||||
xorg.libXfixes
|
||||
xorg.libXtst
|
||||
|
||||
# Some other packages that may be needed if not installed system wide
|
||||
nmap
|
||||
iproute2
|
||||
curl
|
||||
wget
|
||||
xdg-utils
|
||||
];
|
||||
|
||||
runScript = pkgs.writeShellScript "jellyfin2samsung-entry" ''
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
export DOTNET_NOLOGO=1
|
||||
|
||||
dotnet publish Jellyfin2Samsung-CrossOS/Jellyfin2Samsung.csproj \
|
||||
-c Release \
|
||||
-r linux-x64 \
|
||||
--self-contained true \
|
||||
-p:PublishSingleFile=false \
|
||||
-p:PublishTrimmed=false
|
||||
|
||||
Jellyfin2Samsung-CrossOS/bin/Release/net8.0/linux-x64/publish/Jellyfin2Samsung
|
||||
'';
|
||||
};
|
||||
|
||||
in
|
||||
pkgs.mkShell {
|
||||
name = "jellyfin2samsung";
|
||||
|
||||
buildInputs = [
|
||||
fhs
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
jellyfin2samsung-fhs
|
||||
'';
|
||||
}
|
||||
6
transifex.yml
Normal file
6
transifex.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
filters:
|
||||
- filter_type: file
|
||||
file_format: KEYVALUEJSON
|
||||
source_file: Assets/Localization/en.json
|
||||
source_language: en
|
||||
translation_files_expression: 'Assets/Localization/<lang>.json'
|
||||
Reference in New Issue
Block a user