Merge branch 'DonutWare:develop' into develop
2
.fvmrc
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"flutter": "3.29.2"
|
"flutter": "3.32.1"
|
||||||
}
|
}
|
||||||
302
.github/workflows/build.yml
vendored
|
|
@ -14,19 +14,87 @@ on:
|
||||||
types:
|
types:
|
||||||
- opened
|
- opened
|
||||||
- reopened
|
- reopened
|
||||||
|
schedule:
|
||||||
|
# Run nightly at midnight UTC, but only if there were changes to develop
|
||||||
|
- cron: "0 0 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
build_type:
|
||||||
|
description: "Build type (release, nightly, or auto)"
|
||||||
|
required: false
|
||||||
|
default: "auto"
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- auto
|
||||||
|
- release
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
NIGHTLY_TAG: nightly
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# Check if workflow should run based on trigger conditions
|
||||||
|
check-trigger:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
should_run: ${{ steps.check.outputs.should_run }}
|
||||||
|
build_type: ${{ steps.check.outputs.build_type }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Check if should run and determine build type
|
||||||
|
id: check
|
||||||
|
run: |
|
||||||
|
# Determine build type based on trigger and input
|
||||||
|
if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.build_type }}" != "auto" ]]; then
|
||||||
|
BUILD_TYPE="${{ github.event.inputs.build_type }}"
|
||||||
|
elif [[ "${{ startsWith(github.ref, 'refs/tags/v') }}" == "true" ]]; then
|
||||||
|
BUILD_TYPE="release"
|
||||||
|
elif [[ "${{ github.event_name }}" == "schedule" ]]; then
|
||||||
|
# Check if there were commits to develop in the last 24 hours
|
||||||
|
git checkout develop
|
||||||
|
COMMITS_LAST_24H=$(git log --since="24 hours ago" --oneline | wc -l)
|
||||||
|
if [[ $COMMITS_LAST_24H -gt 0 ]]; then
|
||||||
|
BUILD_TYPE="nightly"
|
||||||
|
else
|
||||||
|
echo "No commits to develop in the last 24 hours, skipping nightly build"
|
||||||
|
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||||
|
BUILD_TYPE="nightly"
|
||||||
|
else
|
||||||
|
# For PRs and other events, build but don't release
|
||||||
|
BUILD_TYPE="development"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "build_type=${BUILD_TYPE}" >> $GITHUB_OUTPUT
|
||||||
|
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||||
|
echo "Build type determined: ${BUILD_TYPE}"
|
||||||
|
|
||||||
fetch-info:
|
fetch-info:
|
||||||
|
needs: [check-trigger]
|
||||||
|
if: needs.check-trigger.outputs.should_run == 'true'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
outputs:
|
outputs:
|
||||||
version_name: ${{ steps.fetch.outputs.version_name }}
|
version_name: ${{ steps.fetch.outputs.version_name }}
|
||||||
flutter_version: ${{ steps.fetch.outputs.flutter_version }}
|
flutter_version: ${{ steps.fetch.outputs.flutter_version }}
|
||||||
|
nightly_version: ${{ steps.fetch.outputs.nightly_version }}
|
||||||
|
build_type: ${{ needs.check-trigger.outputs.build_type }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Fetch version name
|
- name: Fetch version name and create nightly version
|
||||||
id: fetch
|
id: fetch
|
||||||
run: |
|
run: |
|
||||||
# Extract version_name from pubspec.yaml
|
# Extract version_name from pubspec.yaml
|
||||||
|
|
@ -36,29 +104,47 @@ jobs:
|
||||||
# Extract flutter_version from .fvmrc
|
# Extract flutter_version from .fvmrc
|
||||||
FLUTTER_VERSION=$(jq -r '.flutter' .fvmrc)
|
FLUTTER_VERSION=$(jq -r '.flutter' .fvmrc)
|
||||||
echo "flutter_version=${FLUTTER_VERSION}" >> "$GITHUB_OUTPUT"
|
echo "flutter_version=${FLUTTER_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
# Create nightly version if needed
|
||||||
|
if [[ "${{ needs.check-trigger.outputs.build_type }}" == "nightly" ]]; then
|
||||||
|
NIGHTLY_VERSION="${VERSION_NAME}-nightly"
|
||||||
|
echo "nightly_version=${NIGHTLY_VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Nightly version: $NIGHTLY_VERSION"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Print versions for logging
|
||||||
|
echo "Base version: $VERSION_NAME"
|
||||||
|
echo "Flutter version: $FLUTTER_VERSION"
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
build-android:
|
build-android:
|
||||||
needs: [fetch-info]
|
needs: [fetch-info]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
artifact_suffix: ${{ env.ARTIFACT_SUFFIX }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Determine Build Type
|
- name: Determine Build Type
|
||||||
run: |
|
run: |
|
||||||
if [[ "${{ startsWith(github.ref, 'refs/tags/v') }}" == "true" ]]; then
|
if [[ "${{ needs.fetch-info.outputs.build_type }}" == "release" ]]; then
|
||||||
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
echo "BUILD_MODE=release" >> $GITHUB_ENV
|
||||||
echo "ARTIFACT_SUFFIX=release-signed" >> $GITHUB_ENV
|
echo "ARTIFACT_SUFFIX=release-signed" >> $GITHUB_ENV
|
||||||
echo "AAB_PATH=productionRelease" >> $GITHUB_ENV
|
echo "AAB_PATH=productionRelease" >> $GITHUB_ENV
|
||||||
|
elif [[ "${{ needs.fetch-info.outputs.build_type }}" == "nightly" ]]; then
|
||||||
|
echo "BUILD_MODE=profile" >> $GITHUB_ENV
|
||||||
|
echo "ARTIFACT_SUFFIX=nightly" >> $GITHUB_ENV
|
||||||
|
echo "AAB_PATH=productionProfile" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "BUILD_MODE=profile" >> $GITHUB_ENV
|
echo "BUILD_MODE=profile" >> $GITHUB_ENV
|
||||||
echo "ARTIFACT_SUFFIX=production" >> $GITHUB_ENV
|
echo "ARTIFACT_SUFFIX=production" >> $GITHUB_ENV
|
||||||
echo "AAB_PATH=productionProfile" >> $GITHUB_ENV
|
echo "AAB_PATH=productionProfile" >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
echo "ARTIFACT_SUFFIX=${ARTIFACT_SUFFIX}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Decode Keystore for release
|
- name: Decode Keystore for release
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
env:
|
env:
|
||||||
ENCODED_STRING: ${{ secrets.KEYSTORE_BASE_64 }}
|
ENCODED_STRING: ${{ secrets.KEYSTORE_BASE_64 }}
|
||||||
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.RELEASE_KEYSTORE_PASSWORD }}
|
||||||
|
|
@ -84,10 +170,10 @@ jobs:
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -121,10 +207,10 @@ jobs:
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -141,7 +227,7 @@ jobs:
|
||||||
path: windows/windows_setup.iss
|
path: windows/windows_setup.iss
|
||||||
options: /O+
|
options: /O+
|
||||||
env:
|
env:
|
||||||
FLADDER_VERSION: ${{needs.fetch-info.outputs.version_name}}
|
FLADDER_VERSION: ${{ needs.fetch-info.outputs.version_name }}
|
||||||
|
|
||||||
- name: Archive Windows portable artifact
|
- name: Archive Windows portable artifact
|
||||||
uses: actions/upload-artifact@v4.0.0
|
uses: actions/upload-artifact@v4.0.0
|
||||||
|
|
@ -164,10 +250,10 @@ jobs:
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -184,7 +270,6 @@ jobs:
|
||||||
mkdir Payload
|
mkdir Payload
|
||||||
mv Runner.app Payload/
|
mv Runner.app Payload/
|
||||||
zip -r iOS.ipa Payload
|
zip -r iOS.ipa Payload
|
||||||
|
|
||||||
- name: Archive iOS IPA artifact
|
- name: Archive iOS IPA artifact
|
||||||
uses: actions/upload-artifact@v4.0.0
|
uses: actions/upload-artifact@v4.0.0
|
||||||
with:
|
with:
|
||||||
|
|
@ -200,10 +285,10 @@ jobs:
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -214,8 +299,16 @@ jobs:
|
||||||
- name: Build macOS app
|
- name: Build macOS app
|
||||||
run: flutter build macos --flavor production --build-number=${{ github.run_number }}
|
run: flutter build macos --flavor production --build-number=${{ github.run_number }}
|
||||||
|
|
||||||
- name: Create DMG file
|
- name: Ensure correct app name casing
|
||||||
run: hdiutil create -format UDZO -srcfolder build/macos/Build/Products/Release-production/fladder.app build/macos/Build/Products/Release-production/macOS.dmg
|
run: |
|
||||||
|
APP_DIR="build/macos/Build/Products/Release-production"
|
||||||
|
mv "$APP_DIR/fladder.app" "$APP_DIR/Fladder.app"
|
||||||
|
|
||||||
|
- name: Install create-dmg
|
||||||
|
run: brew install create-dmg
|
||||||
|
|
||||||
|
- name: Create DMG with custom background
|
||||||
|
run: ./scripts/create_dmg.sh
|
||||||
|
|
||||||
- name: Archive macOS artifact
|
- name: Archive macOS artifact
|
||||||
uses: actions/upload-artifact@v4.0.0
|
uses: actions/upload-artifact@v4.0.0
|
||||||
|
|
@ -232,10 +325,10 @@ jobs:
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -271,7 +364,8 @@ jobs:
|
||||||
|
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
run: |
|
run: |
|
||||||
sed -i -E 's/(version:\s*)latest/\1${{needs.fetch-info.outputs.version_name}}/' AppImageBuilder.yml
|
VERSION_TO_USE="${{ needs.fetch-info.outputs.version_name }}"
|
||||||
|
sed -i -E "s/(version:\\s*)latest/\\1${VERSION_TO_USE}/" AppImageBuilder.yml
|
||||||
wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
|
wget -O appimage-builder-x86_64.AppImage https://github.com/AppImageCrafters/appimage-builder/releases/download/v1.1.0/appimage-builder-1.1.0-x86_64.AppImage
|
||||||
chmod +x appimage-builder-x86_64.AppImage
|
chmod +x appimage-builder-x86_64.AppImage
|
||||||
sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder
|
sudo mv appimage-builder-x86_64.AppImage /usr/local/bin/appimage-builder
|
||||||
|
|
@ -286,9 +380,8 @@ jobs:
|
||||||
Fladder_x86_64.AppImage.zsync
|
Fladder_x86_64.AppImage.zsync
|
||||||
|
|
||||||
build-linux-flatpak:
|
build-linux-flatpak:
|
||||||
name: "Flatpak"
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: needs.fetch-info.outputs.build_type == 'release' || needs.fetch-info.outputs.build_type == 'nightly'
|
||||||
needs: [fetch-info, build-linux]
|
needs: [fetch-info, build-linux]
|
||||||
container:
|
container:
|
||||||
image: bilelmoussaoui/flatpak-github-actions:gnome-46
|
image: bilelmoussaoui/flatpak-github-actions:gnome-46
|
||||||
|
|
@ -325,10 +418,10 @@ jobs:
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
||||||
- name: Set up Flutter
|
- name: Set up Flutter
|
||||||
uses: subosito/flutter-action@v2.16.0
|
uses: subosito/flutter-action@v2.19.0
|
||||||
with:
|
with:
|
||||||
channel: "stable"
|
channel: "stable"
|
||||||
flutter-version: ${{needs.fetch-info.outputs.flutter-version}}
|
flutter-version: ${{needs.fetch-info.outputs.flutter_version}}
|
||||||
cache: true
|
cache: true
|
||||||
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
cache-key: "flutter-:os:-:channel:-:version:-:arch:-:hash:"
|
||||||
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
cache-path: "${{ runner.tool_cache }}/flutter/:channel:-:version:-:arch:"
|
||||||
|
|
@ -347,10 +440,11 @@ jobs:
|
||||||
path: build/web
|
path: build/web
|
||||||
|
|
||||||
- name: Build Github pages web
|
- name: Build Github pages web
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
run: flutter build web --base-href /${{ github.event.repository.name }}/ --release --build-number=$GITHUB_RUN_NUMBER
|
run: flutter build web --base-href /${{ github.event.repository.name }}/ --release --build-number=$GITHUB_RUN_NUMBER
|
||||||
|
|
||||||
- name: Archive web pages artifact
|
- name: Archive web pages artifact
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
uses: actions/upload-artifact@v4.0.0
|
uses: actions/upload-artifact@v4.0.0
|
||||||
with:
|
with:
|
||||||
name: fladder-web-pages
|
name: fladder-web-pages
|
||||||
|
|
@ -368,8 +462,93 @@ jobs:
|
||||||
- build-linux-flatpak
|
- build-linux-flatpak
|
||||||
- build-web
|
- build-web
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: needs.fetch-info.outputs.build_type == 'release' || needs.fetch-info.outputs.build_type == 'nightly'
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'nightly'
|
||||||
|
uses: actions/checkout@v4.1.1
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set version variables
|
||||||
|
id: version
|
||||||
|
run: |
|
||||||
|
if [[ "${{ needs.fetch-info.outputs.build_type }}" == "nightly" ]]; then
|
||||||
|
echo "version=${{ needs.fetch-info.outputs.nightly_version }}" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "version=${{ needs.fetch-info.outputs.version_name }}" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Delete existing nightly release
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'nightly'
|
||||||
|
run: |
|
||||||
|
# Delete existing nightly release and tag with better error handling
|
||||||
|
echo "Checking for existing nightly release..."
|
||||||
|
if gh release view ${{ env.NIGHTLY_TAG }} >/dev/null 2>&1; then
|
||||||
|
echo "Deleting existing nightly release..."
|
||||||
|
gh release delete ${{ env.NIGHTLY_TAG }} --yes --cleanup-tag
|
||||||
|
else
|
||||||
|
echo "No existing nightly release found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up any orphaned tags
|
||||||
|
if git tag -l | grep -q "^${{ env.NIGHTLY_TAG }}$"; then
|
||||||
|
echo "Deleting orphaned nightly tag..."
|
||||||
|
git push origin --delete ${{ env.NIGHTLY_TAG }} || true
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate changelog
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'nightly'
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
# Get the latest stable release tag for comparison
|
||||||
|
LATEST_STABLE_RELEASE=$(gh release list --limit 50 --exclude-drafts --json tagName,isPrerelease --jq '.[] | select(.isPrerelease == false) | .tagName' | head -1 || echo "")
|
||||||
|
|
||||||
|
# Create new changelog with comparison link
|
||||||
|
cat > changelog.md <<-EOF
|
||||||
|
# $(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
|
||||||
|
This is an automated nightly build containing the latest changes from the develop branch.
|
||||||
|
|
||||||
|
**⚠️ Warning:** This is a development build and may contain bugs or incomplete features.
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Add comparison link if we have a latest stable release
|
||||||
|
if [ -n "$LATEST_STABLE_RELEASE" ]; then
|
||||||
|
cat >> changelog.md <<-EOF
|
||||||
|
|
||||||
|
## 📋 What's Changed
|
||||||
|
|
||||||
|
See all changes since the latest release: [Compare $LATEST_STABLE_RELEASE...HEAD](https://github.com/${{ github.repository }}/compare/$LATEST_STABLE_RELEASE...HEAD)
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
cat >> changelog.md <<-EOF
|
||||||
|
|
||||||
|
## 📋 What's Changed
|
||||||
|
|
||||||
|
See all changes: [View commits](https://github.com/${{ github.repository }}/commits/develop)
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add build metadata
|
||||||
|
cat >> changelog.md <<-EOF
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
📅 **Build Date:** $(date -u "+%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
🔧 **Build Number:** ${{ github.run_number }}
|
||||||
|
📝 **Commit:** ${{ github.sha }}
|
||||||
|
🌿 **Branch:** develop
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo "changelog_file=changelog.md" >> $GITHUB_OUTPUT
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Download Artifacts Android
|
- name: Download Artifacts Android
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -378,8 +557,8 @@ jobs:
|
||||||
|
|
||||||
- name: Move Android
|
- name: Move Android
|
||||||
run: |
|
run: |
|
||||||
mv fladder-android/release-signed.apk Fladder-Android-${{needs.fetch-info.outputs.version_name}}.apk
|
mv fladder-android/${{ needs.build-android.outputs.artifact_suffix }}.apk Fladder-Android-${{ steps.version.outputs.version }}.apk
|
||||||
mv fladder-android/release-signed.aab Fladder-Android-${{needs.fetch-info.outputs.version_name}}.aab
|
mv fladder-android/${{ needs.build-android.outputs.artifact_suffix }}.aab Fladder-Android-${{ steps.version.outputs.version }}.aab
|
||||||
|
|
||||||
- name: Download Windows portable artifact
|
- name: Download Windows portable artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -390,7 +569,7 @@ jobs:
|
||||||
- name: Compress Windows
|
- name: Compress Windows
|
||||||
run: |
|
run: |
|
||||||
cd fladder-windows-portable
|
cd fladder-windows-portable
|
||||||
zip -r ../Fladder-Windows-${{needs.fetch-info.outputs.version_name}}.zip .
|
zip -r ../Fladder-Windows-${{ steps.version.outputs.version }}.zip .
|
||||||
|
|
||||||
- name: Download Windows installer artifact
|
- name: Download Windows installer artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -399,7 +578,7 @@ jobs:
|
||||||
path: fladder-windows-installer
|
path: fladder-windows-installer
|
||||||
|
|
||||||
- name: Rename Windows installer
|
- name: Rename Windows installer
|
||||||
run: mv fladder-windows-installer/fladder_setup.exe Fladder-Windows-${{needs.fetch-info.outputs.version_name}}-Setup.exe
|
run: mv fladder-windows-installer/fladder_setup.exe Fladder-Windows-${{ steps.version.outputs.version }}-Setup.exe
|
||||||
|
|
||||||
- name: Download Artifacts iOS
|
- name: Download Artifacts iOS
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -408,7 +587,7 @@ jobs:
|
||||||
path: fladder-iOS
|
path: fladder-iOS
|
||||||
|
|
||||||
- name: Move iOS
|
- name: Move iOS
|
||||||
run: mv fladder-iOS/iOS.ipa Fladder-iOS-${{needs.fetch-info.outputs.version_name}}.ipa
|
run: mv fladder-iOS/iOS.ipa Fladder-iOS-${{ steps.version.outputs.version }}.ipa
|
||||||
|
|
||||||
- name: Download Artifacts macOS
|
- name: Download Artifacts macOS
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -417,7 +596,7 @@ jobs:
|
||||||
path: fladder-macOS
|
path: fladder-macOS
|
||||||
|
|
||||||
- name: Move macOS
|
- name: Move macOS
|
||||||
run: mv fladder-macOS/macOS.dmg Fladder-macOS-${{needs.fetch-info.outputs.version_name}}.dmg
|
run: mv fladder-macOS/macOS.dmg Fladder-macOS-${{ steps.version.outputs.version }}.dmg
|
||||||
|
|
||||||
- name: Download Artifacts Linux
|
- name: Download Artifacts Linux
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -428,16 +607,18 @@ jobs:
|
||||||
- name: Compress Linux
|
- name: Compress Linux
|
||||||
run: |
|
run: |
|
||||||
cd fladder-linux
|
cd fladder-linux
|
||||||
zip -r ../Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.zip .
|
zip -r ../Fladder-Linux-${{ steps.version.outputs.version }}.zip .
|
||||||
|
|
||||||
- name: Download Artifacts Linux Flatpak
|
- name: Download Artifacts Linux Flatpak
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: fladder-linux-flatpak
|
name: fladder-linux-flatpak
|
||||||
path: fladder-linux-flatpak
|
path: fladder-linux-flatpak
|
||||||
|
|
||||||
- name: Move Linux Flatpak
|
- name: Move Linux Flatpak
|
||||||
run: mv fladder-linux-flatpak/Fladder-Linux.flatpak Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.flatpak
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
|
run: mv fladder-linux-flatpak/Fladder-Linux.flatpak Fladder-Linux-${{ steps.version.outputs.version }}.flatpak
|
||||||
|
|
||||||
- name: Download Artifacts Linux AppImage
|
- name: Download Artifacts Linux AppImage
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
|
|
@ -445,11 +626,10 @@ jobs:
|
||||||
name: fladder-linux-appimage
|
name: fladder-linux-appimage
|
||||||
path: fladder-linux-appimage
|
path: fladder-linux-appimage
|
||||||
|
|
||||||
- name: Move linux AppImages
|
- name: Move Linux AppImages
|
||||||
run: |
|
run: |
|
||||||
mv fladder-linux-appimage/Fladder_x86_64.AppImage Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.AppImage
|
mv fladder-linux-appimage/Fladder_x86_64.AppImage Fladder-Linux-${{ steps.version.outputs.version }}.AppImage
|
||||||
mv fladder-linux-appimage/Fladder_x86_64.AppImage.zsync Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.AppImage.zsync
|
mv fladder-linux-appimage/Fladder_x86_64.AppImage.zsync Fladder-Linux-${{ steps.version.outputs.version }}.AppImage.zsync
|
||||||
|
|
||||||
- name: Download Artifacts Web
|
- name: Download Artifacts Web
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -459,32 +639,50 @@ jobs:
|
||||||
- name: Compress Web
|
- name: Compress Web
|
||||||
run: |
|
run: |
|
||||||
cd fladder-web
|
cd fladder-web
|
||||||
zip -r ../Fladder-Web-${{needs.fetch-info.outputs.version_name}}.zip .
|
zip -r ../Fladder-Web-${{ steps.version.outputs.version }}.zip .
|
||||||
|
|
||||||
- name: Release
|
- name: Create Release
|
||||||
|
uses: softprops/action-gh-release@v2.2.2
|
||||||
|
with:
|
||||||
|
tag_name: ${{ needs.fetch-info.outputs.build_type == 'nightly' && env.NIGHTLY_TAG || github.ref_name }}
|
||||||
|
name: ${{ needs.fetch-info.outputs.build_type == 'nightly' && format('🌙 Nightly Build - {0}', steps.version.outputs.version) || '' }}
|
||||||
|
body_path: ${{ needs.fetch-info.outputs.build_type == 'nightly' && 'changelog.md' || '' }}
|
||||||
|
draft: ${{ needs.fetch-info.outputs.build_type == 'release' }}
|
||||||
|
prerelease: ${{ needs.fetch-info.outputs.build_type == 'nightly' }}
|
||||||
|
make_latest: ${{ needs.fetch-info.outputs.build_type == 'release' }}
|
||||||
|
generate_release_notes: ${{ needs.fetch-info.outputs.build_type == 'release' }}
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
files: |
|
||||||
|
Fladder-Android-${{ steps.version.outputs.version }}.apk
|
||||||
|
Fladder-Android-${{ steps.version.outputs.version }}.aab
|
||||||
|
Fladder-Windows-${{ steps.version.outputs.version }}-Setup.exe
|
||||||
|
Fladder-Windows-${{ steps.version.outputs.version }}.zip
|
||||||
|
Fladder-iOS-${{ steps.version.outputs.version }}.ipa
|
||||||
|
Fladder-macOS-${{ steps.version.outputs.version }}.dmg
|
||||||
|
Fladder-Web-${{ steps.version.outputs.version }}.zip
|
||||||
|
Fladder-Linux-${{ steps.version.outputs.version }}.zip
|
||||||
|
Fladder-Linux-${{ steps.version.outputs.version }}.AppImage
|
||||||
|
Fladder-Linux-${{ steps.version.outputs.version }}.AppImage.zsync
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Add Flatpak to release
|
||||||
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
draft: true
|
tag_name: ${{ github.ref_name }}
|
||||||
fail_on_unmatched_files: true
|
|
||||||
generate_release_notes: true
|
|
||||||
files: |
|
files: |
|
||||||
Fladder-Android-${{needs.fetch-info.outputs.version_name}}.apk
|
Fladder-Linux-${{ steps.version.outputs.version }}.flatpak
|
||||||
Fladder-Windows-${{needs.fetch-info.outputs.version_name}}-Setup.exe
|
env:
|
||||||
Fladder-Windows-${{needs.fetch-info.outputs.version_name}}.zip
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
Fladder-iOS-${{needs.fetch-info.outputs.version_name}}.ipa
|
|
||||||
Fladder-macOS-${{needs.fetch-info.outputs.version_name}}.dmg
|
|
||||||
Fladder-Web-${{needs.fetch-info.outputs.version_name}}.zip
|
|
||||||
Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.zip
|
|
||||||
Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.flatpak
|
|
||||||
Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.AppImage
|
|
||||||
Fladder-Linux-${{needs.fetch-info.outputs.version_name}}.AppImage.zsync
|
|
||||||
|
|
||||||
release_web:
|
release_web:
|
||||||
name: Release Web
|
name: Release Web
|
||||||
needs:
|
needs:
|
||||||
|
- fetch-info
|
||||||
- create_release
|
- create_release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: needs.fetch-info.outputs.build_type == 'release'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4.1.1
|
uses: actions/checkout@v4.1.1
|
||||||
|
|
|
||||||
5
.gitignore
vendored
|
|
@ -5,6 +5,8 @@
|
||||||
*.swp
|
*.swp
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.atom/
|
.atom/
|
||||||
|
.build/
|
||||||
|
.dmg_temp/
|
||||||
.buildlog/
|
.buildlog/
|
||||||
.history
|
.history
|
||||||
.svn/
|
.svn/
|
||||||
|
|
@ -73,3 +75,6 @@ local.properties
|
||||||
|
|
||||||
# Inno Setup builds
|
# Inno Setup builds
|
||||||
Output
|
Output
|
||||||
|
|
||||||
|
# Generated localizations
|
||||||
|
lib/l10n/generated
|
||||||
|
|
|
||||||
19
.vscode/launch.json
vendored
|
|
@ -10,7 +10,18 @@
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"development"
|
"development",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Fladder Development HTPC (debug)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--flavor",
|
||||||
|
"development",
|
||||||
|
"-a",
|
||||||
|
"--htpc",
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -69,7 +80,8 @@
|
||||||
"deviceId": "chrome",
|
"deviceId": "chrome",
|
||||||
"args": [
|
"args": [
|
||||||
"--web-port",
|
"--web-port",
|
||||||
"9090"
|
"9090",
|
||||||
|
"--web-experimental-hot-reload"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -80,7 +92,8 @@
|
||||||
"flutterMode": "release",
|
"flutterMode": "release",
|
||||||
"args": [
|
"args": [
|
||||||
"--web-port",
|
"--web-port",
|
||||||
"9090"
|
"9090",
|
||||||
|
"--web-experimental-hot-reload"
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
12
.vscode/settings.json
vendored
|
|
@ -7,7 +7,7 @@
|
||||||
"LTWH",
|
"LTWH",
|
||||||
"outro"
|
"outro"
|
||||||
],
|
],
|
||||||
"dart.flutterSdkPath": ".fvm/versions/3.29.2",
|
"dart.flutterSdkPath": ".fvm/versions/3.32.1",
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/.fvm": true
|
"**/.fvm": true
|
||||||
},
|
},
|
||||||
|
|
@ -15,5 +15,13 @@
|
||||||
"**/.fvm": true
|
"**/.fvm": true
|
||||||
},
|
},
|
||||||
"editor.detectIndentation": true,
|
"editor.detectIndentation": true,
|
||||||
"dart.lineLength": 120
|
"dart.lineLength": 120,
|
||||||
|
"dart.analysisExcludedFolders": [
|
||||||
|
"build",
|
||||||
|
".dart_tool",
|
||||||
|
".fvm",
|
||||||
|
"packages",
|
||||||
|
"lib/jellyfin",
|
||||||
|
"lib/l10n"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
65
DEVELOPEMENT.md
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
# 🚀 Fladder Dev Setup
|
||||||
|
|
||||||
|
## 🔧 Requirements
|
||||||
|
|
||||||
|
Ensure the following tools are installed:
|
||||||
|
|
||||||
|
- [Flutter SDK](https://flutter.dev/docs/get-started/install) (latest stable)
|
||||||
|
- [Android Studio](https://developer.android.com/studio) (for Android development and emulators)
|
||||||
|
- [VS Code](https://code.visualstudio.com/) with:
|
||||||
|
- Flutter extension
|
||||||
|
- Dart extension
|
||||||
|
|
||||||
|
Verify your Flutter setup with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter doctor
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the repository
|
||||||
|
git clone https://github.com/DonutWare/Fladder.git
|
||||||
|
cd Fladder
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
flutter pub get
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐧 Linux Dependencies
|
||||||
|
|
||||||
|
If you're on **Linux**, install the `mpv` dependency:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install libmpv-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Running the App
|
||||||
|
|
||||||
|
1. **Connect a device** or launch an emulator.
|
||||||
|
2. In VS Code:
|
||||||
|
- Select the target device (bottom right corner).
|
||||||
|
- Press `F5` or go to **Run > Start Debugging**.
|
||||||
|
- If prompted, select **"Run Anyway"**.
|
||||||
|
|
||||||
|
## ⚙️ Code Generation
|
||||||
|
|
||||||
|
Generate build files (e.g., for `json_serializable`, `freezed`, etc.):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
flutter pub run build_runner build
|
||||||
|
```
|
||||||
|
|
||||||
|
> Tip: Use `watch` for continuous builds during development:
|
||||||
|
```bash
|
||||||
|
flutter pub run build_runner watch
|
||||||
|
```
|
||||||
|
Update localization definitions:
|
||||||
|
```bash
|
||||||
|
flutter gen-l10n
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌐 Using a demo Server
|
||||||
|
You can use a fake server from Jellyfin.
|
||||||
|
https://demo.jellyfin.org/stable/web/
|
||||||
|
|
@ -16,6 +16,7 @@ Platform-specific installation instructions can be found in this document.
|
||||||
- [iOS](#iosipados)
|
- [iOS](#iosipados)
|
||||||
- [Sideloadly](#sideloadly)
|
- [Sideloadly](#sideloadly)
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
|
- [Web](#web)
|
||||||
|
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
@ -140,3 +141,10 @@ Run `docker-compose up -d` to start the container. It will be available on `http
|
||||||
> [!TIP]
|
> [!TIP]
|
||||||
> We recommend changing the `BASE_URL` environment variable to the URL you use to access Jellyfin, as this will skip entering it when you load the web UI.
|
> We recommend changing the `BASE_URL` environment variable to the URL you use to access Jellyfin, as this will skip entering it when you load the web UI.
|
||||||
|
|
||||||
|
## Web
|
||||||
|
|
||||||
|
You can also manually copy the web .zip build to any static file server such as Nginx, Caddy, or Apache
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> You can preconfigure Fladder by placing a config file in [assets/config/config.json](https://github.com/DonutWare/Fladder/blob/develop/config/config.json)
|
||||||
|
|
||||||
|
|
|
||||||
11
README.md
|
|
@ -53,11 +53,14 @@
|
||||||
<details close>
|
<details close>
|
||||||
<summary>Mobile</summary>
|
<summary>Mobile</summary>
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Dashboard.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Dashboard.png?raw=true" alt="Fladder" width="200">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Details.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Details_2.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Details_2.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Favourites.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Favourites.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Library.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Library.png?raw=true" alt="Fladder" width="200">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Library_Search.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Resume_Tab.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Resume_Tab.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Sync.png?raw=true" alt="Fladder" width="200">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Sync.png?raw=true" alt="Fladder" width="200">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Settings.png?raw=true" alt="Fladder" width="200">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Player.png?raw=true" alt="Fladder" width="1280">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Mobile/Player.png?raw=true" alt="Fladder" width="1280">
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|
@ -65,8 +68,14 @@
|
||||||
<summary>Tablet</summary>
|
<summary>Tablet</summary>
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Dashboard.png?raw=true" alt="Fladder" width="1280">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Dashboard.png?raw=true" alt="Fladder" width="1280">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Details.png?raw=true" alt="Fladder" width="1280">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Details.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Details_2.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Favourites.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Library.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Library_Search.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Resume_Tab.png?raw=true" alt="Fladder" width="1280">
|
||||||
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Sync.png?raw=true" alt="Fladder" width="1280">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Settings.png?raw=true" alt="Fladder" width="1280">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Settings.png?raw=true" alt="Fladder" width="1280">
|
||||||
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Sync.png?raw=true" alt="Fladder" width="1280">
|
<img src="https://github.com/DonutWare/Fladder/blob/develop/assets/marketing/screenshots/Tablet/Player.png?raw=true" alt="Fladder" width="1280">
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
Web/Desktop [try out the web build!](https://DonutWare.github.io/Fladder)
|
Web/Desktop [try out the web build!](https://DonutWare.github.io/Fladder)
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,15 @@ include: package:lints/recommended.yaml
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
- build/**
|
- build/**
|
||||||
|
- .dart_tool/**
|
||||||
|
- .fvm/**
|
||||||
|
- pubspec.yaml
|
||||||
|
- packages/**
|
||||||
- lib/jellyfin/**
|
- lib/jellyfin/**
|
||||||
- lib/**/**.g.dart
|
- lib/**/**.g.dart
|
||||||
- lib/**/**.freezed.dart
|
- lib/**/**.freezed.dart
|
||||||
- lib/**/**.mapped.dart
|
- lib/**/**.mapped.dart
|
||||||
- packages/**
|
- lib/l10n/**
|
||||||
- pubspec.yaml
|
|
||||||
strong-mode:
|
strong-mode:
|
||||||
implicit-casts: false
|
implicit-casts: false
|
||||||
implicit-dynamic: false
|
implicit-dynamic: false
|
||||||
|
|
|
||||||
BIN
assets/macos-dmg/Fladder-DMG-Background.afphoto
Normal file
BIN
assets/macos-dmg/Fladder-DMG-Background.jpg
Normal file
|
After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 732 KiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 731 KiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
BIN
assets/marketing/screenshots/Mobile/Library_Search.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 895 KiB After Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 996 KiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 930 KiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 4.3 MiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 3.3 MiB |
BIN
assets/marketing/screenshots/Tablet/Details_2.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/marketing/screenshots/Tablet/Favourites.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
assets/marketing/screenshots/Tablet/Library.png
Normal file
|
After Width: | Height: | Size: 3.3 MiB |
BIN
assets/marketing/screenshots/Tablet/Library_Search.png
Normal file
|
After Width: | Height: | Size: 2.8 MiB |
BIN
assets/marketing/screenshots/Tablet/Player.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/marketing/screenshots/Tablet/Resume_Tab.png
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 244 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 1.1 MiB |
|
|
@ -27,6 +27,9 @@ targets:
|
||||||
- "**/**_page.dart"
|
- "**/**_page.dart"
|
||||||
freezed|freezed:
|
freezed|freezed:
|
||||||
options:
|
options:
|
||||||
|
copy_with: false
|
||||||
|
equal: false
|
||||||
|
tostring:
|
||||||
generate_for:
|
generate_for:
|
||||||
- "**/**.f.dart"
|
- "**/**.f.dart"
|
||||||
- "**/**.g.dart"
|
- "**/**.g.dart"
|
||||||
|
|
@ -34,7 +37,7 @@ targets:
|
||||||
options:
|
options:
|
||||||
ignoreNull: true
|
ignoreNull: true
|
||||||
discriminatorKey: type
|
discriminatorKey: type
|
||||||
generateMethods: [decode, encode, copy, stringify]
|
generateMethods: [copy]
|
||||||
chopper_generator:
|
chopper_generator:
|
||||||
options:
|
options:
|
||||||
header: "//Generated jellyfin api code"
|
header: "//Generated jellyfin api code"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
id: nl.jknaapen.fladder
|
id: nl.jknaapen.fladder
|
||||||
runtime: org.gnome.Platform
|
runtime: org.gnome.Platform
|
||||||
runtime-version: '46'
|
runtime-version: '48'
|
||||||
sdk: org.gnome.Sdk
|
sdk: org.gnome.Sdk
|
||||||
|
|
||||||
command: Fladder
|
command: Fladder
|
||||||
|
add-extensions:
|
||||||
|
org.freedesktop.Platform.ffmpeg-full:
|
||||||
|
directory: lib/ffmpeg
|
||||||
|
version: '24.08'
|
||||||
|
add-ld-path: .
|
||||||
|
|
||||||
|
cleanup-commands:
|
||||||
|
- mkdir -p /app/lib/ffmpeg
|
||||||
|
|
||||||
finish-args:
|
finish-args:
|
||||||
# X11/Wayland access
|
# X11/Wayland access
|
||||||
- --share=ipc
|
- --share=ipc
|
||||||
|
|
@ -15,6 +24,8 @@ finish-args:
|
||||||
- --share=network
|
- --share=network
|
||||||
# File access for downloads/media
|
# File access for downloads/media
|
||||||
- --filesystem=home
|
- --filesystem=home
|
||||||
|
# File access for downloads temporary directory
|
||||||
|
- --filesystem=/tmp
|
||||||
# Allow access to PipeWire socket for mpv
|
# Allow access to PipeWire socket for mpv
|
||||||
- --filesystem=xdg-run/pipewire-0:ro
|
- --filesystem=xdg-run/pipewire-0:ro
|
||||||
# Hardware acceleration
|
# Hardware acceleration
|
||||||
|
|
@ -61,42 +72,12 @@ modules:
|
||||||
sources:
|
sources:
|
||||||
- type: git
|
- type: git
|
||||||
url: https://github.com/FFmpeg/nv-codec-headers.git
|
url: https://github.com/FFmpeg/nv-codec-headers.git
|
||||||
tag: n12.2.72.0
|
tag: n13.0.19.0
|
||||||
commit: c69278340ab1d5559c7d7bf0edf615dc33ddbba7
|
commit: e844e5b26f46bb77479f063029595293aa8f812d
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: git
|
type: git
|
||||||
tag-pattern: ^n([\d.]+)$
|
tag-pattern: ^n([\d.]+)$
|
||||||
|
|
||||||
- name: ffmpeg
|
|
||||||
config-opts:
|
|
||||||
- --enable-shared
|
|
||||||
- --disable-static
|
|
||||||
- --enable-gnutls
|
|
||||||
- --enable-pic
|
|
||||||
- --disable-doc
|
|
||||||
- --disable-programs
|
|
||||||
- --disable-encoders
|
|
||||||
- --disable-muxers
|
|
||||||
- --disable-devices
|
|
||||||
- --enable-vaapi
|
|
||||||
- --enable-cuvid
|
|
||||||
- --enable-libdav1d
|
|
||||||
- --enable-gpl
|
|
||||||
cleanup:
|
|
||||||
- /lib/pkgconfig
|
|
||||||
- /share
|
|
||||||
- /include
|
|
||||||
sources:
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/FFmpeg/FFmpeg.git
|
|
||||||
tag: n7.1
|
|
||||||
commit: b08d7969c550a804a59511c7b83f2dd8cc0499b8
|
|
||||||
x-checker-data:
|
|
||||||
type: git
|
|
||||||
tag-pattern: ^n([\d.]+)$
|
|
||||||
|
|
||||||
- shared-modules/luajit/luajit.json
|
- shared-modules/luajit/luajit.json
|
||||||
|
|
||||||
- name: libass
|
- name: libass
|
||||||
config-opts:
|
config-opts:
|
||||||
- --enable-shared
|
- --enable-shared
|
||||||
|
|
@ -113,7 +94,6 @@ modules:
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: git
|
type: git
|
||||||
tag-pattern: ^([\d.]+)$
|
tag-pattern: ^([\d.]+)$
|
||||||
|
|
||||||
- name: uchardet
|
- name: uchardet
|
||||||
buildsystem: cmake-ninja
|
buildsystem: cmake-ninja
|
||||||
config-opts:
|
config-opts:
|
||||||
|
|
@ -134,15 +114,12 @@ modules:
|
||||||
url: https://www.freedesktop.org/software/uchardet/releases/
|
url: https://www.freedesktop.org/software/uchardet/releases/
|
||||||
version-pattern: uchardet-(\d+\.\d+\.\d+)\.tar\.xz
|
version-pattern: uchardet-(\d+\.\d+\.\d+)\.tar\.xz
|
||||||
url-template: https://www.freedesktop.org/software/uchardet/releases/uchardet-$version.tar.xz
|
url-template: https://www.freedesktop.org/software/uchardet/releases/uchardet-$version.tar.xz
|
||||||
|
|
||||||
- name: libplacebo
|
- name: libplacebo
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
config-opts:
|
config-opts:
|
||||||
- -Dvulkan=enabled
|
- -Dvulkan=enabled
|
||||||
- -Dshaderc=enabled
|
- -Dshaderc=enabled
|
||||||
cleanup:
|
- --libdir=lib
|
||||||
- /include
|
|
||||||
- /lib/pkgconfig
|
|
||||||
sources:
|
sources:
|
||||||
- type: git
|
- type: git
|
||||||
url: https://code.videolan.org/videolan/libplacebo.git
|
url: https://code.videolan.org/videolan/libplacebo.git
|
||||||
|
|
@ -151,49 +128,6 @@ modules:
|
||||||
x-checker-data:
|
x-checker-data:
|
||||||
type: git
|
type: git
|
||||||
tag-pattern: ^v([\d.]+)$
|
tag-pattern: ^v([\d.]+)$
|
||||||
modules:
|
|
||||||
- name: shaderc
|
|
||||||
buildsystem: cmake-ninja
|
|
||||||
builddir: true
|
|
||||||
config-opts:
|
|
||||||
- -DSHADERC_SKIP_COPYRIGHT_CHECK=ON
|
|
||||||
- -DSHADERC_SKIP_EXAMPLES=ON
|
|
||||||
- -DSHADERC_SKIP_TESTS=ON
|
|
||||||
- -DSPIRV_SKIP_EXECUTABLES=ON
|
|
||||||
- -DENABLE_GLSLANG_BINARIES=OFF
|
|
||||||
- -DCMAKE_BUILD_TYPE=Release
|
|
||||||
cleanup:
|
|
||||||
- /bin
|
|
||||||
- /include
|
|
||||||
- /lib/*.a
|
|
||||||
- /lib/cmake
|
|
||||||
- /lib/pkgconfig
|
|
||||||
sources:
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/google/shaderc.git
|
|
||||||
tag: v2024.2
|
|
||||||
commit: 3ac03b8ad85a8e328a6182cddee8d05810bd5a2c
|
|
||||||
x-checker-data:
|
|
||||||
type: git
|
|
||||||
tag-pattern: ^v([\d.]+)$
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/KhronosGroup/SPIRV-Tools.git
|
|
||||||
tag: v2023.2
|
|
||||||
commit: 44d72a9b36702f093dd20815561a56778b2d181e
|
|
||||||
dest: third_party/spirv-tools
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/KhronosGroup/SPIRV-Headers.git
|
|
||||||
tag: sdk-1.3.250.1
|
|
||||||
commit: 268a061764ee69f09a477a695bf6a11ffe311b8d
|
|
||||||
dest: third_party/spirv-headers
|
|
||||||
- type: git
|
|
||||||
url: https://github.com/KhronosGroup/glslang.git
|
|
||||||
tag: 14.3.0
|
|
||||||
commit: fa9c3deb49e035a8abcabe366f26aac010f6cbfb
|
|
||||||
dest: third_party/glslang
|
|
||||||
x-checker-data:
|
|
||||||
type: git
|
|
||||||
tag-pattern: ^([\d.]+)$
|
|
||||||
|
|
||||||
- name: zenity
|
- name: zenity
|
||||||
buildsystem: meson
|
buildsystem: meson
|
||||||
|
|
@ -215,4 +149,4 @@ modules:
|
||||||
- install -Dm644 icons/fladder_icon.svg /app/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg
|
- install -Dm644 icons/fladder_icon.svg /app/share/icons/hicolor/scalable/apps/${FLATPAK_ID}.svg
|
||||||
sources:
|
sources:
|
||||||
- type: dir
|
- type: dir
|
||||||
path: ..
|
path: ..
|
||||||
|
|
|
||||||
|
|
@ -2,3 +2,5 @@ arb-dir: lib/l10n
|
||||||
template-arb-file: app_en.arb
|
template-arb-file: app_en.arb
|
||||||
output-localization-file: app_localizations.dart
|
output-localization-file: app_localizations.dart
|
||||||
nullable-getter: false
|
nullable-getter: false
|
||||||
|
synthetic-package: false
|
||||||
|
output-dir: lib/l10n/generated
|
||||||
|
|
|
||||||
|
|
@ -1284,8 +1284,62 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settingsPlayerBufferSizeTitle": "حجم ذاكرة التخزين المؤقت للفيديو",
|
"settingsPlayerBufferSizeTitle": "حجم التخزين المؤقت للفيديو",
|
||||||
"@settingsPlayerBufferSizeTitle": {},
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
"settingsPlayerBufferSizeDesc": "قم بتهيئة حجم ذاكرة التخزين المؤقت لتشغيل الفيديو، مما يحدد كمية البيانات التي يتم تحميلها في الذاكرة المؤقتة.",
|
"settingsPlayerBufferSizeDesc": "قم بتهيئة حجم ذاكرة التخزين المؤقت لتشغيل الفيديو، مما يحدد كمية البيانات التي يتم تحميلها في الذاكرة المؤقتة.",
|
||||||
"@settingsPlayerBufferSizeDesc": {}
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "الحد الأقصى للتنزيلات المتزامنة",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "يحدد العدد الأقصى للتنزيلات التي يمكن أن تتم في نفس الوقت. اضبطه على 0 لتعطيل الحد.",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"playbackTrackSelection": "اختيار مسار التشغيل",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberSubtitleSelections": "تذكر اختيارات الترجمة بناءً على العنصر السابق",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberAudioSelections": "تذكر اختيارات الصوت بناءً على العنصر السابق",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberAudioSelectionsDesc": "حاول ضبط مسار الصوت ليكون الأقرب تطابقاً مع الفيديو الأخير.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "حاول ضبط مسار الترجمة ليكون الأقرب تطابقاً مع الفيديو الأخير.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"exitFladderTitle": "خروج فلادر",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"similarToLikedItem": "مشابه للعنصر المفضل",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "يحتوي على ممثل من الأفلام المشغولة مؤخراً",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"hasLikedActor": "يحتوي على ممثل مفضل",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "يحتوي على مخرج من الأفلام المشغولة مؤخراً",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"hasLikedDirector": "يحتوي على مخرج مفضل",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"playbackTypeDirect": "مباشر",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"latestReleases": "أحدث الإصدارات",
|
||||||
|
"@latestReleases": {},
|
||||||
|
"autoCheckForUpdates": "التحقق التلقائي من التحديثات",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"newUpdateFoundOnGithub": "تم اكتشاف تحديث جديد على Github",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"similarToRecentlyPlayed": "مشابه لما تم تشغيله مؤخراً",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"playbackTypeOffline": "في وضع عدم الاتصال",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"latest": "الأحدث",
|
||||||
|
"@latest": {},
|
||||||
|
"playbackTypeTranscode": "تحويل الصيغة",
|
||||||
|
"@playbackTypeTranscode": {},
|
||||||
|
"newReleaseFoundTitle": "تحديث {newRelease} متاح الآن!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recommended": "الموصى به",
|
||||||
|
"@recommended": {},
|
||||||
|
"playbackType": "نوع التشغيل",
|
||||||
|
"@playbackType": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1345
lib/l10n/app_cs.arb
Normal file
|
|
@ -1290,5 +1290,69 @@
|
||||||
"example": "1"
|
"example": "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"settingsPlayerBufferSizeTitle": "Videopuffergröße",
|
||||||
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
|
"settingsPlayerBufferSizeDesc": "Konfigurieren Sie die Puffergröße für die Videowiedergabe und legen Sie fest, wie viele Daten in den Cache geladen werden.",
|
||||||
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"exitFladderTitle": "Fladder Beenden",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "Maximale Anzahl gleichzeitiger Downloads",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "Legt die maximale Anzahl gleichzeitig laufender Downloads fest. Setzen Sie den Wert auf 0, um die Begrenzung zu deaktivieren.",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"rememberSubtitleSelections": "Untertitelspur basierend auf vorherigem Element einstellen",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberAudioSelections": "Audiospur basierend auf vorherigem Element einstellen",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"similarToRecentlyPlayed": "Ähnlich wie kürzlich gespielt",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"similarToLikedItem": "Ähnlich wie der gewünschte Artikel",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "Hat Schauspieler aus vor kurzem gespielt",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"hasLikedActor": "Hat Schauspieler gemocht",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"latest": "Letzte",
|
||||||
|
"@latest": {},
|
||||||
|
"recommended": "Empfohlen",
|
||||||
|
"@recommended": {},
|
||||||
|
"hasLikedDirector": "Hat Regisseur gemocht",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "Hat Regisseur von vor kurzem gespielt",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"playbackType": "Wiedergabetyp",
|
||||||
|
"@playbackType": {},
|
||||||
|
"playbackTypeDirect": "Direkt",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"playbackTypeTranscode": "Transkodieren",
|
||||||
|
"@playbackTypeTranscode": {},
|
||||||
|
"playbackTypeOffline": "Offline",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"latestReleases": "Aktuelle Veröffentlichungen",
|
||||||
|
"@latestReleases": {},
|
||||||
|
"newReleaseFoundTitle": "Update {newRelease} verfügbar!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoCheckForUpdates": "Regelmäßig nach Updates suchen",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"newUpdateFoundOnGithub": "Habe ein neues Update auf Github gefunden",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "Versuchen Sie, die Untertitelspur so einzustellen, dass sie dem letzten Video am nächsten kommt.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"rememberAudioSelectionsDesc": "Versuchen Sie, die Audiospur so einzustellen, dass sie dem letzten Video am nächsten kommt.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"playbackTrackSelection": "Auswahl der Wiedergabespur",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"enableBackgroundPostersDesc": "Zeigen Sie zufällige Poster in den entsprechenden Bildschirmen",
|
||||||
|
"@enableBackgroundPostersDesc": {},
|
||||||
|
"settingsEnableOsMediaControlsDesc": "Ermöglicht die Wiedergabesteuerung mithilfe von Medientasten und zeigt die aktuell wiedergegebenen Medien im Betriebssystem an",
|
||||||
|
"@settingsEnableOsMediaControlsDesc": {},
|
||||||
|
"enableBackgroundPostersTitle": "Hintergrundbilder aktivieren",
|
||||||
|
"@enableBackgroundPostersTitle": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -721,6 +721,7 @@
|
||||||
"@settingsContinue": {},
|
"@settingsContinue": {},
|
||||||
"settingsEnableOsMediaControls": "Enable OS media controls",
|
"settingsEnableOsMediaControls": "Enable OS media controls",
|
||||||
"@settingsEnableOsMediaControls": {},
|
"@settingsEnableOsMediaControls": {},
|
||||||
|
"settingsEnableOsMediaControlsDesc": "Allow for playback control using media-keys and show current playing media in OS",
|
||||||
"settingsHomeBannerDescription": "Display as a slideshow, carousel, or hide the banner",
|
"settingsHomeBannerDescription": "Display as a slideshow, carousel, or hide the banner",
|
||||||
"@settingsHomeBannerDescription": {},
|
"@settingsHomeBannerDescription": {},
|
||||||
"settingsHomeBannerTitle": "Home banner",
|
"settingsHomeBannerTitle": "Home banner",
|
||||||
|
|
@ -1189,6 +1190,7 @@
|
||||||
"segmentActionAskToSkip": "Ask to skip",
|
"segmentActionAskToSkip": "Ask to skip",
|
||||||
"segmentActionSkip": "Skip",
|
"segmentActionSkip": "Skip",
|
||||||
"loading": "Loading",
|
"loading": "Loading",
|
||||||
|
"exitFladderTitle": "Exit Fladder",
|
||||||
"castAndCrew": "Cast & Crew",
|
"castAndCrew": "Cast & Crew",
|
||||||
"guestActor": "{count, plural, other{Guest Actors} one{Guest Actor}}",
|
"guestActor": "{count, plural, other{Guest Actors} one{Guest Actor}}",
|
||||||
"@guestActor": {
|
"@guestActor": {
|
||||||
|
|
@ -1199,5 +1201,42 @@
|
||||||
"example": "1"
|
"example": "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"maxConcurrentDownloadsTitle": "Max concurrent downloads",
|
||||||
|
"maxConcurrentDownloadsDesc": "Sets the maximum number of downloads that can run at the same time. Set to 0 to disable the limit.",
|
||||||
|
"playbackTrackSelection": "Playback track selection",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberSubtitleSelections": "Set subtitle track based on previous item",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberAudioSelections": "Set audio track based on previous item",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "Try to set the subtitle track to the closest match to the last video.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"rememberAudioSelectionsDesc": "Try to set the audio track to the closest match to the last video.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"similarToRecentlyPlayed": "Similar to recently played",
|
||||||
|
"similarToLikedItem": "Similar to liked item",
|
||||||
|
"hasDirectorFromRecentlyPlayed": "Has director from recently played",
|
||||||
|
"hasActorFromRecentlyPlayed": "Has actor from recently played",
|
||||||
|
"hasLikedDirector": "Has liked director",
|
||||||
|
"hasLikedActor": "Has liked actor",
|
||||||
|
"latest": "Latest",
|
||||||
|
"recommended": "Recommended",
|
||||||
|
"playbackType": "Playback type",
|
||||||
|
"playbackTypeDirect": "Direct",
|
||||||
|
"playbackTypeTranscode": "Transcode",
|
||||||
|
"playbackTypeOffline": "Offline",
|
||||||
|
"latestReleases": "Latest releases",
|
||||||
|
"autoCheckForUpdates": "Periodically check for updates",
|
||||||
|
"newReleaseFoundTitle": "Update {newRelease} available!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newUpdateFoundOnGithub": "Found a new update on Github",
|
||||||
|
"enableBackgroundPostersTitle": "Enable background posters",
|
||||||
|
"enableBackgroundPostersDesc": "Show random posters in applicable screens"
|
||||||
}
|
}
|
||||||
|
|
@ -37,5 +37,33 @@
|
||||||
"appLockPasscode": "Codice",
|
"appLockPasscode": "Codice",
|
||||||
"@appLockPasscode": {},
|
"@appLockPasscode": {},
|
||||||
"audio": "Audio",
|
"audio": "Audio",
|
||||||
"@audio": {}
|
"@audio": {},
|
||||||
|
"amoledBlack": "Amoled nero",
|
||||||
|
"@amoledBlack": {},
|
||||||
|
"backgroundBlur": "Sfocatura dello sfondo",
|
||||||
|
"@backgroundBlur": {},
|
||||||
|
"backgroundOpacity": "Opacità dello sfondo",
|
||||||
|
"@backgroundOpacity": {},
|
||||||
|
"biometricsFailedCheckAgain": "Errore biometrico. Controlla le impostazioni e riprova.",
|
||||||
|
"@biometricsFailedCheckAgain": {},
|
||||||
|
"bold": "Grassetto",
|
||||||
|
"@bold": {},
|
||||||
|
"cancel": "Cancellare",
|
||||||
|
"@cancel": {},
|
||||||
|
"change": "Modifica",
|
||||||
|
"@change": {},
|
||||||
|
"clear": "Leggere",
|
||||||
|
"@clear": {},
|
||||||
|
"clearAllSettings": "Cancella tutte le impostazioni",
|
||||||
|
"@clearAllSettings": {},
|
||||||
|
"clearAllSettingsQuestion": "Cancellare tutte le impostazioni?",
|
||||||
|
"@clearAllSettingsQuestion": {},
|
||||||
|
"clearChanges": "Cancella modifiche",
|
||||||
|
"@clearChanges": {},
|
||||||
|
"clearSelection": "Cancella selezione",
|
||||||
|
"@clearSelection": {},
|
||||||
|
"close": "Vicina",
|
||||||
|
"@close": {},
|
||||||
|
"code": "Codice",
|
||||||
|
"@code": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1082
lib/l10n/app_mr.arb
Normal file
1265
lib/l10n/app_mt.arb
Normal file
|
|
@ -1284,5 +1284,63 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"castAndCrew": "Cast en crew",
|
"castAndCrew": "Cast en crew",
|
||||||
"@castAndCrew": {}
|
"@castAndCrew": {},
|
||||||
|
"settingsPlayerBufferSizeTitle": "Video buffer grootte",
|
||||||
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
|
"settingsPlayerBufferSizeDesc": "Configureer de buffergrootte voor het afspelen van video's en bepaal de hoeveelheid vooraf geladen data.",
|
||||||
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "Maximum gelijktijdige downloads",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "Stelt het maximumaantal downloads in dat tegelijkertijd kan worden uitgevoerd. Stel in op 0 om de limiet uit te schakelen.",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"rememberSubtitleSelections": "Ondertitelspoor instellen op basis van vorig item",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "Probeer het ondertitelspoor in te stellen op de best overeenkomende optie van de vorige video.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"playbackTrackSelection": "Selectie van afspeeltrack",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberAudioSelections": "Audiotrack instellen op basis van vorig item",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberAudioSelectionsDesc": "Probeer de audiotrack in te stellen op de best overeenkomende optie van de vorige video.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"hasLikedActor": "Heeft dezelfde favorite acteur",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "Heeft regisseur van recent afgespeeld",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"similarToLikedItem": "Vergelijkbaar met favoriet",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "Heeft acteur van recent afgespeeld",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"hasLikedDirector": "Heeft dezelfde favoriete regisseur",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"recommended": "Aanbevolen",
|
||||||
|
"@recommended": {},
|
||||||
|
"similarToRecentlyPlayed": "Vergelijkbaar met recent afgespeeld",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"latest": "Laatste",
|
||||||
|
"@latest": {},
|
||||||
|
"exitFladderTitle": "Fladder sluiten",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"playbackTypeDirect": "Direct",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"playbackTypeOffline": "Offline",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"playbackType": "Afspeel type",
|
||||||
|
"@playbackType": {},
|
||||||
|
"playbackTypeTranscode": "Transcoderen",
|
||||||
|
"@playbackTypeTranscode": {},
|
||||||
|
"newUpdateFoundOnGithub": "Nieuwe update gevonden op Github",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"autoCheckForUpdates": "Regelmatig controleren op updates",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"newReleaseFoundTitle": "Update {newRelease} beschikbaar!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"latestReleases": "Nieuwste releases",
|
||||||
|
"@latestReleases": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1153,5 +1153,37 @@
|
||||||
"episodeUnaired": "Niewyemitowany",
|
"episodeUnaired": "Niewyemitowany",
|
||||||
"@episodeUnaired": {},
|
"@episodeUnaired": {},
|
||||||
"episodeMissing": "Brakujący",
|
"episodeMissing": "Brakujący",
|
||||||
"@episodeMissing": {}
|
"@episodeMissing": {},
|
||||||
|
"settingsPlayerBufferSizeDesc": "Konfiguruje rozmiar bufora dla odtwarzania wideo, określając jak wiele danych jest załadowanych w pamięci cache.",
|
||||||
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"settingsPlayerBufferSizeTitle": "Wielkość bufora wideo",
|
||||||
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
|
"refreshPopupContentMetadata": "Metadane są odświeżane na podstawie ustawień i usług internetowych włączonych w panelu administracyjnym.",
|
||||||
|
"@refreshPopupContentMetadata": {},
|
||||||
|
"libraryPageSizeDesc": "Ustawia ilość elementów do załadowania na stronie. 0 wyłącza stronicowanie.",
|
||||||
|
"@libraryPageSizeDesc": {},
|
||||||
|
"rating": "{count, plural, other{Ocen} many{Ocen} few{Oceny} one{Ocena}}",
|
||||||
|
"@rating": {
|
||||||
|
"description": "rating",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pathEditDesc": "Ta lokalizacja jest ustawiana dla wszystkich użytkowników, żadne dane zsynchronizowane dotychczas nie będą już dostępne, ale pozostaną na urządzeniu.",
|
||||||
|
"@pathEditDesc": {},
|
||||||
|
"syncRemoveDataDesc": "Usunąć zsynchronizowane dane? Ta operacja jest nieodwracalna w związku z czym konieczna będzie ponowna synchronizacja.",
|
||||||
|
"@syncRemoveDataDesc": {},
|
||||||
|
"writer": "{count, plural, other{Scenarzytów} one{Scenarzysta}}",
|
||||||
|
"@writer": {
|
||||||
|
"description": "writer",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1246,7 +1246,7 @@
|
||||||
"@layoutModeDual": {},
|
"@layoutModeDual": {},
|
||||||
"copiedToClipboard": "Copiado para a área de transferência",
|
"copiedToClipboard": "Copiado para a área de transferência",
|
||||||
"@copiedToClipboard": {},
|
"@copiedToClipboard": {},
|
||||||
"internetStreamingQualityDesc": "Qualidade máxima de streaming pela internet (celular)",
|
"internetStreamingQualityDesc": "Qualidade máxima de streaming por dados móveis (celular)",
|
||||||
"@internetStreamingQualityDesc": {},
|
"@internetStreamingQualityDesc": {},
|
||||||
"homeStreamingQualityTitle": "Qualidade na rede local",
|
"homeStreamingQualityTitle": "Qualidade na rede local",
|
||||||
"@homeStreamingQualityTitle": {},
|
"@homeStreamingQualityTitle": {},
|
||||||
|
|
@ -1289,5 +1289,67 @@
|
||||||
"example": "1"
|
"example": "1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"hasLikedDirector": "Tem diretor favoritado",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"latest": "Mais recente",
|
||||||
|
"@latest": {},
|
||||||
|
"recommended": "Recomendado",
|
||||||
|
"@recommended": {},
|
||||||
|
"playbackType": "Tipo de reprodução",
|
||||||
|
"@playbackType": {},
|
||||||
|
"playbackTypeDirect": "Direta",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"latestReleases": "Lançamentos mais recentes",
|
||||||
|
"@latestReleases": {},
|
||||||
|
"newReleaseFoundTitle": "Atualização {newRelease} disponível!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newUpdateFoundOnGithub": "Nova atualização encontrada no Github",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"enableBackgroundPostersDesc": "Mostra imagens de fundo aleatórias em telas aplicáveis",
|
||||||
|
"@enableBackgroundPostersDesc": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "Tem ator de algo recentemente assistido",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"enableBackgroundPostersTitle": "Ativar imagens de fundo",
|
||||||
|
"@enableBackgroundPostersTitle": {},
|
||||||
|
"settingsPlayerBufferSizeDesc": "Configure o tamanho do buffer para reprodução de vídeo, determinando a quantidade de dados que é carregada no cache.",
|
||||||
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"playbackTypeOffline": "Offline",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"autoCheckForUpdates": "Verificar periodicamente se há atualizações",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"hasLikedActor": "Tem ator favoritado",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"exitFladderTitle": "Sair do Fladder",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "Máximo de downloads simultâneos",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "Define o número máximo de downloads que podem ser executados ao mesmo tempo. Defina como 0 para desativar o limite.",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"playbackTrackSelection": "Seleção de faixa",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberSubtitleSelections": "Definir a legenda com base no item anterior",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberAudioSelections": "Definir o áudio com base no item anterior",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "Tentar definir a faixa de áudio com a correspondência mais próxima do último vídeo.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"rememberAudioSelectionsDesc": "Tentar definir a legenda com a correspondência mais próxima do último vídeo.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"similarToRecentlyPlayed": "Semelhante aos recentemente assistidos",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"similarToLikedItem": "Semelhante a itens curtidos",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "Tem diretor de algo recentemente assistido",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"settingsEnableOsMediaControlsDesc": "Permitir o controle da reprodução usando as teclas de mídia e mostrar a mídia atual em reprodução no OS",
|
||||||
|
"@settingsEnableOsMediaControlsDesc": {},
|
||||||
|
"settingsPlayerBufferSizeTitle": "Tamanho do buffer de vídeo",
|
||||||
|
"@settingsPlayerBufferSizeTitle": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
129
lib/l10n/app_ro.arb
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
{
|
||||||
|
"code": "Cod",
|
||||||
|
"@code": {},
|
||||||
|
"chapter": "{count, plural, other{Capitole} one{Capitol}}",
|
||||||
|
"@chapter": {
|
||||||
|
"description": "chapter",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"continuePage": "Continuă - pagina {page}",
|
||||||
|
"@continuePage": {
|
||||||
|
"description": "Continue - page 1",
|
||||||
|
"placeholders": {
|
||||||
|
"page": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nativeName": "Română",
|
||||||
|
"@nativeName": {},
|
||||||
|
"accept": "Acceptă",
|
||||||
|
"@accept": {},
|
||||||
|
"addAsFavorite": "Adaugă la favorite",
|
||||||
|
"@addAsFavorite": {},
|
||||||
|
"addToPlaylist": "Adaugă în lista de redare",
|
||||||
|
"@addToPlaylist": {},
|
||||||
|
"advanced": "Avansat",
|
||||||
|
"@advanced": {},
|
||||||
|
"all": "Toate",
|
||||||
|
"@all": {},
|
||||||
|
"amoledBlack": "Negru Amoled",
|
||||||
|
"@amoledBlack": {},
|
||||||
|
"appLockAutoLogin": "Conectare automată",
|
||||||
|
"@appLockAutoLogin": {},
|
||||||
|
"appLockPasscode": "Pinul de acces",
|
||||||
|
"@appLockPasscode": {},
|
||||||
|
"backgroundOpacity": "Opacitatea fundalului",
|
||||||
|
"@backgroundOpacity": {},
|
||||||
|
"bold": "Îngroșat",
|
||||||
|
"@bold": {},
|
||||||
|
"cancel": "Anulați",
|
||||||
|
"@cancel": {},
|
||||||
|
"change": "Schimbă",
|
||||||
|
"@change": {},
|
||||||
|
"clear": "",
|
||||||
|
"@clear": {},
|
||||||
|
"combined": "Combinat",
|
||||||
|
"@combined": {},
|
||||||
|
"controls": "Controale",
|
||||||
|
"@controls": {},
|
||||||
|
"dashboardContinueListening": "Continua Ascultarea",
|
||||||
|
"@dashboardContinueListening": {},
|
||||||
|
"dashboardContinueWatching": "Continua Vizionare",
|
||||||
|
"@dashboardContinueWatching": {},
|
||||||
|
"dashboardRecentlyAdded": "Adăugat recent în {name}",
|
||||||
|
"@dashboardRecentlyAdded": {
|
||||||
|
"description": "Recently added on home screen",
|
||||||
|
"placeholders": {
|
||||||
|
"name": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dateAdded": "Data adăugării",
|
||||||
|
"@dateAdded": {},
|
||||||
|
"datePlayed": "Data redării",
|
||||||
|
"@datePlayed": {},
|
||||||
|
"days": "Zile",
|
||||||
|
"@days": {},
|
||||||
|
"about": "Despre",
|
||||||
|
"@about": {},
|
||||||
|
"close": "Închide",
|
||||||
|
"@close": {},
|
||||||
|
"dashboardContinueReading": "Continua Cititul",
|
||||||
|
"@dashboardContinueReading": {},
|
||||||
|
"autoPlay": "Redare automată",
|
||||||
|
"@autoPlay": {},
|
||||||
|
"active": "Activ",
|
||||||
|
"@active": {},
|
||||||
|
"appLockBiometrics": "Biometrice",
|
||||||
|
"@appLockBiometrics": {},
|
||||||
|
"actor": "{count, plural, other{Actori} one{Actor}}",
|
||||||
|
"@actor": {
|
||||||
|
"description": "actor",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"appLockTitle": "Selectează modul de conectare pentru {userName}",
|
||||||
|
"@appLockTitle": {
|
||||||
|
"description": "Pop-up to pick a login method",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dateLastContentAdded": "Data ultimului conținut adăugat",
|
||||||
|
"@dateLastContentAdded": {},
|
||||||
|
"biometricsFailedCheckAgain": "Biometrice eșuate. Verificați setările și încercați din nou.",
|
||||||
|
"@biometricsFailedCheckAgain": {},
|
||||||
|
"communityRating": "Evaluarea comunității",
|
||||||
|
"@communityRating": {},
|
||||||
|
"dashboardContinue": "Continua",
|
||||||
|
"@dashboardContinue": {},
|
||||||
|
"dashboard": "Bord",
|
||||||
|
"@dashboard": {},
|
||||||
|
"delete": "Șterge",
|
||||||
|
"@delete": {},
|
||||||
|
"color": "Culoare",
|
||||||
|
"@color": {},
|
||||||
|
"addToCollection": "Adaugă la colecție",
|
||||||
|
"@addToCollection": {},
|
||||||
|
"audio": "Audio",
|
||||||
|
"@audio": {},
|
||||||
|
"backgroundBlur": "Blur de fundal",
|
||||||
|
"@backgroundBlur": {},
|
||||||
|
"collectionFolder": "Dosar de colectare",
|
||||||
|
"@collectionFolder": {},
|
||||||
|
"nextUp": "Următorul",
|
||||||
|
"@nextUp": {}
|
||||||
|
}
|
||||||
1351
lib/l10n/app_ru.arb
Normal file
137
lib/l10n/app_sk.arb
Normal file
|
|
@ -0,0 +1,137 @@
|
||||||
|
{
|
||||||
|
"active": "Aktívne",
|
||||||
|
"@active": {},
|
||||||
|
"amoledBlack": "Amoled čierna",
|
||||||
|
"@amoledBlack": {},
|
||||||
|
"actor": "{count, plural, other{Herci} one{Herec/Herečka}}",
|
||||||
|
"@actor": {
|
||||||
|
"description": "actor",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addAsFavorite": "Pridať ako obľúbené",
|
||||||
|
"@addAsFavorite": {},
|
||||||
|
"addToCollection": "Pridať do kolekcie",
|
||||||
|
"@addToCollection": {},
|
||||||
|
"addToPlaylist": "Pridať do playlistu",
|
||||||
|
"@addToPlaylist": {},
|
||||||
|
"advanced": "Pokročilé",
|
||||||
|
"@advanced": {},
|
||||||
|
"all": "Všetko",
|
||||||
|
"@all": {},
|
||||||
|
"appLockAutoLogin": "Automatické prihlásenie",
|
||||||
|
"@appLockAutoLogin": {},
|
||||||
|
"appLockBiometrics": "Biometrika",
|
||||||
|
"@appLockBiometrics": {},
|
||||||
|
"appLockPasscode": "Prístupový kód",
|
||||||
|
"@appLockPasscode": {},
|
||||||
|
"accept": "Prijať",
|
||||||
|
"@accept": {},
|
||||||
|
"about": "O appke",
|
||||||
|
"@about": {},
|
||||||
|
"appLockTitle": "Nastavenie metódy prihlásenia pre {userName}",
|
||||||
|
"@appLockTitle": {
|
||||||
|
"description": "Pop-up to pick a login method",
|
||||||
|
"placeholders": {
|
||||||
|
"userName": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ascending": "Vzostupne",
|
||||||
|
"@ascending": {},
|
||||||
|
"audio": "Zvuk",
|
||||||
|
"@audio": {},
|
||||||
|
"autoPlay": "Automatické prehrávanie",
|
||||||
|
"@autoPlay": {},
|
||||||
|
"backgroundBlur": "Rozmazanie pozadia",
|
||||||
|
"@backgroundBlur": {},
|
||||||
|
"backgroundOpacity": "Priehľadnosť pozadia",
|
||||||
|
"@backgroundOpacity": {},
|
||||||
|
"biometricsFailedCheckAgain": "Biometria zlyhala. Skontrolujte nastavenia a skúste to znova.",
|
||||||
|
"@biometricsFailedCheckAgain": {},
|
||||||
|
"bold": "Hrubý",
|
||||||
|
"@bold": {},
|
||||||
|
"cancel": "Zrušiť",
|
||||||
|
"@cancel": {},
|
||||||
|
"change": "Zmeniť",
|
||||||
|
"@change": {},
|
||||||
|
"clearAllSettings": "Vyčistiť všetky nastavenia",
|
||||||
|
"@clearAllSettings": {},
|
||||||
|
"clearAllSettingsQuestion": "Vynulovať všetky nastavenia?",
|
||||||
|
"@clearAllSettingsQuestion": {},
|
||||||
|
"clearChanges": "Vynulovanie zmien",
|
||||||
|
"@clearChanges": {},
|
||||||
|
"clearSelection": "Vynulovať výber",
|
||||||
|
"@clearSelection": {},
|
||||||
|
"close": "Zatvoriť",
|
||||||
|
"@close": {},
|
||||||
|
"code": "Kód",
|
||||||
|
"@code": {},
|
||||||
|
"collectionFolder": "Zložka kolekcie",
|
||||||
|
"@collectionFolder": {},
|
||||||
|
"color": "Farba",
|
||||||
|
"@color": {},
|
||||||
|
"combined": "Kombinované",
|
||||||
|
"@combined": {},
|
||||||
|
"communityRating": "Hodnotenie komunity",
|
||||||
|
"@communityRating": {},
|
||||||
|
"continuePage": "Pokračovať - strana {page}",
|
||||||
|
"@continuePage": {
|
||||||
|
"description": "Continue - page 1",
|
||||||
|
"placeholders": {
|
||||||
|
"page": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"controls": "Ovládanie",
|
||||||
|
"@controls": {},
|
||||||
|
"dashboard": "Ovládací panel",
|
||||||
|
"@dashboard": {},
|
||||||
|
"dashboardContinue": "Pokračovať",
|
||||||
|
"@dashboardContinue": {},
|
||||||
|
"dashboardContinueListening": "Pokračovať v počúvaní",
|
||||||
|
"@dashboardContinueListening": {},
|
||||||
|
"dashboardContinueReading": "Pokračovať v čítaní",
|
||||||
|
"@dashboardContinueReading": {},
|
||||||
|
"dashboardContinueWatching": "Pokračovať v sledovaní",
|
||||||
|
"@dashboardContinueWatching": {},
|
||||||
|
"nextUp": "Ďalej",
|
||||||
|
"@nextUp": {},
|
||||||
|
"dashboardRecentlyAdded": "Nedávno pridané v {name}",
|
||||||
|
"@dashboardRecentlyAdded": {
|
||||||
|
"description": "Recently added on home screen",
|
||||||
|
"placeholders": {
|
||||||
|
"name": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dateAdded": "Dátum pridania",
|
||||||
|
"@dateAdded": {},
|
||||||
|
"dateLastContentAdded": "Dátum posledného pridania obsahu",
|
||||||
|
"@dateLastContentAdded": {},
|
||||||
|
"datePlayed": "Dátum prehrania",
|
||||||
|
"@datePlayed": {},
|
||||||
|
"days": "Dni",
|
||||||
|
"@days": {},
|
||||||
|
"delete": "Odstrániť",
|
||||||
|
"@delete": {},
|
||||||
|
"clear": "Vyčistiť",
|
||||||
|
"@clear": {},
|
||||||
|
"chapter": "{count, plural, other{Chapters} one{Chapter}}",
|
||||||
|
"@chapter": {
|
||||||
|
"description": "chapter",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
"@mediaSegmentRecap": {},
|
"@mediaSegmentRecap": {},
|
||||||
"addAsFavorite": "பிடித்ததாகச் சேர்க்கவும்",
|
"addAsFavorite": "பிடித்ததாகச் சேர்க்கவும்",
|
||||||
"@addAsFavorite": {},
|
"@addAsFavorite": {},
|
||||||
"actor": "{count, plural, other{கூத்தர்கள்} one {கூத்தர்}}",
|
"actor": "{count, plural, other{நடிகர்கள்} one {நடிகர்}}",
|
||||||
"@actor": {
|
"@actor": {
|
||||||
"description": "actor",
|
"description": "actor",
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
|
|
@ -94,7 +94,7 @@
|
||||||
"@advanced": {},
|
"@advanced": {},
|
||||||
"all": "அனைத்தும்",
|
"all": "அனைத்தும்",
|
||||||
"@all": {},
|
"@all": {},
|
||||||
"appLockAutoLogin": "ஆட்டோ உள்நுழைவு",
|
"appLockAutoLogin": "தானாக உள்நுழை",
|
||||||
"@appLockAutoLogin": {},
|
"@appLockAutoLogin": {},
|
||||||
"appLockBiometrics": "பயோமெட்ரிக்ச்",
|
"appLockBiometrics": "பயோமெட்ரிக்ச்",
|
||||||
"@appLockBiometrics": {},
|
"@appLockBiometrics": {},
|
||||||
|
|
@ -111,7 +111,7 @@
|
||||||
},
|
},
|
||||||
"ascending": "ஏறுதல்",
|
"ascending": "ஏறுதல்",
|
||||||
"@ascending": {},
|
"@ascending": {},
|
||||||
"audio": "ஒலி தேர்வு",
|
"audio": "ஆடியோ",
|
||||||
"@audio": {},
|
"@audio": {},
|
||||||
"autoPlay": "தானாக விளையாடும்",
|
"autoPlay": "தானாக விளையாடும்",
|
||||||
"@autoPlay": {},
|
"@autoPlay": {},
|
||||||
|
|
@ -1209,5 +1209,83 @@
|
||||||
"errorLogs": "பிழை பதிவுகள்",
|
"errorLogs": "பிழை பதிவுகள்",
|
||||||
"@errorLogs": {},
|
"@errorLogs": {},
|
||||||
"external": "வெளிப்புற",
|
"external": "வெளிப்புற",
|
||||||
"@external": {}
|
"@external": {},
|
||||||
|
"settingsLayoutSizesTitle": "தளவமைப்பு அளவுகள்",
|
||||||
|
"@settingsLayoutSizesTitle": {},
|
||||||
|
"settingsLayoutSizesDesc": "சாளர அளவின் அடிப்படையில் பயன்பாடு எந்த தளவமைப்பு அளவுகளைப் பயன்படுத்தலாம் என்பதைத் தேர்வுசெய்க",
|
||||||
|
"@settingsLayoutSizesDesc": {},
|
||||||
|
"settingsLayoutModesTitle": "தளவமைப்பு முறைகள்",
|
||||||
|
"@settingsLayoutModesTitle": {},
|
||||||
|
"tablet": "டேப்லெட்",
|
||||||
|
"@tablet": {},
|
||||||
|
"mediaSegmentActions": "ஊடக பிரிவு செயல்கள்",
|
||||||
|
"@mediaSegmentActions": {},
|
||||||
|
"segmentActionNone": "எதுவுமில்லை",
|
||||||
|
"@segmentActionNone": {},
|
||||||
|
"segmentActionAskToSkip": "தவிர்க்கச் சொல்லுங்கள்",
|
||||||
|
"@segmentActionAskToSkip": {},
|
||||||
|
"segmentActionSkip": "தவிர்",
|
||||||
|
"@segmentActionSkip": {},
|
||||||
|
"castAndCrew": "நடிகர்கள் & குழுவினர்",
|
||||||
|
"@castAndCrew": {},
|
||||||
|
"downloadFile": "பதிவிறக்கம் {type}",
|
||||||
|
"@downloadFile": {
|
||||||
|
"placeholders": {
|
||||||
|
"type": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"copyStreamUrl": "ச்ட்ரீம் முகவரி ஐ நகலெடுக்கவும்",
|
||||||
|
"@copyStreamUrl": {},
|
||||||
|
"copiedToClipboard": "இடைநிலைப்பலகைக்கு நகலெடுக்கப்பட்டது",
|
||||||
|
"@copiedToClipboard": {},
|
||||||
|
"internetStreamingQualityTitle": "இணைய தகுதி",
|
||||||
|
"@internetStreamingQualityTitle": {},
|
||||||
|
"internetStreamingQualityDesc": "இணையத்தில் அதிகபட்ச ச்ட்ரீமிங் தகுதி (மொபைல்)",
|
||||||
|
"@internetStreamingQualityDesc": {},
|
||||||
|
"homeStreamingQualityTitle": "வீட்டின் தகுதி",
|
||||||
|
"@homeStreamingQualityTitle": {},
|
||||||
|
"homeStreamingQualityDesc": "வீட்டு நெட்வொர்க்குடன் இணைக்கப்படும்போது அதிகபட்ச ச்ட்ரீமிங் தகுதி",
|
||||||
|
"@homeStreamingQualityDesc": {},
|
||||||
|
"qualityOptionsTitle": "தரமான விருப்பங்கள்",
|
||||||
|
"@qualityOptionsTitle": {},
|
||||||
|
"loading": "ஏற்றுகிறது",
|
||||||
|
"@loading": {},
|
||||||
|
"episodeAvailable": "கிடைக்கிறது",
|
||||||
|
"@episodeAvailable": {},
|
||||||
|
"episodeUnaired": "UNIRED",
|
||||||
|
"@episodeUnaired": {},
|
||||||
|
"episodeMissing": "இல்லை",
|
||||||
|
"@episodeMissing": {},
|
||||||
|
"settingsPlayerBufferSizeTitle": "வீடியோ இடையக அளவு",
|
||||||
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
|
"settingsPlayerBufferSizeDesc": "வீடியோ பிளேபேக்கிற்கான இடையக அளவை உள்ளமைக்கவும், தற்காலிக சேமிப்பில் எவ்வளவு தரவு ஏற்றப்படுகிறது என்பதை தீர்மானிக்கவும்.",
|
||||||
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"settingsLayoutModesDesc": "பயன்பாடு ஒற்றை அல்லது இரட்டை-பேனல் தளவமைப்புகளைப் பயன்படுத்தலாமா என்பதைக் கட்டுப்படுத்தவும்",
|
||||||
|
"@settingsLayoutModesDesc": {},
|
||||||
|
"phone": "தொலைபேசி",
|
||||||
|
"@phone": {},
|
||||||
|
"desktop": "டெச்க்டாப்",
|
||||||
|
"@desktop": {},
|
||||||
|
"layoutModeSingle": "ஒற்றை",
|
||||||
|
"@layoutModeSingle": {},
|
||||||
|
"layoutModeDual": "இருமம்",
|
||||||
|
"@layoutModeDual": {},
|
||||||
|
"qualityOptionsOriginal": "அசல்",
|
||||||
|
"@qualityOptionsOriginal": {},
|
||||||
|
"qualityOptionsAuto": "தானி",
|
||||||
|
"@qualityOptionsAuto": {},
|
||||||
|
"version": "பதிப்பு",
|
||||||
|
"@version": {},
|
||||||
|
"guestActor": "{count, plural, other{கௌரவ நடிகர்கள்} one{கௌரவ நடிகர்}}",
|
||||||
|
"@guestActor": {
|
||||||
|
"description": "Guest actors",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int",
|
||||||
|
"example": "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1295,5 +1295,65 @@
|
||||||
"settingsPlayerBufferSizeTitle": "Розмір буфера відео",
|
"settingsPlayerBufferSizeTitle": "Розмір буфера відео",
|
||||||
"@settingsPlayerBufferSizeTitle": {},
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
"settingsPlayerBufferSizeDesc": "Налаштуйте розмір буфера для відтворення відео, визначивши, скільки даних буде завантажено в кеш.",
|
"settingsPlayerBufferSizeDesc": "Налаштуйте розмір буфера для відтворення відео, визначивши, скільки даних буде завантажено в кеш.",
|
||||||
"@settingsPlayerBufferSizeDesc": {}
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "Максимум одночасних завантажень",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "Встановлює максимальну кількість завантажень, які можуть виконуватися одночасно. Встановіть значення 0, щоб вимкнути обмеження.",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"playbackTrackSelection": "Вибір доріжки відтворення",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "Спробувати встановити доріжку субтитрів, яка найкраще відповідає останньому відео.",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"rememberAudioSelections": "Встановити аудіодоріжку на основі попереднього елемента",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberAudioSelectionsDesc": "Спробувати встановити аудіодоріжку, яка найкраще відповідає останньому відео.",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"rememberSubtitleSelections": "Встановити доріжку субтитрів на основі попереднього елемента",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"similarToRecentlyPlayed": "Схоже на нещодавно відтворене",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"similarToLikedItem": "Схоже на вподобаний елемент",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "Має актора з нещодавно відтвореного",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"latest": "Останнє",
|
||||||
|
"@latest": {},
|
||||||
|
"hasLikedDirector": "Має вподобаного режисера",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"hasLikedActor": "Має вподобаного актора",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"recommended": "Рекомендовано",
|
||||||
|
"@recommended": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "Має режисера з нещодавно відтвореного",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"exitFladderTitle": "Вийти з Fladder",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"playbackTypeDirect": "Пряме",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"playbackType": "Тип відтворення",
|
||||||
|
"@playbackType": {},
|
||||||
|
"playbackTypeTranscode": "Перекодування",
|
||||||
|
"@playbackTypeTranscode": {},
|
||||||
|
"playbackTypeOffline": "Офлайн",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"latestReleases": "Останні релізи",
|
||||||
|
"@latestReleases": {},
|
||||||
|
"autoCheckForUpdates": "Періодично перевіряти наявність оновлень",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"newReleaseFoundTitle": "Доступне оновлення {newRelease}!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newUpdateFoundOnGithub": "Знайдено нове оновлення на Github",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"enableBackgroundPostersTitle": "Увімкнути фонові постери",
|
||||||
|
"@enableBackgroundPostersTitle": {},
|
||||||
|
"settingsEnableOsMediaControlsDesc": "Дозволяє керувати відтворенням за допомогою медіа-клавіш та показувати поточний відтворюваний медіафайл в ОС",
|
||||||
|
"@settingsEnableOsMediaControlsDesc": {},
|
||||||
|
"enableBackgroundPostersDesc": "Показувати випадкові постери на відповідних екранах",
|
||||||
|
"@enableBackgroundPostersDesc": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -892,23 +892,23 @@
|
||||||
"@video": {},
|
"@video": {},
|
||||||
"videoScaling": "视频缩放",
|
"videoScaling": "视频缩放",
|
||||||
"@videoScaling": {},
|
"@videoScaling": {},
|
||||||
"videoScalingContain": "包含",
|
"videoScalingContain": "适应窗口",
|
||||||
"@videoScalingContain": {},
|
"@videoScalingContain": {},
|
||||||
"videoScalingCover": "覆盖",
|
"videoScalingCover": "覆盖填充",
|
||||||
"@videoScalingCover": {},
|
"@videoScalingCover": {},
|
||||||
"videoScalingFill": "填充",
|
"videoScalingFill": "拉伸填充",
|
||||||
"@videoScalingFill": {},
|
"@videoScalingFill": {},
|
||||||
"videoScalingFillScreenDesc": "填充导航栏和状态栏",
|
"videoScalingFillScreenDesc": "延伸至导航栏和状态栏区域",
|
||||||
"@videoScalingFillScreenDesc": {},
|
"@videoScalingFillScreenDesc": {},
|
||||||
"videoScalingFillScreenNotif": "全屏覆盖视频适应,在水平旋转中",
|
"videoScalingFillScreenNotif": "全屏模式将覆盖视频适配设置",
|
||||||
"@videoScalingFillScreenNotif": {},
|
"@videoScalingFillScreenNotif": {},
|
||||||
"videoScalingFillScreenTitle": "填满屏幕",
|
"videoScalingFillScreenTitle": "全屏填充",
|
||||||
"@videoScalingFillScreenTitle": {},
|
"@videoScalingFillScreenTitle": {},
|
||||||
"videoScalingFitHeight": "适应高度",
|
"videoScalingFitHeight": "适应高度",
|
||||||
"@videoScalingFitHeight": {},
|
"@videoScalingFitHeight": {},
|
||||||
"videoScalingFitWidth": "适应宽度",
|
"videoScalingFitWidth": "适应宽度",
|
||||||
"@videoScalingFitWidth": {},
|
"@videoScalingFitWidth": {},
|
||||||
"videoScalingScaleDown": "缩小",
|
"videoScalingScaleDown": "自适应缩小",
|
||||||
"@videoScalingScaleDown": {},
|
"@videoScalingScaleDown": {},
|
||||||
"viewPhotos": "查看照片",
|
"viewPhotos": "查看照片",
|
||||||
"@viewPhotos": {},
|
"@viewPhotos": {},
|
||||||
|
|
@ -946,13 +946,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"syncStatusComplete": "完成",
|
"syncStatusComplete": "同步完成",
|
||||||
"@syncStatusComplete": {},
|
"@syncStatusComplete": {},
|
||||||
"syncNoFolderSetup": "未设置同步文件夹",
|
"syncNoFolderSetup": "未设置同步文件夹",
|
||||||
"@syncNoFolderSetup": {},
|
"@syncNoFolderSetup": {},
|
||||||
"syncStatusNotFound": "未找到",
|
"syncStatusNotFound": "资源未找到",
|
||||||
"@syncStatusNotFound": {},
|
"@syncStatusNotFound": {},
|
||||||
"syncStatusPaused": "已暂停",
|
"syncStatusPaused": "同步已暂停",
|
||||||
"@syncStatusPaused": {},
|
"@syncStatusPaused": {},
|
||||||
"syncOverlayDeleting": "正在删除已同步的项目",
|
"syncOverlayDeleting": "正在删除已同步的项目",
|
||||||
"@syncOverlayDeleting": {},
|
"@syncOverlayDeleting": {},
|
||||||
|
|
@ -964,7 +964,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"syncStatusCanceled": "已取消",
|
"syncStatusCanceled": "用户已取消",
|
||||||
"@syncStatusCanceled": {},
|
"@syncStatusCanceled": {},
|
||||||
"settingsHomeBannerInformationDesc": "主页横幅中显示的信息",
|
"settingsHomeBannerInformationDesc": "主页横幅中显示的信息",
|
||||||
"@settingsHomeBannerInformationDesc": {},
|
"@settingsHomeBannerInformationDesc": {},
|
||||||
|
|
@ -972,21 +972,21 @@
|
||||||
"@settingsHomeBannerInformationTitle": {},
|
"@settingsHomeBannerInformationTitle": {},
|
||||||
"syncStatusEnqueued": "已添加到队列",
|
"syncStatusEnqueued": "已添加到队列",
|
||||||
"@syncStatusEnqueued": {},
|
"@syncStatusEnqueued": {},
|
||||||
"syncStatusRunning": "运行中",
|
"syncStatusRunning": "同步中",
|
||||||
"@syncStatusRunning": {},
|
"@syncStatusRunning": {},
|
||||||
"syncStatusFailed": "已失败",
|
"syncStatusFailed": "同步失败",
|
||||||
"@syncStatusFailed": {},
|
"@syncStatusFailed": {},
|
||||||
"syncStatusWaitingToRetry": "等待重试中",
|
"syncStatusWaitingToRetry": "等待自动重试",
|
||||||
"@syncStatusWaitingToRetry": {},
|
"@syncStatusWaitingToRetry": {},
|
||||||
"syncStatusSynced": "已同步",
|
"syncStatusSynced": "已完成同步",
|
||||||
"@syncStatusSynced": {},
|
"@syncStatusSynced": {},
|
||||||
"syncStatusPartially": "部分地",
|
"syncStatusPartially": "部分同步成功",
|
||||||
"@syncStatusPartially": {},
|
"@syncStatusPartially": {},
|
||||||
"syncOverlaySyncing": "正在同步项目详情",
|
"syncOverlaySyncing": "正在同步项目详情",
|
||||||
"@syncOverlaySyncing": {},
|
"@syncOverlaySyncing": {},
|
||||||
"syncSelectDownloadsFolder": "选择下载文件夹",
|
"syncSelectDownloadsFolder": "选择下载文件夹",
|
||||||
"@syncSelectDownloadsFolder": {},
|
"@syncSelectDownloadsFolder": {},
|
||||||
"syncRemoveUnableToDeleteItem": "无法移除已同步的项目,出了点问题",
|
"syncRemoveUnableToDeleteItem": "出现了一些问题,导致无法移除已同步的项目",
|
||||||
"@syncRemoveUnableToDeleteItem": {},
|
"@syncRemoveUnableToDeleteItem": {},
|
||||||
"syncAddItemForSyncing": "已添加 {item} 进行同步",
|
"syncAddItemForSyncing": "已添加 {item} 进行同步",
|
||||||
"@syncAddItemForSyncing": {
|
"@syncAddItemForSyncing": {
|
||||||
|
|
@ -1086,9 +1086,9 @@
|
||||||
"@settingsAutoNextDesc": {},
|
"@settingsAutoNextDesc": {},
|
||||||
"autoNextOffStaticDesc": "播放时间还剩 30 秒时显示接下来预览窗",
|
"autoNextOffStaticDesc": "播放时间还剩 30 秒时显示接下来预览窗",
|
||||||
"@autoNextOffStaticDesc": {},
|
"@autoNextOffStaticDesc": {},
|
||||||
"autoNextOffSmartTitle": "自动的",
|
"autoNextOffSmartTitle": "智能模式",
|
||||||
"@autoNextOffSmartTitle": {},
|
"@autoNextOffSmartTitle": {},
|
||||||
"autoNextOffStaticTitle": "静态的",
|
"autoNextOffStaticTitle": "固定模式",
|
||||||
"@autoNextOffStaticTitle": {},
|
"@autoNextOffStaticTitle": {},
|
||||||
"playbackRate": "播放速率",
|
"playbackRate": "播放速率",
|
||||||
"@playbackRate": {},
|
"@playbackRate": {},
|
||||||
|
|
@ -1286,5 +1286,65 @@
|
||||||
"settingsPlayerBufferSizeDesc": "配置视频播放的缓冲区大小,确定加载到缓存中的数据量。",
|
"settingsPlayerBufferSizeDesc": "配置视频播放的缓冲区大小,确定加载到缓存中的数据量。",
|
||||||
"@settingsPlayerBufferSizeDesc": {},
|
"@settingsPlayerBufferSizeDesc": {},
|
||||||
"settingsPlayerBufferSizeTitle": "视频缓冲区大小",
|
"settingsPlayerBufferSizeTitle": "视频缓冲区大小",
|
||||||
"@settingsPlayerBufferSizeTitle": {}
|
"@settingsPlayerBufferSizeTitle": {},
|
||||||
|
"maxConcurrentDownloadsTitle": "最大并发下载",
|
||||||
|
"@maxConcurrentDownloadsTitle": {},
|
||||||
|
"maxConcurrentDownloadsDesc": "设置可同时运行的最大下载数量。设置为 0 则禁用限制。",
|
||||||
|
"@maxConcurrentDownloadsDesc": {},
|
||||||
|
"playbackTrackSelection": "播放轨道选择",
|
||||||
|
"@playbackTrackSelection": {},
|
||||||
|
"rememberAudioSelections": "根据上一项设置音轨",
|
||||||
|
"@rememberAudioSelections": {},
|
||||||
|
"rememberAudioSelectionsDesc": "尝试将音轨设置为与上一个视频最接近的匹配。",
|
||||||
|
"@rememberAudioSelectionsDesc": {},
|
||||||
|
"rememberSubtitleSelections": "根据上一项设置字幕轨",
|
||||||
|
"@rememberSubtitleSelections": {},
|
||||||
|
"rememberSubtitleSelectionsDesc": "尝试将字幕轨设置为与上一个视频最接近的匹配。",
|
||||||
|
"@rememberSubtitleSelectionsDesc": {},
|
||||||
|
"similarToLikedItem": "类似的喜欢项目",
|
||||||
|
"@similarToLikedItem": {},
|
||||||
|
"exitFladderTitle": "退出 Fladder",
|
||||||
|
"@exitFladderTitle": {},
|
||||||
|
"similarToRecentlyPlayed": "类似的最近播放",
|
||||||
|
"@similarToRecentlyPlayed": {},
|
||||||
|
"hasActorFromRecentlyPlayed": "最近播出的演员",
|
||||||
|
"@hasActorFromRecentlyPlayed": {},
|
||||||
|
"hasLikedDirector": "喜欢的导演",
|
||||||
|
"@hasLikedDirector": {},
|
||||||
|
"hasLikedActor": "喜欢的演员",
|
||||||
|
"@hasLikedActor": {},
|
||||||
|
"latest": "最新的",
|
||||||
|
"@latest": {},
|
||||||
|
"recommended": "建议",
|
||||||
|
"@recommended": {},
|
||||||
|
"hasDirectorFromRecentlyPlayed": "最近播出的导演",
|
||||||
|
"@hasDirectorFromRecentlyPlayed": {},
|
||||||
|
"playbackType": "播放类型",
|
||||||
|
"@playbackType": {},
|
||||||
|
"playbackTypeDirect": "直接播放",
|
||||||
|
"@playbackTypeDirect": {},
|
||||||
|
"playbackTypeTranscode": "转码",
|
||||||
|
"@playbackTypeTranscode": {},
|
||||||
|
"playbackTypeOffline": "离线",
|
||||||
|
"@playbackTypeOffline": {},
|
||||||
|
"latestReleases": "最新版本",
|
||||||
|
"@latestReleases": {},
|
||||||
|
"autoCheckForUpdates": "定期检查更新",
|
||||||
|
"@autoCheckForUpdates": {},
|
||||||
|
"newReleaseFoundTitle": "更新 {newRelease} 可用!",
|
||||||
|
"@newReleaseFoundTitle": {
|
||||||
|
"placeholders": {
|
||||||
|
"newRelease": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"newUpdateFoundOnGithub": "在 Github 上发现新的更新",
|
||||||
|
"@newUpdateFoundOnGithub": {},
|
||||||
|
"settingsEnableOsMediaControlsDesc": "允许使用媒体键控制播放并在操作系统中显示当前正在播放的媒体",
|
||||||
|
"@settingsEnableOsMediaControlsDesc": {},
|
||||||
|
"enableBackgroundPostersTitle": "启用背景海报",
|
||||||
|
"@enableBackgroundPostersTitle": {},
|
||||||
|
"enableBackgroundPostersDesc": "在适用的屏幕上显示随机海报",
|
||||||
|
"@enableBackgroundPostersDesc": {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
@ -18,9 +17,11 @@ import 'package:smtc_windows/smtc_windows.dart' if (dart.library.html) 'package:
|
||||||
import 'package:universal_html/html.dart' as html;
|
import 'package:universal_html/html.dart' as html;
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
|
import 'package:fladder/l10n/generated/app_localizations.dart';
|
||||||
import 'package:fladder/models/account_model.dart';
|
import 'package:fladder/models/account_model.dart';
|
||||||
import 'package:fladder/models/settings/home_settings_model.dart';
|
import 'package:fladder/models/settings/arguments_model.dart';
|
||||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||||
|
import 'package:fladder/providers/arguments_provider.dart';
|
||||||
import 'package:fladder/providers/crash_log_provider.dart';
|
import 'package:fladder/providers/crash_log_provider.dart';
|
||||||
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
import 'package:fladder/providers/settings/client_settings_provider.dart';
|
||||||
import 'package:fladder/providers/shared_provider.dart';
|
import 'package:fladder/providers/shared_provider.dart';
|
||||||
|
|
@ -31,7 +32,7 @@ import 'package:fladder/routes/auto_router.dart';
|
||||||
import 'package:fladder/routes/auto_router.gr.dart';
|
import 'package:fladder/routes/auto_router.gr.dart';
|
||||||
import 'package:fladder/screens/login/lock_screen.dart';
|
import 'package:fladder/screens/login/lock_screen.dart';
|
||||||
import 'package:fladder/theme.dart';
|
import 'package:fladder/theme.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/application_info.dart';
|
import 'package:fladder/util/application_info.dart';
|
||||||
import 'package:fladder/util/fladder_config.dart';
|
import 'package:fladder/util/fladder_config.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
@ -52,7 +53,7 @@ Future<Map<String, dynamic>> loadConfig() async {
|
||||||
return jsonDecode(configString);
|
return jsonDecode(configString);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() async {
|
void main(List<String> args) async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
final crashProvider = CrashLogNotifier();
|
final crashProvider = CrashLogNotifier();
|
||||||
|
|
||||||
|
|
@ -96,6 +97,7 @@ void main() async {
|
||||||
sharedPreferencesProvider.overrideWith((ref) => sharedPreferences),
|
sharedPreferencesProvider.overrideWith((ref) => sharedPreferences),
|
||||||
applicationInfoProvider.overrideWith((ref) => applicationInfo),
|
applicationInfoProvider.overrideWith((ref) => applicationInfo),
|
||||||
crashLogProvider.overrideWith((ref) => crashProvider),
|
crashLogProvider.overrideWith((ref) => crashProvider),
|
||||||
|
argumentsStateProvider.overrideWith((ref) => ArgumentsModel.fromArguments(args)),
|
||||||
syncProvider.overrideWith((ref) => SyncNotifier(
|
syncProvider.overrideWith((ref) => SyncNotifier(
|
||||||
ref,
|
ref,
|
||||||
!kIsWeb
|
!kIsWeb
|
||||||
|
|
@ -108,13 +110,7 @@ void main() async {
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
child: AdaptiveLayoutBuilder(
|
child: AdaptiveLayoutBuilder(
|
||||||
fallBack: ViewSize.tablet,
|
child: (context) => const Main(),
|
||||||
layoutPoints: [
|
|
||||||
LayoutPoints(start: 0, end: 599, type: ViewSize.phone),
|
|
||||||
LayoutPoints(start: 600, end: 1919, type: ViewSize.tablet),
|
|
||||||
LayoutPoints(start: 1920, end: 3180, type: ViewSize.desktop),
|
|
||||||
],
|
|
||||||
child: const Main(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -241,6 +237,10 @@ class _MainState extends ConsumerState<Main> with WindowListener, WidgetsBinding
|
||||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||||
await windowManager.show();
|
await windowManager.show();
|
||||||
await windowManager.focus();
|
await windowManager.focus();
|
||||||
|
final startupArguments = ref.read(argumentsStateProvider);
|
||||||
|
if (startupArguments.htpcMode && !(await windowManager.isFullScreen())) {
|
||||||
|
await windowManager.setFullScreen(true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: []);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: []);
|
||||||
|
|
@ -288,13 +288,15 @@ class _MainState extends ConsumerState<Main> with WindowListener, WidgetsBinding
|
||||||
),
|
),
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AppLocalizations.supportedLocales,
|
supportedLocales: AppLocalizations.supportedLocales,
|
||||||
builder: (context, child) => Localizations.override(
|
locale: language,
|
||||||
context: context,
|
localeResolutionCallback: (locale, supportedLocales) {
|
||||||
locale: AppLocalizations.supportedLocales.firstWhere(
|
if (locale == null || !supportedLocales.contains(locale)) {
|
||||||
(element) => element.languageCode == language.languageCode,
|
return const Locale('en');
|
||||||
orElse: () => const Locale('en', "GB"),
|
}
|
||||||
),
|
return locale;
|
||||||
child: LocalizationContextWrapper(child: ScaffoldMessenger(child: child ?? Container())),
|
},
|
||||||
|
builder: (context, child) => LocalizationContextWrapper(
|
||||||
|
child: ScaffoldMessenger(child: child ?? Container()),
|
||||||
),
|
),
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
darkTheme: darkTheme.copyWith(
|
darkTheme: darkTheme.copyWith(
|
||||||
|
|
@ -304,6 +306,7 @@ class _MainState extends ConsumerState<Main> with WindowListener, WidgetsBinding
|
||||||
colorScheme: darkTheme.colorScheme.copyWith(
|
colorScheme: darkTheme.colorScheme.copyWith(
|
||||||
surface: amoledOverwrite,
|
surface: amoledOverwrite,
|
||||||
surfaceContainerHighest: amoledOverwrite,
|
surfaceContainerHighest: amoledOverwrite,
|
||||||
|
surfaceContainerLow: amoledOverwrite,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,19 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:fladder/models/credentials_model.dart';
|
import 'package:fladder/models/credentials_model.dart';
|
||||||
import 'package:fladder/models/library_filters_model.dart';
|
import 'package:fladder/models/library_filters_model.dart';
|
||||||
import 'package:fladder/util/adaptive_layout.dart';
|
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
part 'account_model.freezed.dart';
|
part 'account_model.freezed.dart';
|
||||||
part 'account_model.g.dart';
|
part 'account_model.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class AccountModel with _$AccountModel {
|
class AccountModel with _$AccountModel {
|
||||||
const AccountModel._();
|
const AccountModel._();
|
||||||
|
|
||||||
|
|
@ -34,6 +34,7 @@ class AccountModel with _$AccountModel {
|
||||||
@Default([]) List<LibraryFiltersModel> savedFilters,
|
@Default([]) List<LibraryFiltersModel> savedFilters,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false) ServerConfiguration? serverConfiguration,
|
@JsonKey(includeFromJson: false, includeToJson: false) ServerConfiguration? serverConfiguration,
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false) UserConfiguration? userConfiguration,
|
||||||
}) = _AccountModel;
|
}) = _AccountModel;
|
||||||
|
|
||||||
factory AccountModel.fromJson(Map<String, dynamic> json) => _$AccountModelFromJson(json);
|
factory AccountModel.fromJson(Map<String, dynamic> json) => _$AccountModelFromJson(json);
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ mixin _$AccountModel {
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
ServerConfiguration? get serverConfiguration =>
|
ServerConfiguration? get serverConfiguration =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
UserConfiguration? get userConfiguration =>
|
||||||
|
throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this AccountModel to a JSON map.
|
/// Serializes this AccountModel to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
@ -68,7 +71,9 @@ abstract class $AccountModelCopyWith<$Res> {
|
||||||
List<LibraryFiltersModel> savedFilters,
|
List<LibraryFiltersModel> savedFilters,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
ServerConfiguration? serverConfiguration});
|
ServerConfiguration? serverConfiguration,
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
UserConfiguration? userConfiguration});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -99,6 +104,7 @@ class _$AccountModelCopyWithImpl<$Res, $Val extends AccountModel>
|
||||||
Object? savedFilters = null,
|
Object? savedFilters = null,
|
||||||
Object? policy = freezed,
|
Object? policy = freezed,
|
||||||
Object? serverConfiguration = freezed,
|
Object? serverConfiguration = freezed,
|
||||||
|
Object? userConfiguration = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
name: null == name
|
name: null == name
|
||||||
|
|
@ -153,6 +159,10 @@ class _$AccountModelCopyWithImpl<$Res, $Val extends AccountModel>
|
||||||
? _value.serverConfiguration
|
? _value.serverConfiguration
|
||||||
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
||||||
as ServerConfiguration?,
|
as ServerConfiguration?,
|
||||||
|
userConfiguration: freezed == userConfiguration
|
||||||
|
? _value.userConfiguration
|
||||||
|
: userConfiguration // ignore: cast_nullable_to_non_nullable
|
||||||
|
as UserConfiguration?,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +189,9 @@ abstract class _$$AccountModelImplCopyWith<$Res>
|
||||||
List<LibraryFiltersModel> savedFilters,
|
List<LibraryFiltersModel> savedFilters,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
@JsonKey(includeFromJson: false, includeToJson: false) UserPolicy? policy,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
ServerConfiguration? serverConfiguration});
|
ServerConfiguration? serverConfiguration,
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
UserConfiguration? userConfiguration});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -208,6 +220,7 @@ class __$$AccountModelImplCopyWithImpl<$Res>
|
||||||
Object? savedFilters = null,
|
Object? savedFilters = null,
|
||||||
Object? policy = freezed,
|
Object? policy = freezed,
|
||||||
Object? serverConfiguration = freezed,
|
Object? serverConfiguration = freezed,
|
||||||
|
Object? userConfiguration = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$AccountModelImpl(
|
return _then(_$AccountModelImpl(
|
||||||
name: null == name
|
name: null == name
|
||||||
|
|
@ -262,6 +275,10 @@ class __$$AccountModelImplCopyWithImpl<$Res>
|
||||||
? _value.serverConfiguration
|
? _value.serverConfiguration
|
||||||
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
: serverConfiguration // ignore: cast_nullable_to_non_nullable
|
||||||
as ServerConfiguration?,
|
as ServerConfiguration?,
|
||||||
|
userConfiguration: freezed == userConfiguration
|
||||||
|
? _value.userConfiguration
|
||||||
|
: userConfiguration // ignore: cast_nullable_to_non_nullable
|
||||||
|
as UserConfiguration?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +300,9 @@ class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
|
||||||
final List<LibraryFiltersModel> savedFilters = const [],
|
final List<LibraryFiltersModel> savedFilters = const [],
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false) this.policy,
|
@JsonKey(includeFromJson: false, includeToJson: false) this.policy,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
this.serverConfiguration})
|
this.serverConfiguration,
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
this.userConfiguration})
|
||||||
: _latestItemsExcludes = latestItemsExcludes,
|
: _latestItemsExcludes = latestItemsExcludes,
|
||||||
_searchQueryHistory = searchQueryHistory,
|
_searchQueryHistory = searchQueryHistory,
|
||||||
_savedFilters = savedFilters,
|
_savedFilters = savedFilters,
|
||||||
|
|
@ -346,10 +365,13 @@ class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
|
||||||
@override
|
@override
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
final ServerConfiguration? serverConfiguration;
|
final ServerConfiguration? serverConfiguration;
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
final UserConfiguration? userConfiguration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'AccountModel(name: $name, id: $id, avatar: $avatar, lastUsed: $lastUsed, authMethod: $authMethod, localPin: $localPin, credentials: $credentials, latestItemsExcludes: $latestItemsExcludes, searchQueryHistory: $searchQueryHistory, quickConnectState: $quickConnectState, savedFilters: $savedFilters, policy: $policy, serverConfiguration: $serverConfiguration)';
|
return 'AccountModel(name: $name, id: $id, avatar: $avatar, lastUsed: $lastUsed, authMethod: $authMethod, localPin: $localPin, credentials: $credentials, latestItemsExcludes: $latestItemsExcludes, searchQueryHistory: $searchQueryHistory, quickConnectState: $quickConnectState, savedFilters: $savedFilters, policy: $policy, serverConfiguration: $serverConfiguration, userConfiguration: $userConfiguration)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -369,56 +391,10 @@ class _$AccountModelImpl extends _AccountModel with DiagnosticableTreeMixin {
|
||||||
..add(DiagnosticsProperty('quickConnectState', quickConnectState))
|
..add(DiagnosticsProperty('quickConnectState', quickConnectState))
|
||||||
..add(DiagnosticsProperty('savedFilters', savedFilters))
|
..add(DiagnosticsProperty('savedFilters', savedFilters))
|
||||||
..add(DiagnosticsProperty('policy', policy))
|
..add(DiagnosticsProperty('policy', policy))
|
||||||
..add(DiagnosticsProperty('serverConfiguration', serverConfiguration));
|
..add(DiagnosticsProperty('serverConfiguration', serverConfiguration))
|
||||||
|
..add(DiagnosticsProperty('userConfiguration', userConfiguration));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$AccountModelImpl &&
|
|
||||||
(identical(other.name, name) || other.name == name) &&
|
|
||||||
(identical(other.id, id) || other.id == id) &&
|
|
||||||
(identical(other.avatar, avatar) || other.avatar == avatar) &&
|
|
||||||
(identical(other.lastUsed, lastUsed) ||
|
|
||||||
other.lastUsed == lastUsed) &&
|
|
||||||
(identical(other.authMethod, authMethod) ||
|
|
||||||
other.authMethod == authMethod) &&
|
|
||||||
(identical(other.localPin, localPin) ||
|
|
||||||
other.localPin == localPin) &&
|
|
||||||
(identical(other.credentials, credentials) ||
|
|
||||||
other.credentials == credentials) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._latestItemsExcludes, _latestItemsExcludes) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._searchQueryHistory, _searchQueryHistory) &&
|
|
||||||
(identical(other.quickConnectState, quickConnectState) ||
|
|
||||||
other.quickConnectState == quickConnectState) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._savedFilters, _savedFilters) &&
|
|
||||||
(identical(other.policy, policy) || other.policy == policy) &&
|
|
||||||
(identical(other.serverConfiguration, serverConfiguration) ||
|
|
||||||
other.serverConfiguration == serverConfiguration));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
name,
|
|
||||||
id,
|
|
||||||
avatar,
|
|
||||||
lastUsed,
|
|
||||||
authMethod,
|
|
||||||
localPin,
|
|
||||||
credentials,
|
|
||||||
const DeepCollectionEquality().hash(_latestItemsExcludes),
|
|
||||||
const DeepCollectionEquality().hash(_searchQueryHistory),
|
|
||||||
quickConnectState,
|
|
||||||
const DeepCollectionEquality().hash(_savedFilters),
|
|
||||||
policy,
|
|
||||||
serverConfiguration);
|
|
||||||
|
|
||||||
/// Create a copy of AccountModel
|
/// Create a copy of AccountModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
@ -451,7 +427,9 @@ abstract class _AccountModel extends AccountModel {
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
final UserPolicy? policy,
|
final UserPolicy? policy,
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
final ServerConfiguration? serverConfiguration}) = _$AccountModelImpl;
|
final ServerConfiguration? serverConfiguration,
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
final UserConfiguration? userConfiguration}) = _$AccountModelImpl;
|
||||||
const _AccountModel._() : super._();
|
const _AccountModel._() : super._();
|
||||||
|
|
||||||
factory _AccountModel.fromJson(Map<String, dynamic> json) =
|
factory _AccountModel.fromJson(Map<String, dynamic> json) =
|
||||||
|
|
@ -485,6 +463,9 @@ abstract class _AccountModel extends AccountModel {
|
||||||
@override
|
@override
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
ServerConfiguration? get serverConfiguration;
|
ServerConfiguration? get serverConfiguration;
|
||||||
|
@override
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
UserConfiguration? get userConfiguration;
|
||||||
|
|
||||||
/// Create a copy of AccountModel
|
/// Create a copy of AccountModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
|
|
||||||
|
|
@ -108,34 +108,11 @@ class BoxSetModelMapper extends SubClassMapperBase<BoxSetModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static BoxSetModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<BoxSetModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static BoxSetModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<BoxSetModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin BoxSetModelMappable {
|
mixin BoxSetModelMappable {
|
||||||
String toJson() {
|
|
||||||
return BoxSetModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<BoxSetModel>(this as BoxSetModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return BoxSetModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<BoxSetModel>(this as BoxSetModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
BoxSetModelCopyWith<BoxSetModel, BoxSetModel, BoxSetModel> get copyWith =>
|
BoxSetModelCopyWith<BoxSetModel, BoxSetModel, BoxSetModel> get copyWith =>
|
||||||
_BoxSetModelCopyWithImpl(this as BoxSetModel, $identity, $identity);
|
_BoxSetModelCopyWithImpl(this as BoxSetModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return BoxSetModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as BoxSetModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension BoxSetModelValueCopy<$R, $Out>
|
extension BoxSetModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ extension CollectionTypeExtension on CollectionType {
|
||||||
|
|
||||||
Set<FladderItemType> get itemKinds {
|
Set<FladderItemType> get itemKinds {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
case CollectionType.music:
|
||||||
|
return {FladderItemType.musicAlbum};
|
||||||
case CollectionType.movies:
|
case CollectionType.movies:
|
||||||
return {FladderItemType.movie};
|
return {FladderItemType.movie};
|
||||||
case CollectionType.tvshows:
|
case CollectionType.tvshows:
|
||||||
|
|
@ -30,6 +32,8 @@ extension CollectionTypeExtension on CollectionType {
|
||||||
|
|
||||||
IconData getIconType(bool outlined) {
|
IconData getIconType(bool outlined) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
|
case CollectionType.music:
|
||||||
|
return outlined ? IconsaxPlusLinear.music_square : IconsaxPlusBold.music_square;
|
||||||
case CollectionType.movies:
|
case CollectionType.movies:
|
||||||
return outlined ? IconsaxPlusLinear.video_horizontal : IconsaxPlusBold.video_horizontal;
|
return outlined ? IconsaxPlusLinear.video_horizontal : IconsaxPlusBold.video_horizontal;
|
||||||
case CollectionType.tvshows:
|
case CollectionType.tvshows:
|
||||||
|
|
@ -48,4 +52,16 @@ extension CollectionTypeExtension on CollectionType {
|
||||||
return IconsaxPlusLinear.information;
|
return IconsaxPlusLinear.information;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double? get aspectRatio => switch (this) {
|
||||||
|
CollectionType.music ||
|
||||||
|
CollectionType.homevideos ||
|
||||||
|
CollectionType.boxsets ||
|
||||||
|
CollectionType.photos ||
|
||||||
|
CollectionType.livetv ||
|
||||||
|
CollectionType.playlists =>
|
||||||
|
0.8,
|
||||||
|
CollectionType.folders => 1.3,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:dart_mappable/dart_mappable.dart';
|
import 'package:dart_mappable/dart_mappable.dart';
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||||
|
|
@ -304,6 +304,15 @@ enum FladderItemType {
|
||||||
|
|
||||||
const FladderItemType({required this.icon, required this.selectedicon});
|
const FladderItemType({required this.icon, required this.selectedicon});
|
||||||
|
|
||||||
|
double get aspectRatio => switch (this) {
|
||||||
|
FladderItemType.video => 0.8,
|
||||||
|
FladderItemType.photo => 0.8,
|
||||||
|
FladderItemType.photoAlbum => 0.8,
|
||||||
|
FladderItemType.musicAlbum => 0.8,
|
||||||
|
FladderItemType.baseType => 0.8,
|
||||||
|
_ => 0.55,
|
||||||
|
};
|
||||||
|
|
||||||
static Set<FladderItemType> get playable => {
|
static Set<FladderItemType> get playable => {
|
||||||
FladderItemType.series,
|
FladderItemType.series,
|
||||||
FladderItemType.episode,
|
FladderItemType.episode,
|
||||||
|
|
@ -317,27 +326,25 @@ enum FladderItemType {
|
||||||
FladderItemType.video,
|
FladderItemType.video,
|
||||||
};
|
};
|
||||||
|
|
||||||
String label(BuildContext context) {
|
String label(BuildContext context) => switch (this) {
|
||||||
return switch (this) {
|
FladderItemType.baseType => context.localized.mediaTypeBase,
|
||||||
FladderItemType.baseType => context.localized.mediaTypeBase,
|
FladderItemType.audio => context.localized.audio,
|
||||||
FladderItemType.audio => context.localized.audio,
|
FladderItemType.collectionFolder => context.localized.collectionFolder,
|
||||||
FladderItemType.collectionFolder => context.localized.collectionFolder,
|
FladderItemType.musicAlbum => context.localized.musicAlbum,
|
||||||
FladderItemType.musicAlbum => context.localized.musicAlbum,
|
FladderItemType.musicVideo => context.localized.video,
|
||||||
FladderItemType.musicVideo => context.localized.video,
|
FladderItemType.video => context.localized.video,
|
||||||
FladderItemType.video => context.localized.video,
|
FladderItemType.movie => context.localized.mediaTypeMovie,
|
||||||
FladderItemType.movie => context.localized.mediaTypeMovie,
|
FladderItemType.series => context.localized.mediaTypeSeries,
|
||||||
FladderItemType.series => context.localized.mediaTypeSeries,
|
FladderItemType.season => context.localized.mediaTypeSeason,
|
||||||
FladderItemType.season => context.localized.mediaTypeSeason,
|
FladderItemType.episode => context.localized.mediaTypeEpisode,
|
||||||
FladderItemType.episode => context.localized.mediaTypeEpisode,
|
FladderItemType.photo => context.localized.mediaTypePhoto,
|
||||||
FladderItemType.photo => context.localized.mediaTypePhoto,
|
FladderItemType.person => context.localized.mediaTypePerson,
|
||||||
FladderItemType.person => context.localized.mediaTypePerson,
|
FladderItemType.photoAlbum => context.localized.mediaTypePhotoAlbum,
|
||||||
FladderItemType.photoAlbum => context.localized.mediaTypePhotoAlbum,
|
FladderItemType.folder => context.localized.mediaTypeFolder,
|
||||||
FladderItemType.folder => context.localized.mediaTypeFolder,
|
FladderItemType.boxset => context.localized.mediaTypeBoxset,
|
||||||
FladderItemType.boxset => context.localized.mediaTypeBoxset,
|
FladderItemType.playlist => context.localized.mediaTypePlaylist,
|
||||||
FladderItemType.playlist => context.localized.mediaTypePlaylist,
|
FladderItemType.book => context.localized.mediaTypeBook,
|
||||||
FladderItemType.book => context.localized.mediaTypeBook,
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseItemKind get dtoKind => switch (this) {
|
BaseItemKind get dtoKind => switch (this) {
|
||||||
FladderItemType.baseType => BaseItemKind.userrootfolder,
|
FladderItemType.baseType => BaseItemKind.userrootfolder,
|
||||||
|
|
|
||||||
|
|
@ -93,35 +93,12 @@ class ItemBaseModelMapper extends ClassMapperBase<ItemBaseModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static ItemBaseModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<ItemBaseModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemBaseModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<ItemBaseModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin ItemBaseModelMappable {
|
mixin ItemBaseModelMappable {
|
||||||
String toJson() {
|
|
||||||
return ItemBaseModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<ItemBaseModel>(this as ItemBaseModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return ItemBaseModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<ItemBaseModel>(this as ItemBaseModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemBaseModelCopyWith<ItemBaseModel, ItemBaseModel, ItemBaseModel>
|
ItemBaseModelCopyWith<ItemBaseModel, ItemBaseModel, ItemBaseModel>
|
||||||
get copyWith => _ItemBaseModelCopyWithImpl(
|
get copyWith => _ItemBaseModelCopyWithImpl(
|
||||||
this as ItemBaseModel, $identity, $identity);
|
this as ItemBaseModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return ItemBaseModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as ItemBaseModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ItemBaseModelValueCopy<$R, $Out>
|
extension ItemBaseModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -199,17 +199,34 @@ extension EpisodeListExtensions on List<EpisodeModel> {
|
||||||
}
|
}
|
||||||
|
|
||||||
EpisodeModel? get nextUp {
|
EpisodeModel? get nextUp {
|
||||||
final lastProgress =
|
final episodes = where((e) => e.season > 0 && e.status == EpisodeStatus.available).toList();
|
||||||
lastIndexWhere((element) => element.userData.progress != 0 && element.status == EpisodeStatus.available);
|
if (episodes.isEmpty) return null;
|
||||||
final lastPlayed =
|
|
||||||
lastIndexWhere((element) => element.userData.played && element.status == EpisodeStatus.available);
|
|
||||||
|
|
||||||
if (lastProgress == -1 && lastPlayed == -1) {
|
final lastProgressIndex = episodes.lastIndexWhere((e) => e.userData.progress != 0);
|
||||||
return firstWhereOrNull((element) => element.status == EpisodeStatus.available);
|
final lastPlayedIndex = episodes.lastIndexWhere((e) => e.userData.played);
|
||||||
} else {
|
final lastWatchedIndex = [lastProgressIndex, lastPlayedIndex].reduce((a, b) => a > b ? a : b);
|
||||||
return getRange(lastProgress > lastPlayed ? lastProgress : lastPlayed + 1, length)
|
|
||||||
.firstWhereOrNull((element) => element.status == EpisodeStatus.available);
|
if (lastWatchedIndex >= 0) {
|
||||||
|
final current = episodes[lastWatchedIndex];
|
||||||
|
if (!current.userData.played && current.userData.progress != 0) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
final nextIndex = lastWatchedIndex + 1;
|
||||||
|
if (nextIndex < episodes.length) {
|
||||||
|
final next = episodes[nextIndex];
|
||||||
|
if (!next.userData.played && next.userData.progress != 0) {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
final nextUnplayed = episodes.sublist(nextIndex).firstWhereOrNull(
|
||||||
|
(e) => e.status == EpisodeStatus.available && !e.userData.played,
|
||||||
|
);
|
||||||
|
if (nextUnplayed != null) return nextUnplayed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return episodes.firstOrNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get allPlayed {
|
bool get allPlayed {
|
||||||
|
|
|
||||||
|
|
@ -141,34 +141,11 @@ class EpisodeModelMapper extends SubClassMapperBase<EpisodeModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static EpisodeModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<EpisodeModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static EpisodeModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<EpisodeModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin EpisodeModelMappable {
|
mixin EpisodeModelMappable {
|
||||||
String toJson() {
|
|
||||||
return EpisodeModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<EpisodeModel>(this as EpisodeModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return EpisodeModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<EpisodeModel>(this as EpisodeModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
EpisodeModelCopyWith<EpisodeModel, EpisodeModel, EpisodeModel> get copyWith =>
|
EpisodeModelCopyWith<EpisodeModel, EpisodeModel, EpisodeModel> get copyWith =>
|
||||||
_EpisodeModelCopyWithImpl(this as EpisodeModel, $identity, $identity);
|
_EpisodeModelCopyWithImpl(this as EpisodeModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return EpisodeModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as EpisodeModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EpisodeModelValueCopy<$R, $Out>
|
extension EpisodeModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -108,34 +108,11 @@ class FolderModelMapper extends SubClassMapperBase<FolderModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static FolderModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<FolderModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FolderModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<FolderModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin FolderModelMappable {
|
mixin FolderModelMappable {
|
||||||
String toJson() {
|
|
||||||
return FolderModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<FolderModel>(this as FolderModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return FolderModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<FolderModel>(this as FolderModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderModelCopyWith<FolderModel, FolderModel, FolderModel> get copyWith =>
|
FolderModelCopyWith<FolderModel, FolderModel, FolderModel> get copyWith =>
|
||||||
_FolderModelCopyWithImpl(this as FolderModel, $identity, $identity);
|
_FolderModelCopyWithImpl(this as FolderModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return FolderModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as FolderModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension FolderModelValueCopy<$R, $Out>
|
extension FolderModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -18,92 +18,6 @@ final _privateConstructorUsedError = UnsupportedError(
|
||||||
mixin _$ItemPropertiesModel {
|
mixin _$ItemPropertiesModel {
|
||||||
bool get canDelete => throw _privateConstructorUsedError;
|
bool get canDelete => throw _privateConstructorUsedError;
|
||||||
bool get canDownload => throw _privateConstructorUsedError;
|
bool get canDownload => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of ItemPropertiesModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$ItemPropertiesModelCopyWith<ItemPropertiesModel> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $ItemPropertiesModelCopyWith<$Res> {
|
|
||||||
factory $ItemPropertiesModelCopyWith(
|
|
||||||
ItemPropertiesModel value, $Res Function(ItemPropertiesModel) then) =
|
|
||||||
_$ItemPropertiesModelCopyWithImpl<$Res, ItemPropertiesModel>;
|
|
||||||
@useResult
|
|
||||||
$Res call({bool canDelete, bool canDownload});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$ItemPropertiesModelCopyWithImpl<$Res, $Val extends ItemPropertiesModel>
|
|
||||||
implements $ItemPropertiesModelCopyWith<$Res> {
|
|
||||||
_$ItemPropertiesModelCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of ItemPropertiesModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? canDelete = null,
|
|
||||||
Object? canDownload = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
canDelete: null == canDelete
|
|
||||||
? _value.canDelete
|
|
||||||
: canDelete // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
canDownload: null == canDownload
|
|
||||||
? _value.canDownload
|
|
||||||
: canDownload // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$ItemPropertiesModelImplCopyWith<$Res>
|
|
||||||
implements $ItemPropertiesModelCopyWith<$Res> {
|
|
||||||
factory _$$ItemPropertiesModelImplCopyWith(_$ItemPropertiesModelImpl value,
|
|
||||||
$Res Function(_$ItemPropertiesModelImpl) then) =
|
|
||||||
__$$ItemPropertiesModelImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({bool canDelete, bool canDownload});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$ItemPropertiesModelImplCopyWithImpl<$Res>
|
|
||||||
extends _$ItemPropertiesModelCopyWithImpl<$Res, _$ItemPropertiesModelImpl>
|
|
||||||
implements _$$ItemPropertiesModelImplCopyWith<$Res> {
|
|
||||||
__$$ItemPropertiesModelImplCopyWithImpl(_$ItemPropertiesModelImpl _value,
|
|
||||||
$Res Function(_$ItemPropertiesModelImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of ItemPropertiesModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? canDelete = null,
|
|
||||||
Object? canDownload = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$ItemPropertiesModelImpl(
|
|
||||||
canDelete: null == canDelete
|
|
||||||
? _value.canDelete
|
|
||||||
: canDelete // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
canDownload: null == canDownload
|
|
||||||
? _value.canDownload
|
|
||||||
: canDownload // ignore: cast_nullable_to_non_nullable
|
|
||||||
as bool,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -122,29 +36,6 @@ class _$ItemPropertiesModelImpl extends _ItemPropertiesModel {
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'ItemPropertiesModel._internal(canDelete: $canDelete, canDownload: $canDownload)';
|
return 'ItemPropertiesModel._internal(canDelete: $canDelete, canDownload: $canDownload)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$ItemPropertiesModelImpl &&
|
|
||||||
(identical(other.canDelete, canDelete) ||
|
|
||||||
other.canDelete == canDelete) &&
|
|
||||||
(identical(other.canDownload, canDownload) ||
|
|
||||||
other.canDownload == canDownload));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, canDelete, canDownload);
|
|
||||||
|
|
||||||
/// Create a copy of ItemPropertiesModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
|
|
||||||
__$$ItemPropertiesModelImplCopyWithImpl<_$ItemPropertiesModelImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _ItemPropertiesModel extends ItemPropertiesModel {
|
abstract class _ItemPropertiesModel extends ItemPropertiesModel {
|
||||||
|
|
@ -157,11 +48,4 @@ abstract class _ItemPropertiesModel extends ItemPropertiesModel {
|
||||||
bool get canDelete;
|
bool get canDelete;
|
||||||
@override
|
@override
|
||||||
bool get canDownload;
|
bool get canDownload;
|
||||||
|
|
||||||
/// Create a copy of ItemPropertiesModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$ItemPropertiesModelImplCopyWith<_$ItemPropertiesModelImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:fladder/models/items/images_models.dart';
|
||||||
|
|
||||||
part 'item_shared_models.mapper.dart';
|
part 'item_shared_models.mapper.dart';
|
||||||
|
|
||||||
@MappableClass()
|
@MappableClass(generateMethods: GenerateMethods.encode | GenerateMethods.decode | GenerateMethods.copy)
|
||||||
class UserData with UserDataMappable {
|
class UserData with UserDataMappable {
|
||||||
final bool isFavourite;
|
final bool isFavourite;
|
||||||
final int playCount;
|
final int playCount;
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,6 @@ mixin UserDataMappable {
|
||||||
|
|
||||||
UserDataCopyWith<UserData, UserData, UserData> get copyWith =>
|
UserDataCopyWith<UserData, UserData, UserData> get copyWith =>
|
||||||
_UserDataCopyWithImpl(this as UserData, $identity, $identity);
|
_UserDataCopyWithImpl(this as UserData, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return UserDataMapper.ensureInitialized().stringifyValue(this as UserData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension UserDataValueCopy<$R, $Out> on ObjectCopyWith<$R, UserData, $Out> {
|
extension UserDataValueCopy<$R, $Out> on ObjectCopyWith<$R, UserData, $Out> {
|
||||||
|
|
|
||||||
|
|
@ -112,35 +112,12 @@ class ItemStreamModelMapper extends SubClassMapperBase<ItemStreamModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static ItemStreamModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<ItemStreamModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ItemStreamModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<ItemStreamModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin ItemStreamModelMappable {
|
mixin ItemStreamModelMappable {
|
||||||
String toJson() {
|
|
||||||
return ItemStreamModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<ItemStreamModel>(this as ItemStreamModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return ItemStreamModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<ItemStreamModel>(this as ItemStreamModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemStreamModelCopyWith<ItemStreamModel, ItemStreamModel, ItemStreamModel>
|
ItemStreamModelCopyWith<ItemStreamModel, ItemStreamModel, ItemStreamModel>
|
||||||
get copyWith => _ItemStreamModelCopyWithImpl(
|
get copyWith => _ItemStreamModelCopyWithImpl(
|
||||||
this as ItemStreamModel, $identity, $identity);
|
this as ItemStreamModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return ItemStreamModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as ItemStreamModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ItemStreamModelValueCopy<$R, $Out>
|
extension ItemStreamModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
@ -39,7 +40,19 @@ class MediaSegment with _$MediaSegment {
|
||||||
|
|
||||||
bool inRange(Duration position) => (position.compareTo(start) >= 0 && position.compareTo(end) <= 0);
|
bool inRange(Duration position) => (position.compareTo(start) >= 0 && position.compareTo(end) <= 0);
|
||||||
|
|
||||||
bool forceShow(Duration position) => (position - start).inSeconds < (end - start).inSeconds * 0.20;
|
SegmentVisibility visibility(Duration position, {bool force = false}) {
|
||||||
|
if (force) return SegmentVisibility.visible;
|
||||||
|
var difference = (position - start);
|
||||||
|
if (difference > const Duration(minutes: 1, seconds: 30)) return SegmentVisibility.hidden;
|
||||||
|
Duration clamp = ((end - start) * 0.20).clamp(Duration.zero, const Duration(minutes: 1));
|
||||||
|
return difference < clamp ? SegmentVisibility.visible : SegmentVisibility.partially;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SegmentVisibility {
|
||||||
|
hidden,
|
||||||
|
partially,
|
||||||
|
visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Map<MediaSegmentType, SegmentSkip> defaultSegmentSkipValues = {
|
const Map<MediaSegmentType, SegmentSkip> defaultSegmentSkipValues = {
|
||||||
|
|
|
||||||
|
|
@ -24,82 +24,6 @@ mixin _$MediaSegmentsModel {
|
||||||
|
|
||||||
/// Serializes this MediaSegmentsModel to a JSON map.
|
/// Serializes this MediaSegmentsModel to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of MediaSegmentsModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$MediaSegmentsModelCopyWith<MediaSegmentsModel> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $MediaSegmentsModelCopyWith<$Res> {
|
|
||||||
factory $MediaSegmentsModelCopyWith(
|
|
||||||
MediaSegmentsModel value, $Res Function(MediaSegmentsModel) then) =
|
|
||||||
_$MediaSegmentsModelCopyWithImpl<$Res, MediaSegmentsModel>;
|
|
||||||
@useResult
|
|
||||||
$Res call({List<MediaSegment> segments});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$MediaSegmentsModelCopyWithImpl<$Res, $Val extends MediaSegmentsModel>
|
|
||||||
implements $MediaSegmentsModelCopyWith<$Res> {
|
|
||||||
_$MediaSegmentsModelCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegmentsModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? segments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
segments: null == segments
|
|
||||||
? _value.segments
|
|
||||||
: segments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<MediaSegment>,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$MediaSegmentsModelImplCopyWith<$Res>
|
|
||||||
implements $MediaSegmentsModelCopyWith<$Res> {
|
|
||||||
factory _$$MediaSegmentsModelImplCopyWith(_$MediaSegmentsModelImpl value,
|
|
||||||
$Res Function(_$MediaSegmentsModelImpl) then) =
|
|
||||||
__$$MediaSegmentsModelImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({List<MediaSegment> segments});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$MediaSegmentsModelImplCopyWithImpl<$Res>
|
|
||||||
extends _$MediaSegmentsModelCopyWithImpl<$Res, _$MediaSegmentsModelImpl>
|
|
||||||
implements _$$MediaSegmentsModelImplCopyWith<$Res> {
|
|
||||||
__$$MediaSegmentsModelImplCopyWithImpl(_$MediaSegmentsModelImpl _value,
|
|
||||||
$Res Function(_$MediaSegmentsModelImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegmentsModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? segments = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$MediaSegmentsModelImpl(
|
|
||||||
segments: null == segments
|
|
||||||
? _value._segments
|
|
||||||
: segments // ignore: cast_nullable_to_non_nullable
|
|
||||||
as List<MediaSegment>,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -126,28 +50,6 @@ class _$MediaSegmentsModelImpl extends _MediaSegmentsModel {
|
||||||
return 'MediaSegmentsModel(segments: $segments)';
|
return 'MediaSegmentsModel(segments: $segments)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$MediaSegmentsModelImpl &&
|
|
||||||
const DeepCollectionEquality().equals(other._segments, _segments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_segments));
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegmentsModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$MediaSegmentsModelImplCopyWith<_$MediaSegmentsModelImpl> get copyWith =>
|
|
||||||
__$$MediaSegmentsModelImplCopyWithImpl<_$MediaSegmentsModelImpl>(
|
|
||||||
this, _$identity);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$$MediaSegmentsModelImplToJson(
|
return _$$MediaSegmentsModelImplToJson(
|
||||||
|
|
@ -166,13 +68,6 @@ abstract class _MediaSegmentsModel extends MediaSegmentsModel {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<MediaSegment> get segments;
|
List<MediaSegment> get segments;
|
||||||
|
|
||||||
/// Create a copy of MediaSegmentsModel
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$MediaSegmentsModelImplCopyWith<_$MediaSegmentsModelImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaSegment _$MediaSegmentFromJson(Map<String, dynamic> json) {
|
MediaSegment _$MediaSegmentFromJson(Map<String, dynamic> json) {
|
||||||
|
|
@ -187,102 +82,6 @@ mixin _$MediaSegment {
|
||||||
|
|
||||||
/// Serializes this MediaSegment to a JSON map.
|
/// Serializes this MediaSegment to a JSON map.
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Create a copy of MediaSegment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
$MediaSegmentCopyWith<MediaSegment> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class $MediaSegmentCopyWith<$Res> {
|
|
||||||
factory $MediaSegmentCopyWith(
|
|
||||||
MediaSegment value, $Res Function(MediaSegment) then) =
|
|
||||||
_$MediaSegmentCopyWithImpl<$Res, MediaSegment>;
|
|
||||||
@useResult
|
|
||||||
$Res call({MediaSegmentType type, Duration start, Duration end});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class _$MediaSegmentCopyWithImpl<$Res, $Val extends MediaSegment>
|
|
||||||
implements $MediaSegmentCopyWith<$Res> {
|
|
||||||
_$MediaSegmentCopyWithImpl(this._value, this._then);
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Val _value;
|
|
||||||
// ignore: unused_field
|
|
||||||
final $Res Function($Val) _then;
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? type = null,
|
|
||||||
Object? start = null,
|
|
||||||
Object? end = null,
|
|
||||||
}) {
|
|
||||||
return _then(_value.copyWith(
|
|
||||||
type: null == type
|
|
||||||
? _value.type
|
|
||||||
: type // ignore: cast_nullable_to_non_nullable
|
|
||||||
as MediaSegmentType,
|
|
||||||
start: null == start
|
|
||||||
? _value.start
|
|
||||||
: start // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Duration,
|
|
||||||
end: null == end
|
|
||||||
? _value.end
|
|
||||||
: end // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Duration,
|
|
||||||
) as $Val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
abstract class _$$MediaSegmentImplCopyWith<$Res>
|
|
||||||
implements $MediaSegmentCopyWith<$Res> {
|
|
||||||
factory _$$MediaSegmentImplCopyWith(
|
|
||||||
_$MediaSegmentImpl value, $Res Function(_$MediaSegmentImpl) then) =
|
|
||||||
__$$MediaSegmentImplCopyWithImpl<$Res>;
|
|
||||||
@override
|
|
||||||
@useResult
|
|
||||||
$Res call({MediaSegmentType type, Duration start, Duration end});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @nodoc
|
|
||||||
class __$$MediaSegmentImplCopyWithImpl<$Res>
|
|
||||||
extends _$MediaSegmentCopyWithImpl<$Res, _$MediaSegmentImpl>
|
|
||||||
implements _$$MediaSegmentImplCopyWith<$Res> {
|
|
||||||
__$$MediaSegmentImplCopyWithImpl(
|
|
||||||
_$MediaSegmentImpl _value, $Res Function(_$MediaSegmentImpl) _then)
|
|
||||||
: super(_value, _then);
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
@override
|
|
||||||
$Res call({
|
|
||||||
Object? type = null,
|
|
||||||
Object? start = null,
|
|
||||||
Object? end = null,
|
|
||||||
}) {
|
|
||||||
return _then(_$MediaSegmentImpl(
|
|
||||||
type: null == type
|
|
||||||
? _value.type
|
|
||||||
: type // ignore: cast_nullable_to_non_nullable
|
|
||||||
as MediaSegmentType,
|
|
||||||
start: null == start
|
|
||||||
? _value.start
|
|
||||||
: start // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Duration,
|
|
||||||
end: null == end
|
|
||||||
? _value.end
|
|
||||||
: end // ignore: cast_nullable_to_non_nullable
|
|
||||||
as Duration,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
|
|
@ -307,28 +106,6 @@ class _$MediaSegmentImpl extends _MediaSegment {
|
||||||
return 'MediaSegment(type: $type, start: $start, end: $end)';
|
return 'MediaSegment(type: $type, start: $start, end: $end)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$MediaSegmentImpl &&
|
|
||||||
(identical(other.type, type) || other.type == type) &&
|
|
||||||
(identical(other.start, start) || other.start == start) &&
|
|
||||||
(identical(other.end, end) || other.end == end));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(runtimeType, type, start, end);
|
|
||||||
|
|
||||||
/// Create a copy of MediaSegment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
_$$MediaSegmentImplCopyWith<_$MediaSegmentImpl> get copyWith =>
|
|
||||||
__$$MediaSegmentImplCopyWithImpl<_$MediaSegmentImpl>(this, _$identity);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return _$$MediaSegmentImplToJson(
|
return _$$MediaSegmentImplToJson(
|
||||||
|
|
@ -353,11 +130,4 @@ abstract class _MediaSegment extends MediaSegment {
|
||||||
Duration get start;
|
Duration get start;
|
||||||
@override
|
@override
|
||||||
Duration get end;
|
Duration get end;
|
||||||
|
|
||||||
/// Create a copy of MediaSegment
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
_$$MediaSegmentImplCopyWith<_$MediaSegmentImpl> get copyWith =>
|
|
||||||
throw _privateConstructorUsedError;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,20 @@ class StreamModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AudioAndSubStreamModel extends StreamModel {
|
||||||
|
final String language;
|
||||||
|
final String displayTitle;
|
||||||
|
AudioAndSubStreamModel({
|
||||||
|
required this.displayTitle,
|
||||||
|
required super.name,
|
||||||
|
required super.codec,
|
||||||
|
required super.isDefault,
|
||||||
|
required super.isExternal,
|
||||||
|
required super.index,
|
||||||
|
required this.language,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class VersionStreamModel {
|
class VersionStreamModel {
|
||||||
final String name;
|
final String name;
|
||||||
final int index;
|
final int index;
|
||||||
|
|
@ -250,19 +264,17 @@ extension SortByExternalExtension<T extends StreamModel> on Iterable<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioStreamModel extends StreamModel {
|
class AudioStreamModel extends AudioAndSubStreamModel {
|
||||||
final String displayTitle;
|
|
||||||
final String language;
|
|
||||||
final String channelLayout;
|
final String channelLayout;
|
||||||
|
|
||||||
AudioStreamModel({
|
AudioStreamModel({
|
||||||
required this.displayTitle,
|
required super.displayTitle,
|
||||||
required super.name,
|
required super.name,
|
||||||
required super.codec,
|
required super.codec,
|
||||||
required super.isDefault,
|
required super.isDefault,
|
||||||
required super.isExternal,
|
required super.isExternal,
|
||||||
required super.index,
|
required super.index,
|
||||||
required this.language,
|
required super.language,
|
||||||
required this.channelLayout,
|
required this.channelLayout,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -292,8 +304,8 @@ class AudioStreamModel extends StreamModel {
|
||||||
|
|
||||||
AudioStreamModel.no({
|
AudioStreamModel.no({
|
||||||
super.name = 'Off',
|
super.name = 'Off',
|
||||||
this.displayTitle = 'Off',
|
super.displayTitle = 'Off',
|
||||||
this.language = '',
|
super.language = '',
|
||||||
super.codec = '',
|
super.codec = '',
|
||||||
this.channelLayout = '',
|
this.channelLayout = '',
|
||||||
super.isDefault = false,
|
super.isDefault = false,
|
||||||
|
|
@ -302,19 +314,17 @@ class AudioStreamModel extends StreamModel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class SubStreamModel extends StreamModel {
|
class SubStreamModel extends AudioAndSubStreamModel {
|
||||||
String id;
|
String id;
|
||||||
String title;
|
String title;
|
||||||
String displayTitle;
|
|
||||||
String language;
|
|
||||||
String? url;
|
String? url;
|
||||||
bool supportsExternalStream;
|
bool supportsExternalStream;
|
||||||
SubStreamModel({
|
SubStreamModel({
|
||||||
required super.name,
|
required super.name,
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.displayTitle,
|
required super.displayTitle,
|
||||||
required this.language,
|
required super.language,
|
||||||
this.url,
|
this.url,
|
||||||
required super.codec,
|
required super.codec,
|
||||||
required super.isDefault,
|
required super.isDefault,
|
||||||
|
|
@ -327,8 +337,8 @@ class SubStreamModel extends StreamModel {
|
||||||
super.name = 'Off',
|
super.name = 'Off',
|
||||||
this.id = 'Off',
|
this.id = 'Off',
|
||||||
this.title = 'Off',
|
this.title = 'Off',
|
||||||
this.displayTitle = 'Off',
|
super.displayTitle = 'Off',
|
||||||
this.language = '',
|
super.language = '',
|
||||||
this.url = '',
|
this.url = '',
|
||||||
super.codec = '',
|
super.codec = '',
|
||||||
super.isDefault = false,
|
super.isDefault = false,
|
||||||
|
|
|
||||||
|
|
@ -147,34 +147,11 @@ class MovieModelMapper extends SubClassMapperBase<MovieModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static MovieModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<MovieModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static MovieModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<MovieModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin MovieModelMappable {
|
mixin MovieModelMappable {
|
||||||
String toJson() {
|
|
||||||
return MovieModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<MovieModel>(this as MovieModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return MovieModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<MovieModel>(this as MovieModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovieModelCopyWith<MovieModel, MovieModel, MovieModel> get copyWith =>
|
MovieModelCopyWith<MovieModel, MovieModel, MovieModel> get copyWith =>
|
||||||
_MovieModelCopyWithImpl(this as MovieModel, $identity, $identity);
|
_MovieModelCopyWithImpl(this as MovieModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return MovieModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as MovieModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MovieModelValueCopy<$R, $Out>
|
extension MovieModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:dart_mappable/dart_mappable.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
|
|
@ -6,8 +7,6 @@ import 'package:fladder/models/items/item_shared_models.dart';
|
||||||
import 'package:fladder/models/items/trick_play_model.dart';
|
import 'package:fladder/models/items/trick_play_model.dart';
|
||||||
import 'package:fladder/util/duration_extensions.dart';
|
import 'package:fladder/util/duration_extensions.dart';
|
||||||
|
|
||||||
import 'package:dart_mappable/dart_mappable.dart';
|
|
||||||
|
|
||||||
part 'overview_model.mapper.dart';
|
part 'overview_model.mapper.dart';
|
||||||
|
|
||||||
@MappableClass()
|
@MappableClass()
|
||||||
|
|
@ -76,7 +75,4 @@ class OverviewModel with OverviewModelMappable {
|
||||||
people: Person.peopleFromDto(item.people ?? [], ref),
|
people: Person.peopleFromDto(item.people ?? [], ref),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory OverviewModel.fromMap(Map<String, dynamic> map) => OverviewModelMapper.fromMap(map);
|
|
||||||
factory OverviewModel.fromJson(String json) => OverviewModelMapper.fromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,35 +114,12 @@ class OverviewModelMapper extends ClassMapperBase<OverviewModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static OverviewModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<OverviewModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OverviewModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<OverviewModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin OverviewModelMappable {
|
mixin OverviewModelMappable {
|
||||||
String toJson() {
|
|
||||||
return OverviewModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<OverviewModel>(this as OverviewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return OverviewModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<OverviewModel>(this as OverviewModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
OverviewModelCopyWith<OverviewModel, OverviewModel, OverviewModel>
|
OverviewModelCopyWith<OverviewModel, OverviewModel, OverviewModel>
|
||||||
get copyWith => _OverviewModelCopyWithImpl(
|
get copyWith => _OverviewModelCopyWithImpl(
|
||||||
this as OverviewModel, $identity, $identity);
|
this as OverviewModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return OverviewModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as OverviewModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OverviewModelValueCopy<$R, $Out>
|
extension OverviewModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -124,34 +124,11 @@ class PersonModelMapper extends SubClassMapperBase<PersonModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static PersonModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<PersonModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PersonModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<PersonModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin PersonModelMappable {
|
mixin PersonModelMappable {
|
||||||
String toJson() {
|
|
||||||
return PersonModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<PersonModel>(this as PersonModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return PersonModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<PersonModel>(this as PersonModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
PersonModelCopyWith<PersonModel, PersonModel, PersonModel> get copyWith =>
|
PersonModelCopyWith<PersonModel, PersonModel, PersonModel> get copyWith =>
|
||||||
_PersonModelCopyWithImpl(this as PersonModel, $identity, $identity);
|
_PersonModelCopyWithImpl(this as PersonModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return PersonModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as PersonModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PersonModelValueCopy<$R, $Out>
|
extension PersonModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -108,35 +108,12 @@ class PhotoAlbumModelMapper extends SubClassMapperBase<PhotoAlbumModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static PhotoAlbumModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<PhotoAlbumModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PhotoAlbumModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<PhotoAlbumModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin PhotoAlbumModelMappable {
|
mixin PhotoAlbumModelMappable {
|
||||||
String toJson() {
|
|
||||||
return PhotoAlbumModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<PhotoAlbumModel>(this as PhotoAlbumModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return PhotoAlbumModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<PhotoAlbumModel>(this as PhotoAlbumModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
PhotoAlbumModelCopyWith<PhotoAlbumModel, PhotoAlbumModel, PhotoAlbumModel>
|
PhotoAlbumModelCopyWith<PhotoAlbumModel, PhotoAlbumModel, PhotoAlbumModel>
|
||||||
get copyWith => _PhotoAlbumModelCopyWithImpl(
|
get copyWith => _PhotoAlbumModelCopyWithImpl(
|
||||||
this as PhotoAlbumModel, $identity, $identity);
|
this as PhotoAlbumModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return PhotoAlbumModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as PhotoAlbumModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PhotoAlbumModelValueCopy<$R, $Out>
|
extension PhotoAlbumModelValueCopy<$R, $Out>
|
||||||
|
|
@ -359,34 +336,11 @@ class PhotoModelMapper extends SubClassMapperBase<PhotoModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static PhotoModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<PhotoModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PhotoModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<PhotoModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin PhotoModelMappable {
|
mixin PhotoModelMappable {
|
||||||
String toJson() {
|
|
||||||
return PhotoModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<PhotoModel>(this as PhotoModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return PhotoModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<PhotoModel>(this as PhotoModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
PhotoModelCopyWith<PhotoModel, PhotoModel, PhotoModel> get copyWith =>
|
PhotoModelCopyWith<PhotoModel, PhotoModel, PhotoModel> get copyWith =>
|
||||||
_PhotoModelCopyWithImpl(this as PhotoModel, $identity, $identity);
|
_PhotoModelCopyWithImpl(this as PhotoModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return PhotoModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as PhotoModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PhotoModelValueCopy<$R, $Out>
|
extension PhotoModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -137,34 +137,11 @@ class SeasonModelMapper extends SubClassMapperBase<SeasonModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static SeasonModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<SeasonModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SeasonModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<SeasonModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin SeasonModelMappable {
|
mixin SeasonModelMappable {
|
||||||
String toJson() {
|
|
||||||
return SeasonModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<SeasonModel>(this as SeasonModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return SeasonModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<SeasonModel>(this as SeasonModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
SeasonModelCopyWith<SeasonModel, SeasonModel, SeasonModel> get copyWith =>
|
SeasonModelCopyWith<SeasonModel, SeasonModel, SeasonModel> get copyWith =>
|
||||||
_SeasonModelCopyWithImpl(this as SeasonModel, $identity, $identity);
|
_SeasonModelCopyWithImpl(this as SeasonModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return SeasonModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as SeasonModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SeasonModelValueCopy<$R, $Out>
|
extension SeasonModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:fladder/screens/details_screens/series_detail_screen.dart';
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'package:dart_mappable/dart_mappable.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart' as dto;
|
||||||
|
|
@ -9,8 +10,7 @@ import 'package:fladder/models/items/images_models.dart';
|
||||||
import 'package:fladder/models/items/item_shared_models.dart';
|
import 'package:fladder/models/items/item_shared_models.dart';
|
||||||
import 'package:fladder/models/items/overview_model.dart';
|
import 'package:fladder/models/items/overview_model.dart';
|
||||||
import 'package:fladder/models/items/season_model.dart';
|
import 'package:fladder/models/items/season_model.dart';
|
||||||
|
import 'package:fladder/screens/details_screens/series_detail_screen.dart';
|
||||||
import 'package:dart_mappable/dart_mappable.dart';
|
|
||||||
|
|
||||||
part 'series_model.mapper.dart';
|
part 'series_model.mapper.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,34 +135,11 @@ class SeriesModelMapper extends SubClassMapperBase<SeriesModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static SeriesModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<SeriesModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SeriesModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<SeriesModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin SeriesModelMappable {
|
mixin SeriesModelMappable {
|
||||||
String toJson() {
|
|
||||||
return SeriesModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<SeriesModel>(this as SeriesModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return SeriesModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<SeriesModel>(this as SeriesModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
SeriesModelCopyWith<SeriesModel, SeriesModel, SeriesModel> get copyWith =>
|
SeriesModelCopyWith<SeriesModel, SeriesModel, SeriesModel> get copyWith =>
|
||||||
_SeriesModelCopyWithImpl(this as SeriesModel, $identity, $identity);
|
_SeriesModelCopyWithImpl(this as SeriesModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return SeriesModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as SeriesModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SeriesModelValueCopy<$R, $Out>
|
extension SeriesModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
part 'trick_play_model.freezed.dart';
|
part 'trick_play_model.freezed.dart';
|
||||||
part 'trick_play_model.g.dart';
|
part 'trick_play_model.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class TrickPlayModel with _$TrickPlayModel {
|
class TrickPlayModel with _$TrickPlayModel {
|
||||||
factory TrickPlayModel({
|
factory TrickPlayModel({
|
||||||
required int width,
|
required int width,
|
||||||
|
|
|
||||||
|
|
@ -225,36 +225,6 @@ class _$TrickPlayModelImpl extends _TrickPlayModel {
|
||||||
return 'TrickPlayModel(width: $width, height: $height, tileWidth: $tileWidth, tileHeight: $tileHeight, thumbnailCount: $thumbnailCount, interval: $interval, images: $images)';
|
return 'TrickPlayModel(width: $width, height: $height, tileWidth: $tileWidth, tileHeight: $tileHeight, thumbnailCount: $thumbnailCount, interval: $interval, images: $images)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$TrickPlayModelImpl &&
|
|
||||||
(identical(other.width, width) || other.width == width) &&
|
|
||||||
(identical(other.height, height) || other.height == height) &&
|
|
||||||
(identical(other.tileWidth, tileWidth) ||
|
|
||||||
other.tileWidth == tileWidth) &&
|
|
||||||
(identical(other.tileHeight, tileHeight) ||
|
|
||||||
other.tileHeight == tileHeight) &&
|
|
||||||
(identical(other.thumbnailCount, thumbnailCount) ||
|
|
||||||
other.thumbnailCount == thumbnailCount) &&
|
|
||||||
(identical(other.interval, interval) ||
|
|
||||||
other.interval == interval) &&
|
|
||||||
const DeepCollectionEquality().equals(other._images, _images));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
tileWidth,
|
|
||||||
tileHeight,
|
|
||||||
thumbnailCount,
|
|
||||||
interval,
|
|
||||||
const DeepCollectionEquality().hash(_images));
|
|
||||||
|
|
||||||
/// Create a copy of TrickPlayModel
|
/// Create a copy of TrickPlayModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import 'package:fladder/util/map_bool_helper.dart';
|
||||||
part 'library_filters_model.freezed.dart';
|
part 'library_filters_model.freezed.dart';
|
||||||
part 'library_filters_model.g.dart';
|
part 'library_filters_model.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class LibraryFiltersModel with _$LibraryFiltersModel {
|
class LibraryFiltersModel with _$LibraryFiltersModel {
|
||||||
const LibraryFiltersModel._();
|
const LibraryFiltersModel._();
|
||||||
|
|
||||||
|
|
@ -40,11 +40,16 @@ class LibraryFiltersModel with _$LibraryFiltersModel {
|
||||||
|
|
||||||
factory LibraryFiltersModel.fromJson(Map<String, dynamic> json) => _$LibraryFiltersModelFromJson(json);
|
factory LibraryFiltersModel.fromJson(Map<String, dynamic> json) => _$LibraryFiltersModelFromJson(json);
|
||||||
|
|
||||||
factory LibraryFiltersModel.fromLibrarySearch(String name, LibrarySearchModel searchModel) {
|
factory LibraryFiltersModel.fromLibrarySearch(
|
||||||
|
String name,
|
||||||
|
LibrarySearchModel searchModel, {
|
||||||
|
bool? isFavourite,
|
||||||
|
String? id,
|
||||||
|
}) {
|
||||||
return LibraryFiltersModel._internal(
|
return LibraryFiltersModel._internal(
|
||||||
id: Xid().toString(),
|
id: id ?? Xid().toString(),
|
||||||
name: name,
|
name: name,
|
||||||
isFavourite: false,
|
isFavourite: isFavourite ?? false,
|
||||||
ids: searchModel.views.included.map((e) => e.id).toList(),
|
ids: searchModel.views.included.map((e) => e.id).toList(),
|
||||||
genres: searchModel.genres,
|
genres: searchModel.genres,
|
||||||
filters: searchModel.filters,
|
filters: searchModel.filters,
|
||||||
|
|
|
||||||
|
|
@ -436,59 +436,6 @@ class _$LibraryFiltersModelImpl extends _LibraryFiltersModel {
|
||||||
return 'LibraryFiltersModel._internal(id: $id, name: $name, isFavourite: $isFavourite, ids: $ids, genres: $genres, filters: $filters, studios: $studios, tags: $tags, years: $years, officialRatings: $officialRatings, types: $types, sortingOption: $sortingOption, sortOrder: $sortOrder, favourites: $favourites, hideEmptyShows: $hideEmptyShows, recursive: $recursive, groupBy: $groupBy)';
|
return 'LibraryFiltersModel._internal(id: $id, name: $name, isFavourite: $isFavourite, ids: $ids, genres: $genres, filters: $filters, studios: $studios, tags: $tags, years: $years, officialRatings: $officialRatings, types: $types, sortingOption: $sortingOption, sortOrder: $sortOrder, favourites: $favourites, hideEmptyShows: $hideEmptyShows, recursive: $recursive, groupBy: $groupBy)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$LibraryFiltersModelImpl &&
|
|
||||||
(identical(other.id, id) || other.id == id) &&
|
|
||||||
(identical(other.name, name) || other.name == name) &&
|
|
||||||
(identical(other.isFavourite, isFavourite) ||
|
|
||||||
other.isFavourite == isFavourite) &&
|
|
||||||
const DeepCollectionEquality().equals(other._ids, _ids) &&
|
|
||||||
const DeepCollectionEquality().equals(other._genres, _genres) &&
|
|
||||||
const DeepCollectionEquality().equals(other._filters, _filters) &&
|
|
||||||
const DeepCollectionEquality().equals(other._studios, _studios) &&
|
|
||||||
const DeepCollectionEquality().equals(other._tags, _tags) &&
|
|
||||||
const DeepCollectionEquality().equals(other._years, _years) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._officialRatings, _officialRatings) &&
|
|
||||||
const DeepCollectionEquality().equals(other._types, _types) &&
|
|
||||||
(identical(other.sortingOption, sortingOption) ||
|
|
||||||
other.sortingOption == sortingOption) &&
|
|
||||||
(identical(other.sortOrder, sortOrder) ||
|
|
||||||
other.sortOrder == sortOrder) &&
|
|
||||||
(identical(other.favourites, favourites) ||
|
|
||||||
other.favourites == favourites) &&
|
|
||||||
(identical(other.hideEmptyShows, hideEmptyShows) ||
|
|
||||||
other.hideEmptyShows == hideEmptyShows) &&
|
|
||||||
(identical(other.recursive, recursive) ||
|
|
||||||
other.recursive == recursive) &&
|
|
||||||
(identical(other.groupBy, groupBy) || other.groupBy == groupBy));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
isFavourite,
|
|
||||||
const DeepCollectionEquality().hash(_ids),
|
|
||||||
const DeepCollectionEquality().hash(_genres),
|
|
||||||
const DeepCollectionEquality().hash(_filters),
|
|
||||||
const DeepCollectionEquality().hash(_studios),
|
|
||||||
const DeepCollectionEquality().hash(_tags),
|
|
||||||
const DeepCollectionEquality().hash(_years),
|
|
||||||
const DeepCollectionEquality().hash(_officialRatings),
|
|
||||||
const DeepCollectionEquality().hash(_types),
|
|
||||||
sortingOption,
|
|
||||||
sortOrder,
|
|
||||||
favourites,
|
|
||||||
hideEmptyShows,
|
|
||||||
recursive,
|
|
||||||
groupBy);
|
|
||||||
|
|
||||||
/// Create a copy of LibraryFiltersModel
|
/// Create a copy of LibraryFiltersModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ const _$FladderItemTypeEnumMap = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$SortingOptionsEnumMap = {
|
const _$SortingOptionsEnumMap = {
|
||||||
SortingOptions.name: 'name',
|
SortingOptions.sortName: 'sortName',
|
||||||
SortingOptions.communityRating: 'communityRating',
|
SortingOptions.communityRating: 'communityRating',
|
||||||
SortingOptions.parentalRating: 'parentalRating',
|
SortingOptions.parentalRating: 'parentalRating',
|
||||||
SortingOptions.dateAdded: 'dateAdded',
|
SortingOptions.dateAdded: 'dateAdded',
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class LibrarySearchModel with LibrarySearchModelMappable {
|
||||||
FladderItemType.video: true,
|
FladderItemType.video: true,
|
||||||
},
|
},
|
||||||
this.favourites = false,
|
this.favourites = false,
|
||||||
this.sortingOption = SortingOptions.name,
|
this.sortingOption = SortingOptions.sortName,
|
||||||
this.sortOrder = SortingOrder.ascending,
|
this.sortOrder = SortingOrder.ascending,
|
||||||
this.hideEmptyShows = true,
|
this.hideEmptyShows = true,
|
||||||
this.recursive = false,
|
this.recursive = false,
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ class LibrarySearchModelMapper extends ClassMapperBase<LibrarySearchModel> {
|
||||||
v.sortingOption;
|
v.sortingOption;
|
||||||
static const Field<LibrarySearchModel, SortingOptions> _f$sortingOption =
|
static const Field<LibrarySearchModel, SortingOptions> _f$sortingOption =
|
||||||
Field('sortingOption', _$sortingOption,
|
Field('sortingOption', _$sortingOption,
|
||||||
opt: true, def: SortingOptions.name);
|
opt: true, def: SortingOptions.sortName);
|
||||||
static SortingOrder _$sortOrder(LibrarySearchModel v) => v.sortOrder;
|
static SortingOrder _$sortOrder(LibrarySearchModel v) => v.sortOrder;
|
||||||
static const Field<LibrarySearchModel, SortingOrder> _f$sortOrder =
|
static const Field<LibrarySearchModel, SortingOrder> _f$sortOrder =
|
||||||
Field('sortOrder', _$sortOrder, opt: true, def: SortingOrder.ascending);
|
Field('sortOrder', _$sortOrder, opt: true, def: SortingOrder.ascending);
|
||||||
|
|
@ -177,36 +177,13 @@ class LibrarySearchModelMapper extends ClassMapperBase<LibrarySearchModel> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Function instantiate = _instantiate;
|
final Function instantiate = _instantiate;
|
||||||
|
|
||||||
static LibrarySearchModel fromMap(Map<String, dynamic> map) {
|
|
||||||
return ensureInitialized().decodeMap<LibrarySearchModel>(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
static LibrarySearchModel fromJson(String json) {
|
|
||||||
return ensureInitialized().decodeJson<LibrarySearchModel>(json);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin LibrarySearchModelMappable {
|
mixin LibrarySearchModelMappable {
|
||||||
String toJson() {
|
|
||||||
return LibrarySearchModelMapper.ensureInitialized()
|
|
||||||
.encodeJson<LibrarySearchModel>(this as LibrarySearchModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
|
||||||
return LibrarySearchModelMapper.ensureInitialized()
|
|
||||||
.encodeMap<LibrarySearchModel>(this as LibrarySearchModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
LibrarySearchModelCopyWith<LibrarySearchModel, LibrarySearchModel,
|
LibrarySearchModelCopyWith<LibrarySearchModel, LibrarySearchModel,
|
||||||
LibrarySearchModel>
|
LibrarySearchModel>
|
||||||
get copyWith => _LibrarySearchModelCopyWithImpl(
|
get copyWith => _LibrarySearchModelCopyWithImpl(
|
||||||
this as LibrarySearchModel, $identity, $identity);
|
this as LibrarySearchModel, $identity, $identity);
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return LibrarySearchModelMapper.ensureInitialized()
|
|
||||||
.stringifyValue(this as LibrarySearchModel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LibrarySearchModelValueCopy<$R, $Out>
|
extension LibrarySearchModelValueCopy<$R, $Out>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.enums.swagger.dart';
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
enum SortingOptions {
|
enum SortingOptions {
|
||||||
name([ItemSortBy.name]),
|
sortName([ItemSortBy.sortname]),
|
||||||
communityRating([ItemSortBy.communityrating]),
|
communityRating([ItemSortBy.communityrating]),
|
||||||
// criticsRating([ItemSortBy.criticrating]),
|
|
||||||
parentalRating([ItemSortBy.officialrating]),
|
parentalRating([ItemSortBy.officialrating]),
|
||||||
dateAdded([ItemSortBy.datecreated]),
|
dateAdded([ItemSortBy.datecreated]),
|
||||||
dateLastContentAdded([ItemSortBy.datelastcontentadded]),
|
dateLastContentAdded([ItemSortBy.datelastcontentadded]),
|
||||||
|
|
@ -23,10 +22,10 @@ enum SortingOptions {
|
||||||
const SortingOptions(this.value);
|
const SortingOptions(this.value);
|
||||||
final List<ItemSortBy> value;
|
final List<ItemSortBy> value;
|
||||||
|
|
||||||
List<ItemSortBy> get toSortBy => [...value, ItemSortBy.name];
|
List<ItemSortBy> get toSortBy => [...value, ItemSortBy.sortname];
|
||||||
|
|
||||||
String label(BuildContext context) => switch (this) {
|
String label(BuildContext context) => switch (this) {
|
||||||
name => context.localized.name,
|
sortName => context.localized.name,
|
||||||
communityRating => context.localized.communityRating,
|
communityRating => context.localized.communityRating,
|
||||||
parentalRating => context.localized.parentalRating,
|
parentalRating => context.localized.parentalRating,
|
||||||
dateAdded => context.localized.dateAdded,
|
dateAdded => context.localized.dateAdded,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:chopper/chopper.dart';
|
import 'package:chopper/chopper.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
@ -16,6 +18,7 @@ import 'package:fladder/models/items/series_model.dart';
|
||||||
import 'package:fladder/models/items/trick_play_model.dart';
|
import 'package:fladder/models/items/trick_play_model.dart';
|
||||||
import 'package:fladder/models/playback/direct_playback_model.dart';
|
import 'package:fladder/models/playback/direct_playback_model.dart';
|
||||||
import 'package:fladder/models/playback/offline_playback_model.dart';
|
import 'package:fladder/models/playback/offline_playback_model.dart';
|
||||||
|
import 'package:fladder/models/playback/playback_options_dialogue.dart';
|
||||||
import 'package:fladder/models/playback/transcode_playback_model.dart';
|
import 'package:fladder/models/playback/transcode_playback_model.dart';
|
||||||
import 'package:fladder/models/syncing/sync_item.dart';
|
import 'package:fladder/models/syncing/sync_item.dart';
|
||||||
import 'package:fladder/models/video_stream_model.dart';
|
import 'package:fladder/models/video_stream_model.dart';
|
||||||
|
|
@ -31,6 +34,7 @@ import 'package:fladder/util/bitrate_helper.dart';
|
||||||
import 'package:fladder/util/duration_extensions.dart';
|
import 'package:fladder/util/duration_extensions.dart';
|
||||||
import 'package:fladder/util/list_extensions.dart';
|
import 'package:fladder/util/list_extensions.dart';
|
||||||
import 'package:fladder/util/map_bool_helper.dart';
|
import 'package:fladder/util/map_bool_helper.dart';
|
||||||
|
import 'package:fladder/util/streams_selection.dart';
|
||||||
import 'package:fladder/wrappers/media_control_wrapper.dart';
|
import 'package:fladder/wrappers/media_control_wrapper.dart';
|
||||||
|
|
||||||
class Media {
|
class Media {
|
||||||
|
|
@ -48,10 +52,10 @@ extension PlaybackModelExtension on PlaybackModel? {
|
||||||
AudioStreamModel? get defaultAudioStream =>
|
AudioStreamModel? get defaultAudioStream =>
|
||||||
this?.audioStreams?.firstWhereOrNull((element) => element.index == this?.mediaStreams?.defaultAudioStreamIndex);
|
this?.audioStreams?.firstWhereOrNull((element) => element.index == this?.mediaStreams?.defaultAudioStreamIndex);
|
||||||
|
|
||||||
String? get label => switch (this) {
|
String? label(BuildContext context) => switch (this) {
|
||||||
DirectPlaybackModel _ => PlaybackType.directStream.name,
|
DirectPlaybackModel _ => PlaybackType.directStream.name(context),
|
||||||
TranscodePlaybackModel _ => PlaybackType.transcode.name,
|
TranscodePlaybackModel _ => PlaybackType.transcode.name(context),
|
||||||
OfflinePlaybackModel _ => PlaybackType.offline.name,
|
OfflinePlaybackModel _ => PlaybackType.offline.name(context),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -117,13 +121,14 @@ class PlaybackModelHelper {
|
||||||
ref.read(videoPlayerProvider).pause();
|
ref.read(videoPlayerProvider).pause();
|
||||||
ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(buffering: true));
|
ref.read(mediaPlaybackProvider.notifier).update((state) => state.copyWith(buffering: true));
|
||||||
final currentModel = ref.read(playBackModel);
|
final currentModel = ref.read(playBackModel);
|
||||||
final newModel = (await createServerPlaybackModel(
|
final newModel = (await createPlaybackModel(
|
||||||
newItem,
|
|
||||||
null,
|
null,
|
||||||
|
newItem,
|
||||||
oldModel: currentModel,
|
oldModel: currentModel,
|
||||||
)) ??
|
)) ??
|
||||||
await createOfflinePlaybackModel(
|
await _createOfflinePlaybackModel(
|
||||||
newItem,
|
newItem,
|
||||||
|
null,
|
||||||
ref.read(syncProvider.notifier).getSyncedItem(newItem),
|
ref.read(syncProvider.notifier).getSyncedItem(newItem),
|
||||||
oldModel: currentModel,
|
oldModel: currentModel,
|
||||||
);
|
);
|
||||||
|
|
@ -132,8 +137,9 @@ class PlaybackModelHelper {
|
||||||
return newModel;
|
return newModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<OfflinePlaybackModel?> createOfflinePlaybackModel(
|
Future<OfflinePlaybackModel?> _createOfflinePlaybackModel(
|
||||||
ItemBaseModel item,
|
ItemBaseModel item,
|
||||||
|
MediaStreamsModel? streamModel,
|
||||||
SyncedItem? syncedItem, {
|
SyncedItem? syncedItem, {
|
||||||
PlaybackModel? oldModel,
|
PlaybackModel? oldModel,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
@ -156,53 +162,121 @@ class PlaybackModelHelper {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<EpisodeModel?> getNextUpEpisode(String itemId) async {
|
Future<PlaybackModel?> createPlaybackModel(
|
||||||
final response = await api.showsNextUpGet(parentId: itemId, fields: [ItemFields.overview]);
|
BuildContext? context,
|
||||||
final episode = response.body?.items?.firstOrNull;
|
ItemBaseModel? item, {
|
||||||
if (episode == null) {
|
PlaybackModel? oldModel,
|
||||||
return null;
|
List<ItemBaseModel>? libraryQueue,
|
||||||
|
bool showPlaybackOptions = false,
|
||||||
|
Duration? startPosition,
|
||||||
|
}) async {
|
||||||
|
if (item == null) return null;
|
||||||
|
final userId = ref.read(userProvider)?.id;
|
||||||
|
if (userId?.isEmpty == true) return null;
|
||||||
|
|
||||||
|
final queue = oldModel?.queue ?? libraryQueue ?? await collectQueue(item);
|
||||||
|
|
||||||
|
final firstItemToPlay = switch (item) {
|
||||||
|
SeriesModel _ || SeasonModel _ => (queue.whereType<EpisodeModel>().toList().nextUp),
|
||||||
|
_ => item,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (firstItemToPlay == null) return null;
|
||||||
|
|
||||||
|
final fullItem = (await api.usersUserIdItemsItemIdGet(itemId: firstItemToPlay.id)).body;
|
||||||
|
|
||||||
|
if (fullItem == null) return null;
|
||||||
|
|
||||||
|
SyncedItem? syncedItem = ref.read(syncProvider.notifier).getSyncedItem(fullItem);
|
||||||
|
|
||||||
|
final firstItemIsSynced = syncedItem != null && syncedItem.status == SyncStatus.complete;
|
||||||
|
|
||||||
|
final options = {
|
||||||
|
PlaybackType.directStream,
|
||||||
|
PlaybackType.transcode,
|
||||||
|
if (firstItemIsSynced) PlaybackType.offline,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((showPlaybackOptions || firstItemIsSynced) && context != null) {
|
||||||
|
final playbackType = await showPlaybackTypeSelection(
|
||||||
|
context: context,
|
||||||
|
options: options,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context.mounted) return null;
|
||||||
|
|
||||||
|
return switch (playbackType) {
|
||||||
|
PlaybackType.directStream || PlaybackType.transcode => await _createServerPlaybackModel(
|
||||||
|
fullItem,
|
||||||
|
item.streamModel,
|
||||||
|
playbackType,
|
||||||
|
oldModel: oldModel,
|
||||||
|
libraryQueue: queue,
|
||||||
|
startPosition: startPosition,
|
||||||
|
),
|
||||||
|
PlaybackType.offline => await _createOfflinePlaybackModel(
|
||||||
|
fullItem,
|
||||||
|
item.streamModel,
|
||||||
|
syncedItem,
|
||||||
|
),
|
||||||
|
null => null
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
return EpisodeModel.fromBaseDto(episode, ref);
|
return (await _createServerPlaybackModel(
|
||||||
|
fullItem,
|
||||||
|
item.streamModel,
|
||||||
|
PlaybackType.directStream,
|
||||||
|
startPosition: startPosition,
|
||||||
|
oldModel: oldModel,
|
||||||
|
libraryQueue: queue,
|
||||||
|
)) ??
|
||||||
|
await _createOfflinePlaybackModel(
|
||||||
|
fullItem,
|
||||||
|
item.streamModel,
|
||||||
|
syncedItem,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<PlaybackModel?> createServerPlaybackModel(
|
Future<PlaybackModel?> _createServerPlaybackModel(
|
||||||
ItemBaseModel? item,
|
ItemBaseModel item,
|
||||||
|
MediaStreamsModel? streamModel,
|
||||||
PlaybackType? type, {
|
PlaybackType? type, {
|
||||||
PlaybackModel? oldModel,
|
PlaybackModel? oldModel,
|
||||||
List<ItemBaseModel>? libraryQueue,
|
required List<ItemBaseModel> libraryQueue,
|
||||||
Duration? startPosition,
|
Duration? startPosition,
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
if (item == null) return null;
|
|
||||||
final userId = ref.read(userProvider)?.id;
|
final userId = ref.read(userProvider)?.id;
|
||||||
if (userId?.isEmpty == true) return null;
|
if (userId?.isEmpty == true) return null;
|
||||||
|
|
||||||
final queue = oldModel?.queue ?? libraryQueue ?? await collectQueue(item);
|
final newStreamModel = streamModel ?? item.streamModel;
|
||||||
|
|
||||||
final firstItemToPlay = switch (item) {
|
|
||||||
SeriesModel _ || SeasonModel _ => (await getNextUpEpisode(item.id) ?? queue.first),
|
|
||||||
_ => item,
|
|
||||||
};
|
|
||||||
|
|
||||||
final fullItem = await api.usersUserIdItemsItemIdGet(itemId: firstItemToPlay.id);
|
|
||||||
|
|
||||||
Map<Bitrate, bool> qualityOptions = getVideoQualityOptions(
|
Map<Bitrate, bool> qualityOptions = getVideoQualityOptions(
|
||||||
VideoQualitySettings(
|
VideoQualitySettings(
|
||||||
maxBitRate: ref.read(videoPlayerSettingsProvider.select((value) => value.maxHomeBitrate)),
|
maxBitRate: ref.read(videoPlayerSettingsProvider.select((value) => value.maxHomeBitrate)),
|
||||||
videoBitRate: firstItemToPlay.streamModel?.videoStreams.firstOrNull?.bitRate ?? 0,
|
videoBitRate: newStreamModel?.videoStreams.firstOrNull?.bitRate ?? 0,
|
||||||
videoCodec: firstItemToPlay.streamModel?.videoStreams.firstOrNull?.codec,
|
videoCodec: newStreamModel?.videoStreams.firstOrNull?.codec,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final streamModel = firstItemToPlay.streamModel;
|
final audioStreamIndex = selectAudioStream(
|
||||||
|
ref.read(userProvider.select((value) => value?.userConfiguration?.rememberAudioSelections ?? true)),
|
||||||
|
oldModel?.mediaStreams?.currentAudioStream,
|
||||||
|
newStreamModel?.audioStreams,
|
||||||
|
newStreamModel?.defaultAudioStreamIndex);
|
||||||
|
final subStreamIndex = selectSubStream(
|
||||||
|
ref.read(userProvider.select((value) => value?.userConfiguration?.rememberSubtitleSelections ?? true)),
|
||||||
|
oldModel?.mediaStreams?.currentSubStream,
|
||||||
|
newStreamModel?.subStreams,
|
||||||
|
newStreamModel?.defaultSubStreamIndex);
|
||||||
|
|
||||||
final Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
final Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
||||||
itemId: firstItemToPlay.id,
|
itemId: item.id,
|
||||||
body: PlaybackInfoDto(
|
body: PlaybackInfoDto(
|
||||||
startTimeTicks: startPosition?.toRuntimeTicks,
|
startTimeTicks: startPosition?.toRuntimeTicks,
|
||||||
audioStreamIndex: streamModel?.defaultAudioStreamIndex,
|
audioStreamIndex: audioStreamIndex,
|
||||||
subtitleStreamIndex: streamModel?.defaultSubStreamIndex,
|
subtitleStreamIndex: subStreamIndex,
|
||||||
enableTranscoding: true,
|
enableTranscoding: true,
|
||||||
autoOpenLiveStream: true,
|
autoOpenLiveStream: true,
|
||||||
deviceProfile: ref.read(videoProfileProvider),
|
deviceProfile: ref.read(videoProfileProvider),
|
||||||
|
|
@ -210,7 +284,7 @@ class PlaybackModelHelper {
|
||||||
enableDirectPlay: type != PlaybackType.transcode,
|
enableDirectPlay: type != PlaybackType.transcode,
|
||||||
enableDirectStream: type != PlaybackType.transcode,
|
enableDirectStream: type != PlaybackType.transcode,
|
||||||
maxStreamingBitrate: qualityOptions.enabledFirst.keys.firstOrNull?.bitRate,
|
maxStreamingBitrate: qualityOptions.enabledFirst.keys.firstOrNull?.bitRate,
|
||||||
mediaSourceId: streamModel?.currentVersionStream?.id,
|
mediaSourceId: newStreamModel?.currentVersionStream?.id,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -218,18 +292,18 @@ class PlaybackModelHelper {
|
||||||
|
|
||||||
if (playbackInfo == null) return null;
|
if (playbackInfo == null) return null;
|
||||||
|
|
||||||
final mediaSource = playbackInfo.mediaSources?[streamModel?.versionStreamIndex ?? 0];
|
final mediaSource = playbackInfo.mediaSources?[newStreamModel?.versionStreamIndex ?? 0];
|
||||||
|
|
||||||
if (mediaSource == null) return null;
|
if (mediaSource == null) return null;
|
||||||
|
|
||||||
final mediaStreamsWithUrls = MediaStreamsModel.fromMediaStreamsList(playbackInfo.mediaSources, ref).copyWith(
|
final mediaStreamsWithUrls = MediaStreamsModel.fromMediaStreamsList(playbackInfo.mediaSources, ref).copyWith(
|
||||||
defaultAudioStreamIndex: streamModel?.defaultAudioStreamIndex,
|
defaultAudioStreamIndex: audioStreamIndex,
|
||||||
defaultSubStreamIndex: streamModel?.defaultSubStreamIndex,
|
defaultSubStreamIndex: subStreamIndex,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mediaSegments = await api.mediaSegmentsGet(id: item.id);
|
final mediaSegments = await api.mediaSegmentsGet(id: item.id);
|
||||||
final trickPlay = (await api.getTrickPlay(item: fullItem.body, ref: ref))?.body;
|
final trickPlay = (await api.getTrickPlay(item: item, ref: ref))?.body;
|
||||||
final chapters = fullItem.body?.overview.chapters ?? [];
|
final chapters = item.overview.chapters ?? [];
|
||||||
|
|
||||||
final mediaPath = isValidVideoUrl(mediaSource.path ?? "");
|
final mediaPath = isValidVideoUrl(mediaSource.path ?? "");
|
||||||
|
|
||||||
|
|
@ -252,8 +326,8 @@ class PlaybackModelHelper {
|
||||||
final playbackUrl = joinAll([ref.read(userProvider)!.server, "Videos", mediaSource.id!, "stream?$params"]);
|
final playbackUrl = joinAll([ref.read(userProvider)!.server, "Videos", mediaSource.id!, "stream?$params"]);
|
||||||
|
|
||||||
return DirectPlaybackModel(
|
return DirectPlaybackModel(
|
||||||
item: fullItem.body ?? item,
|
item: item,
|
||||||
queue: queue,
|
queue: libraryQueue,
|
||||||
mediaSegments: mediaSegments?.body,
|
mediaSegments: mediaSegments?.body,
|
||||||
chapters: chapters,
|
chapters: chapters,
|
||||||
playbackInfo: playbackInfo,
|
playbackInfo: playbackInfo,
|
||||||
|
|
@ -264,8 +338,8 @@ class PlaybackModelHelper {
|
||||||
);
|
);
|
||||||
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
|
} else if ((mediaSource.supportsTranscoding ?? false) && mediaSource.transcodingUrl != null) {
|
||||||
return TranscodePlaybackModel(
|
return TranscodePlaybackModel(
|
||||||
item: fullItem.body ?? item,
|
item: item,
|
||||||
queue: queue,
|
queue: libraryQueue,
|
||||||
mediaSegments: mediaSegments?.body,
|
mediaSegments: mediaSegments?.body,
|
||||||
chapters: chapters,
|
chapters: chapters,
|
||||||
trickPlay: trickPlay,
|
trickPlay: trickPlay,
|
||||||
|
|
@ -328,8 +402,16 @@ class PlaybackModelHelper {
|
||||||
|
|
||||||
final currentPosition = ref.read(mediaPlaybackProvider.select((value) => value.position));
|
final currentPosition = ref.read(mediaPlaybackProvider.select((value) => value.position));
|
||||||
|
|
||||||
final audioIndex = playbackModel.mediaStreams?.defaultAudioStreamIndex;
|
final audioIndex = selectAudioStream(
|
||||||
final subIndex = playbackModel.mediaStreams?.defaultSubStreamIndex;
|
ref.read(userProvider.select((value) => value?.userConfiguration?.rememberAudioSelections ?? true)),
|
||||||
|
playbackModel.mediaStreams?.currentAudioStream,
|
||||||
|
playbackModel.audioStreams,
|
||||||
|
playbackModel.mediaStreams?.defaultAudioStreamIndex);
|
||||||
|
final subIndex = selectSubStream(
|
||||||
|
ref.read(userProvider.select((value) => value?.userConfiguration?.rememberSubtitleSelections ?? true)),
|
||||||
|
playbackModel.mediaStreams?.currentSubStream,
|
||||||
|
playbackModel.subStreams,
|
||||||
|
playbackModel.mediaStreams?.defaultSubStreamIndex);
|
||||||
|
|
||||||
Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
Response<PlaybackInfoResponse> response = await api.itemsItemIdPlaybackInfoPost(
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
|
|
|
||||||
57
lib/models/playback/playback_options_dialogue.dart
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:fladder/models/video_stream_model.dart';
|
||||||
|
import 'package:fladder/screens/shared/adaptive_dialog.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
|
Future<PlaybackType?> showPlaybackTypeSelection({
|
||||||
|
required BuildContext context,
|
||||||
|
required Set<PlaybackType> options,
|
||||||
|
}) async {
|
||||||
|
PlaybackType? playbackType;
|
||||||
|
|
||||||
|
await showDialogAdaptive(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return PlaybackDialogue(
|
||||||
|
options: options,
|
||||||
|
onClose: (type) {
|
||||||
|
playbackType = type;
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return playbackType;
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlaybackDialogue extends StatelessWidget {
|
||||||
|
final Set<PlaybackType> options;
|
||||||
|
final Function(PlaybackType type) onClose;
|
||||||
|
const PlaybackDialogue({required this.options, required this.onClose, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16).add(const EdgeInsets.only(top: 16, bottom: 8)),
|
||||||
|
child: Text(
|
||||||
|
context.localized.playbackType,
|
||||||
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
...options.map((type) => ListTile(
|
||||||
|
title: Text(type.name(context)),
|
||||||
|
leading: Icon(type.icon),
|
||||||
|
onTap: () {
|
||||||
|
onClose(type);
|
||||||
|
},
|
||||||
|
))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,66 @@
|
||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
|
sealed class NameSwitch {
|
||||||
|
const NameSwitch();
|
||||||
|
|
||||||
|
String label(BuildContext context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class NextUp extends NameSwitch {
|
||||||
|
const NextUp();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String label(BuildContext context) => context.localized.nextUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Latest extends NameSwitch {
|
||||||
|
const Latest();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String label(BuildContext context) => context.localized.latest;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Other extends NameSwitch {
|
||||||
|
final String customLabel;
|
||||||
|
|
||||||
|
const Other(this.customLabel);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String label(BuildContext context) => customLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RecommendationTypeExtenstion on RecommendationType {
|
||||||
|
String label(BuildContext context) => switch (this) {
|
||||||
|
RecommendationType.similartorecentlyplayed => context.localized.similarToRecentlyPlayed,
|
||||||
|
RecommendationType.similartolikeditem => context.localized.similarToLikedItem,
|
||||||
|
RecommendationType.hasdirectorfromrecentlyplayed => context.localized.hasDirectorFromRecentlyPlayed,
|
||||||
|
RecommendationType.hasactorfromrecentlyplayed => context.localized.hasActorFromRecentlyPlayed,
|
||||||
|
RecommendationType.haslikeddirector => context.localized.hasLikedDirector,
|
||||||
|
RecommendationType.haslikedactor => context.localized.hasLikedActor,
|
||||||
|
_ => "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class RecommendedModel {
|
class RecommendedModel {
|
||||||
final String name;
|
final NameSwitch name;
|
||||||
final List<ItemBaseModel> posters;
|
final List<ItemBaseModel> posters;
|
||||||
final String type;
|
final RecommendationType? type;
|
||||||
RecommendedModel({
|
RecommendedModel({
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.posters,
|
required this.posters,
|
||||||
required this.type,
|
this.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
RecommendedModel copyWith({
|
RecommendedModel copyWith({
|
||||||
String? name,
|
NameSwitch? name,
|
||||||
List<ItemBaseModel>? posters,
|
List<ItemBaseModel>? posters,
|
||||||
String? type,
|
RecommendationType? type,
|
||||||
}) {
|
}) {
|
||||||
return RecommendedModel(
|
return RecommendedModel(
|
||||||
name: name ?? this.name,
|
name: name ?? this.name,
|
||||||
|
|
@ -22,4 +68,12 @@ class RecommendedModel {
|
||||||
type: type ?? this.type,
|
type: type ?? this.type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory RecommendedModel.fromBaseDto(RecommendationDto e, Ref ref) {
|
||||||
|
return RecommendedModel(
|
||||||
|
name: Other(e.baselineItemName ?? ""),
|
||||||
|
posters: e.items?.map((e) => ItemBaseModel.fromBaseDto(e, ref)).toList() ?? [],
|
||||||
|
type: e.recommendationType,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
lib/models/settings/arguments_model.dart
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'arguments_model.freezed.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class ArgumentsModel with _$ArgumentsModel {
|
||||||
|
const ArgumentsModel._();
|
||||||
|
|
||||||
|
factory ArgumentsModel({
|
||||||
|
@Default(false) bool htpcMode,
|
||||||
|
}) = _ArgumentsModel;
|
||||||
|
|
||||||
|
factory ArgumentsModel.fromArguments(List<String> arguments) {
|
||||||
|
arguments = arguments.map((e) => e.trim()).toList();
|
||||||
|
return ArgumentsModel(
|
||||||
|
htpcMode: arguments.contains('--htpc'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
43
lib/models/settings/arguments_model.freezed.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
// coverage:ignore-file
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'arguments_model.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
|
||||||
|
final _privateConstructorUsedError = UnsupportedError(
|
||||||
|
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models');
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$ArgumentsModel {
|
||||||
|
bool get htpcMode => throw _privateConstructorUsedError;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
class _$ArgumentsModelImpl extends _ArgumentsModel {
|
||||||
|
_$ArgumentsModelImpl({this.htpcMode = false}) : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool htpcMode;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'ArgumentsModel(htpcMode: $htpcMode)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class _ArgumentsModel extends ArgumentsModel {
|
||||||
|
factory _ArgumentsModel({final bool htpcMode}) = _$ArgumentsModelImpl;
|
||||||
|
_ArgumentsModel._() : super._();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get htpcMode;
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@ import 'package:fladder/util/custom_color_themes.dart';
|
||||||
part 'client_settings_model.freezed.dart';
|
part 'client_settings_model.freezed.dart';
|
||||||
part 'client_settings_model.g.dart';
|
part 'client_settings_model.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class ClientSettingsModel with _$ClientSettingsModel {
|
class ClientSettingsModel with _$ClientSettingsModel {
|
||||||
const ClientSettingsModel._();
|
const ClientSettingsModel._();
|
||||||
factory ClientSettingsModel({
|
factory ClientSettingsModel({
|
||||||
|
|
@ -32,7 +32,11 @@ class ClientSettingsModel with _$ClientSettingsModel {
|
||||||
@Default(false) bool mouseDragSupport,
|
@Default(false) bool mouseDragSupport,
|
||||||
@Default(true) bool requireWifi,
|
@Default(true) bool requireWifi,
|
||||||
@Default(false) bool showAllCollectionTypes,
|
@Default(false) bool showAllCollectionTypes,
|
||||||
@Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant,
|
@Default(2) int maxConcurrentDownloads,
|
||||||
|
@Default(DynamicSchemeVariant.rainbow) DynamicSchemeVariant schemeVariant,
|
||||||
|
@Default(true) bool backgroundPosters,
|
||||||
|
@Default(true) bool checkForUpdates,
|
||||||
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize,
|
int? libraryPageSize,
|
||||||
}) = _ClientSettingsModel;
|
}) = _ClientSettingsModel;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,11 @@ mixin _$ClientSettingsModel {
|
||||||
bool get mouseDragSupport => throw _privateConstructorUsedError;
|
bool get mouseDragSupport => throw _privateConstructorUsedError;
|
||||||
bool get requireWifi => throw _privateConstructorUsedError;
|
bool get requireWifi => throw _privateConstructorUsedError;
|
||||||
bool get showAllCollectionTypes => throw _privateConstructorUsedError;
|
bool get showAllCollectionTypes => throw _privateConstructorUsedError;
|
||||||
|
int get maxConcurrentDownloads => throw _privateConstructorUsedError;
|
||||||
DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError;
|
DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError;
|
||||||
|
bool get backgroundPosters => throw _privateConstructorUsedError;
|
||||||
|
bool get checkForUpdates => throw _privateConstructorUsedError;
|
||||||
|
String? get lastViewedUpdate => throw _privateConstructorUsedError;
|
||||||
int? get libraryPageSize => throw _privateConstructorUsedError;
|
int? get libraryPageSize => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
/// Serializes this ClientSettingsModel to a JSON map.
|
/// Serializes this ClientSettingsModel to a JSON map.
|
||||||
|
|
@ -75,7 +79,11 @@ abstract class $ClientSettingsModelCopyWith<$Res> {
|
||||||
bool mouseDragSupport,
|
bool mouseDragSupport,
|
||||||
bool requireWifi,
|
bool requireWifi,
|
||||||
bool showAllCollectionTypes,
|
bool showAllCollectionTypes,
|
||||||
|
int maxConcurrentDownloads,
|
||||||
DynamicSchemeVariant schemeVariant,
|
DynamicSchemeVariant schemeVariant,
|
||||||
|
bool backgroundPosters,
|
||||||
|
bool checkForUpdates,
|
||||||
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize});
|
int? libraryPageSize});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +119,11 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
||||||
Object? mouseDragSupport = null,
|
Object? mouseDragSupport = null,
|
||||||
Object? requireWifi = null,
|
Object? requireWifi = null,
|
||||||
Object? showAllCollectionTypes = null,
|
Object? showAllCollectionTypes = null,
|
||||||
|
Object? maxConcurrentDownloads = null,
|
||||||
Object? schemeVariant = null,
|
Object? schemeVariant = null,
|
||||||
|
Object? backgroundPosters = null,
|
||||||
|
Object? checkForUpdates = null,
|
||||||
|
Object? lastViewedUpdate = freezed,
|
||||||
Object? libraryPageSize = freezed,
|
Object? libraryPageSize = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
|
|
@ -183,10 +195,26 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel>
|
||||||
? _value.showAllCollectionTypes
|
? _value.showAllCollectionTypes
|
||||||
: showAllCollectionTypes // ignore: cast_nullable_to_non_nullable
|
: showAllCollectionTypes // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
maxConcurrentDownloads: null == maxConcurrentDownloads
|
||||||
|
? _value.maxConcurrentDownloads
|
||||||
|
: maxConcurrentDownloads // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
schemeVariant: null == schemeVariant
|
schemeVariant: null == schemeVariant
|
||||||
? _value.schemeVariant
|
? _value.schemeVariant
|
||||||
: schemeVariant // ignore: cast_nullable_to_non_nullable
|
: schemeVariant // ignore: cast_nullable_to_non_nullable
|
||||||
as DynamicSchemeVariant,
|
as DynamicSchemeVariant,
|
||||||
|
backgroundPosters: null == backgroundPosters
|
||||||
|
? _value.backgroundPosters
|
||||||
|
: backgroundPosters // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
checkForUpdates: null == checkForUpdates
|
||||||
|
? _value.checkForUpdates
|
||||||
|
: checkForUpdates // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastViewedUpdate: freezed == lastViewedUpdate
|
||||||
|
? _value.lastViewedUpdate
|
||||||
|
: lastViewedUpdate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
libraryPageSize: freezed == libraryPageSize
|
libraryPageSize: freezed == libraryPageSize
|
||||||
? _value.libraryPageSize
|
? _value.libraryPageSize
|
||||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -221,7 +249,11 @@ abstract class _$$ClientSettingsModelImplCopyWith<$Res>
|
||||||
bool mouseDragSupport,
|
bool mouseDragSupport,
|
||||||
bool requireWifi,
|
bool requireWifi,
|
||||||
bool showAllCollectionTypes,
|
bool showAllCollectionTypes,
|
||||||
|
int maxConcurrentDownloads,
|
||||||
DynamicSchemeVariant schemeVariant,
|
DynamicSchemeVariant schemeVariant,
|
||||||
|
bool backgroundPosters,
|
||||||
|
bool checkForUpdates,
|
||||||
|
String? lastViewedUpdate,
|
||||||
int? libraryPageSize});
|
int? libraryPageSize});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,7 +287,11 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
||||||
Object? mouseDragSupport = null,
|
Object? mouseDragSupport = null,
|
||||||
Object? requireWifi = null,
|
Object? requireWifi = null,
|
||||||
Object? showAllCollectionTypes = null,
|
Object? showAllCollectionTypes = null,
|
||||||
|
Object? maxConcurrentDownloads = null,
|
||||||
Object? schemeVariant = null,
|
Object? schemeVariant = null,
|
||||||
|
Object? backgroundPosters = null,
|
||||||
|
Object? checkForUpdates = null,
|
||||||
|
Object? lastViewedUpdate = freezed,
|
||||||
Object? libraryPageSize = freezed,
|
Object? libraryPageSize = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$ClientSettingsModelImpl(
|
return _then(_$ClientSettingsModelImpl(
|
||||||
|
|
@ -327,10 +363,26 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res>
|
||||||
? _value.showAllCollectionTypes
|
? _value.showAllCollectionTypes
|
||||||
: showAllCollectionTypes // ignore: cast_nullable_to_non_nullable
|
: showAllCollectionTypes // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
maxConcurrentDownloads: null == maxConcurrentDownloads
|
||||||
|
? _value.maxConcurrentDownloads
|
||||||
|
: maxConcurrentDownloads // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,
|
||||||
schemeVariant: null == schemeVariant
|
schemeVariant: null == schemeVariant
|
||||||
? _value.schemeVariant
|
? _value.schemeVariant
|
||||||
: schemeVariant // ignore: cast_nullable_to_non_nullable
|
: schemeVariant // ignore: cast_nullable_to_non_nullable
|
||||||
as DynamicSchemeVariant,
|
as DynamicSchemeVariant,
|
||||||
|
backgroundPosters: null == backgroundPosters
|
||||||
|
? _value.backgroundPosters
|
||||||
|
: backgroundPosters // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
checkForUpdates: null == checkForUpdates
|
||||||
|
? _value.checkForUpdates
|
||||||
|
: checkForUpdates // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
lastViewedUpdate: freezed == lastViewedUpdate
|
||||||
|
? _value.lastViewedUpdate
|
||||||
|
: lastViewedUpdate // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String?,
|
||||||
libraryPageSize: freezed == libraryPageSize
|
libraryPageSize: freezed == libraryPageSize
|
||||||
? _value.libraryPageSize
|
? _value.libraryPageSize
|
||||||
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
: libraryPageSize // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -361,7 +413,11 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
||||||
this.mouseDragSupport = false,
|
this.mouseDragSupport = false,
|
||||||
this.requireWifi = true,
|
this.requireWifi = true,
|
||||||
this.showAllCollectionTypes = false,
|
this.showAllCollectionTypes = false,
|
||||||
this.schemeVariant = DynamicSchemeVariant.tonalSpot,
|
this.maxConcurrentDownloads = 2,
|
||||||
|
this.schemeVariant = DynamicSchemeVariant.rainbow,
|
||||||
|
this.backgroundPosters = true,
|
||||||
|
this.checkForUpdates = true,
|
||||||
|
this.lastViewedUpdate,
|
||||||
this.libraryPageSize})
|
this.libraryPageSize})
|
||||||
: super._();
|
: super._();
|
||||||
|
|
||||||
|
|
@ -418,13 +474,24 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
||||||
final bool showAllCollectionTypes;
|
final bool showAllCollectionTypes;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
|
final int maxConcurrentDownloads;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
final DynamicSchemeVariant schemeVariant;
|
final DynamicSchemeVariant schemeVariant;
|
||||||
@override
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool backgroundPosters;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool checkForUpdates;
|
||||||
|
@override
|
||||||
|
final String? lastViewedUpdate;
|
||||||
|
@override
|
||||||
final int? libraryPageSize;
|
final int? libraryPageSize;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, schemeVariant: $schemeVariant, libraryPageSize: $libraryPageSize)';
|
return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, backgroundPosters: $backgroundPosters, checkForUpdates: $checkForUpdates, lastViewedUpdate: $lastViewedUpdate, libraryPageSize: $libraryPageSize)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -450,78 +517,15 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel
|
||||||
..add(DiagnosticsProperty('requireWifi', requireWifi))
|
..add(DiagnosticsProperty('requireWifi', requireWifi))
|
||||||
..add(
|
..add(
|
||||||
DiagnosticsProperty('showAllCollectionTypes', showAllCollectionTypes))
|
DiagnosticsProperty('showAllCollectionTypes', showAllCollectionTypes))
|
||||||
|
..add(
|
||||||
|
DiagnosticsProperty('maxConcurrentDownloads', maxConcurrentDownloads))
|
||||||
..add(DiagnosticsProperty('schemeVariant', schemeVariant))
|
..add(DiagnosticsProperty('schemeVariant', schemeVariant))
|
||||||
|
..add(DiagnosticsProperty('backgroundPosters', backgroundPosters))
|
||||||
|
..add(DiagnosticsProperty('checkForUpdates', checkForUpdates))
|
||||||
|
..add(DiagnosticsProperty('lastViewedUpdate', lastViewedUpdate))
|
||||||
..add(DiagnosticsProperty('libraryPageSize', libraryPageSize));
|
..add(DiagnosticsProperty('libraryPageSize', libraryPageSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$ClientSettingsModelImpl &&
|
|
||||||
(identical(other.syncPath, syncPath) ||
|
|
||||||
other.syncPath == syncPath) &&
|
|
||||||
(identical(other.position, position) ||
|
|
||||||
other.position == position) &&
|
|
||||||
(identical(other.size, size) || other.size == size) &&
|
|
||||||
(identical(other.timeOut, timeOut) || other.timeOut == timeOut) &&
|
|
||||||
(identical(other.nextUpDateCutoff, nextUpDateCutoff) ||
|
|
||||||
other.nextUpDateCutoff == nextUpDateCutoff) &&
|
|
||||||
(identical(other.themeMode, themeMode) ||
|
|
||||||
other.themeMode == themeMode) &&
|
|
||||||
(identical(other.themeColor, themeColor) ||
|
|
||||||
other.themeColor == themeColor) &&
|
|
||||||
(identical(other.amoledBlack, amoledBlack) ||
|
|
||||||
other.amoledBlack == amoledBlack) &&
|
|
||||||
(identical(other.blurPlaceHolders, blurPlaceHolders) ||
|
|
||||||
other.blurPlaceHolders == blurPlaceHolders) &&
|
|
||||||
(identical(other.blurUpcomingEpisodes, blurUpcomingEpisodes) ||
|
|
||||||
other.blurUpcomingEpisodes == blurUpcomingEpisodes) &&
|
|
||||||
(identical(other.selectedLocale, selectedLocale) ||
|
|
||||||
other.selectedLocale == selectedLocale) &&
|
|
||||||
(identical(other.enableMediaKeys, enableMediaKeys) ||
|
|
||||||
other.enableMediaKeys == enableMediaKeys) &&
|
|
||||||
(identical(other.posterSize, posterSize) ||
|
|
||||||
other.posterSize == posterSize) &&
|
|
||||||
(identical(other.pinchPosterZoom, pinchPosterZoom) ||
|
|
||||||
other.pinchPosterZoom == pinchPosterZoom) &&
|
|
||||||
(identical(other.mouseDragSupport, mouseDragSupport) ||
|
|
||||||
other.mouseDragSupport == mouseDragSupport) &&
|
|
||||||
(identical(other.requireWifi, requireWifi) ||
|
|
||||||
other.requireWifi == requireWifi) &&
|
|
||||||
(identical(other.showAllCollectionTypes, showAllCollectionTypes) ||
|
|
||||||
other.showAllCollectionTypes == showAllCollectionTypes) &&
|
|
||||||
(identical(other.schemeVariant, schemeVariant) ||
|
|
||||||
other.schemeVariant == schemeVariant) &&
|
|
||||||
(identical(other.libraryPageSize, libraryPageSize) ||
|
|
||||||
other.libraryPageSize == libraryPageSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hashAll([
|
|
||||||
runtimeType,
|
|
||||||
syncPath,
|
|
||||||
position,
|
|
||||||
size,
|
|
||||||
timeOut,
|
|
||||||
nextUpDateCutoff,
|
|
||||||
themeMode,
|
|
||||||
themeColor,
|
|
||||||
amoledBlack,
|
|
||||||
blurPlaceHolders,
|
|
||||||
blurUpcomingEpisodes,
|
|
||||||
selectedLocale,
|
|
||||||
enableMediaKeys,
|
|
||||||
posterSize,
|
|
||||||
pinchPosterZoom,
|
|
||||||
mouseDragSupport,
|
|
||||||
requireWifi,
|
|
||||||
showAllCollectionTypes,
|
|
||||||
schemeVariant,
|
|
||||||
libraryPageSize
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// Create a copy of ClientSettingsModel
|
/// Create a copy of ClientSettingsModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
@ -558,7 +562,11 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
|
||||||
final bool mouseDragSupport,
|
final bool mouseDragSupport,
|
||||||
final bool requireWifi,
|
final bool requireWifi,
|
||||||
final bool showAllCollectionTypes,
|
final bool showAllCollectionTypes,
|
||||||
|
final int maxConcurrentDownloads,
|
||||||
final DynamicSchemeVariant schemeVariant,
|
final DynamicSchemeVariant schemeVariant,
|
||||||
|
final bool backgroundPosters,
|
||||||
|
final bool checkForUpdates,
|
||||||
|
final String? lastViewedUpdate,
|
||||||
final int? libraryPageSize}) = _$ClientSettingsModelImpl;
|
final int? libraryPageSize}) = _$ClientSettingsModelImpl;
|
||||||
_ClientSettingsModel._() : super._();
|
_ClientSettingsModel._() : super._();
|
||||||
|
|
||||||
|
|
@ -601,8 +609,16 @@ abstract class _ClientSettingsModel extends ClientSettingsModel {
|
||||||
@override
|
@override
|
||||||
bool get showAllCollectionTypes;
|
bool get showAllCollectionTypes;
|
||||||
@override
|
@override
|
||||||
|
int get maxConcurrentDownloads;
|
||||||
|
@override
|
||||||
DynamicSchemeVariant get schemeVariant;
|
DynamicSchemeVariant get schemeVariant;
|
||||||
@override
|
@override
|
||||||
|
bool get backgroundPosters;
|
||||||
|
@override
|
||||||
|
bool get checkForUpdates;
|
||||||
|
@override
|
||||||
|
String? get lastViewedUpdate;
|
||||||
|
@override
|
||||||
int? get libraryPageSize;
|
int? get libraryPageSize;
|
||||||
|
|
||||||
/// Create a copy of ClientSettingsModel
|
/// Create a copy of ClientSettingsModel
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,14 @@ _$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson(
|
||||||
mouseDragSupport: json['mouseDragSupport'] as bool? ?? false,
|
mouseDragSupport: json['mouseDragSupport'] as bool? ?? false,
|
||||||
requireWifi: json['requireWifi'] as bool? ?? true,
|
requireWifi: json['requireWifi'] as bool? ?? true,
|
||||||
showAllCollectionTypes: json['showAllCollectionTypes'] as bool? ?? false,
|
showAllCollectionTypes: json['showAllCollectionTypes'] as bool? ?? false,
|
||||||
|
maxConcurrentDownloads:
|
||||||
|
(json['maxConcurrentDownloads'] as num?)?.toInt() ?? 2,
|
||||||
schemeVariant: $enumDecodeNullable(
|
schemeVariant: $enumDecodeNullable(
|
||||||
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
|
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
|
||||||
DynamicSchemeVariant.tonalSpot,
|
DynamicSchemeVariant.rainbow,
|
||||||
|
backgroundPosters: json['backgroundPosters'] as bool? ?? true,
|
||||||
|
checkForUpdates: json['checkForUpdates'] as bool? ?? true,
|
||||||
|
lastViewedUpdate: json['lastViewedUpdate'] as String?,
|
||||||
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
libraryPageSize: (json['libraryPageSize'] as num?)?.toInt(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -62,7 +67,11 @@ Map<String, dynamic> _$$ClientSettingsModelImplToJson(
|
||||||
'mouseDragSupport': instance.mouseDragSupport,
|
'mouseDragSupport': instance.mouseDragSupport,
|
||||||
'requireWifi': instance.requireWifi,
|
'requireWifi': instance.requireWifi,
|
||||||
'showAllCollectionTypes': instance.showAllCollectionTypes,
|
'showAllCollectionTypes': instance.showAllCollectionTypes,
|
||||||
|
'maxConcurrentDownloads': instance.maxConcurrentDownloads,
|
||||||
'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,
|
'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,
|
||||||
|
'backgroundPosters': instance.backgroundPosters,
|
||||||
|
'checkForUpdates': instance.checkForUpdates,
|
||||||
|
'lastViewedUpdate': instance.lastViewedUpdate,
|
||||||
'libraryPageSize': instance.libraryPageSize,
|
'libraryPageSize': instance.libraryPageSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,13 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
import 'package:fladder/util/adaptive_layout/adaptive_layout.dart';
|
||||||
import 'package:fladder/util/localization_helper.dart';
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
part 'home_settings_model.freezed.dart';
|
part 'home_settings_model.freezed.dart';
|
||||||
part 'home_settings_model.g.dart';
|
part 'home_settings_model.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class HomeSettingsModel with _$HomeSettingsModel {
|
class HomeSettingsModel with _$HomeSettingsModel {
|
||||||
factory HomeSettingsModel({
|
factory HomeSettingsModel({
|
||||||
@Default({...LayoutMode.values}) Set<LayoutMode> screenLayouts,
|
@Default({...LayoutMode.values}) Set<LayoutMode> screenLayouts,
|
||||||
|
|
@ -36,42 +37,6 @@ T selectAvailableOrSmaller<T>(T value, Set<T> availableOptions, List<T> allOptio
|
||||||
return availableOptions.first;
|
return availableOptions.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ViewSize {
|
|
||||||
phone,
|
|
||||||
tablet,
|
|
||||||
desktop;
|
|
||||||
|
|
||||||
const ViewSize();
|
|
||||||
|
|
||||||
String label(BuildContext context) => switch (this) {
|
|
||||||
ViewSize.phone => context.localized.phone,
|
|
||||||
ViewSize.tablet => context.localized.tablet,
|
|
||||||
ViewSize.desktop => context.localized.desktop,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator >(ViewSize other) => index > other.index;
|
|
||||||
bool operator >=(ViewSize other) => index >= other.index;
|
|
||||||
bool operator <(ViewSize other) => index < other.index;
|
|
||||||
bool operator <=(ViewSize other) => index <= other.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LayoutMode {
|
|
||||||
single,
|
|
||||||
dual;
|
|
||||||
|
|
||||||
const LayoutMode();
|
|
||||||
|
|
||||||
String label(BuildContext context) => switch (this) {
|
|
||||||
LayoutMode.single => context.localized.layoutModeSingle,
|
|
||||||
LayoutMode.dual => context.localized.layoutModeDual,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator >(ViewSize other) => index > other.index;
|
|
||||||
bool operator >=(ViewSize other) => index >= other.index;
|
|
||||||
bool operator <(ViewSize other) => index < other.index;
|
|
||||||
bool operator <=(ViewSize other) => index <= other.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum HomeBanner {
|
enum HomeBanner {
|
||||||
hide,
|
hide,
|
||||||
carousel,
|
carousel,
|
||||||
|
|
|
||||||
|
|
@ -205,32 +205,6 @@ class _$HomeSettingsModelImpl implements _HomeSettingsModel {
|
||||||
return 'HomeSettingsModel(screenLayouts: $screenLayouts, layoutStates: $layoutStates, homeBanner: $homeBanner, carouselSettings: $carouselSettings, nextUp: $nextUp)';
|
return 'HomeSettingsModel(screenLayouts: $screenLayouts, layoutStates: $layoutStates, homeBanner: $homeBanner, carouselSettings: $carouselSettings, nextUp: $nextUp)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$HomeSettingsModelImpl &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._screenLayouts, _screenLayouts) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._layoutStates, _layoutStates) &&
|
|
||||||
(identical(other.homeBanner, homeBanner) ||
|
|
||||||
other.homeBanner == homeBanner) &&
|
|
||||||
(identical(other.carouselSettings, carouselSettings) ||
|
|
||||||
other.carouselSettings == carouselSettings) &&
|
|
||||||
(identical(other.nextUp, nextUp) || other.nextUp == nextUp));
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
const DeepCollectionEquality().hash(_screenLayouts),
|
|
||||||
const DeepCollectionEquality().hash(_layoutStates),
|
|
||||||
homeBanner,
|
|
||||||
carouselSettings,
|
|
||||||
nextUp);
|
|
||||||
|
|
||||||
/// Create a copy of HomeSettingsModel
|
/// Create a copy of HomeSettingsModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import 'package:fladder/util/localization_helper.dart';
|
||||||
part 'video_player_settings.freezed.dart';
|
part 'video_player_settings.freezed.dart';
|
||||||
part 'video_player_settings.g.dart';
|
part 'video_player_settings.g.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
||||||
const VideoPlayerSettingsModel._();
|
const VideoPlayerSettingsModel._();
|
||||||
|
|
||||||
|
|
@ -43,7 +43,10 @@ class VideoPlayerSettingsModel with _$VideoPlayerSettingsModel {
|
||||||
PlayerOptions get wantedPlayer => playerOptions ?? PlayerOptions.platformDefaults;
|
PlayerOptions get wantedPlayer => playerOptions ?? PlayerOptions.platformDefaults;
|
||||||
|
|
||||||
bool playerSame(VideoPlayerSettingsModel other) {
|
bool playerSame(VideoPlayerSettingsModel other) {
|
||||||
return other.hardwareAccel == hardwareAccel && other.useLibass == useLibass && other.bufferSize == bufferSize && other.wantedPlayer == wantedPlayer;
|
return other.hardwareAccel == hardwareAccel &&
|
||||||
|
other.useLibass == useLibass &&
|
||||||
|
other.bufferSize == bufferSize &&
|
||||||
|
other.wantedPlayer == wantedPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,17 @@ import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:background_downloader/background_downloader.dart';
|
import 'package:background_downloader/background_downloader.dart';
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:fladder/models/item_base_model.dart';
|
import 'package:fladder/models/item_base_model.dart';
|
||||||
import 'package:fladder/models/items/chapters_model.dart';
|
import 'package:fladder/models/items/chapters_model.dart';
|
||||||
import 'package:fladder/models/items/images_models.dart';
|
import 'package:fladder/models/items/images_models.dart';
|
||||||
import 'package:fladder/models/items/media_segments_model.dart';
|
|
||||||
import 'package:fladder/models/items/item_shared_models.dart';
|
import 'package:fladder/models/items/item_shared_models.dart';
|
||||||
|
import 'package:fladder/models/items/media_segments_model.dart';
|
||||||
import 'package:fladder/models/items/media_streams_model.dart';
|
import 'package:fladder/models/items/media_streams_model.dart';
|
||||||
import 'package:fladder/models/items/trick_play_model.dart';
|
import 'package:fladder/models/items/trick_play_model.dart';
|
||||||
import 'package:fladder/models/syncing/i_synced_item.dart';
|
import 'package:fladder/models/syncing/i_synced_item.dart';
|
||||||
|
|
@ -24,7 +24,7 @@ import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
part 'sync_item.freezed.dart';
|
part 'sync_item.freezed.dart';
|
||||||
|
|
||||||
@freezed
|
@Freezed(copyWith: true)
|
||||||
class SyncedItem with _$SyncedItem {
|
class SyncedItem with _$SyncedItem {
|
||||||
const SyncedItem._();
|
const SyncedItem._();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,6 @@ abstract class $SyncedItemCopyWith<$Res> {
|
||||||
List<SubStreamModel> subtitles,
|
List<SubStreamModel> subtitles,
|
||||||
@UserDataJsonSerializer() UserData? userData});
|
@UserDataJsonSerializer() UserData? userData});
|
||||||
|
|
||||||
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments;
|
|
||||||
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,20 +161,6 @@ class _$SyncedItemCopyWithImpl<$Res, $Val extends SyncedItem>
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a copy of SyncedItem
|
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
|
||||||
@override
|
|
||||||
@pragma('vm:prefer-inline')
|
|
||||||
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments {
|
|
||||||
if (_value.mediaSegments == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $MediaSegmentsModelCopyWith<$Res>(_value.mediaSegments!, (value) {
|
|
||||||
return _then(_value.copyWith(mediaSegments: value) as $Val);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a copy of SyncedItem
|
/// Create a copy of SyncedItem
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override
|
@override
|
||||||
|
|
@ -216,8 +201,6 @@ abstract class _$$SyncItemImplCopyWith<$Res>
|
||||||
List<SubStreamModel> subtitles,
|
List<SubStreamModel> subtitles,
|
||||||
@UserDataJsonSerializer() UserData? userData});
|
@UserDataJsonSerializer() UserData? userData});
|
||||||
|
|
||||||
@override
|
|
||||||
$MediaSegmentsModelCopyWith<$Res>? get mediaSegments;
|
|
||||||
@override
|
@override
|
||||||
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
$TrickPlayModelCopyWith<$Res>? get fTrickPlayModel;
|
||||||
}
|
}
|
||||||
|
|
@ -392,57 +375,6 @@ class _$SyncItemImpl extends _SyncItem {
|
||||||
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, mediaSegments: $mediaSegments, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
|
return 'SyncedItem(id: $id, syncing: $syncing, parentId: $parentId, userId: $userId, path: $path, markedForDelete: $markedForDelete, sortName: $sortName, fileSize: $fileSize, videoFileName: $videoFileName, mediaSegments: $mediaSegments, fTrickPlayModel: $fTrickPlayModel, fImages: $fImages, fChapters: $fChapters, subtitles: $subtitles, userData: $userData)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$SyncItemImpl &&
|
|
||||||
(identical(other.id, id) || other.id == id) &&
|
|
||||||
(identical(other.syncing, syncing) || other.syncing == syncing) &&
|
|
||||||
(identical(other.parentId, parentId) ||
|
|
||||||
other.parentId == parentId) &&
|
|
||||||
(identical(other.userId, userId) || other.userId == userId) &&
|
|
||||||
(identical(other.path, path) || other.path == path) &&
|
|
||||||
(identical(other.markedForDelete, markedForDelete) ||
|
|
||||||
other.markedForDelete == markedForDelete) &&
|
|
||||||
(identical(other.sortName, sortName) ||
|
|
||||||
other.sortName == sortName) &&
|
|
||||||
(identical(other.fileSize, fileSize) ||
|
|
||||||
other.fileSize == fileSize) &&
|
|
||||||
(identical(other.videoFileName, videoFileName) ||
|
|
||||||
other.videoFileName == videoFileName) &&
|
|
||||||
(identical(other.mediaSegments, mediaSegments) ||
|
|
||||||
other.mediaSegments == mediaSegments) &&
|
|
||||||
(identical(other.fTrickPlayModel, fTrickPlayModel) ||
|
|
||||||
other.fTrickPlayModel == fTrickPlayModel) &&
|
|
||||||
(identical(other.fImages, fImages) || other.fImages == fImages) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._fChapters, _fChapters) &&
|
|
||||||
const DeepCollectionEquality()
|
|
||||||
.equals(other._subtitles, _subtitles) &&
|
|
||||||
(identical(other.userData, userData) ||
|
|
||||||
other.userData == userData));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode => Object.hash(
|
|
||||||
runtimeType,
|
|
||||||
id,
|
|
||||||
syncing,
|
|
||||||
parentId,
|
|
||||||
userId,
|
|
||||||
path,
|
|
||||||
markedForDelete,
|
|
||||||
sortName,
|
|
||||||
fileSize,
|
|
||||||
videoFileName,
|
|
||||||
mediaSegments,
|
|
||||||
fTrickPlayModel,
|
|
||||||
fImages,
|
|
||||||
const DeepCollectionEquality().hash(_fChapters),
|
|
||||||
const DeepCollectionEquality().hash(_subtitles),
|
|
||||||
userData);
|
|
||||||
|
|
||||||
/// Create a copy of SyncedItem
|
/// Create a copy of SyncedItem
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import 'package:fladder/models/syncing/sync_item.dart';
|
||||||
|
|
||||||
part 'sync_settings_model.freezed.dart';
|
part 'sync_settings_model.freezed.dart';
|
||||||
|
|
||||||
@Freezed(toJson: false, fromJson: false)
|
@Freezed(toJson: false, fromJson: false, copyWith: true)
|
||||||
class SyncSettingsModel with _$SyncSettingsModel {
|
class SyncSettingsModel with _$SyncSettingsModel {
|
||||||
const SyncSettingsModel._();
|
const SyncSettingsModel._();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,18 +116,6 @@ class _$SyncSettignsModelImpl extends _SyncSettignsModel {
|
||||||
return 'SyncSettingsModel(items: $items)';
|
return 'SyncSettingsModel(items: $items)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return identical(this, other) ||
|
|
||||||
(other.runtimeType == runtimeType &&
|
|
||||||
other is _$SyncSettignsModelImpl &&
|
|
||||||
const DeepCollectionEquality().equals(other._items, _items));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
Object.hash(runtimeType, const DeepCollectionEquality().hash(_items));
|
|
||||||
|
|
||||||
/// Create a copy of SyncSettingsModel
|
/// Create a copy of SyncSettingsModel
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
// ignore_for_file: public_member_api_docs, sort_constructors_first
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:iconsax_plus/iconsax_plus.dart';
|
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:iconsax_plus/iconsax_plus.dart';
|
||||||
|
|
||||||
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
import 'package:fladder/jellyfin/jellyfin_open_api.swagger.dart';
|
||||||
import 'package:fladder/models/items/chapters_model.dart';
|
import 'package:fladder/models/items/chapters_model.dart';
|
||||||
|
|
@ -12,6 +12,7 @@ import 'package:fladder/models/items/media_segments_model.dart';
|
||||||
import 'package:fladder/models/items/media_streams_model.dart';
|
import 'package:fladder/models/items/media_streams_model.dart';
|
||||||
import 'package:fladder/models/syncing/sync_item.dart';
|
import 'package:fladder/models/syncing/sync_item.dart';
|
||||||
import 'package:fladder/providers/user_provider.dart';
|
import 'package:fladder/providers/user_provider.dart';
|
||||||
|
import 'package:fladder/util/localization_helper.dart';
|
||||||
|
|
||||||
enum PlaybackType {
|
enum PlaybackType {
|
||||||
directStream,
|
directStream,
|
||||||
|
|
@ -24,16 +25,11 @@ enum PlaybackType {
|
||||||
PlaybackType.transcode => IconsaxPlusLinear.convert,
|
PlaybackType.transcode => IconsaxPlusLinear.convert,
|
||||||
};
|
};
|
||||||
|
|
||||||
String get name {
|
String name(BuildContext context) => switch (this) {
|
||||||
switch (this) {
|
PlaybackType.directStream => context.localized.playbackTypeDirect,
|
||||||
case PlaybackType.directStream:
|
PlaybackType.offline => context.localized.playbackTypeOffline,
|
||||||
return "Direct";
|
PlaybackType.transcode => context.localized.playbackTypeTranscode
|
||||||
case PlaybackType.offline:
|
};
|
||||||
return "Offline";
|
|
||||||
case PlaybackType.transcode:
|
|
||||||
return "Transcoding";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class VideoPlayback {
|
class VideoPlayback {
|
||||||
|
|
|
||||||